This is an automated email from Gerrit. "Tarek BOCHKATI <[email protected]>" just uploaded a new patch set to Gerrit, which you can find at https://review.openocd.org/c/openocd/+/6688
-- gerrit commit cd77d92fcefc4378446aca78e1e0ef923cfca551 Author: Tarek BOCHKATI <[email protected]> Date: Thu Nov 11 23:58:54 2021 +0100 stlink: isolate usb and tcp backends' code this is an absolute move of code without unnecessary modification. the driver is now split into: - stlink_usb.c : contains STLink USB backend code - stlink_tcp.c : contains ST-Link TCP server backend code this change replaces https://review.openocd.org/c/openocd/+/5652 Change-Id: I8b8ea0fea3a5f0b24805bbfdf842444e52938aa0 Signed-off-by: Tarek BOCHKATI <[email protected]> diff --git a/src/jtag/drivers/Makefile.am b/src/jtag/drivers/Makefile.am index da60f366e..f4e3e6461 100644 --- a/src/jtag/drivers/Makefile.am +++ b/src/jtag/drivers/Makefile.am @@ -136,6 +136,8 @@ DRIVERFILES += %D%/remote_bitbang.c endif if HLADAPTER_STLINK DRIVERFILES += %D%/stlink_usb.c +DRIVERFILES += %D%/stlink/stlink_tcp.c +DRIVERFILES += %D%/stlink/stlink_usb.c endif if HLADAPTER_ICDI DRIVERFILES += %D%/ti_icdi_usb.c @@ -197,6 +199,7 @@ DRIVERHEADERS = \ %D%/rlink_dtc_cmd.h \ %D%/rlink_ep1_cmd.h \ %D%/rlink_st7.h \ + %D%/stlink/stlink.h \ %D%/versaloon/usbtoxxx/usbtoxxx.h \ %D%/versaloon/usbtoxxx/usbtoxxx_internal.h \ %D%/versaloon/versaloon.h \ diff --git a/src/jtag/drivers/stlink/stlink.h b/src/jtag/drivers/stlink/stlink.h new file mode 100644 index 000000000..6e37e98d5 --- /dev/null +++ b/src/jtag/drivers/stlink/stlink.h @@ -0,0 +1,465 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2020 by Antonio Borneo <[email protected]> + * Copyright (C) 2020 by Tarek BOCHKATI <[email protected]> + * Copyright (C) 2017 by Ake Rehnman <[email protected]> + * Copyright (C) 2012 by Mathias Kuester <[email protected]> + * Copyright (C) 2012 by Spencer Oliver <[email protected]> + * + * This code is based on https://github.com/texane/stlink + */ + +#ifndef OPENOCD_JTAG_DRIVERS_STLINK_STLINK_H +#define OPENOCD_JTAG_DRIVERS_STLINK_STLINK_H + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <jtag/hla/hla_layout.h> +#include <jtag/hla/hla_transport.h> +#include <jtag/hla/hla_interface.h> + +#define STLINK_SERIAL_LEN 24 + +#define ENDPOINT_IN 0x80 +#define ENDPOINT_OUT 0x00 + +#define STLINK_WRITE_TIMEOUT 1000 +#define STLINK_READ_TIMEOUT 1000 + +#define STLINK_RX_EP (1|ENDPOINT_IN) +#define STLINK_TX_EP (2|ENDPOINT_OUT) +#define STLINK_TRACE_EP (3|ENDPOINT_IN) + +#define STLINK_V2_1_TX_EP (1|ENDPOINT_OUT) +#define STLINK_V2_1_TRACE_EP (2|ENDPOINT_IN) + +#define STLINK_SG_SIZE (31) +#define STLINK_DATA_SIZE (6144) +#define STLINK_CMD_SIZE_V2 (16) +#define STLINK_CMD_SIZE_V1 (10) + +#define STLINK_V1_PID (0x3744) +#define STLINK_V2_PID (0x3748) +#define STLINK_V2_1_PID (0x374B) +#define STLINK_V2_1_NO_MSD_PID (0x3752) +#define STLINK_V3_USBLOADER_PID (0x374D) +#define STLINK_V3E_PID (0x374E) +#define STLINK_V3S_PID (0x374F) +#define STLINK_V3_2VCP_PID (0x3753) +#define STLINK_V3E_NO_MSD_PID (0x3754) + +/* + * ST-Link/V1, ST-Link/V2 and ST-Link/V2.1 are full-speed USB devices and + * this limits the bulk packet size and the 8bit read/writes to max 64 bytes. + * STLINK-V3 is a high speed USB 2.0 and the limit is 512 bytes from FW V3J6. + * + * For 16 and 32bit read/writes stlink handles USB packet split and the limit + * is the internal buffer size of 6144 bytes. + * TODO: override ADIv5 layer's tar_autoincr_block that limits the transfer + * to 1024 or 4096 bytes + */ +#define STLINK_MAX_RW8 (64) +#define STLINKV3_MAX_RW8 (512) +#define STLINK_MAX_RW16_32 STLINK_DATA_SIZE +#define STLINK_SWIM_DATA_SIZE STLINK_DATA_SIZE + +/* "WAIT" responses will be retried (with exponential backoff) at + * most this many times before failing to caller. + */ +#define MAX_WAIT_RETRIES 8 + +/* HLA is currently limited at AP#0 and no control on CSW */ +#define STLINK_HLA_AP_NUM 0 +#define STLINK_HLA_CSW 0 + +#define STLINK_SWIM_ERR_OK 0x00 +#define STLINK_SWIM_BUSY 0x01 +#define STLINK_DEBUG_ERR_OK 0x80 +#define STLINK_DEBUG_ERR_FAULT 0x81 +#define STLINK_SWD_AP_WAIT 0x10 +#define STLINK_SWD_AP_FAULT 0x11 +#define STLINK_SWD_AP_ERROR 0x12 +#define STLINK_SWD_AP_PARITY_ERROR 0x13 +#define STLINK_JTAG_GET_IDCODE_ERROR 0x09 +#define STLINK_JTAG_WRITE_ERROR 0x0c +#define STLINK_JTAG_WRITE_VERIF_ERROR 0x0d +#define STLINK_SWD_DP_WAIT 0x14 +#define STLINK_SWD_DP_FAULT 0x15 +#define STLINK_SWD_DP_ERROR 0x16 +#define STLINK_SWD_DP_PARITY_ERROR 0x17 + +#define STLINK_SWD_AP_WDATA_ERROR 0x18 +#define STLINK_SWD_AP_STICKY_ERROR 0x19 +#define STLINK_SWD_AP_STICKYORUN_ERROR 0x1a + +#define STLINK_BAD_AP_ERROR 0x1d + +#define STLINK_CORE_RUNNING 0x80 +#define STLINK_CORE_HALTED 0x81 +#define STLINK_CORE_STAT_UNKNOWN -1 + +#define STLINK_GET_VERSION 0xF1 +#define STLINK_DEBUG_COMMAND 0xF2 +#define STLINK_DFU_COMMAND 0xF3 +#define STLINK_SWIM_COMMAND 0xF4 +#define STLINK_GET_CURRENT_MODE 0xF5 +#define STLINK_GET_TARGET_VOLTAGE 0xF7 + +#define STLINK_DEV_DFU_MODE 0x00 +#define STLINK_DEV_MASS_MODE 0x01 +#define STLINK_DEV_DEBUG_MODE 0x02 +#define STLINK_DEV_SWIM_MODE 0x03 +#define STLINK_DEV_BOOTLOADER_MODE 0x04 +#define STLINK_DEV_UNKNOWN_MODE -1 + +#define STLINK_DFU_EXIT 0x07 + +/* + STLINK_SWIM_ENTER_SEQ + 1.3ms low then 750Hz then 1.5kHz + + STLINK_SWIM_GEN_RST + STM8 DM pulls reset pin low 50us + + STLINK_SWIM_SPEED + uint8_t (0=low|1=high) + + STLINK_SWIM_WRITEMEM + uint16_t length + uint32_t address + + STLINK_SWIM_RESET + send synchronization seq (16us low, response 64 clocks low) +*/ +#define STLINK_SWIM_ENTER 0x00 +#define STLINK_SWIM_EXIT 0x01 +#define STLINK_SWIM_READ_CAP 0x02 +#define STLINK_SWIM_SPEED 0x03 +#define STLINK_SWIM_ENTER_SEQ 0x04 +#define STLINK_SWIM_GEN_RST 0x05 +#define STLINK_SWIM_RESET 0x06 +#define STLINK_SWIM_ASSERT_RESET 0x07 +#define STLINK_SWIM_DEASSERT_RESET 0x08 +#define STLINK_SWIM_READSTATUS 0x09 +#define STLINK_SWIM_WRITEMEM 0x0a +#define STLINK_SWIM_READMEM 0x0b +#define STLINK_SWIM_READBUF 0x0c + +#define STLINK_DEBUG_GETSTATUS 0x01 +#define STLINK_DEBUG_FORCEDEBUG 0x02 +#define STLINK_DEBUG_APIV1_RESETSYS 0x03 +#define STLINK_DEBUG_APIV1_READALLREGS 0x04 +#define STLINK_DEBUG_APIV1_READREG 0x05 +#define STLINK_DEBUG_APIV1_WRITEREG 0x06 +#define STLINK_DEBUG_READMEM_32BIT 0x07 +#define STLINK_DEBUG_WRITEMEM_32BIT 0x08 +#define STLINK_DEBUG_RUNCORE 0x09 +#define STLINK_DEBUG_STEPCORE 0x0a +#define STLINK_DEBUG_APIV1_SETFP 0x0b +#define STLINK_DEBUG_READMEM_8BIT 0x0c +#define STLINK_DEBUG_WRITEMEM_8BIT 0x0d +#define STLINK_DEBUG_APIV1_CLEARFP 0x0e +#define STLINK_DEBUG_APIV1_WRITEDEBUGREG 0x0f +#define STLINK_DEBUG_APIV1_SETWATCHPOINT 0x10 + +#define STLINK_DEBUG_ENTER_JTAG_RESET 0x00 +#define STLINK_DEBUG_ENTER_SWD_NO_RESET 0xa3 +#define STLINK_DEBUG_ENTER_JTAG_NO_RESET 0xa4 + +#define STLINK_DEBUG_APIV1_ENTER 0x20 +#define STLINK_DEBUG_EXIT 0x21 +#define STLINK_DEBUG_READCOREID 0x22 + +#define STLINK_DEBUG_APIV2_ENTER 0x30 +#define STLINK_DEBUG_APIV2_READ_IDCODES 0x31 +#define STLINK_DEBUG_APIV2_RESETSYS 0x32 +#define STLINK_DEBUG_APIV2_READREG 0x33 +#define STLINK_DEBUG_APIV2_WRITEREG 0x34 +#define STLINK_DEBUG_APIV2_WRITEDEBUGREG 0x35 +#define STLINK_DEBUG_APIV2_READDEBUGREG 0x36 + +#define STLINK_DEBUG_APIV2_READALLREGS 0x3A +#define STLINK_DEBUG_APIV2_GETLASTRWSTATUS 0x3B +#define STLINK_DEBUG_APIV2_DRIVE_NRST 0x3C + +#define STLINK_DEBUG_APIV2_GETLASTRWSTATUS2 0x3E + +#define STLINK_DEBUG_APIV2_START_TRACE_RX 0x40 +#define STLINK_DEBUG_APIV2_STOP_TRACE_RX 0x41 +#define STLINK_DEBUG_APIV2_GET_TRACE_NB 0x42 +#define STLINK_DEBUG_APIV2_SWD_SET_FREQ 0x43 +#define STLINK_DEBUG_APIV2_JTAG_SET_FREQ 0x44 +#define STLINK_DEBUG_APIV2_READ_DAP_REG 0x45 +#define STLINK_DEBUG_APIV2_WRITE_DAP_REG 0x46 +#define STLINK_DEBUG_APIV2_READMEM_16BIT 0x47 +#define STLINK_DEBUG_APIV2_WRITEMEM_16BIT 0x48 + +#define STLINK_DEBUG_APIV2_INIT_AP 0x4B +#define STLINK_DEBUG_APIV2_CLOSE_AP_DBG 0x4C + +#define STLINK_DEBUG_WRITEMEM_32BIT_NO_ADDR_INC 0x50 +#define STLINK_DEBUG_APIV2_RW_MISC_OUT 0x51 +#define STLINK_DEBUG_APIV2_RW_MISC_IN 0x52 + +#define STLINK_DEBUG_READMEM_32BIT_NO_ADDR_INC 0x54 + +#define STLINK_APIV3_SET_COM_FREQ 0x61 +#define STLINK_APIV3_GET_COM_FREQ 0x62 + +#define STLINK_APIV3_GET_VERSION_EX 0xFB + +#define STLINK_DEBUG_APIV2_DRIVE_NRST_LOW 0x00 +#define STLINK_DEBUG_APIV2_DRIVE_NRST_HIGH 0x01 +#define STLINK_DEBUG_APIV2_DRIVE_NRST_PULSE 0x02 + +#define STLINK_DEBUG_PORT_ACCESS 0xffff + +#define STLINK_TRACE_SIZE 4096 +#define STLINK_TRACE_MAX_HZ 2000000 +#define STLINK_V3_TRACE_MAX_HZ 24000000 + +#define STLINK_V3_MAX_FREQ_NB 10 + +#define REQUEST_SENSE 0x03 +#define REQUEST_SENSE_LENGTH 18 + +/* STLINK TCP commands */ +#define STLINK_TCP_CMD_REFRESH_DEVICE_LIST 0x00 +#define STLINK_TCP_CMD_GET_NB_DEV 0x01 +#define STLINK_TCP_CMD_GET_DEV_INFO 0x02 +#define STLINK_TCP_CMD_OPEN_DEV 0x03 +#define STLINK_TCP_CMD_CLOSE_DEV 0x04 +#define STLINK_TCP_CMD_SEND_USB_CMD 0x05 +#define STLINK_TCP_CMD_GET_SERVER_VERSION 0x06 +#define STLINK_TCP_CMD_GET_NB_OF_DEV_CLIENTS 0x07 + +/* STLINK TCP constants */ +#define OPENOCD_STLINK_TCP_API_VERSION 1 +#define STLINK_TCP_REQUEST_WRITE 0 +#define STLINK_TCP_REQUEST_READ 1 +#define STLINK_TCP_REQUEST_READ_SWO 3 +#define STLINK_TCP_SS_SIZE 4 +#define STLINK_TCP_USB_CMD_SIZE 32 +#define STLINK_TCP_SERIAL_SIZE 32 +#define STLINK_TCP_SEND_BUFFER_SIZE 10240 +#define STLINK_TCP_RECV_BUFFER_SIZE 10240 + +/* STLINK TCP command status */ +#define STLINK_TCP_SS_OK 0x00000001 +#define STLINK_TCP_SS_MEMORY_PROBLEM 0x00001000 +#define STLINK_TCP_SS_TIMEOUT 0x00001001 +#define STLINK_TCP_SS_BAD_PARAMETER 0x00001002 +#define STLINK_TCP_SS_OPEN_ERR 0x00001003 +#define STLINK_TCP_SS_TRUNCATED_DATA 0x00001052 +#define STLINK_TCP_SS_CMD_NOT_AVAILABLE 0x00001053 +#define STLINK_TCP_SS_TCP_ERROR 0x00002001 +#define STLINK_TCP_SS_TCP_CANT_CONNECT 0x00002002 +#define STLINK_TCP_SS_WIN32_ERROR 0x00010000 + +/* + * Map the relevant features, quirks and workaround for specific firmware + * version of stlink + */ +#define STLINK_F_HAS_TRACE BIT(0) /* v2>=j13 || v3 */ +#define STLINK_F_HAS_GETLASTRWSTATUS2 BIT(1) /* v2>=j15 || v3 */ +#define STLINK_F_HAS_SWD_SET_FREQ BIT(2) /* v2>=j22 */ +#define STLINK_F_HAS_JTAG_SET_FREQ BIT(3) /* v2>=j24 */ +#define STLINK_F_QUIRK_JTAG_DP_READ BIT(4) /* v2>=j24 && v2<j32 */ +#define STLINK_F_HAS_DAP_REG BIT(5) /* v2>=j24 || v3 */ +#define STLINK_F_HAS_MEM_16BIT BIT(6) /* v2>=j26 || v3 */ +#define STLINK_F_HAS_AP_INIT BIT(7) /* v2>=j28 || v3 */ +#define STLINK_F_FIX_CLOSE_AP BIT(8) /* v2>=j29 || v3 */ +#define STLINK_F_HAS_DPBANKSEL BIT(9) /* v2>=j32 || v3>=j2 */ +#define STLINK_F_HAS_RW8_512BYTES BIT(10) /* v3>=j6 */ + +/* aliases */ +#define STLINK_F_HAS_TARGET_VOLT STLINK_F_HAS_TRACE +#define STLINK_F_HAS_FPU_REG STLINK_F_HAS_GETLASTRWSTATUS2 +#define STLINK_F_HAS_MEM_WR_NO_INC STLINK_F_HAS_MEM_16BIT +#define STLINK_F_HAS_MEM_RD_NO_INC STLINK_F_HAS_DPBANKSEL +#define STLINK_F_HAS_RW_MISC STLINK_F_HAS_DPBANKSEL +#define STLINK_F_HAS_CSW STLINK_F_HAS_DPBANKSEL + +#define STLINK_REGSEL_IS_FPU(x) ((x) > 0x1F) + +enum stlink_jtag_api_version { + STLINK_JTAG_API_V1 = 1, + STLINK_JTAG_API_V2, + STLINK_JTAG_API_V3, +}; + +enum stlink_mode { + STLINK_MODE_UNKNOWN = 0, + STLINK_MODE_DFU, + STLINK_MODE_MASS, + STLINK_MODE_DEBUG_JTAG, + STLINK_MODE_DEBUG_SWD, + STLINK_MODE_DEBUG_SWIM +}; + +/** */ +struct stlink_usb_version { + /** */ + int stlink; + /** */ + int jtag; + /** */ + int swim; + /** jtag api version supported */ + enum stlink_jtag_api_version jtag_api; + /** one bit for each feature supported. See macros STLINK_F_* */ + uint32_t flags; +}; + +struct stlink_usb_priv_s { + /** */ + struct libusb_device_handle *fd; + /** */ + struct libusb_transfer *trans; +}; + +struct stlink_tcp_priv_s { + /** */ + int fd; + /** */ + bool connected; + /** */ + uint32_t device_id; + /** */ + uint32_t connect_id; + /** */ + uint8_t *send_buf; + /** */ + uint8_t *recv_buf; +}; + +struct stlink_backend_s { + /** */ + int (*open)(void *handle, struct hl_interface_param_s *param); + /** */ + int (*close)(void *handle); + /** */ + int (*xfer_noerrcheck)(void *handle, const uint8_t *buf, int size); + /** */ + int (*read_trace)(void *handle, const uint8_t *buf, int size); +}; + +/* TODO: make queue size dynamic */ +/* TODO: don't allocate queue for HLA */ +#define MAX_QUEUE_DEPTH (4096) + +enum queue_cmd { + CMD_DP_READ = 1, + CMD_DP_WRITE, + + CMD_AP_READ, + CMD_AP_WRITE, + + /* + * encode the bytes size in the enum's value. This makes easy to extract it + * with a simple logic AND, by using the macro CMD_MEM_AP_2_SIZE() below + */ + CMD_MEM_AP_READ8 = 0x10 + 1, + CMD_MEM_AP_READ16 = 0x10 + 2, + CMD_MEM_AP_READ32 = 0x10 + 4, + + CMD_MEM_AP_WRITE8 = 0x20 + 1, + CMD_MEM_AP_WRITE16 = 0x20 + 2, + CMD_MEM_AP_WRITE32 = 0x20 + 4, +}; + +#define CMD_MEM_AP_2_SIZE(cmd) ((cmd) & 7) + +struct dap_queue { + enum queue_cmd cmd; + union { + struct dp_r { + unsigned int reg; + struct adiv5_dap *dap; + uint32_t *p_data; + } dp_r; + struct dp_w { + unsigned int reg; + struct adiv5_dap *dap; + uint32_t data; + } dp_w; + struct ap_r { + unsigned int reg; + struct adiv5_ap *ap; + uint32_t *p_data; + } ap_r; + struct ap_w { + unsigned int reg; + struct adiv5_ap *ap; + uint32_t data; + bool changes_csw_default; + } ap_w; + struct mem_ap { + uint32_t addr; + struct adiv5_ap *ap; + union { + uint32_t *p_data; + uint32_t data; + }; + uint32_t csw; + } mem_ap; + }; +}; + +/** */ +struct stlink_usb_handle_s { + /** */ + struct stlink_backend_s *backend; + /** */ + union { + struct stlink_usb_priv_s usb_backend_priv; + struct stlink_tcp_priv_s tcp_backend_priv; + }; + /** */ + uint8_t rx_ep; + /** */ + uint8_t tx_ep; + /** */ + uint8_t trace_ep; + /** */ + uint8_t *cmdbuf; + /** */ + uint8_t cmdidx; + /** */ + uint8_t direction; + /** */ + uint8_t *databuf; + /** */ + uint32_t max_mem_packet; + /** */ + enum stlink_mode st_mode; + /** */ + struct stlink_usb_version version; + /** */ + uint16_t vid; + /** */ + uint16_t pid; + /** */ + struct { + /** whether SWO tracing is enabled or not */ + bool enabled; + /** trace module source clock */ + uint32_t source_hz; + } trace; + /** reconnect is needed next time we try to query the + * status */ + bool reconnect_pending; + /** queue of dap_direct operations */ + struct dap_queue queue[MAX_QUEUE_DEPTH]; + /** first element available in the queue */ + unsigned int queue_index; +}; + +/** shared functions */ +void stlink_usb_init_buffer(void *handle, uint8_t direction, uint32_t size); +int stlink_usb_version(void *handle); +int stlink_usb_exit_mode(void *handle); + +#endif /* OPENOCD_JTAG_DRIVERS_STLINK_STLINK_H */ diff --git a/src/jtag/drivers/stlink/stlink_tcp.c b/src/jtag/drivers/stlink/stlink_tcp.c new file mode 100644 index 000000000..5181669db --- /dev/null +++ b/src/jtag/drivers/stlink/stlink_tcp.c @@ -0,0 +1,402 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2020 by Antonio Borneo <[email protected]> + * Copyright (C) 2020 by Tarek BOCHKATI <[email protected]> + * Copyright (C) 2017 by Ake Rehnman <[email protected]> + * Copyright (C) 2012 by Mathias Kuester <[email protected]> + * Copyright (C) 2012 by Spencer Oliver <[email protected]> + * + * This code is based on https://github.com/texane/stlink + */ + +#include "stlink.h" + +#include <helper/log.h> +#include <helper/system.h> +#include <helper/time_support.h> + +#ifdef HAVE_ARPA_INET_H +#include <arpa/inet.h> +#endif + +#ifdef HAVE_NETINET_TCP_H +#include <netinet/tcp.h> +#endif + +static int stlink_tcp_send_cmd(void *handle, int send_size, int recv_size, bool check_tcp_status) +{ + struct stlink_usb_handle_s *h = handle; + + assert(handle); + + /* send the TCP command */ + int sent_size = send(h->tcp_backend_priv.fd, (void *)h->tcp_backend_priv.send_buf, send_size, 0); + if (sent_size != send_size) { + LOG_ERROR("failed to send USB CMD"); + if (sent_size == -1) + LOG_DEBUG("socket send error: %s (errno %d)", strerror(errno), errno); + else + LOG_DEBUG("sent size %d (expected %d)", sent_size, send_size); + return ERROR_FAIL; + } + + /* read the TCP response */ + int retval = ERROR_OK; + int remaining_bytes = recv_size; + uint8_t *recv_buf = h->tcp_backend_priv.recv_buf; + const int64_t timeout = timeval_ms() + 1000; /* 1 second */ + + while (remaining_bytes > 0) { + if (timeval_ms() > timeout) { + LOG_DEBUG("received size %d (expected %d)", recv_size - remaining_bytes, recv_size); + retval = ERROR_TIMEOUT_REACHED; + break; + } + + keep_alive(); + int received = recv(h->tcp_backend_priv.fd, (void *)recv_buf, remaining_bytes, 0); + + if (received == -1) { + LOG_DEBUG("socket recv error: %s (errno %d)", strerror(errno), errno); + retval = ERROR_FAIL; + break; + } + + recv_buf += received; + remaining_bytes -= received; + } + + if (retval != ERROR_OK) { + LOG_ERROR("failed to receive USB CMD response"); + return retval; + } + + if (check_tcp_status) { + uint32_t tcp_ss = le_to_h_u32(h->tcp_backend_priv.recv_buf); + if (tcp_ss != STLINK_TCP_SS_OK) { + LOG_ERROR("TCP error status 0x%X", tcp_ss); + return ERROR_FAIL; + } + } + + return ERROR_OK; +} + +/** */ +static int stlink_tcp_xfer_noerrcheck(void *handle, const uint8_t *buf, int size) +{ + struct stlink_usb_handle_s *h = handle; + + int send_size = STLINK_TCP_USB_CMD_SIZE; + int recv_size = STLINK_TCP_SS_SIZE; + + assert(handle); + + /* prepare the TCP command */ + h->tcp_backend_priv.send_buf[0] = STLINK_TCP_CMD_SEND_USB_CMD; + memset(&h->tcp_backend_priv.send_buf[1], 0, 3); /* reserved for alignment and future use, must be zero */ + h_u32_to_le(&h->tcp_backend_priv.send_buf[4], h->tcp_backend_priv.connect_id); + /* tcp_backend_priv.send_buf[8..23] already contains the constructed stlink command */ + h->tcp_backend_priv.send_buf[24] = h->direction; + memset(&h->tcp_backend_priv.send_buf[25], 0, 3); /* reserved for alignment and future use, must be zero */ + + h_u32_to_le(&h->tcp_backend_priv.send_buf[28], size); + + /* + * if the xfer is a write request (tx_ep) + * > then buf content will be copied + * into &cmdbuf[32]. + * else : the xfer is a read or trace read request (rx_ep or trace_ep) + * > the buf content will be filled from &databuf[4]. + * + * note : if h->direction is trace_ep, h->cmdbuf is zeros. + */ + + if (h->direction == h->tx_ep) { /* STLINK_TCP_REQUEST_WRITE */ + send_size += size; + if (send_size > STLINK_TCP_SEND_BUFFER_SIZE) { + LOG_ERROR("STLINK_TCP command buffer overflow"); + return ERROR_FAIL; + } + memcpy(&h->tcp_backend_priv.send_buf[32], buf, size); + } else { /* STLINK_TCP_REQUEST_READ or STLINK_TCP_REQUEST_READ_SWO */ + recv_size += size; + if (recv_size > STLINK_TCP_RECV_BUFFER_SIZE) { + LOG_ERROR("STLINK_TCP data buffer overflow"); + return ERROR_FAIL; + } + } + + int ret = stlink_tcp_send_cmd(h, send_size, recv_size, true); + if (ret != ERROR_OK) + return ret; + + if (h->direction != h->tx_ep) { + /* the read data is located in tcp_backend_priv.recv_buf[4] */ + /* most of the case it will be copying the data from tcp_backend_priv.recv_buf[4] + * to handle->cmd_buff which are the same, so let's avoid unnecessary copying */ + if (buf != &h->tcp_backend_priv.recv_buf[4]) + memcpy((uint8_t *)buf, &h->tcp_backend_priv.recv_buf[4], size); + } + + return ERROR_OK; +} + +/** */ +static int stlink_tcp_read_trace(void *handle, const uint8_t *buf, int size) +{ + struct stlink_usb_handle_s *h = handle; + + stlink_usb_init_buffer(h, h->trace_ep, 0); + return stlink_tcp_xfer_noerrcheck(handle, buf, size); +} + +/** */ +static int stlink_tcp_open(void *handle, struct hl_interface_param_s *param) +{ + struct stlink_usb_handle_s *h = handle; + int ret; + + /* SWIM is not supported using stlink-server */ + if (h->st_mode == STLINK_MODE_DEBUG_SWIM) { + LOG_ERROR("stlink-server does not support SWIM mode"); + return ERROR_FAIL; + } + + h->tcp_backend_priv.send_buf = malloc(STLINK_TCP_SEND_BUFFER_SIZE); + h->tcp_backend_priv.recv_buf = malloc(STLINK_TCP_RECV_BUFFER_SIZE); + + if (!h->tcp_backend_priv.send_buf || !h->tcp_backend_priv.recv_buf) + return ERROR_FAIL; + + h->cmdbuf = &h->tcp_backend_priv.send_buf[8]; + h->databuf = &h->tcp_backend_priv.recv_buf[4]; + + /* configure directions */ + h->rx_ep = STLINK_TCP_REQUEST_READ; + h->tx_ep = STLINK_TCP_REQUEST_WRITE; + h->trace_ep = STLINK_TCP_REQUEST_READ_SWO; + + h->tcp_backend_priv.fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + h->tcp_backend_priv.connected = false; + h->tcp_backend_priv.device_id = 0; + h->tcp_backend_priv.connect_id = 0; + + if (h->tcp_backend_priv.fd == -1) { + LOG_ERROR("error creating the socket, errno: %s", strerror(errno)); + return ERROR_FAIL; + } + + struct sockaddr_in serv; + memset(&serv, 0, sizeof(struct sockaddr_in)); + serv.sin_family = AF_INET; + serv.sin_port = htons(param->stlink_tcp_port); + serv.sin_addr.s_addr = inet_addr("127.0.0.1"); + + LOG_DEBUG("socket : %x", h->tcp_backend_priv.fd); + + int optval = 1; + if (setsockopt(h->tcp_backend_priv.fd, IPPROTO_TCP, TCP_NODELAY, (const void *)&optval, sizeof(int)) == -1) { + LOG_ERROR("cannot set sock option 'TCP_NODELAY', errno: %s", strerror(errno)); + return ERROR_FAIL; + } + + optval = STLINK_TCP_RECV_BUFFER_SIZE; + if (setsockopt(h->tcp_backend_priv.fd, SOL_SOCKET, SO_RCVBUF, (const void *)&optval, sizeof(int)) == -1) { + LOG_ERROR("cannot set sock option 'SO_RCVBUF', errno: %s", strerror(errno)); + return ERROR_FAIL; + } + + optval = STLINK_TCP_SEND_BUFFER_SIZE; + if (setsockopt(h->tcp_backend_priv.fd, SOL_SOCKET, SO_SNDBUF, (const void *)&optval, sizeof(int)) == -1) { + LOG_ERROR("cannot set sock option 'SO_SNDBUF', errno: %s", strerror(errno)); + return ERROR_FAIL; + } + + if (connect(h->tcp_backend_priv.fd, (const struct sockaddr *)&serv, sizeof(serv)) == -1) { + LOG_ERROR("cannot connect to stlink server, errno: %s", strerror(errno)); + return ERROR_FAIL; + } + + h->tcp_backend_priv.connected = true; + + LOG_INFO("connected to stlink-server"); + + /* print stlink-server version */ + h->tcp_backend_priv.send_buf[0] = STLINK_TCP_CMD_GET_SERVER_VERSION; + h->tcp_backend_priv.send_buf[1] = OPENOCD_STLINK_TCP_API_VERSION; + memset(&h->tcp_backend_priv.send_buf[2], 0, 2); /* reserved */ + ret = stlink_tcp_send_cmd(h, 4, 16, false); + if (ret != ERROR_OK) { + LOG_ERROR("cannot get the stlink-server version"); + return ERROR_FAIL; + } + + uint32_t api_ver = le_to_h_u32(&h->tcp_backend_priv.recv_buf[0]); + uint32_t ver_major = le_to_h_u32(&h->tcp_backend_priv.recv_buf[4]); + uint32_t ver_minor = le_to_h_u32(&h->tcp_backend_priv.recv_buf[8]); + uint32_t ver_build = le_to_h_u32(&h->tcp_backend_priv.recv_buf[12]); + LOG_INFO("stlink-server API v%d, version %d.%d.%d", + api_ver, ver_major, ver_minor, ver_build); + + /* in stlink-server API v1 sending more than 1428 bytes will cause stlink-server + * to crash in windows: select a safe default value (1K) */ + if (api_ver < 2) + h->max_mem_packet = (1 << 10); + + /* refresh stlink list (re-enumerate) */ + h->tcp_backend_priv.send_buf[0] = STLINK_TCP_CMD_REFRESH_DEVICE_LIST; + h->tcp_backend_priv.send_buf[1] = 0; /* don't clear the list, just refresh it */ + ret = stlink_tcp_send_cmd(h, 2, 4, true); + if (ret != ERROR_OK) + return ret; + + /* get the number of connected stlinks */ + h->tcp_backend_priv.send_buf[0] = STLINK_TCP_CMD_GET_NB_DEV; + ret = stlink_tcp_send_cmd(h, 1, 4, false); + if (ret != ERROR_OK) + return ret; + + uint32_t connected_stlinks = le_to_h_u32(h->tcp_backend_priv.recv_buf); + + if (connected_stlinks == 0) { + LOG_ERROR("no ST-LINK detected"); + return ERROR_FAIL; + } + + LOG_DEBUG("%d ST-LINK detected", connected_stlinks); + + if (connected_stlinks > 255) { + LOG_WARNING("STLink server cannot handle more than 255 ST-LINK connected"); + connected_stlinks = 255; + } + + /* list all connected ST-Link and seek for the requested vid:pid and serial */ + char serial[STLINK_TCP_SERIAL_SIZE + 1] = {0}; + uint8_t stlink_used; + bool stlink_id_matched = false; + bool stlink_serial_matched = (!param->serial); + + for (uint32_t stlink_id = 0; stlink_id < connected_stlinks; stlink_id++) { + /* get the stlink info */ + h->tcp_backend_priv.send_buf[0] = STLINK_TCP_CMD_GET_DEV_INFO; + h->tcp_backend_priv.send_buf[1] = (uint8_t)stlink_id; + memset(&h->tcp_backend_priv.send_buf[2], 0, 2); /* reserved */ + h_u32_to_le(&h->tcp_backend_priv.send_buf[4], 41); /* size of TDeviceInfo2 */ + ret = stlink_tcp_send_cmd(h, 8, 45, true); + if (ret != ERROR_OK) + return ret; + + h->tcp_backend_priv.device_id = le_to_h_u32(&h->tcp_backend_priv.recv_buf[4]); + memcpy(serial, &h->tcp_backend_priv.recv_buf[8], STLINK_TCP_SERIAL_SIZE); + h->vid = le_to_h_u16(&h->tcp_backend_priv.recv_buf[40]); + h->pid = le_to_h_u16(&h->tcp_backend_priv.recv_buf[42]); + stlink_used = h->tcp_backend_priv.recv_buf[44]; + + /* check the vid:pid */ + for (int i = 0; param->vid[i]; i++) { + if (param->vid[i] == h->vid && param->pid[i] == h->pid) { + stlink_id_matched = true; + break; + } + } + + if (!stlink_id_matched) + continue; + + /* check the serial if specified */ + if (param->serial) { + /* ST-Link server fixes the buggy serial returned by old ST-Link DFU + * for further details refer to stlink_usb_get_alternate_serial + * so if the user passes the buggy serial, we need to fix it before + * comparing with the serial returned by ST-Link server */ + if (strlen(param->serial) == STLINK_SERIAL_LEN / 2) { + char fixed_serial[STLINK_SERIAL_LEN + 1]; + + for (unsigned int i = 0; i < STLINK_SERIAL_LEN; i += 2) + sprintf(fixed_serial + i, "%02X", param->serial[i / 2]); + + fixed_serial[STLINK_SERIAL_LEN] = '\0'; + + stlink_serial_matched = strcmp(fixed_serial, serial) == 0; + } else + stlink_serial_matched = strcmp(param->serial, serial) == 0; + } + + if (!stlink_serial_matched) + LOG_DEBUG("Device serial number '%s' doesn't match requested serial '%s'", + serial, param->serial); + else /* exit the search loop if there is match */ + break; + } + + if (!stlink_id_matched) { + LOG_ERROR("ST-LINK open failed (vid/pid mismatch)"); + return ERROR_FAIL; + } + + if (!stlink_serial_matched) { + LOG_ERROR("ST-LINK open failed (serial mismatch)"); + return ERROR_FAIL; + } + + /* check if device is 'exclusively' used by another application */ + if (stlink_used) { + LOG_ERROR("the selected device is already used"); + return ERROR_FAIL; + } + + LOG_DEBUG("transport: vid: 0x%04x pid: 0x%04x serial: %s", h->vid, h->pid, serial); + + /* now let's open the stlink */ + h->tcp_backend_priv.send_buf[0] = STLINK_TCP_CMD_OPEN_DEV; + memset(&h->tcp_backend_priv.send_buf[1], 0, 4); /* reserved */ + h_u32_to_le(&h->tcp_backend_priv.send_buf[4], h->tcp_backend_priv.device_id); + ret = stlink_tcp_send_cmd(h, 8, 8, true); + if (ret != ERROR_OK) + return ret; + + h->tcp_backend_priv.connect_id = le_to_h_u32(&h->tcp_backend_priv.recv_buf[4]); + + /* get stlink version */ + return stlink_usb_version(h); +} + +/** */ +static int stlink_tcp_close(void *handle) +{ + struct stlink_usb_handle_s *h = handle; + + if (!h) + return ERROR_OK; + + int ret = ERROR_OK; + if (h->tcp_backend_priv.connected) { + if (h->tcp_backend_priv.connect_id) { + stlink_usb_exit_mode(h); + + /* close the stlink */ + h->tcp_backend_priv.send_buf[0] = STLINK_TCP_CMD_CLOSE_DEV; + memset(&h->tcp_backend_priv.send_buf[1], 0, 4); /* reserved */ + h_u32_to_le(&h->tcp_backend_priv.send_buf[4], h->tcp_backend_priv.connect_id); + ret = stlink_tcp_send_cmd(h, 8, 4, true); + if (ret != ERROR_OK) + LOG_ERROR("cannot close the STLINK"); + } + + if (close_socket(h->tcp_backend_priv.fd) != 0) + LOG_ERROR("error closing the socket, errno: %s", strerror(errno)); + } + + free(h->tcp_backend_priv.send_buf); + free(h->tcp_backend_priv.recv_buf); + + return ret; +} + +struct stlink_backend_s stlink_tcp_backend = { + .open = stlink_tcp_open, + .close = stlink_tcp_close, + .xfer_noerrcheck = stlink_tcp_xfer_noerrcheck, + .read_trace = stlink_tcp_read_trace, +}; diff --git a/src/jtag/drivers/stlink/stlink_usb.c b/src/jtag/drivers/stlink/stlink_usb.c new file mode 100644 index 000000000..5c1486c8c --- /dev/null +++ b/src/jtag/drivers/stlink/stlink_usb.c @@ -0,0 +1,564 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2020 by Antonio Borneo <[email protected]> + * Copyright (C) 2020 by Tarek BOCHKATI <[email protected]> + * Copyright (C) 2017 by Ake Rehnman <[email protected]> + * Copyright (C) 2012 by Mathias Kuester <[email protected]> + * Copyright (C) 2012 by Spencer Oliver <[email protected]> + * + * This code is based on https://github.com/texane/stlink + */ + +#include "stlink.h" +#include "../libusb_helper.h" +#include <helper/binarybuffer.h> +#include <helper/log.h> + +#ifdef HAVE_LIBUSB1 + +static LIBUSB_CALL void sync_transfer_cb(struct libusb_transfer *transfer) +{ + int *completed = transfer->user_data; + *completed = 1; + /* caller interprets result and frees transfer */ +} + +static void sync_transfer_wait_for_completion(struct libusb_transfer *transfer) +{ + int r, *completed = transfer->user_data; + + while (!*completed) { + r = jtag_libusb_handle_events_completed(completed); + if (r < 0) { + if (r == LIBUSB_ERROR_INTERRUPTED) + continue; + libusb_cancel_transfer(transfer); + continue; + } + } +} + + +static int transfer_error_status(const struct libusb_transfer *transfer) +{ + int r = 0; + + switch (transfer->status) { + case LIBUSB_TRANSFER_COMPLETED: + r = 0; + break; + case LIBUSB_TRANSFER_TIMED_OUT: + r = LIBUSB_ERROR_TIMEOUT; + break; + case LIBUSB_TRANSFER_STALL: + r = LIBUSB_ERROR_PIPE; + break; + case LIBUSB_TRANSFER_OVERFLOW: + r = LIBUSB_ERROR_OVERFLOW; + break; + case LIBUSB_TRANSFER_NO_DEVICE: + r = LIBUSB_ERROR_NO_DEVICE; + break; + case LIBUSB_TRANSFER_ERROR: + case LIBUSB_TRANSFER_CANCELLED: + r = LIBUSB_ERROR_IO; + break; + default: + r = LIBUSB_ERROR_OTHER; + break; + } + + return r; +} + +struct jtag_xfer { + int ep; + uint8_t *buf; + size_t size; + /* Internal */ + int retval; + int completed; + size_t transfer_size; + struct libusb_transfer *transfer; +}; + +static int jtag_libusb_bulk_transfer_n( + struct libusb_device_handle *dev_handle, + struct jtag_xfer *transfers, + size_t n_transfers, + int timeout) +{ + int retval = 0; + int returnval = ERROR_OK; + + + for (size_t i = 0; i < n_transfers; ++i) { + transfers[i].retval = 0; + transfers[i].completed = 0; + transfers[i].transfer_size = 0; + transfers[i].transfer = libusb_alloc_transfer(0); + + if (!transfers[i].transfer) { + for (size_t j = 0; j < i; ++j) + libusb_free_transfer(transfers[j].transfer); + + LOG_DEBUG("ERROR, failed to alloc usb transfers"); + for (size_t k = 0; k < n_transfers; ++k) + transfers[k].retval = LIBUSB_ERROR_NO_MEM; + return ERROR_FAIL; + } + } + + for (size_t i = 0; i < n_transfers; ++i) { + libusb_fill_bulk_transfer( + transfers[i].transfer, + dev_handle, + transfers[i].ep, transfers[i].buf, transfers[i].size, + sync_transfer_cb, &transfers[i].completed, timeout); + transfers[i].transfer->type = LIBUSB_TRANSFER_TYPE_BULK; + + retval = libusb_submit_transfer(transfers[i].transfer); + if (retval < 0) { + LOG_DEBUG("ERROR, failed to submit transfer %zu, error %d", i, retval); + + /* Probably no point continuing to submit transfers once a submission fails. + * As a result, tag all remaining transfers as errors. + */ + for (size_t j = i; j < n_transfers; ++j) + transfers[j].retval = retval; + + returnval = ERROR_FAIL; + break; + } + } + + /* Wait for every submitted USB transfer to complete. + */ + for (size_t i = 0; i < n_transfers; ++i) { + if (transfers[i].retval == 0) { + sync_transfer_wait_for_completion(transfers[i].transfer); + + retval = transfer_error_status(transfers[i].transfer); + if (retval) { + returnval = ERROR_FAIL; + transfers[i].retval = retval; + LOG_DEBUG("ERROR, transfer %zu failed, error %d", i, retval); + } else { + /* Assuming actual_length is only valid if there is no transfer error. + */ + transfers[i].transfer_size = transfers[i].transfer->actual_length; + } + } + + libusb_free_transfer(transfers[i].transfer); + transfers[i].transfer = NULL; + } + + return returnval; +} + +static int stlink_usb_xfer_rw(void *handle, int cmdsize, const uint8_t *buf, int size) +{ + struct stlink_usb_handle_s *h = handle; + + assert(handle); + + size_t n_transfers = 0; + struct jtag_xfer transfers[2]; + + memset(transfers, 0, sizeof(transfers)); + + transfers[0].ep = h->tx_ep; + transfers[0].buf = h->cmdbuf; + transfers[0].size = cmdsize; + + ++n_transfers; + + if (h->direction == h->tx_ep && size) { + transfers[1].ep = h->tx_ep; + transfers[1].buf = (uint8_t *)buf; + transfers[1].size = size; + + ++n_transfers; + } else if (h->direction == h->rx_ep && size) { + transfers[1].ep = h->rx_ep; + transfers[1].buf = (uint8_t *)buf; + transfers[1].size = size; + + ++n_transfers; + } + + return jtag_libusb_bulk_transfer_n( + h->usb_backend_priv.fd, + transfers, + n_transfers, + STLINK_WRITE_TIMEOUT); +} + +#else /* HAVE_LIBUSB1 not defined */ + +static int stlink_usb_xfer_rw(void *handle, int cmdsize, const uint8_t *buf, int size) +{ + struct stlink_usb_handle_s *h = handle; + int tr, ret; + + assert(handle); + + ret = jtag_libusb_bulk_write(h->usb_backend_priv.fd, h->tx_ep, (char *)h->cmdbuf, + cmdsize, STLINK_WRITE_TIMEOUT, &tr); + if (ret || tr != cmdsize) + return ERROR_FAIL; + + if (h->direction == h->tx_ep && size) { + ret = jtag_libusb_bulk_write(h->usb_backend_priv.fd, h->tx_ep, (char *)buf, + size, STLINK_WRITE_TIMEOUT, &tr); + if (ret || tr != size) { + LOG_DEBUG("bulk write failed"); + return ERROR_FAIL; + } + } else if (h->direction == h->rx_ep && size) { + ret = jtag_libusb_bulk_read(h->usb_backend_priv.fd, h->rx_ep, (char *)buf, + size, STLINK_READ_TIMEOUT, &tr); + if (ret || tr != size) { + LOG_DEBUG("bulk read failed"); + return ERROR_FAIL; + } + } + + return ERROR_OK; +} + +#endif /* HAVE_LIBUSB1 */ + +/** */ +static int stlink_usb_xfer_v1_get_status(void *handle) +{ + struct stlink_usb_handle_s *h = handle; + int tr, ret; + + assert(handle); + + /* read status */ + memset(h->cmdbuf, 0, STLINK_SG_SIZE); + + ret = jtag_libusb_bulk_read(h->usb_backend_priv.fd, h->rx_ep, (char *)h->cmdbuf, 13, + STLINK_READ_TIMEOUT, &tr); + if (ret || tr != 13) + return ERROR_FAIL; + + uint32_t t1; + + t1 = buf_get_u32(h->cmdbuf, 0, 32); + + /* check for USBS */ + if (t1 != 0x53425355) + return ERROR_FAIL; + /* + * CSW status: + * 0 success + * 1 command failure + * 2 phase error + */ + if (h->cmdbuf[12] != 0) + return ERROR_FAIL; + + return ERROR_OK; +} + +/** */ +static int stlink_usb_xfer_v1_get_sense(void *handle) +{ + int res; + struct stlink_usb_handle_s *h = handle; + + assert(handle); + + stlink_usb_init_buffer(handle, h->rx_ep, 16); + + h->cmdbuf[h->cmdidx++] = REQUEST_SENSE; + h->cmdbuf[h->cmdidx++] = 0; + h->cmdbuf[h->cmdidx++] = 0; + h->cmdbuf[h->cmdidx++] = 0; + h->cmdbuf[h->cmdidx++] = REQUEST_SENSE_LENGTH; + + res = stlink_usb_xfer_rw(handle, REQUEST_SENSE_LENGTH, h->databuf, 16); + + if (res != ERROR_OK) + return res; + + if (stlink_usb_xfer_v1_get_status(handle) != ERROR_OK) + return ERROR_FAIL; + + return ERROR_OK; +} + +/** */ +static int stlink_usb_read_trace(void *handle, const uint8_t *buf, int size) +{ + struct stlink_usb_handle_s *h = handle; + int tr, ret; + + ret = jtag_libusb_bulk_read(h->usb_backend_priv.fd, h->trace_ep, (char *)buf, size, + STLINK_READ_TIMEOUT, &tr); + if (ret || tr != size) { + LOG_ERROR("bulk trace read failed"); + return ERROR_FAIL; + } + + return ERROR_OK; +} + +/* + transfers block in cmdbuf + <size> indicates number of bytes in the following + data phase. + Ignore the (eventual) error code in the received packet. +*/ +static int stlink_usb_xfer_noerrcheck(void *handle, const uint8_t *buf, int size) +{ + int err, cmdsize = STLINK_CMD_SIZE_V2; + struct stlink_usb_handle_s *h = handle; + + assert(handle); + + if (h->version.stlink == 1) { + cmdsize = STLINK_SG_SIZE; + /* put length in bCBWCBLength */ + h->cmdbuf[14] = h->cmdidx-15; + } + + err = stlink_usb_xfer_rw(handle, cmdsize, buf, size); + + if (err != ERROR_OK) + return err; + + if (h->version.stlink == 1) { + if (stlink_usb_xfer_v1_get_status(handle) != ERROR_OK) { + /* check csw status */ + if (h->cmdbuf[12] == 1) { + LOG_DEBUG("get sense"); + if (stlink_usb_xfer_v1_get_sense(handle) != ERROR_OK) + return ERROR_FAIL; + } + return ERROR_FAIL; + } + } + + return ERROR_OK; +} + +/* Compute ST-Link serial number from the device descriptor + * this function will help to work-around a bug in old ST-Link/V2 DFU + * the buggy DFU returns an incorrect serial in the USB descriptor + * example for the following serial "57FF72067265575742132067" + * - the correct descriptor serial is: + * 0x32, 0x03, 0x35, 0x00, 0x37, 0x00, 0x46, 0x00, 0x46, 0x00, 0x37, 0x00, 0x32, 0x00 ... + * this contains the length (0x32 = 50), the type (0x3 = DT_STRING) and the serial in unicode format + * the serial part is: 0x0035, 0x0037, 0x0046, 0x0046, 0x0037, 0x0032 ... >> 57FF72 ... + * this format could be read correctly by 'libusb_get_string_descriptor_ascii' + * so this case is managed by libusb_helper::string_descriptor_equal + * - the buggy DFU is not doing any unicode conversion and returns a raw serial data in the descriptor + * 0x1a, 0x03, 0x57, 0x00, 0xFF, 0x00, 0x72, 0x00 ... + * >> 57 FF 72 ... + * based on the length (0x1a = 26) we could easily decide if we have to fixup the serial + * and then we have just to convert the raw data into printable characters using sprintf + */ +static char *stlink_usb_get_alternate_serial(struct libusb_device_handle *device, + struct libusb_device_descriptor *dev_desc) +{ + int usb_retval; + unsigned char desc_serial[(STLINK_SERIAL_LEN + 1) * 2]; + + if (dev_desc->iSerialNumber == 0) + return NULL; + + /* get the LANGID from String Descriptor Zero */ + usb_retval = libusb_get_string_descriptor(device, 0, 0, desc_serial, + sizeof(desc_serial)); + + if (usb_retval < LIBUSB_SUCCESS) { + LOG_ERROR("libusb_get_string_descriptor() failed: %s(%d)", + libusb_error_name(usb_retval), usb_retval); + return NULL; + } else if (usb_retval < 4) { + /* the size should be least 4 bytes to contain a minimum of 1 supported LANGID */ + LOG_ERROR("could not get the LANGID"); + return NULL; + } + + uint32_t langid = desc_serial[2] | (desc_serial[3] << 8); + + /* get the serial */ + usb_retval = libusb_get_string_descriptor(device, dev_desc->iSerialNumber, + langid, desc_serial, sizeof(desc_serial)); + + unsigned char len = desc_serial[0]; + + if (usb_retval < LIBUSB_SUCCESS) { + LOG_ERROR("libusb_get_string_descriptor() failed: %s(%d)", + libusb_error_name(usb_retval), usb_retval); + return NULL; + } else if (desc_serial[1] != LIBUSB_DT_STRING || len > usb_retval) { + LOG_ERROR("invalid string in ST-LINK USB serial descriptor"); + return NULL; + } + + if (len == ((STLINK_SERIAL_LEN + 1) * 2)) { + /* good ST-Link adapter, this case is managed by + * libusb::libusb_get_string_descriptor_ascii */ + return NULL; + } else if (len != ((STLINK_SERIAL_LEN / 2 + 1) * 2)) { + LOG_ERROR("unexpected serial length (%d) in descriptor", len); + return NULL; + } + + /* else (len == 26) => buggy ST-Link */ + + char *alternate_serial = malloc((STLINK_SERIAL_LEN + 1) * sizeof(char)); + if (!alternate_serial) + return NULL; + + for (unsigned int i = 0; i < STLINK_SERIAL_LEN; i += 2) + sprintf(alternate_serial + i, "%02X", desc_serial[i + 2]); + + alternate_serial[STLINK_SERIAL_LEN] = '\0'; + + return alternate_serial; +} + +/** */ +static int stlink_usb_open(void *handle, struct hl_interface_param_s *param) +{ + struct stlink_usb_handle_s *h = handle; + int err, retry_count = 1; + + h->cmdbuf = malloc(STLINK_SG_SIZE); + h->databuf = malloc(STLINK_DATA_SIZE); + + if (!h->cmdbuf || !h->databuf) + return ERROR_FAIL; + + /* + On certain host USB configurations(e.g. MacBook Air) + STLINKv2 dongle seems to have its FW in a funky state if, + after plugging it in, you try to use openocd with it more + then once (by launching and closing openocd). In cases like + that initial attempt to read the FW info via + stlink_usb_version will fail and the device has to be reset + in order to become operational. + */ + do { + if (jtag_libusb_open(param->vid, param->pid, param->serial, + &h->usb_backend_priv.fd, stlink_usb_get_alternate_serial) != ERROR_OK) { + LOG_ERROR("open failed"); + return ERROR_FAIL; + } + + jtag_libusb_set_configuration(h->usb_backend_priv.fd, 0); + + if (libusb_claim_interface(h->usb_backend_priv.fd, 0) != ERROR_OK) { + LOG_DEBUG("claim interface failed"); + return ERROR_FAIL; + } + + /* RX EP is common for all versions */ + h->rx_ep = STLINK_RX_EP; + + uint16_t pid; + if (jtag_libusb_get_pid(libusb_get_device(h->usb_backend_priv.fd), &pid) != ERROR_OK) { + LOG_DEBUG("libusb_get_pid failed"); + return ERROR_FAIL; + } + + /* wrap version for first read */ + switch (pid) { + case STLINK_V1_PID: + h->version.stlink = 1; + h->tx_ep = STLINK_TX_EP; + break; + case STLINK_V3_USBLOADER_PID: + case STLINK_V3E_PID: + case STLINK_V3S_PID: + case STLINK_V3_2VCP_PID: + case STLINK_V3E_NO_MSD_PID: + h->version.stlink = 3; + h->tx_ep = STLINK_V2_1_TX_EP; + h->trace_ep = STLINK_V2_1_TRACE_EP; + break; + case STLINK_V2_1_PID: + case STLINK_V2_1_NO_MSD_PID: + h->version.stlink = 2; + h->tx_ep = STLINK_V2_1_TX_EP; + h->trace_ep = STLINK_V2_1_TRACE_EP; + break; + default: + /* fall through - we assume V2 to be the default version*/ + case STLINK_V2_PID: + h->version.stlink = 2; + h->tx_ep = STLINK_TX_EP; + h->trace_ep = STLINK_TRACE_EP; + break; + } + + /* get the device version */ + err = stlink_usb_version(h); + + if (err == ERROR_OK) { + break; + } else if (h->version.stlink == 1 || + retry_count == 0) { + LOG_ERROR("read version failed"); + return ERROR_FAIL; + } else { + err = libusb_release_interface(h->usb_backend_priv.fd, 0); + if (err != ERROR_OK) { + LOG_ERROR("release interface failed"); + return ERROR_FAIL; + } + + err = libusb_reset_device(h->usb_backend_priv.fd); + if (err != ERROR_OK) { + LOG_ERROR("reset device failed"); + return ERROR_FAIL; + } + + jtag_libusb_close(h->usb_backend_priv.fd); + /* + Give the device one second to settle down and + reenumerate. + */ + usleep(1 * 1000 * 1000); + retry_count--; + } + } while (1); + + return ERROR_OK; +} + +/** */ +static int stlink_usb_close(void *handle) +{ + struct stlink_usb_handle_s *h = handle; + + if (!h) + return ERROR_OK; + + if (h->usb_backend_priv.fd) { + stlink_usb_exit_mode(h); + /* do not check return code, it prevent + us from closing jtag_libusb */ + jtag_libusb_close(h->usb_backend_priv.fd); + } + + free(h->cmdbuf); + free(h->databuf); + + return ERROR_OK; +} + +struct stlink_backend_s stlink_usb_backend = { + .open = stlink_usb_open, + .close = stlink_usb_close, + .xfer_noerrcheck = stlink_usb_xfer_noerrcheck, + .read_trace = stlink_usb_read_trace, +}; diff --git a/src/jtag/drivers/stlink_usb.c b/src/jtag/drivers/stlink_usb.c index 87e6ddc20..8d10b3d51 100644 --- a/src/jtag/drivers/stlink_usb.c +++ b/src/jtag/drivers/stlink_usb.c @@ -1,47 +1,20 @@ -/*************************************************************************** - * Copyright (C) 2020 by Tarek Bochkati * - * Tarek Bochkati <[email protected]> * - * * - * SWIM contributions by Ake Rehnman * - * Copyright (C) 2017 Ake Rehnman * - * ake.rehnman(at)gmail.com * - * * - * Copyright (C) 2011-2012 by Mathias Kuester * - * Mathias Kuester <[email protected]> * - * * - * Copyright (C) 2012 by Spencer Oliver * - * [email protected] * - * * - * This code is based on https://github.com/texane/stlink * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see <http://www.gnu.org/licenses/>. * - ***************************************************************************/ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2020 by Antonio Borneo <[email protected]> + * Copyright (C) 2020 by Tarek BOCHKATI <[email protected]> + * Copyright (C) 2017 by Ake Rehnman <[email protected]> + * Copyright (C) 2012 by Mathias Kuester <[email protected]> + * Copyright (C) 2012 by Spencer Oliver <[email protected]> + * + * This code is based on https://github.com/texane/stlink + */ + +#include "stlink/stlink.h" /* project specific includes */ #include <helper/align.h> -#include <helper/binarybuffer.h> #include <helper/bits.h> -#include <helper/system.h> -#include <helper/time_support.h> #include <jtag/interface.h> -#include <jtag/hla/hla_layout.h> -#include <jtag/hla/hla_transport.h> -#include <jtag/hla/hla_interface.h> #include <jtag/swim.h> #include <target/arm_adi_v5.h> #include <target/target.h> @@ -49,249 +22,6 @@ #include <target/cortex_m.h> -#include <helper/system.h> - -#ifdef HAVE_ARPA_INET_H -#include <arpa/inet.h> -#endif - -#ifdef HAVE_NETINET_TCP_H -#include <netinet/tcp.h> -#endif - -#include "libusb_helper.h" - -#ifdef HAVE_LIBUSB1 -#define USE_LIBUSB_ASYNCIO -#endif - -#define STLINK_SERIAL_LEN 24 - -#define ENDPOINT_IN 0x80 -#define ENDPOINT_OUT 0x00 - -#define STLINK_WRITE_TIMEOUT 1000 -#define STLINK_READ_TIMEOUT 1000 - -#define STLINK_RX_EP (1|ENDPOINT_IN) -#define STLINK_TX_EP (2|ENDPOINT_OUT) -#define STLINK_TRACE_EP (3|ENDPOINT_IN) - -#define STLINK_V2_1_TX_EP (1|ENDPOINT_OUT) -#define STLINK_V2_1_TRACE_EP (2|ENDPOINT_IN) - -#define STLINK_SG_SIZE (31) -#define STLINK_DATA_SIZE (6144) -#define STLINK_CMD_SIZE_V2 (16) -#define STLINK_CMD_SIZE_V1 (10) - -#define STLINK_V1_PID (0x3744) -#define STLINK_V2_PID (0x3748) -#define STLINK_V2_1_PID (0x374B) -#define STLINK_V2_1_NO_MSD_PID (0x3752) -#define STLINK_V3_USBLOADER_PID (0x374D) -#define STLINK_V3E_PID (0x374E) -#define STLINK_V3S_PID (0x374F) -#define STLINK_V3_2VCP_PID (0x3753) -#define STLINK_V3E_NO_MSD_PID (0x3754) - -/* - * ST-Link/V1, ST-Link/V2 and ST-Link/V2.1 are full-speed USB devices and - * this limits the bulk packet size and the 8bit read/writes to max 64 bytes. - * STLINK-V3 is a high speed USB 2.0 and the limit is 512 bytes from FW V3J6. - * - * For 16 and 32bit read/writes stlink handles USB packet split and the limit - * is the internal buffer size of 6144 bytes. - * TODO: override ADIv5 layer's tar_autoincr_block that limits the transfer - * to 1024 or 4096 bytes - */ -#define STLINK_MAX_RW8 (64) -#define STLINKV3_MAX_RW8 (512) -#define STLINK_MAX_RW16_32 STLINK_DATA_SIZE -#define STLINK_SWIM_DATA_SIZE STLINK_DATA_SIZE - -/* "WAIT" responses will be retried (with exponential backoff) at - * most this many times before failing to caller. - */ -#define MAX_WAIT_RETRIES 8 - -/* HLA is currently limited at AP#0 and no control on CSW */ -#define STLINK_HLA_AP_NUM 0 -#define STLINK_HLA_CSW 0 - -enum stlink_jtag_api_version { - STLINK_JTAG_API_V1 = 1, - STLINK_JTAG_API_V2, - STLINK_JTAG_API_V3, -}; - -enum stlink_mode { - STLINK_MODE_UNKNOWN = 0, - STLINK_MODE_DFU, - STLINK_MODE_MASS, - STLINK_MODE_DEBUG_JTAG, - STLINK_MODE_DEBUG_SWD, - STLINK_MODE_DEBUG_SWIM -}; - -/** */ -struct stlink_usb_version { - /** */ - int stlink; - /** */ - int jtag; - /** */ - int swim; - /** jtag api version supported */ - enum stlink_jtag_api_version jtag_api; - /** one bit for each feature supported. See macros STLINK_F_* */ - uint32_t flags; -}; - -struct stlink_usb_priv_s { - /** */ - struct libusb_device_handle *fd; - /** */ - struct libusb_transfer *trans; -}; - -struct stlink_tcp_priv_s { - /** */ - int fd; - /** */ - bool connected; - /** */ - uint32_t device_id; - /** */ - uint32_t connect_id; - /** */ - uint8_t *send_buf; - /** */ - uint8_t *recv_buf; -}; - -struct stlink_backend_s { - /** */ - int (*open)(void *handle, struct hl_interface_param_s *param); - /** */ - int (*close)(void *handle); - /** */ - int (*xfer_noerrcheck)(void *handle, const uint8_t *buf, int size); - /** */ - int (*read_trace)(void *handle, const uint8_t *buf, int size); -}; - -/* TODO: make queue size dynamic */ -/* TODO: don't allocate queue for HLA */ -#define MAX_QUEUE_DEPTH (4096) - -enum queue_cmd { - CMD_DP_READ = 1, - CMD_DP_WRITE, - - CMD_AP_READ, - CMD_AP_WRITE, - - /* - * encode the bytes size in the enum's value. This makes easy to extract it - * with a simple logic AND, by using the macro CMD_MEM_AP_2_SIZE() below - */ - CMD_MEM_AP_READ8 = 0x10 + 1, - CMD_MEM_AP_READ16 = 0x10 + 2, - CMD_MEM_AP_READ32 = 0x10 + 4, - - CMD_MEM_AP_WRITE8 = 0x20 + 1, - CMD_MEM_AP_WRITE16 = 0x20 + 2, - CMD_MEM_AP_WRITE32 = 0x20 + 4, -}; - -#define CMD_MEM_AP_2_SIZE(cmd) ((cmd) & 7) - -struct dap_queue { - enum queue_cmd cmd; - union { - struct dp_r { - unsigned int reg; - struct adiv5_dap *dap; - uint32_t *p_data; - } dp_r; - struct dp_w { - unsigned int reg; - struct adiv5_dap *dap; - uint32_t data; - } dp_w; - struct ap_r { - unsigned int reg; - struct adiv5_ap *ap; - uint32_t *p_data; - } ap_r; - struct ap_w { - unsigned int reg; - struct adiv5_ap *ap; - uint32_t data; - bool changes_csw_default; - } ap_w; - struct mem_ap { - uint32_t addr; - struct adiv5_ap *ap; - union { - uint32_t *p_data; - uint32_t data; - }; - uint32_t csw; - } mem_ap; - }; -}; - -/** */ -struct stlink_usb_handle_s { - /** */ - struct stlink_backend_s *backend; - /** */ - union { - struct stlink_usb_priv_s usb_backend_priv; - struct stlink_tcp_priv_s tcp_backend_priv; - }; - /** */ - uint8_t rx_ep; - /** */ - uint8_t tx_ep; - /** */ - uint8_t trace_ep; - /** */ - uint8_t *cmdbuf; - /** */ - uint8_t cmdidx; - /** */ - uint8_t direction; - /** */ - uint8_t *databuf; - /** */ - uint32_t max_mem_packet; - /** */ - enum stlink_mode st_mode; - /** */ - struct stlink_usb_version version; - /** */ - uint16_t vid; - /** */ - uint16_t pid; - /** */ - struct { - /** whether SWO tracing is enabled or not */ - bool enabled; - /** trace module source clock */ - uint32_t source_hz; - } trace; - /** reconnect is needed next time we try to query the - * status */ - bool reconnect_pending; - /** queue of dap_direct operations */ - struct dap_queue queue[MAX_QUEUE_DEPTH]; - /** first element available in the queue */ - unsigned int queue_index; -}; - /** */ static inline int stlink_usb_open(void *handle, struct hl_interface_param_s *param) { @@ -312,216 +42,6 @@ static inline int stlink_usb_xfer_noerrcheck(void *handle, const uint8_t *buf, i return h->backend->xfer_noerrcheck(handle, buf, size); } -#define STLINK_SWIM_ERR_OK 0x00 -#define STLINK_SWIM_BUSY 0x01 -#define STLINK_DEBUG_ERR_OK 0x80 -#define STLINK_DEBUG_ERR_FAULT 0x81 -#define STLINK_SWD_AP_WAIT 0x10 -#define STLINK_SWD_AP_FAULT 0x11 -#define STLINK_SWD_AP_ERROR 0x12 -#define STLINK_SWD_AP_PARITY_ERROR 0x13 -#define STLINK_JTAG_GET_IDCODE_ERROR 0x09 -#define STLINK_JTAG_WRITE_ERROR 0x0c -#define STLINK_JTAG_WRITE_VERIF_ERROR 0x0d -#define STLINK_SWD_DP_WAIT 0x14 -#define STLINK_SWD_DP_FAULT 0x15 -#define STLINK_SWD_DP_ERROR 0x16 -#define STLINK_SWD_DP_PARITY_ERROR 0x17 - -#define STLINK_SWD_AP_WDATA_ERROR 0x18 -#define STLINK_SWD_AP_STICKY_ERROR 0x19 -#define STLINK_SWD_AP_STICKYORUN_ERROR 0x1a - -#define STLINK_BAD_AP_ERROR 0x1d - -#define STLINK_CORE_RUNNING 0x80 -#define STLINK_CORE_HALTED 0x81 -#define STLINK_CORE_STAT_UNKNOWN -1 - -#define STLINK_GET_VERSION 0xF1 -#define STLINK_DEBUG_COMMAND 0xF2 -#define STLINK_DFU_COMMAND 0xF3 -#define STLINK_SWIM_COMMAND 0xF4 -#define STLINK_GET_CURRENT_MODE 0xF5 -#define STLINK_GET_TARGET_VOLTAGE 0xF7 - -#define STLINK_DEV_DFU_MODE 0x00 -#define STLINK_DEV_MASS_MODE 0x01 -#define STLINK_DEV_DEBUG_MODE 0x02 -#define STLINK_DEV_SWIM_MODE 0x03 -#define STLINK_DEV_BOOTLOADER_MODE 0x04 -#define STLINK_DEV_UNKNOWN_MODE -1 - -#define STLINK_DFU_EXIT 0x07 - -/* - STLINK_SWIM_ENTER_SEQ - 1.3ms low then 750Hz then 1.5kHz - - STLINK_SWIM_GEN_RST - STM8 DM pulls reset pin low 50us - - STLINK_SWIM_SPEED - uint8_t (0=low|1=high) - - STLINK_SWIM_WRITEMEM - uint16_t length - uint32_t address - - STLINK_SWIM_RESET - send synchronization seq (16us low, response 64 clocks low) -*/ -#define STLINK_SWIM_ENTER 0x00 -#define STLINK_SWIM_EXIT 0x01 -#define STLINK_SWIM_READ_CAP 0x02 -#define STLINK_SWIM_SPEED 0x03 -#define STLINK_SWIM_ENTER_SEQ 0x04 -#define STLINK_SWIM_GEN_RST 0x05 -#define STLINK_SWIM_RESET 0x06 -#define STLINK_SWIM_ASSERT_RESET 0x07 -#define STLINK_SWIM_DEASSERT_RESET 0x08 -#define STLINK_SWIM_READSTATUS 0x09 -#define STLINK_SWIM_WRITEMEM 0x0a -#define STLINK_SWIM_READMEM 0x0b -#define STLINK_SWIM_READBUF 0x0c - -#define STLINK_DEBUG_GETSTATUS 0x01 -#define STLINK_DEBUG_FORCEDEBUG 0x02 -#define STLINK_DEBUG_APIV1_RESETSYS 0x03 -#define STLINK_DEBUG_APIV1_READALLREGS 0x04 -#define STLINK_DEBUG_APIV1_READREG 0x05 -#define STLINK_DEBUG_APIV1_WRITEREG 0x06 -#define STLINK_DEBUG_READMEM_32BIT 0x07 -#define STLINK_DEBUG_WRITEMEM_32BIT 0x08 -#define STLINK_DEBUG_RUNCORE 0x09 -#define STLINK_DEBUG_STEPCORE 0x0a -#define STLINK_DEBUG_APIV1_SETFP 0x0b -#define STLINK_DEBUG_READMEM_8BIT 0x0c -#define STLINK_DEBUG_WRITEMEM_8BIT 0x0d -#define STLINK_DEBUG_APIV1_CLEARFP 0x0e -#define STLINK_DEBUG_APIV1_WRITEDEBUGREG 0x0f -#define STLINK_DEBUG_APIV1_SETWATCHPOINT 0x10 - -#define STLINK_DEBUG_ENTER_JTAG_RESET 0x00 -#define STLINK_DEBUG_ENTER_SWD_NO_RESET 0xa3 -#define STLINK_DEBUG_ENTER_JTAG_NO_RESET 0xa4 - -#define STLINK_DEBUG_APIV1_ENTER 0x20 -#define STLINK_DEBUG_EXIT 0x21 -#define STLINK_DEBUG_READCOREID 0x22 - -#define STLINK_DEBUG_APIV2_ENTER 0x30 -#define STLINK_DEBUG_APIV2_READ_IDCODES 0x31 -#define STLINK_DEBUG_APIV2_RESETSYS 0x32 -#define STLINK_DEBUG_APIV2_READREG 0x33 -#define STLINK_DEBUG_APIV2_WRITEREG 0x34 -#define STLINK_DEBUG_APIV2_WRITEDEBUGREG 0x35 -#define STLINK_DEBUG_APIV2_READDEBUGREG 0x36 - -#define STLINK_DEBUG_APIV2_READALLREGS 0x3A -#define STLINK_DEBUG_APIV2_GETLASTRWSTATUS 0x3B -#define STLINK_DEBUG_APIV2_DRIVE_NRST 0x3C - -#define STLINK_DEBUG_APIV2_GETLASTRWSTATUS2 0x3E - -#define STLINK_DEBUG_APIV2_START_TRACE_RX 0x40 -#define STLINK_DEBUG_APIV2_STOP_TRACE_RX 0x41 -#define STLINK_DEBUG_APIV2_GET_TRACE_NB 0x42 -#define STLINK_DEBUG_APIV2_SWD_SET_FREQ 0x43 -#define STLINK_DEBUG_APIV2_JTAG_SET_FREQ 0x44 -#define STLINK_DEBUG_APIV2_READ_DAP_REG 0x45 -#define STLINK_DEBUG_APIV2_WRITE_DAP_REG 0x46 -#define STLINK_DEBUG_APIV2_READMEM_16BIT 0x47 -#define STLINK_DEBUG_APIV2_WRITEMEM_16BIT 0x48 - -#define STLINK_DEBUG_APIV2_INIT_AP 0x4B -#define STLINK_DEBUG_APIV2_CLOSE_AP_DBG 0x4C - -#define STLINK_DEBUG_WRITEMEM_32BIT_NO_ADDR_INC 0x50 -#define STLINK_DEBUG_APIV2_RW_MISC_OUT 0x51 -#define STLINK_DEBUG_APIV2_RW_MISC_IN 0x52 - -#define STLINK_DEBUG_READMEM_32BIT_NO_ADDR_INC 0x54 - -#define STLINK_APIV3_SET_COM_FREQ 0x61 -#define STLINK_APIV3_GET_COM_FREQ 0x62 - -#define STLINK_APIV3_GET_VERSION_EX 0xFB - -#define STLINK_DEBUG_APIV2_DRIVE_NRST_LOW 0x00 -#define STLINK_DEBUG_APIV2_DRIVE_NRST_HIGH 0x01 -#define STLINK_DEBUG_APIV2_DRIVE_NRST_PULSE 0x02 - -#define STLINK_DEBUG_PORT_ACCESS 0xffff - -#define STLINK_TRACE_SIZE 4096 -#define STLINK_TRACE_MAX_HZ 2000000 -#define STLINK_V3_TRACE_MAX_HZ 24000000 - -#define STLINK_V3_MAX_FREQ_NB 10 - -#define REQUEST_SENSE 0x03 -#define REQUEST_SENSE_LENGTH 18 - -/* STLINK TCP commands */ -#define STLINK_TCP_CMD_REFRESH_DEVICE_LIST 0x00 -#define STLINK_TCP_CMD_GET_NB_DEV 0x01 -#define STLINK_TCP_CMD_GET_DEV_INFO 0x02 -#define STLINK_TCP_CMD_OPEN_DEV 0x03 -#define STLINK_TCP_CMD_CLOSE_DEV 0x04 -#define STLINK_TCP_CMD_SEND_USB_CMD 0x05 -#define STLINK_TCP_CMD_GET_SERVER_VERSION 0x06 -#define STLINK_TCP_CMD_GET_NB_OF_DEV_CLIENTS 0x07 - -/* STLINK TCP constants */ -#define OPENOCD_STLINK_TCP_API_VERSION 1 -#define STLINK_TCP_REQUEST_WRITE 0 -#define STLINK_TCP_REQUEST_READ 1 -#define STLINK_TCP_REQUEST_READ_SWO 3 -#define STLINK_TCP_SS_SIZE 4 -#define STLINK_TCP_USB_CMD_SIZE 32 -#define STLINK_TCP_SERIAL_SIZE 32 -#define STLINK_TCP_SEND_BUFFER_SIZE 10240 -#define STLINK_TCP_RECV_BUFFER_SIZE 10240 - -/* STLINK TCP command status */ -#define STLINK_TCP_SS_OK 0x00000001 -#define STLINK_TCP_SS_MEMORY_PROBLEM 0x00001000 -#define STLINK_TCP_SS_TIMEOUT 0x00001001 -#define STLINK_TCP_SS_BAD_PARAMETER 0x00001002 -#define STLINK_TCP_SS_OPEN_ERR 0x00001003 -#define STLINK_TCP_SS_TRUNCATED_DATA 0x00001052 -#define STLINK_TCP_SS_CMD_NOT_AVAILABLE 0x00001053 -#define STLINK_TCP_SS_TCP_ERROR 0x00002001 -#define STLINK_TCP_SS_TCP_CANT_CONNECT 0x00002002 -#define STLINK_TCP_SS_WIN32_ERROR 0x00010000 - -/* - * Map the relevant features, quirks and workaround for specific firmware - * version of stlink - */ -#define STLINK_F_HAS_TRACE BIT(0) /* v2>=j13 || v3 */ -#define STLINK_F_HAS_GETLASTRWSTATUS2 BIT(1) /* v2>=j15 || v3 */ -#define STLINK_F_HAS_SWD_SET_FREQ BIT(2) /* v2>=j22 */ -#define STLINK_F_HAS_JTAG_SET_FREQ BIT(3) /* v2>=j24 */ -#define STLINK_F_QUIRK_JTAG_DP_READ BIT(4) /* v2>=j24 && v2<j32 */ -#define STLINK_F_HAS_DAP_REG BIT(5) /* v2>=j24 || v3 */ -#define STLINK_F_HAS_MEM_16BIT BIT(6) /* v2>=j26 || v3 */ -#define STLINK_F_HAS_AP_INIT BIT(7) /* v2>=j28 || v3 */ -#define STLINK_F_FIX_CLOSE_AP BIT(8) /* v2>=j29 || v3 */ -#define STLINK_F_HAS_DPBANKSEL BIT(9) /* v2>=j32 || v3>=j2 */ -#define STLINK_F_HAS_RW8_512BYTES BIT(10) /* v3>=j6 */ - -/* aliases */ -#define STLINK_F_HAS_TARGET_VOLT STLINK_F_HAS_TRACE -#define STLINK_F_HAS_FPU_REG STLINK_F_HAS_GETLASTRWSTATUS2 -#define STLINK_F_HAS_MEM_WR_NO_INC STLINK_F_HAS_MEM_16BIT -#define STLINK_F_HAS_MEM_RD_NO_INC STLINK_F_HAS_DPBANKSEL -#define STLINK_F_HAS_RW_MISC STLINK_F_HAS_DPBANKSEL -#define STLINK_F_HAS_CSW STLINK_F_HAS_DPBANKSEL - -#define STLINK_REGSEL_IS_FPU(x) ((x) > 0x1F) - struct speed_map { int speed; int speed_divisor; @@ -554,7 +74,6 @@ static const struct speed_map stlink_khz_to_speed_map_jtag[] = { {140, 256} }; -static void stlink_usb_init_buffer(void *handle, uint8_t direction, uint32_t size); static int stlink_swim_status(void *handle); static void stlink_dump_speed_map(const struct speed_map *map, unsigned int map_size); static int stlink_get_com_freq(void *handle, bool is_jtag, struct speed_map *map); @@ -574,470 +93,6 @@ static unsigned int stlink_usb_block(void *handle) return STLINK_MAX_RW8; } -#ifdef USE_LIBUSB_ASYNCIO - -static LIBUSB_CALL void sync_transfer_cb(struct libusb_transfer *transfer) -{ - int *completed = transfer->user_data; - *completed = 1; - /* caller interprets result and frees transfer */ -} - - -static void sync_transfer_wait_for_completion(struct libusb_transfer *transfer) -{ - int r, *completed = transfer->user_data; - - while (!*completed) { - r = jtag_libusb_handle_events_completed(completed); - if (r < 0) { - if (r == LIBUSB_ERROR_INTERRUPTED) - continue; - libusb_cancel_transfer(transfer); - continue; - } - } -} - - -static int transfer_error_status(const struct libusb_transfer *transfer) -{ - int r = 0; - - switch (transfer->status) { - case LIBUSB_TRANSFER_COMPLETED: - r = 0; - break; - case LIBUSB_TRANSFER_TIMED_OUT: - r = LIBUSB_ERROR_TIMEOUT; - break; - case LIBUSB_TRANSFER_STALL: - r = LIBUSB_ERROR_PIPE; - break; - case LIBUSB_TRANSFER_OVERFLOW: - r = LIBUSB_ERROR_OVERFLOW; - break; - case LIBUSB_TRANSFER_NO_DEVICE: - r = LIBUSB_ERROR_NO_DEVICE; - break; - case LIBUSB_TRANSFER_ERROR: - case LIBUSB_TRANSFER_CANCELLED: - r = LIBUSB_ERROR_IO; - break; - default: - r = LIBUSB_ERROR_OTHER; - break; - } - - return r; -} - -struct jtag_xfer { - int ep; - uint8_t *buf; - size_t size; - /* Internal */ - int retval; - int completed; - size_t transfer_size; - struct libusb_transfer *transfer; -}; - -static int jtag_libusb_bulk_transfer_n( - struct libusb_device_handle *dev_handle, - struct jtag_xfer *transfers, - size_t n_transfers, - int timeout) -{ - int retval = 0; - int returnval = ERROR_OK; - - - for (size_t i = 0; i < n_transfers; ++i) { - transfers[i].retval = 0; - transfers[i].completed = 0; - transfers[i].transfer_size = 0; - transfers[i].transfer = libusb_alloc_transfer(0); - - if (!transfers[i].transfer) { - for (size_t j = 0; j < i; ++j) - libusb_free_transfer(transfers[j].transfer); - - LOG_DEBUG("ERROR, failed to alloc usb transfers"); - for (size_t k = 0; k < n_transfers; ++k) - transfers[k].retval = LIBUSB_ERROR_NO_MEM; - return ERROR_FAIL; - } - } - - for (size_t i = 0; i < n_transfers; ++i) { - libusb_fill_bulk_transfer( - transfers[i].transfer, - dev_handle, - transfers[i].ep, transfers[i].buf, transfers[i].size, - sync_transfer_cb, &transfers[i].completed, timeout); - transfers[i].transfer->type = LIBUSB_TRANSFER_TYPE_BULK; - - retval = libusb_submit_transfer(transfers[i].transfer); - if (retval < 0) { - LOG_DEBUG("ERROR, failed to submit transfer %zu, error %d", i, retval); - - /* Probably no point continuing to submit transfers once a submission fails. - * As a result, tag all remaining transfers as errors. - */ - for (size_t j = i; j < n_transfers; ++j) - transfers[j].retval = retval; - - returnval = ERROR_FAIL; - break; - } - } - - /* Wait for every submitted USB transfer to complete. - */ - for (size_t i = 0; i < n_transfers; ++i) { - if (transfers[i].retval == 0) { - sync_transfer_wait_for_completion(transfers[i].transfer); - - retval = transfer_error_status(transfers[i].transfer); - if (retval) { - returnval = ERROR_FAIL; - transfers[i].retval = retval; - LOG_DEBUG("ERROR, transfer %zu failed, error %d", i, retval); - } else { - /* Assuming actual_length is only valid if there is no transfer error. - */ - transfers[i].transfer_size = transfers[i].transfer->actual_length; - } - } - - libusb_free_transfer(transfers[i].transfer); - transfers[i].transfer = NULL; - } - - return returnval; -} - -#endif - - -/** */ -static int stlink_usb_xfer_v1_get_status(void *handle) -{ - struct stlink_usb_handle_s *h = handle; - int tr, ret; - - assert(handle); - - /* read status */ - memset(h->cmdbuf, 0, STLINK_SG_SIZE); - - ret = jtag_libusb_bulk_read(h->usb_backend_priv.fd, h->rx_ep, (char *)h->cmdbuf, 13, - STLINK_READ_TIMEOUT, &tr); - if (ret || tr != 13) - return ERROR_FAIL; - - uint32_t t1; - - t1 = buf_get_u32(h->cmdbuf, 0, 32); - - /* check for USBS */ - if (t1 != 0x53425355) - return ERROR_FAIL; - /* - * CSW status: - * 0 success - * 1 command failure - * 2 phase error - */ - if (h->cmdbuf[12] != 0) - return ERROR_FAIL; - - return ERROR_OK; -} - -#ifdef USE_LIBUSB_ASYNCIO -static int stlink_usb_xfer_rw(void *handle, int cmdsize, const uint8_t *buf, int size) -{ - struct stlink_usb_handle_s *h = handle; - - assert(handle); - - size_t n_transfers = 0; - struct jtag_xfer transfers[2]; - - memset(transfers, 0, sizeof(transfers)); - - transfers[0].ep = h->tx_ep; - transfers[0].buf = h->cmdbuf; - transfers[0].size = cmdsize; - - ++n_transfers; - - if (h->direction == h->tx_ep && size) { - transfers[1].ep = h->tx_ep; - transfers[1].buf = (uint8_t *)buf; - transfers[1].size = size; - - ++n_transfers; - } else if (h->direction == h->rx_ep && size) { - transfers[1].ep = h->rx_ep; - transfers[1].buf = (uint8_t *)buf; - transfers[1].size = size; - - ++n_transfers; - } - - return jtag_libusb_bulk_transfer_n( - h->usb_backend_priv.fd, - transfers, - n_transfers, - STLINK_WRITE_TIMEOUT); -} -#else -static int stlink_usb_xfer_rw(void *handle, int cmdsize, const uint8_t *buf, int size) -{ - struct stlink_usb_handle_s *h = handle; - int tr, ret; - - assert(handle); - - ret = jtag_libusb_bulk_write(h->usb_backend_priv.fd, h->tx_ep, (char *)h->cmdbuf, - cmdsize, STLINK_WRITE_TIMEOUT, &tr); - if (ret || tr != cmdsize) - return ERROR_FAIL; - - if (h->direction == h->tx_ep && size) { - ret = jtag_libusb_bulk_write(h->usb_backend_priv.fd, h->tx_ep, (char *)buf, - size, STLINK_WRITE_TIMEOUT, &tr); - if (ret || tr != size) { - LOG_DEBUG("bulk write failed"); - return ERROR_FAIL; - } - } else if (h->direction == h->rx_ep && size) { - ret = jtag_libusb_bulk_read(h->usb_backend_priv.fd, h->rx_ep, (char *)buf, - size, STLINK_READ_TIMEOUT, &tr); - if (ret || tr != size) { - LOG_DEBUG("bulk read failed"); - return ERROR_FAIL; - } - } - - return ERROR_OK; -} -#endif - -/** */ -static int stlink_usb_xfer_v1_get_sense(void *handle) -{ - int res; - struct stlink_usb_handle_s *h = handle; - - assert(handle); - - stlink_usb_init_buffer(handle, h->rx_ep, 16); - - h->cmdbuf[h->cmdidx++] = REQUEST_SENSE; - h->cmdbuf[h->cmdidx++] = 0; - h->cmdbuf[h->cmdidx++] = 0; - h->cmdbuf[h->cmdidx++] = 0; - h->cmdbuf[h->cmdidx++] = REQUEST_SENSE_LENGTH; - - res = stlink_usb_xfer_rw(handle, REQUEST_SENSE_LENGTH, h->databuf, 16); - - if (res != ERROR_OK) - return res; - - if (stlink_usb_xfer_v1_get_status(handle) != ERROR_OK) - return ERROR_FAIL; - - return ERROR_OK; -} - -/** */ -static int stlink_usb_usb_read_trace(void *handle, const uint8_t *buf, int size) -{ - struct stlink_usb_handle_s *h = handle; - int tr, ret; - - ret = jtag_libusb_bulk_read(h->usb_backend_priv.fd, h->trace_ep, (char *)buf, size, - STLINK_READ_TIMEOUT, &tr); - if (ret || tr != size) { - LOG_ERROR("bulk trace read failed"); - return ERROR_FAIL; - } - - return ERROR_OK; -} - -/* - transfers block in cmdbuf - <size> indicates number of bytes in the following - data phase. - Ignore the (eventual) error code in the received packet. -*/ -static int stlink_usb_usb_xfer_noerrcheck(void *handle, const uint8_t *buf, int size) -{ - int err, cmdsize = STLINK_CMD_SIZE_V2; - struct stlink_usb_handle_s *h = handle; - - assert(handle); - - if (h->version.stlink == 1) { - cmdsize = STLINK_SG_SIZE; - /* put length in bCBWCBLength */ - h->cmdbuf[14] = h->cmdidx-15; - } - - err = stlink_usb_xfer_rw(handle, cmdsize, buf, size); - - if (err != ERROR_OK) - return err; - - if (h->version.stlink == 1) { - if (stlink_usb_xfer_v1_get_status(handle) != ERROR_OK) { - /* check csw status */ - if (h->cmdbuf[12] == 1) { - LOG_DEBUG("get sense"); - if (stlink_usb_xfer_v1_get_sense(handle) != ERROR_OK) - return ERROR_FAIL; - } - return ERROR_FAIL; - } - } - - return ERROR_OK; -} - - -static int stlink_tcp_send_cmd(void *handle, int send_size, int recv_size, bool check_tcp_status) -{ - struct stlink_usb_handle_s *h = handle; - - assert(handle); - - /* send the TCP command */ - int sent_size = send(h->tcp_backend_priv.fd, (void *)h->tcp_backend_priv.send_buf, send_size, 0); - if (sent_size != send_size) { - LOG_ERROR("failed to send USB CMD"); - if (sent_size == -1) - LOG_DEBUG("socket send error: %s (errno %d)", strerror(errno), errno); - else - LOG_DEBUG("sent size %d (expected %d)", sent_size, send_size); - return ERROR_FAIL; - } - - /* read the TCP response */ - int retval = ERROR_OK; - int remaining_bytes = recv_size; - uint8_t *recv_buf = h->tcp_backend_priv.recv_buf; - const int64_t timeout = timeval_ms() + 1000; /* 1 second */ - - while (remaining_bytes > 0) { - if (timeval_ms() > timeout) { - LOG_DEBUG("received size %d (expected %d)", recv_size - remaining_bytes, recv_size); - retval = ERROR_TIMEOUT_REACHED; - break; - } - - keep_alive(); - int received = recv(h->tcp_backend_priv.fd, (void *)recv_buf, remaining_bytes, 0); - - if (received == -1) { - LOG_DEBUG("socket recv error: %s (errno %d)", strerror(errno), errno); - retval = ERROR_FAIL; - break; - } - - recv_buf += received; - remaining_bytes -= received; - } - - if (retval != ERROR_OK) { - LOG_ERROR("failed to receive USB CMD response"); - return retval; - } - - if (check_tcp_status) { - uint32_t tcp_ss = le_to_h_u32(h->tcp_backend_priv.recv_buf); - if (tcp_ss != STLINK_TCP_SS_OK) { - LOG_ERROR("TCP error status 0x%X", tcp_ss); - return ERROR_FAIL; - } - } - - return ERROR_OK; -} - -/** */ -static int stlink_tcp_xfer_noerrcheck(void *handle, const uint8_t *buf, int size) -{ - struct stlink_usb_handle_s *h = handle; - - int send_size = STLINK_TCP_USB_CMD_SIZE; - int recv_size = STLINK_TCP_SS_SIZE; - - assert(handle); - - /* prepare the TCP command */ - h->tcp_backend_priv.send_buf[0] = STLINK_TCP_CMD_SEND_USB_CMD; - memset(&h->tcp_backend_priv.send_buf[1], 0, 3); /* reserved for alignment and future use, must be zero */ - h_u32_to_le(&h->tcp_backend_priv.send_buf[4], h->tcp_backend_priv.connect_id); - /* tcp_backend_priv.send_buf[8..23] already contains the constructed stlink command */ - h->tcp_backend_priv.send_buf[24] = h->direction; - memset(&h->tcp_backend_priv.send_buf[25], 0, 3); /* reserved for alignment and future use, must be zero */ - - h_u32_to_le(&h->tcp_backend_priv.send_buf[28], size); - - /* - * if the xfer is a write request (tx_ep) - * > then buf content will be copied - * into &cmdbuf[32]. - * else : the xfer is a read or trace read request (rx_ep or trace_ep) - * > the buf content will be filled from &databuf[4]. - * - * note : if h->direction is trace_ep, h->cmdbuf is zeros. - */ - - if (h->direction == h->tx_ep) { /* STLINK_TCP_REQUEST_WRITE */ - send_size += size; - if (send_size > STLINK_TCP_SEND_BUFFER_SIZE) { - LOG_ERROR("STLINK_TCP command buffer overflow"); - return ERROR_FAIL; - } - memcpy(&h->tcp_backend_priv.send_buf[32], buf, size); - } else { /* STLINK_TCP_REQUEST_READ or STLINK_TCP_REQUEST_READ_SWO */ - recv_size += size; - if (recv_size > STLINK_TCP_RECV_BUFFER_SIZE) { - LOG_ERROR("STLINK_TCP data buffer overflow"); - return ERROR_FAIL; - } - } - - int ret = stlink_tcp_send_cmd(h, send_size, recv_size, true); - if (ret != ERROR_OK) - return ret; - - if (h->direction != h->tx_ep) { - /* the read data is located in tcp_backend_priv.recv_buf[4] */ - /* most of the case it will be copying the data from tcp_backend_priv.recv_buf[4] - * to handle->cmd_buff which are the same, so let's avoid unnecessary copying */ - if (buf != &h->tcp_backend_priv.recv_buf[4]) - memcpy((uint8_t *)buf, &h->tcp_backend_priv.recv_buf[4], size); - } - - return ERROR_OK; -} - -/** */ -static int stlink_tcp_read_trace(void *handle, const uint8_t *buf, int size) -{ - struct stlink_usb_handle_s *h = handle; - - stlink_usb_init_buffer(h, h->trace_ep, 0); - return stlink_tcp_xfer_noerrcheck(handle, buf, size); -} - /** Converts an STLINK status code held in the first byte of a response to an openocd error, logs any error/wait status as debug output. @@ -1224,7 +279,7 @@ static void stlink_usb_xfer_v1_create_cmd(void *handle, uint8_t direction, uint3 } /** */ -static void stlink_usb_init_buffer(void *handle, uint8_t direction, uint32_t size) +void stlink_usb_init_buffer(void *handle, uint8_t direction, uint32_t size) { struct stlink_usb_handle_s *h = handle; @@ -1240,7 +295,7 @@ static void stlink_usb_init_buffer(void *handle, uint8_t direction, uint32_t siz } /** */ -static int stlink_usb_version(void *handle) +int stlink_usb_version(void *handle) { int res; uint32_t flags; @@ -1637,7 +692,7 @@ static enum stlink_mode stlink_get_mode(enum hl_transports t) } /** */ -static int stlink_usb_exit_mode(void *handle) +int stlink_usb_exit_mode(void *handle) { int res; uint8_t mode; @@ -3195,59 +2250,6 @@ static int stlink_speed(void *handle, int khz, bool query) return khz; } -/** */ -static int stlink_usb_usb_close(void *handle) -{ - struct stlink_usb_handle_s *h = handle; - - if (!h) - return ERROR_OK; - - if (h->usb_backend_priv.fd) { - stlink_usb_exit_mode(h); - /* do not check return code, it prevent - us from closing jtag_libusb */ - jtag_libusb_close(h->usb_backend_priv.fd); - } - - free(h->cmdbuf); - free(h->databuf); - - return ERROR_OK; -} - -/** */ -static int stlink_tcp_close(void *handle) -{ - struct stlink_usb_handle_s *h = handle; - - if (!h) - return ERROR_OK; - - int ret = ERROR_OK; - if (h->tcp_backend_priv.connected) { - if (h->tcp_backend_priv.connect_id) { - stlink_usb_exit_mode(h); - - /* close the stlink */ - h->tcp_backend_priv.send_buf[0] = STLINK_TCP_CMD_CLOSE_DEV; - memset(&h->tcp_backend_priv.send_buf[1], 0, 4); /* reserved */ - h_u32_to_le(&h->tcp_backend_priv.send_buf[4], h->tcp_backend_priv.connect_id); - ret = stlink_tcp_send_cmd(h, 8, 4, true); - if (ret != ERROR_OK) - LOG_ERROR("cannot close the STLINK"); - } - - if (close_socket(h->tcp_backend_priv.fd) != 0) - LOG_ERROR("error closing the socket, errno: %s", strerror(errno)); - } - - free(h->tcp_backend_priv.send_buf); - free(h->tcp_backend_priv.recv_buf); - - return ret; -} - /** */ static int stlink_close(void *handle) { @@ -3262,418 +2264,8 @@ static int stlink_close(void *handle) return ERROR_OK; } -/* Compute ST-Link serial number from the device descriptor - * this function will help to work-around a bug in old ST-Link/V2 DFU - * the buggy DFU returns an incorrect serial in the USB descriptor - * example for the following serial "57FF72067265575742132067" - * - the correct descriptor serial is: - * 0x32, 0x03, 0x35, 0x00, 0x37, 0x00, 0x46, 0x00, 0x46, 0x00, 0x37, 0x00, 0x32, 0x00 ... - * this contains the length (0x32 = 50), the type (0x3 = DT_STRING) and the serial in unicode format - * the serial part is: 0x0035, 0x0037, 0x0046, 0x0046, 0x0037, 0x0032 ... >> 57FF72 ... - * this format could be read correctly by 'libusb_get_string_descriptor_ascii' - * so this case is managed by libusb_helper::string_descriptor_equal - * - the buggy DFU is not doing any unicode conversion and returns a raw serial data in the descriptor - * 0x1a, 0x03, 0x57, 0x00, 0xFF, 0x00, 0x72, 0x00 ... - * >> 57 FF 72 ... - * based on the length (0x1a = 26) we could easily decide if we have to fixup the serial - * and then we have just to convert the raw data into printable characters using sprintf - */ -static char *stlink_usb_get_alternate_serial(struct libusb_device_handle *device, - struct libusb_device_descriptor *dev_desc) -{ - int usb_retval; - unsigned char desc_serial[(STLINK_SERIAL_LEN + 1) * 2]; - - if (dev_desc->iSerialNumber == 0) - return NULL; - - /* get the LANGID from String Descriptor Zero */ - usb_retval = libusb_get_string_descriptor(device, 0, 0, desc_serial, - sizeof(desc_serial)); - - if (usb_retval < LIBUSB_SUCCESS) { - LOG_ERROR("libusb_get_string_descriptor() failed: %s(%d)", - libusb_error_name(usb_retval), usb_retval); - return NULL; - } else if (usb_retval < 4) { - /* the size should be least 4 bytes to contain a minimum of 1 supported LANGID */ - LOG_ERROR("could not get the LANGID"); - return NULL; - } - - uint32_t langid = desc_serial[2] | (desc_serial[3] << 8); - - /* get the serial */ - usb_retval = libusb_get_string_descriptor(device, dev_desc->iSerialNumber, - langid, desc_serial, sizeof(desc_serial)); - - unsigned char len = desc_serial[0]; - - if (usb_retval < LIBUSB_SUCCESS) { - LOG_ERROR("libusb_get_string_descriptor() failed: %s(%d)", - libusb_error_name(usb_retval), usb_retval); - return NULL; - } else if (desc_serial[1] != LIBUSB_DT_STRING || len > usb_retval) { - LOG_ERROR("invalid string in ST-LINK USB serial descriptor"); - return NULL; - } - - if (len == ((STLINK_SERIAL_LEN + 1) * 2)) { - /* good ST-Link adapter, this case is managed by - * libusb::libusb_get_string_descriptor_ascii */ - return NULL; - } else if (len != ((STLINK_SERIAL_LEN / 2 + 1) * 2)) { - LOG_ERROR("unexpected serial length (%d) in descriptor", len); - return NULL; - } - - /* else (len == 26) => buggy ST-Link */ - - char *alternate_serial = malloc((STLINK_SERIAL_LEN + 1) * sizeof(char)); - if (!alternate_serial) - return NULL; - - for (unsigned int i = 0; i < STLINK_SERIAL_LEN; i += 2) - sprintf(alternate_serial + i, "%02X", desc_serial[i + 2]); - - alternate_serial[STLINK_SERIAL_LEN] = '\0'; - - return alternate_serial; -} - -/** */ -static int stlink_usb_usb_open(void *handle, struct hl_interface_param_s *param) -{ - struct stlink_usb_handle_s *h = handle; - int err, retry_count = 1; - - h->cmdbuf = malloc(STLINK_SG_SIZE); - h->databuf = malloc(STLINK_DATA_SIZE); - - if (!h->cmdbuf || !h->databuf) - return ERROR_FAIL; - - /* - On certain host USB configurations(e.g. MacBook Air) - STLINKv2 dongle seems to have its FW in a funky state if, - after plugging it in, you try to use openocd with it more - then once (by launching and closing openocd). In cases like - that initial attempt to read the FW info via - stlink_usb_version will fail and the device has to be reset - in order to become operational. - */ - do { - if (jtag_libusb_open(param->vid, param->pid, param->serial, - &h->usb_backend_priv.fd, stlink_usb_get_alternate_serial) != ERROR_OK) { - LOG_ERROR("open failed"); - return ERROR_FAIL; - } - - jtag_libusb_set_configuration(h->usb_backend_priv.fd, 0); - - if (libusb_claim_interface(h->usb_backend_priv.fd, 0) != ERROR_OK) { - LOG_DEBUG("claim interface failed"); - return ERROR_FAIL; - } - - /* RX EP is common for all versions */ - h->rx_ep = STLINK_RX_EP; - - uint16_t pid; - if (jtag_libusb_get_pid(libusb_get_device(h->usb_backend_priv.fd), &pid) != ERROR_OK) { - LOG_DEBUG("libusb_get_pid failed"); - return ERROR_FAIL; - } - - /* wrap version for first read */ - switch (pid) { - case STLINK_V1_PID: - h->version.stlink = 1; - h->tx_ep = STLINK_TX_EP; - break; - case STLINK_V3_USBLOADER_PID: - case STLINK_V3E_PID: - case STLINK_V3S_PID: - case STLINK_V3_2VCP_PID: - case STLINK_V3E_NO_MSD_PID: - h->version.stlink = 3; - h->tx_ep = STLINK_V2_1_TX_EP; - h->trace_ep = STLINK_V2_1_TRACE_EP; - break; - case STLINK_V2_1_PID: - case STLINK_V2_1_NO_MSD_PID: - h->version.stlink = 2; - h->tx_ep = STLINK_V2_1_TX_EP; - h->trace_ep = STLINK_V2_1_TRACE_EP; - break; - default: - /* fall through - we assume V2 to be the default version*/ - case STLINK_V2_PID: - h->version.stlink = 2; - h->tx_ep = STLINK_TX_EP; - h->trace_ep = STLINK_TRACE_EP; - break; - } - - /* get the device version */ - err = stlink_usb_version(h); - - if (err == ERROR_OK) { - break; - } else if (h->version.stlink == 1 || - retry_count == 0) { - LOG_ERROR("read version failed"); - return ERROR_FAIL; - } else { - err = libusb_release_interface(h->usb_backend_priv.fd, 0); - if (err != ERROR_OK) { - LOG_ERROR("release interface failed"); - return ERROR_FAIL; - } - - err = libusb_reset_device(h->usb_backend_priv.fd); - if (err != ERROR_OK) { - LOG_ERROR("reset device failed"); - return ERROR_FAIL; - } - - jtag_libusb_close(h->usb_backend_priv.fd); - /* - Give the device one second to settle down and - reenumerate. - */ - usleep(1 * 1000 * 1000); - retry_count--; - } - } while (1); - - return ERROR_OK; -} - -/** */ -static int stlink_tcp_open(void *handle, struct hl_interface_param_s *param) -{ - struct stlink_usb_handle_s *h = handle; - int ret; - - /* SWIM is not supported using stlink-server */ - if (h->st_mode == STLINK_MODE_DEBUG_SWIM) { - LOG_ERROR("stlink-server does not support SWIM mode"); - return ERROR_FAIL; - } - - h->tcp_backend_priv.send_buf = malloc(STLINK_TCP_SEND_BUFFER_SIZE); - h->tcp_backend_priv.recv_buf = malloc(STLINK_TCP_RECV_BUFFER_SIZE); - - if (!h->tcp_backend_priv.send_buf || !h->tcp_backend_priv.recv_buf) - return ERROR_FAIL; - - h->cmdbuf = &h->tcp_backend_priv.send_buf[8]; - h->databuf = &h->tcp_backend_priv.recv_buf[4]; - - /* configure directions */ - h->rx_ep = STLINK_TCP_REQUEST_READ; - h->tx_ep = STLINK_TCP_REQUEST_WRITE; - h->trace_ep = STLINK_TCP_REQUEST_READ_SWO; - - h->tcp_backend_priv.fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); - h->tcp_backend_priv.connected = false; - h->tcp_backend_priv.device_id = 0; - h->tcp_backend_priv.connect_id = 0; - - if (h->tcp_backend_priv.fd == -1) { - LOG_ERROR("error creating the socket, errno: %s", strerror(errno)); - return ERROR_FAIL; - } - - struct sockaddr_in serv; - memset(&serv, 0, sizeof(struct sockaddr_in)); - serv.sin_family = AF_INET; - serv.sin_port = htons(param->stlink_tcp_port); - serv.sin_addr.s_addr = inet_addr("127.0.0.1"); - - LOG_DEBUG("socket : %x", h->tcp_backend_priv.fd); - - int optval = 1; - if (setsockopt(h->tcp_backend_priv.fd, IPPROTO_TCP, TCP_NODELAY, (const void *)&optval, sizeof(int)) == -1) { - LOG_ERROR("cannot set sock option 'TCP_NODELAY', errno: %s", strerror(errno)); - return ERROR_FAIL; - } - - optval = STLINK_TCP_RECV_BUFFER_SIZE; - if (setsockopt(h->tcp_backend_priv.fd, SOL_SOCKET, SO_RCVBUF, (const void *)&optval, sizeof(int)) == -1) { - LOG_ERROR("cannot set sock option 'SO_RCVBUF', errno: %s", strerror(errno)); - return ERROR_FAIL; - } - - optval = STLINK_TCP_SEND_BUFFER_SIZE; - if (setsockopt(h->tcp_backend_priv.fd, SOL_SOCKET, SO_SNDBUF, (const void *)&optval, sizeof(int)) == -1) { - LOG_ERROR("cannot set sock option 'SO_SNDBUF', errno: %s", strerror(errno)); - return ERROR_FAIL; - } - - if (connect(h->tcp_backend_priv.fd, (const struct sockaddr *)&serv, sizeof(serv)) == -1) { - LOG_ERROR("cannot connect to stlink server, errno: %s", strerror(errno)); - return ERROR_FAIL; - } - - h->tcp_backend_priv.connected = true; - - LOG_INFO("connected to stlink-server"); - - /* print stlink-server version */ - h->tcp_backend_priv.send_buf[0] = STLINK_TCP_CMD_GET_SERVER_VERSION; - h->tcp_backend_priv.send_buf[1] = OPENOCD_STLINK_TCP_API_VERSION; - memset(&h->tcp_backend_priv.send_buf[2], 0, 2); /* reserved */ - ret = stlink_tcp_send_cmd(h, 4, 16, false); - if (ret != ERROR_OK) { - LOG_ERROR("cannot get the stlink-server version"); - return ERROR_FAIL; - } - - uint32_t api_ver = le_to_h_u32(&h->tcp_backend_priv.recv_buf[0]); - uint32_t ver_major = le_to_h_u32(&h->tcp_backend_priv.recv_buf[4]); - uint32_t ver_minor = le_to_h_u32(&h->tcp_backend_priv.recv_buf[8]); - uint32_t ver_build = le_to_h_u32(&h->tcp_backend_priv.recv_buf[12]); - LOG_INFO("stlink-server API v%d, version %d.%d.%d", - api_ver, ver_major, ver_minor, ver_build); - - /* in stlink-server API v1 sending more than 1428 bytes will cause stlink-server - * to crash in windows: select a safe default value (1K) */ - if (api_ver < 2) - h->max_mem_packet = (1 << 10); - - /* refresh stlink list (re-enumerate) */ - h->tcp_backend_priv.send_buf[0] = STLINK_TCP_CMD_REFRESH_DEVICE_LIST; - h->tcp_backend_priv.send_buf[1] = 0; /* don't clear the list, just refresh it */ - ret = stlink_tcp_send_cmd(h, 2, 4, true); - if (ret != ERROR_OK) - return ret; - - /* get the number of connected stlinks */ - h->tcp_backend_priv.send_buf[0] = STLINK_TCP_CMD_GET_NB_DEV; - ret = stlink_tcp_send_cmd(h, 1, 4, false); - if (ret != ERROR_OK) - return ret; - - uint32_t connected_stlinks = le_to_h_u32(h->tcp_backend_priv.recv_buf); - - if (connected_stlinks == 0) { - LOG_ERROR("no ST-LINK detected"); - return ERROR_FAIL; - } - - LOG_DEBUG("%d ST-LINK detected", connected_stlinks); - - if (connected_stlinks > 255) { - LOG_WARNING("STLink server cannot handle more than 255 ST-LINK connected"); - connected_stlinks = 255; - } - - /* list all connected ST-Link and seek for the requested vid:pid and serial */ - char serial[STLINK_TCP_SERIAL_SIZE + 1] = {0}; - uint8_t stlink_used; - bool stlink_id_matched = false; - bool stlink_serial_matched = (!param->serial); - - for (uint32_t stlink_id = 0; stlink_id < connected_stlinks; stlink_id++) { - /* get the stlink info */ - h->tcp_backend_priv.send_buf[0] = STLINK_TCP_CMD_GET_DEV_INFO; - h->tcp_backend_priv.send_buf[1] = (uint8_t)stlink_id; - memset(&h->tcp_backend_priv.send_buf[2], 0, 2); /* reserved */ - h_u32_to_le(&h->tcp_backend_priv.send_buf[4], 41); /* size of TDeviceInfo2 */ - ret = stlink_tcp_send_cmd(h, 8, 45, true); - if (ret != ERROR_OK) - return ret; - - h->tcp_backend_priv.device_id = le_to_h_u32(&h->tcp_backend_priv.recv_buf[4]); - memcpy(serial, &h->tcp_backend_priv.recv_buf[8], STLINK_TCP_SERIAL_SIZE); - h->vid = le_to_h_u16(&h->tcp_backend_priv.recv_buf[40]); - h->pid = le_to_h_u16(&h->tcp_backend_priv.recv_buf[42]); - stlink_used = h->tcp_backend_priv.recv_buf[44]; - - /* check the vid:pid */ - for (int i = 0; param->vid[i]; i++) { - if (param->vid[i] == h->vid && param->pid[i] == h->pid) { - stlink_id_matched = true; - break; - } - } - - if (!stlink_id_matched) - continue; - - /* check the serial if specified */ - if (param->serial) { - /* ST-Link server fixes the buggy serial returned by old ST-Link DFU - * for further details refer to stlink_usb_get_alternate_serial - * so if the user passes the buggy serial, we need to fix it before - * comparing with the serial returned by ST-Link server */ - if (strlen(param->serial) == STLINK_SERIAL_LEN / 2) { - char fixed_serial[STLINK_SERIAL_LEN + 1]; - - for (unsigned int i = 0; i < STLINK_SERIAL_LEN; i += 2) - sprintf(fixed_serial + i, "%02X", param->serial[i / 2]); - - fixed_serial[STLINK_SERIAL_LEN] = '\0'; - - stlink_serial_matched = strcmp(fixed_serial, serial) == 0; - } else - stlink_serial_matched = strcmp(param->serial, serial) == 0; - } - - if (!stlink_serial_matched) - LOG_DEBUG("Device serial number '%s' doesn't match requested serial '%s'", - serial, param->serial); - else /* exit the search loop if there is match */ - break; - } - - if (!stlink_id_matched) { - LOG_ERROR("ST-LINK open failed (vid/pid mismatch)"); - return ERROR_FAIL; - } - - if (!stlink_serial_matched) { - LOG_ERROR("ST-LINK open failed (serial mismatch)"); - return ERROR_FAIL; - } - - /* check if device is 'exclusively' used by another application */ - if (stlink_used) { - LOG_ERROR("the selected device is already used"); - return ERROR_FAIL; - } - - LOG_DEBUG("transport: vid: 0x%04x pid: 0x%04x serial: %s", h->vid, h->pid, serial); - - /* now let's open the stlink */ - h->tcp_backend_priv.send_buf[0] = STLINK_TCP_CMD_OPEN_DEV; - memset(&h->tcp_backend_priv.send_buf[1], 0, 4); /* reserved */ - h_u32_to_le(&h->tcp_backend_priv.send_buf[4], h->tcp_backend_priv.device_id); - ret = stlink_tcp_send_cmd(h, 8, 8, true); - if (ret != ERROR_OK) - return ret; - - h->tcp_backend_priv.connect_id = le_to_h_u32(&h->tcp_backend_priv.recv_buf[4]); - - /* get stlink version */ - return stlink_usb_version(h); -} - -static struct stlink_backend_s stlink_usb_backend = { - .open = stlink_usb_usb_open, - .close = stlink_usb_usb_close, - .xfer_noerrcheck = stlink_usb_usb_xfer_noerrcheck, - .read_trace = stlink_usb_usb_read_trace, -}; - -static struct stlink_backend_s stlink_tcp_backend = { - .open = stlink_tcp_open, - .close = stlink_tcp_close, - .xfer_noerrcheck = stlink_tcp_xfer_noerrcheck, - .read_trace = stlink_tcp_read_trace, -}; +extern struct stlink_backend_s stlink_usb_backend; +extern struct stlink_backend_s stlink_tcp_backend; static int stlink_open(struct hl_interface_param_s *param, enum stlink_mode mode, void **fd) { --
