This is an automated email from Gerrit. Tarek BOCHKATI ([email protected]) just uploaded a new patch set to Gerrit, which you can find at http://openocd.zylin.com/5652
-- gerrit commit e64b1333c38bed42137b04a29aa2a501df092006 Author: Tarek BOCHKATI <[email protected]> Date: Sun May 10 13:19:57 2020 +0100 stlink: split driver [RFC] this is an absolute move of code without unneeded modification. the driver is now split into: - stlink_usb.c : contains USB related functions. - stlink_server.c : contains the implementation to support ST-Link server Note: in stlink_usb there is special commands for stlink v1, needed only in stlink_usb.c Change-Id: Id4428424a6c835419f27c78064a291605225569c Signed-off-by: Tarek BOCHKATI <[email protected]> diff --git a/src/jtag/drivers/Makefile.am b/src/jtag/drivers/Makefile.am index 052a4ea..f7172de 100644 --- a/src/jtag/drivers/Makefile.am +++ b/src/jtag/drivers/Makefile.am @@ -131,6 +131,8 @@ DRIVERFILES += %D%/remote_bitbang.c endif if HLADAPTER DRIVERFILES += %D%/stlink/stlink.c +DRIVERFILES += %D%/stlink/stlink_usb.c +DRIVERFILES += %D%/stlink/stlink_server.c DRIVERFILES += %D%/ti_icdi_usb.c endif if OSBDM @@ -180,5 +182,6 @@ DRIVERHEADERS = \ %D%/versaloon/usbtoxxx/usbtoxxx_internal.h \ %D%/versaloon/versaloon.h \ %D%/versaloon/versaloon_include.h \ - %D%/versaloon/versaloon_internal.h + %D%/versaloon/versaloon_internal.h \ + %D%/stlink/stlink.h diff --git a/src/jtag/drivers/stlink/stlink.c b/src/jtag/drivers/stlink/stlink.c index d772a9d..934d7a0 100644 --- a/src/jtag/drivers/stlink/stlink.c +++ b/src/jtag/drivers/stlink/stlink.c @@ -28,374 +28,20 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. * ***************************************************************************/ -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif +#include "stlink.h" + + /* project specific includes */ -#include <helper/binarybuffer.h> + #include <helper/bits.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/target.h> #include <transport/transport.h> #include <target/cortex_m.h> -#if WIN32 -#include <winsock2.h> -#else -#include <sys/types.h> -#include <sys/socket.h> -#include <netinet/in.h> -#include <arpa/inet.h> -#include <sys/uio.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 (4096) -#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) - -/* - * 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. - */ -#define STLINK_MAX_RW8 (64) -#define STLINKV3_MAX_RW8 (512) - -/* "WAIT" responses will be retried (with exponential backoff) at - * most this many times before failing to caller. - */ -#define MAX_WAIT_RETRIES 8 - -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_server_priv_s { - /** */ - int fd; - /** */ - bool connected; - /** */ - uint32_t device_id; - /** */ - uint32_t connect_id; -}; - -struct stlink_interface_s { - /** */ - void *priv; - /** */ - int (*open)(void *handle, struct hl_interface_param_s *param); - /** */ - int (*close)(void *handle); - /** */ - int (*xfer)(void *handle, const uint8_t *buf, int size); - /** */ - int (*read)(void *handle, const uint8_t *buf, int size); -}; - -/** */ -struct stlink_usb_handle_s { - /** */ - struct stlink_interface_s *itf; - /** */ - uint8_t rx_ep; - /** */ - uint8_t tx_ep; - /** */ - uint8_t trace_ep; - /** */ - uint8_t cmdbuf[STLINK_SG_SIZE]; - /** */ - uint8_t cmdidx; - /** */ - uint8_t direction; - /** */ - uint8_t databuf[STLINK_DATA_SIZE]; - /** */ - 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; -}; - -#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 syncronization 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_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_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 2048 -#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) -#define STLINK_F_HAS_SWD_SET_FREQ BIT(1) -#define STLINK_F_HAS_JTAG_SET_FREQ BIT(2) -#define STLINK_F_HAS_MEM_16BIT BIT(3) -#define STLINK_F_HAS_GETLASTRWSTATUS2 BIT(4) -#define STLINK_F_HAS_DAP_REG BIT(5) -#define STLINK_F_QUIRK_JTAG_DP_READ BIT(6) -#define STLINK_F_HAS_AP_INIT BIT(7) -#define STLINK_F_HAS_DPBANKSEL BIT(8) -#define STLINK_F_HAS_RW8_512BYTES BIT(9) - /* aliases */ #define STLINK_F_HAS_TARGET_VOLT STLINK_F_HAS_TRACE @@ -431,7 +77,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); 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); @@ -450,453 +95,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; - - /* Assuming a single libusb context exists. There no existing interface into this - * module to pass a libusb context. - */ - struct libusb_context *ctx = NULL; - - while (!*completed) { - r = libusb_handle_events_completed(ctx, 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 == NULL) { - 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; - struct stlink_usb_priv_s *itf_priv = h->itf->priv; - int tr, ret; - - assert(handle != NULL); - - /* read status */ - memset(h->cmdbuf, 0, STLINK_SG_SIZE); - - ret = jtag_libusb_bulk_read(itf_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; - struct stlink_usb_priv_s *itf_priv = h->itf->priv; - - assert(handle != NULL); - - 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( - itf_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; - struct stlink_usb_priv_s *itf_priv = h->itf->priv; - int tr, ret; - - assert(handle != NULL); - - ret = jtag_libusb_bulk_write(itf_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(itf_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(itf_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 != NULL); - - 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_bulk_read(void *handle, const uint8_t *buf, int size) -{ - struct stlink_usb_handle_s *h = handle; - struct stlink_usb_priv_s *itf_priv = h->itf->priv; - int tr, ret; - - ret = jtag_libusb_bulk_read(itf_priv->fd, h->direction, (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 != NULL); - - 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_server_send_cmd(void *handle, const uint8_t *cmd, int cmd_size, - uint8_t *data, int data_size, bool check_tcp_status) -{ - struct stlink_usb_handle_s *h = handle; - struct stlink_server_priv_s *itf_priv = h->itf->priv; - - assert(handle != NULL); - - /* send the TCP command */ - int sent_size = send(itf_priv->fd, (char *) cmd, cmd_size, 0); - if (sent_size != cmd_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, cmd_size); - return ERROR_FAIL; - } - - keep_alive(); - - /* read the TCP response */ - int received_size = recv(itf_priv->fd, (char *) data, data_size, 0); - if (received_size != data_size) { - LOG_ERROR("failed to receive USB CMD response"); - if (received_size == -1) - LOG_DEBUG("socket recv error: %s (errno %d)", strerror(errno), errno); - else - LOG_DEBUG("received size %d (expected %d)", received_size, data_size); - return ERROR_FAIL; - } - - if (check_tcp_status) { - uint32_t tcp_ss = le_to_h_u32(data); - 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_server_xfer(void *handle, const uint8_t *buf, int size) -{ - struct stlink_usb_handle_s *h = handle; - struct stlink_server_priv_s *itf_priv = h->itf->priv; - - uint8_t cmdbuf[STLINK_TCP_SEND_BUFFER_SIZE]; - uint8_t databuf[STLINK_TCP_RECV_BUFFER_SIZE]; - int cmd_size = STLINK_TCP_USB_CMD_SIZE; - int data_size = STLINK_TCP_SS_SIZE; - - assert(handle != NULL); - - /* prepare the TCP command */ - cmdbuf[0] = STLINK_TCP_CMD_SEND_USB_CMD; - memset(&cmdbuf[1], 0, 3); /* reserved for alignment and future use, must be zero */ - h_u32_to_le(&cmdbuf[4], itf_priv->connect_id); - memcpy(&cmdbuf[8], h->cmdbuf, 16); - cmdbuf[24] = h->direction; - memset(&cmdbuf[25], 0, 3); /* reserved for alignment and future use, must be zero */ - - h_u32_to_le(&cmdbuf[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 */ - cmd_size += size; - if (cmd_size > STLINK_TCP_SEND_BUFFER_SIZE) { - LOG_ERROR("STLINK_TCP command buffer overflow"); - return ERROR_FAIL; - } - memcpy(&cmdbuf[32], buf, size); - } else { /* STLINK_TCP_REQUEST_READ or STLINK_TCP_REQUEST_READ_SWO */ - data_size += size; - if (data_size > STLINK_TCP_RECV_BUFFER_SIZE) { - LOG_ERROR("STLINK_TCP data buffer overflow"); - return ERROR_FAIL; - } - } - - int ret = stlink_server_send_cmd(h, cmdbuf, cmd_size, databuf, data_size, true); - if (ret != ERROR_OK) - return ret; - - if (h->direction != h->tx_ep) - memcpy((uint8_t *) buf, &databuf[STLINK_TCP_SS_SIZE], size); - - return ERROR_OK; -} - /** 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. @@ -1086,7 +284,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; @@ -1102,7 +300,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; @@ -1486,7 +684,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; @@ -2877,54 +2075,6 @@ static int stlink_speed(void *handle, int khz, bool query) } /** */ -static int stlink_usb_close(void *handle) -{ - struct stlink_usb_handle_s *h = handle; - struct stlink_usb_priv_s *itf_priv = h->itf->priv; - - if (h && itf_priv->fd) { - stlink_usb_exit_mode(h); - /* do not check return code, it prevent - us from closing jtag_libusb */ - jtag_libusb_close(itf_priv->fd); - } - - return ERROR_OK; -} - -/** */ -static int stlink_server_close(void *handle) -{ - struct stlink_usb_handle_s *h = handle; - struct stlink_server_priv_s *itf_priv = h->itf->priv; - - int ret = ERROR_OK; - uint8_t cmdbuf[STLINK_TCP_SEND_BUFFER_SIZE]; - uint8_t databuf[STLINK_TCP_RECV_BUFFER_SIZE]; - - if (h && itf_priv->connected) { - if (itf_priv->connect_id) { - stlink_usb_exit_mode(h); - - /* close the stlink */ - cmdbuf[0] = STLINK_TCP_CMD_CLOSE_DEV; - memset(&cmdbuf[1], 0, 4); /* reserved */ - h_u32_to_le(&cmdbuf[4], itf_priv->connect_id); - ret = stlink_server_send_cmd(h, cmdbuf, 8, databuf, 4, true); - if (ret != ERROR_OK) - LOG_ERROR("cannot close the STLINK"); - } - - if (close_socket(itf_priv->fd) != 0) { - LOG_ERROR("error closing the socket"); - LOG_DEBUG("close error: %s (errno %d)", strerror(errno), errno); - } - } - - return ret; -} - -/** */ static int stlink_close(void *handle) { if (handle != NULL) { @@ -2939,383 +2089,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 - */ -char *stlink_usb_get_alternate_serial(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 == NULL) - 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; - struct stlink_usb_priv_s *itf_priv = h->itf->priv; - int err, retry_count = 1; - - /* - 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, - &itf_priv->fd, stlink_usb_get_alternate_serial) != ERROR_OK) { - LOG_ERROR("open failed"); - return ERROR_FAIL; - } - - jtag_libusb_set_configuration(itf_priv->fd, 0); - - if (libusb_claim_interface(itf_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(itf_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: - 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(itf_priv->fd, 0); - if (err != ERROR_OK) { - LOG_ERROR("release interface failed"); - return ERROR_FAIL; - } - - err = libusb_reset_device(itf_priv->fd); - if (err != ERROR_OK) { - LOG_ERROR("reset device failed"); - return ERROR_FAIL; - } - - jtag_libusb_close(itf_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_server_open(void *handle, struct hl_interface_param_s *param) -{ - struct stlink_usb_handle_s *h = handle; - struct stlink_server_priv_s *itf_priv = h->itf->priv; - int ret; - uint8_t cmdbuf[STLINK_TCP_SEND_BUFFER_SIZE]; - uint8_t databuf[STLINK_TCP_RECV_BUFFER_SIZE]; - - /* 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; - } - - /* configure directions */ - h->rx_ep = STLINK_TCP_REQUEST_READ; - h->tx_ep = STLINK_TCP_REQUEST_WRITE; - h->trace_ep = STLINK_TCP_REQUEST_READ_SWO; - - itf_priv->fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); - itf_priv->connected = false; - itf_priv->device_id = 0; - itf_priv->connect_id = 0; - - struct sockaddr_in serv; - memset(&serv, 0, sizeof(struct sockaddr_in)); - serv.sin_family = AF_INET; - serv.sin_port = htons(param->stlink_server_port); - serv.sin_addr.s_addr = inet_addr("127.0.0.1"); - - LOG_DEBUG("socket : %x", itf_priv->fd); - - int res; - - int flag = 1; - res = setsockopt(itf_priv->fd, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(flag)); - if (res == -1) { - LOG_ERROR("cannot set sock option 'TCP_NODELEAY', errno: %s", strerror(errno)); - return ERROR_FAIL; - } - - int a = 49152; - res = setsockopt(itf_priv->fd, SOL_SOCKET, SO_RCVBUF, (char *) &a, sizeof(a)); - if (res == -1) { - LOG_ERROR("cannot set sock option 'SO_RCVBUF', errno: %s", strerror(errno)); - return ERROR_FAIL; - } - - res = setsockopt(itf_priv->fd, SOL_SOCKET, SO_SNDBUF, (char *) &a, sizeof(a)); - if (res == -1) { - LOG_ERROR("cannot set sock option 'SO_SNDBUF', errno: %s", strerror(errno)); - return ERROR_FAIL; - } - - if (connect(itf_priv->fd, (const struct sockaddr *) &serv, sizeof(serv)) == -1) { - LOG_ERROR("cannot connect to stlink server"); - return ERROR_FAIL; - } - - itf_priv->connected = true; - - LOG_INFO("connected to stlink-server"); - - /* print stlink-server version */ - cmdbuf[0] = STLINK_TCP_CMD_GET_SERVER_VERSION; - cmdbuf[1] = OPENOCD_STLINK_TCP_API_VERSION; - memset(&cmdbuf[2], 0, 2); /* reserved */ - ret = stlink_server_send_cmd(h, cmdbuf, 4, databuf, 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(&databuf[0]); - uint32_t ver_major = le_to_h_u32(&databuf[4]); - uint32_t ver_minor = le_to_h_u32(&databuf[8]); - uint32_t ver_build = le_to_h_u32(&databuf[12]); - LOG_INFO("stlink-server API v%d, version %d.%d.%d", - api_ver, ver_major, ver_minor, ver_build); - - /* refresh stlink list (re-enumerate) */ - cmdbuf[0] = STLINK_TCP_CMD_REFRESH_DEVICE_LIST; - cmdbuf[1] = 0; /* don't clear the list, just refresh it */ - ret = stlink_server_send_cmd(h, cmdbuf, 2, databuf, 4, true); - if (ret != ERROR_OK) - return ret; - - /* get the number of connected stlinks */ - cmdbuf[0] = STLINK_TCP_CMD_GET_NB_DEV; - ret = stlink_server_send_cmd(h, cmdbuf, 1, databuf, 4, false); - if (ret != ERROR_OK) - return ret; - - uint32_t connected_stlinks = le_to_h_u32(databuf); - - if (connected_stlinks == 0) { - LOG_ERROR("no ST-LINK detected"); - return ERROR_FAIL; - } - - LOG_DEBUG("%d ST-LINK detected", connected_stlinks); - - /* list all connected ST-Link and seek for the requested vid:pid and serial */ - uint8_t serial[STLINK_TCP_SERIAL_SIZE+1] = {0}; - uint8_t stlink_used; - bool stlink_id_matched = false; - bool stlink_serial_matched = (param->serial == NULL); - - for (uint32_t stlink_id = 0; stlink_id < connected_stlinks; stlink_id++) { - /* get the stlink info */ - cmdbuf[0] = STLINK_TCP_CMD_GET_DEV_INFO; - cmdbuf[1] = (uint8_t) stlink_id; - memset(&cmdbuf[2], 0, 2); /* reserved */ - h_u32_to_le(&cmdbuf[4], 41); /* size of TDeviceInfo2 */ - ret = stlink_server_send_cmd(h, cmdbuf, 8, databuf, 45, true); - if (ret != ERROR_OK) - return ret; - - itf_priv->device_id = le_to_h_u32(&databuf[4]); - memcpy(serial, &databuf[8], STLINK_TCP_SERIAL_SIZE); - h->vid = le_to_h_u16(&databuf[40]); - h->pid = le_to_h_u16(&databuf[42]); - stlink_used = databuf[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) - stlink_serial_matched = strcmp(param->serial, (const char *) serial) == 0; - - if (!stlink_serial_matched) - LOG_DEBUG("Device serial number '%s' doesn't match requested serial '%s'", - (const char *) 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 */ - cmdbuf[0] = STLINK_TCP_CMD_OPEN_DEV; - memset(&cmdbuf[1], 0, 4); /* reserved */ - h_u32_to_le(&cmdbuf[4], itf_priv->device_id); - ret = stlink_server_send_cmd(h, cmdbuf, 8, databuf, 8, true); - if (ret != ERROR_OK) - return ret; - - itf_priv->connect_id = le_to_h_u32(&databuf[4]); - - /* get stlink version */ - ret = stlink_usb_version(h); - if (ret != ERROR_OK) - return ERROR_FAIL; - - return ERROR_OK; -} - -static struct stlink_interface_s stlink_usb_itf = { - .open = stlink_usb_open, - .close = stlink_usb_close, - .xfer = stlink_usb_xfer_noerrcheck, - .read = stlink_usb_bulk_read, -}; - -static struct stlink_interface_s stlink_server_itf = { - .open = stlink_server_open, - .close = stlink_server_close, - .xfer = stlink_server_xfer, - .read = stlink_server_xfer, -}; +extern struct stlink_interface_s stlink_usb_itf; +extern struct stlink_interface_s stlink_server_itf; static int stlink_open(struct hl_interface_param_s *param, enum stlink_mode mode, void **fd) { diff --git a/src/jtag/drivers/stlink/stlink.h b/src/jtag/drivers/stlink/stlink.h new file mode 100644 index 0000000..a934e13 --- /dev/null +++ b/src/jtag/drivers/stlink/stlink.h @@ -0,0 +1,377 @@ +/*************************************************************************** + * 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/>. * + ***************************************************************************/ + +#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 (4096) +#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) + +/* + * 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. + */ +#define STLINK_MAX_RW8 (64) +#define STLINKV3_MAX_RW8 (512) + +/* "WAIT" responses will be retried (with exponential backoff) at + * most this many times before failing to caller. + */ +#define MAX_WAIT_RETRIES 8 + +#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 syncronization 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_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_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 2048 +#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) +#define STLINK_F_HAS_SWD_SET_FREQ BIT(1) +#define STLINK_F_HAS_JTAG_SET_FREQ BIT(2) +#define STLINK_F_HAS_MEM_16BIT BIT(3) +#define STLINK_F_HAS_GETLASTRWSTATUS2 BIT(4) +#define STLINK_F_HAS_DAP_REG BIT(5) +#define STLINK_F_QUIRK_JTAG_DP_READ BIT(6) +#define STLINK_F_HAS_AP_INIT BIT(7) +#define STLINK_F_HAS_DPBANKSEL BIT(8) +#define STLINK_F_HAS_RW8_512BYTES BIT(9) + +/** */ +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 +}; + +/** */ +enum stlink_jtag_api_version { + STLINK_JTAG_API_V1 = 1, + STLINK_JTAG_API_V2, + STLINK_JTAG_API_V3, +}; + +/** */ +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_server_priv_s { + /** */ + int fd; + /** */ + bool connected; + /** */ + uint32_t device_id; + /** */ + uint32_t connect_id; +}; + +/** */ +struct stlink_interface_s { + /** */ + void *priv; + /** */ + int (*open)(void *handle, struct hl_interface_param_s *param); + /** */ + int (*close)(void *handle); + /** */ + int (*xfer)(void *handle, const uint8_t *buf, int size); + /** */ + int (*read)(void *handle, const uint8_t *buf, int size); +}; + +/** */ +struct stlink_usb_handle_s { + /** */ + struct stlink_interface_s *itf; + /** */ + uint8_t rx_ep; + /** */ + uint8_t tx_ep; + /** */ + uint8_t trace_ep; + /** */ + uint8_t cmdbuf[STLINK_SG_SIZE]; + /** */ + uint8_t cmdidx; + /** */ + uint8_t direction; + /** */ + uint8_t databuf[STLINK_DATA_SIZE]; + /** */ + 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; +}; + +/** 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); diff --git a/src/jtag/drivers/stlink/stlink_server.c b/src/jtag/drivers/stlink/stlink_server.c new file mode 100644 index 0000000..cd780fe --- /dev/null +++ b/src/jtag/drivers/stlink/stlink_server.c @@ -0,0 +1,372 @@ +/*************************************************************************** + * 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 + +#include "stlink.h" +#include <helper/log.h> + +#if WIN32 +#include <winsock2.h> +#else +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <sys/uio.h> +#include <netinet/tcp.h> +#endif + +/** */ +static int stlink_server_send_cmd(void *handle, const uint8_t *cmd, int cmd_size, + uint8_t *data, int data_size, bool check_tcp_status) +{ + struct stlink_usb_handle_s *h = handle; + struct stlink_server_priv_s *itf_priv = h->itf->priv; + + assert(handle != NULL); + + /* send the TCP command */ + int sent_size = send(itf_priv->fd, (char *) cmd, cmd_size, 0); + if (sent_size != cmd_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, cmd_size); + return ERROR_FAIL; + } + + keep_alive(); + + /* read the TCP response */ + int received_size = recv(itf_priv->fd, (char *) data, data_size, 0); + if (received_size != data_size) { + LOG_ERROR("failed to receive USB CMD response"); + if (received_size == -1) + LOG_DEBUG("socket recv error: %s (errno %d)", strerror(errno), errno); + else + LOG_DEBUG("received size %d (expected %d)", received_size, data_size); + return ERROR_FAIL; + } + + if (check_tcp_status) { + uint32_t tcp_ss = le_to_h_u32(data); + 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_server_xfer(void *handle, const uint8_t *buf, int size) +{ + struct stlink_usb_handle_s *h = handle; + struct stlink_server_priv_s *itf_priv = h->itf->priv; + + uint8_t cmdbuf[STLINK_TCP_SEND_BUFFER_SIZE]; + uint8_t databuf[STLINK_TCP_RECV_BUFFER_SIZE]; + int cmd_size = STLINK_TCP_USB_CMD_SIZE; + int data_size = STLINK_TCP_SS_SIZE; + + assert(handle != NULL); + + /* prepare the TCP command */ + cmdbuf[0] = STLINK_TCP_CMD_SEND_USB_CMD; + memset(&cmdbuf[1], 0, 3); /* reserved for alignment and future use, must be zero */ + h_u32_to_le(&cmdbuf[4], itf_priv->connect_id); + memcpy(&cmdbuf[8], h->cmdbuf, 16); + cmdbuf[24] = h->direction; + memset(&cmdbuf[25], 0, 3); /* reserved for alignment and future use, must be zero */ + + h_u32_to_le(&cmdbuf[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 */ + cmd_size += size; + if (cmd_size > STLINK_TCP_SEND_BUFFER_SIZE) { + LOG_ERROR("STLINK_TCP command buffer overflow"); + return ERROR_FAIL; + } + memcpy(&cmdbuf[32], buf, size); + } else { /* STLINK_TCP_REQUEST_READ or STLINK_TCP_REQUEST_READ_SWO */ + data_size += size; + if (data_size > STLINK_TCP_RECV_BUFFER_SIZE) { + LOG_ERROR("STLINK_TCP data buffer overflow"); + return ERROR_FAIL; + } + } + + int ret = stlink_server_send_cmd(h, cmdbuf, cmd_size, databuf, data_size, true); + if (ret != ERROR_OK) + return ret; + + if (h->direction != h->tx_ep) + memcpy((uint8_t *) buf, &databuf[STLINK_TCP_SS_SIZE], size); + + return ERROR_OK; +} + +/** */ +static int stlink_server_close(void *handle) +{ + struct stlink_usb_handle_s *h = handle; + struct stlink_server_priv_s *itf_priv = h->itf->priv; + + int ret = ERROR_OK; + uint8_t cmdbuf[STLINK_TCP_SEND_BUFFER_SIZE]; + uint8_t databuf[STLINK_TCP_RECV_BUFFER_SIZE]; + + if (h && itf_priv->connected) { + if (itf_priv->connect_id) { + stlink_usb_exit_mode(h); + + /* close the stlink */ + cmdbuf[0] = STLINK_TCP_CMD_CLOSE_DEV; + memset(&cmdbuf[1], 0, 4); /* reserved */ + h_u32_to_le(&cmdbuf[4], itf_priv->connect_id); + ret = stlink_server_send_cmd(h, cmdbuf, 8, databuf, 4, true); + if (ret != ERROR_OK) + LOG_ERROR("cannot close the STLINK"); + } + + if (close_socket(itf_priv->fd) != 0) { + LOG_ERROR("error closing the socket"); + LOG_DEBUG("close error: %s (errno %d)", strerror(errno), errno); + } + } + + return ret; +} + +/** */ +static int stlink_server_open(void *handle, struct hl_interface_param_s *param) +{ + struct stlink_usb_handle_s *h = handle; + struct stlink_server_priv_s *itf_priv = h->itf->priv; + int ret; + uint8_t cmdbuf[STLINK_TCP_SEND_BUFFER_SIZE]; + uint8_t databuf[STLINK_TCP_RECV_BUFFER_SIZE]; + + /* 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; + } + + /* configure directions */ + h->rx_ep = STLINK_TCP_REQUEST_READ; + h->tx_ep = STLINK_TCP_REQUEST_WRITE; + h->trace_ep = STLINK_TCP_REQUEST_READ_SWO; + + itf_priv->fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + itf_priv->connected = false; + itf_priv->device_id = 0; + itf_priv->connect_id = 0; + + struct sockaddr_in serv; + memset(&serv, 0, sizeof(struct sockaddr_in)); + serv.sin_family = AF_INET; + serv.sin_port = htons(param->stlink_server_port); + serv.sin_addr.s_addr = inet_addr("127.0.0.1"); + + LOG_DEBUG("socket : %x", itf_priv->fd); + + int res; + + int flag = 1; + res = setsockopt(itf_priv->fd, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(flag)); + if (res == -1) { + LOG_ERROR("cannot set sock option 'TCP_NODELEAY', errno: %s", strerror(errno)); + return ERROR_FAIL; + } + + int a = 49152; + res = setsockopt(itf_priv->fd, SOL_SOCKET, SO_RCVBUF, (char *) &a, sizeof(a)); + if (res == -1) { + LOG_ERROR("cannot set sock option 'SO_RCVBUF', errno: %s", strerror(errno)); + return ERROR_FAIL; + } + + res = setsockopt(itf_priv->fd, SOL_SOCKET, SO_SNDBUF, (char *) &a, sizeof(a)); + if (res == -1) { + LOG_ERROR("cannot set sock option 'SO_SNDBUF', errno: %s", strerror(errno)); + return ERROR_FAIL; + } + + if (connect(itf_priv->fd, (const struct sockaddr *) &serv, sizeof(serv)) == -1) { + LOG_ERROR("cannot connect to stlink server"); + return ERROR_FAIL; + } + + itf_priv->connected = true; + + LOG_INFO("connected to stlink-server"); + + /* print stlink-server version */ + cmdbuf[0] = STLINK_TCP_CMD_GET_SERVER_VERSION; + cmdbuf[1] = OPENOCD_STLINK_TCP_API_VERSION; + memset(&cmdbuf[2], 0, 2); /* reserved */ + ret = stlink_server_send_cmd(h, cmdbuf, 4, databuf, 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(&databuf[0]); + uint32_t ver_major = le_to_h_u32(&databuf[4]); + uint32_t ver_minor = le_to_h_u32(&databuf[8]); + uint32_t ver_build = le_to_h_u32(&databuf[12]); + LOG_INFO("stlink-server API v%d, version %d.%d.%d", + api_ver, ver_major, ver_minor, ver_build); + + /* refresh stlink list (re-enumerate) */ + cmdbuf[0] = STLINK_TCP_CMD_REFRESH_DEVICE_LIST; + cmdbuf[1] = 0; /* don't clear the list, just refresh it */ + ret = stlink_server_send_cmd(h, cmdbuf, 2, databuf, 4, true); + if (ret != ERROR_OK) + return ret; + + /* get the number of connected stlinks */ + cmdbuf[0] = STLINK_TCP_CMD_GET_NB_DEV; + ret = stlink_server_send_cmd(h, cmdbuf, 1, databuf, 4, false); + if (ret != ERROR_OK) + return ret; + + uint32_t connected_stlinks = le_to_h_u32(databuf); + + if (connected_stlinks == 0) { + LOG_ERROR("no ST-LINK detected"); + return ERROR_FAIL; + } + + LOG_DEBUG("%d ST-LINK detected", connected_stlinks); + + /* list all connected ST-Link and seek for the requested vid:pid and serial */ + uint8_t serial[STLINK_TCP_SERIAL_SIZE+1] = {0}; + uint8_t stlink_used; + bool stlink_id_matched = false; + bool stlink_serial_matched = (param->serial == NULL); + + for (uint32_t stlink_id = 0; stlink_id < connected_stlinks; stlink_id++) { + /* get the stlink info */ + cmdbuf[0] = STLINK_TCP_CMD_GET_DEV_INFO; + cmdbuf[1] = (uint8_t) stlink_id; + memset(&cmdbuf[2], 0, 2); /* reserved */ + h_u32_to_le(&cmdbuf[4], 41); /* size of TDeviceInfo2 */ + ret = stlink_server_send_cmd(h, cmdbuf, 8, databuf, 45, true); + if (ret != ERROR_OK) + return ret; + + itf_priv->device_id = le_to_h_u32(&databuf[4]); + memcpy(serial, &databuf[8], STLINK_TCP_SERIAL_SIZE); + h->vid = le_to_h_u16(&databuf[40]); + h->pid = le_to_h_u16(&databuf[42]); + stlink_used = databuf[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) + stlink_serial_matched = strcmp(param->serial, (const char *) serial) == 0; + + if (!stlink_serial_matched) + LOG_DEBUG("Device serial number '%s' doesn't match requested serial '%s'", + (const char *) 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 */ + cmdbuf[0] = STLINK_TCP_CMD_OPEN_DEV; + memset(&cmdbuf[1], 0, 4); /* reserved */ + h_u32_to_le(&cmdbuf[4], itf_priv->device_id); + ret = stlink_server_send_cmd(h, cmdbuf, 8, databuf, 8, true); + if (ret != ERROR_OK) + return ret; + + itf_priv->connect_id = le_to_h_u32(&databuf[4]); + + /* get stlink version */ + ret = stlink_usb_version(h); + if (ret != ERROR_OK) + return ERROR_FAIL; + + return ERROR_OK; +} + +/** */ +struct stlink_interface_s stlink_server_itf = { + .open = stlink_server_open, + .close = stlink_server_close, + .xfer = stlink_server_xfer, + .read = stlink_server_xfer, +}; diff --git a/src/jtag/drivers/stlink/stlink_usb.c b/src/jtag/drivers/stlink/stlink_usb.c new file mode 100644 index 0000000..5caede7 --- /dev/null +++ b/src/jtag/drivers/stlink/stlink_usb.c @@ -0,0 +1,588 @@ +/*************************************************************************** + * 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 + +#include "stlink.h" +#include <helper/binarybuffer.h> +#include <helper/log.h> +#include "../libusb_helper.h" + +#ifdef HAVE_LIBUSB1 +#define USE_LIBUSB_ASYNCIO +#endif + +#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; + + /* Assuming a single libusb context exists. There no existing interface into this + * module to pass a libusb context. + */ + struct libusb_context *ctx = NULL; + + while (!*completed) { + r = libusb_handle_events_completed(ctx, 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 == NULL) { + 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 + +#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; + struct stlink_usb_priv_s *itf_priv = h->itf->priv; + + assert(handle != NULL); + + 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( + itf_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; + struct stlink_usb_priv_s *itf_priv = h->itf->priv; + int tr, ret; + + assert(handle != NULL); + + ret = jtag_libusb_bulk_write(itf_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(itf_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(itf_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_bulk_read(void *handle, const uint8_t *buf, int size) +{ + struct stlink_usb_handle_s *h = handle; + struct stlink_usb_priv_s *itf_priv = h->itf->priv; + int tr, ret; + + ret = jtag_libusb_bulk_read(itf_priv->fd, h->direction, (char *)buf, size, + STLINK_READ_TIMEOUT, &tr); + if (ret || tr != size) { + LOG_ERROR("bulk trace read failed"); + return ERROR_FAIL; + } + + return ERROR_OK; +} + +/** */ +static int stlink_usb_xfer_v1_get_status(void *handle) +{ + struct stlink_usb_handle_s *h = handle; + struct stlink_usb_priv_s *itf_priv = h->itf->priv; + int tr, ret; + + assert(handle != NULL); + + /* read status */ + memset(h->cmdbuf, 0, STLINK_SG_SIZE); + + ret = jtag_libusb_bulk_read(itf_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 != NULL); + + 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; +} + +/* + 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 != NULL); + + 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_usb_close(void *handle) +{ + struct stlink_usb_handle_s *h = handle; + struct stlink_usb_priv_s *itf_priv = h->itf->priv; + + if (h && itf_priv->fd) { + stlink_usb_exit_mode(h); + /* do not check return code, it prevent + us from closing jtag_libusb */ + jtag_libusb_close(itf_priv->fd); + } + + 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(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 == NULL) + 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; + struct stlink_usb_priv_s *itf_priv = h->itf->priv; + int err, retry_count = 1; + + /* + 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, + &itf_priv->fd, stlink_usb_get_alternate_serial) != ERROR_OK) { + LOG_ERROR("open failed"); + return ERROR_FAIL; + } + + jtag_libusb_set_configuration(itf_priv->fd, 0); + + if (libusb_claim_interface(itf_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(itf_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: + 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(itf_priv->fd, 0); + if (err != ERROR_OK) { + LOG_ERROR("release interface failed"); + return ERROR_FAIL; + } + + err = libusb_reset_device(itf_priv->fd); + if (err != ERROR_OK) { + LOG_ERROR("reset device failed"); + return ERROR_FAIL; + } + + jtag_libusb_close(itf_priv->fd); + /* + Give the device one second to settle down and + reenumerate. + */ + usleep(1 * 1000 * 1000); + retry_count--; + } + } while (1); + + return ERROR_OK; +} + +/** */ +struct stlink_interface_s stlink_usb_itf = { + .open = stlink_usb_open, + .close = stlink_usb_close, + .xfer = stlink_usb_xfer_noerrcheck, + .read = stlink_usb_bulk_read, +}; -- _______________________________________________ OpenOCD-devel mailing list [email protected] https://lists.sourceforge.net/lists/listinfo/openocd-devel
