+#include <target/cortex_m.h>
+
+#include "libusb_common.h"
+
+#ifdef HAVE_LIBUSB1
+#define USE_LIBUSB_ASYNCIO
+#endif
+
+#define ENDPOINT_IN 0x80
+#define ENDPOINT_OUT 0x00
+
+#define STLINK_WRITE_TIMEOUT 1000
+#define STLINK_READ_TIMEOUT 1000
+
+#define STLINK_NULL_EP 0
+#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.
+ */
+#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,
+};
+
+/** */
+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_handle_s {
+ /** */
+ struct jtag_libusb_device_handle *fd;
+ /** */
+ struct libusb_transfer *trans;
+ /** */
+ 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 hl_transports transport;
+ /** */
+ 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_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_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_READMEM_16BIT 0x47
+#define STLINK_DEBUG_APIV2_WRITEMEM_16BIT 0x48
+
+#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_TRACE_SIZE 4096
+#define STLINK_TRACE_MAX_HZ 2000000
+
+#define STLINK_V3_MAX_FREQ_NB 10
+
+/** */
+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
+};
+
+#define REQUEST_SENSE 0x03
+#define REQUEST_SENSE_LENGTH 18
+
+/*
+ * Map the relevant features, quirks and workaround for specific firmware
+ * version of stlink
+ */
+#define STLINK_F_HAS_TRACE (1UL << 0)
+#define STLINK_F_HAS_SWD_SET_FREQ (1UL << 1)
+#define STLINK_F_HAS_JTAG_SET_FREQ (1UL << 2)
+#define STLINK_F_HAS_MEM_16BIT (1UL << 3)
+#define STLINK_F_HAS_GETLASTRWSTATUS2 (1UL << 4)
+
+/* aliases */
+#define STLINK_F_HAS_TARGET_VOLT STLINK_F_HAS_TRACE
+
+struct speed_map {
+ int speed;
+ int speed_divisor;
+};
+
+/* SWD clock speed */
+static const struct speed_map stlink_khz_to_speed_map_swd[] = {
+ {4000, 0},
+ {1800, 1}, /* default */
+ {1200, 2},
+ {950, 3},
+ {480, 7},
+ {240, 15},
+ {125, 31},
+ {100, 40},
+ {50, 79},
+ {25, 158},
+ {15, 265},
+ {5, 798}
+};
+
+/* JTAG clock speed */
+static const struct speed_map stlink_khz_to_speed_map_jtag[] = {
+ {18000, 2},
+ {9000, 4},
+ {4500, 8},
+ {2250, 16},
+ {1125, 32}, /* default */
+ {562, 64},
+ {281, 128},
+ {140, 256}
+};
+
+static void stlink_usb_init_buffer(void *handle, uint8_t direction, uint32_t size);
+static int stlink_swim_status(void *handle);
+
+/** */
+static unsigned int stlink_usb_block(void *handle)
+{
+ struct stlink_usb_handle_s *h = handle;
+
+ assert(handle != NULL);
+
+ if (h->version.stlink == 3)
+ return STLINKV3_MAX_RW8;
+ else
+ 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(
+ jtag_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;
+
+ assert(handle != NULL);
+
+ /* read status */
+ memset(h->cmdbuf, 0, STLINK_SG_SIZE);
+
+ if (jtag_libusb_bulk_read(h->fd, h->rx_ep, (char *)h->cmdbuf,
+ 13, STLINK_READ_TIMEOUT) != 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 != 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(
+ h->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;
+
+ assert(handle != NULL);
+
+ if (jtag_libusb_bulk_write(h->fd, h->tx_ep, (char *)h->cmdbuf, cmdsize,
+ STLINK_WRITE_TIMEOUT) != cmdsize) {
+ return ERROR_FAIL;
+ }
+
+ if (h->direction == h->tx_ep && size) {
+ if (jtag_libusb_bulk_write(h->fd, h->tx_ep, (char *)buf,
+ size, STLINK_WRITE_TIMEOUT) != size) {
+ LOG_DEBUG("bulk write failed");
+ return ERROR_FAIL;
+ }
+ } else if (h->direction == h->rx_ep && size) {
+ if (jtag_libusb_bulk_read(h->fd, h->rx_ep, (char *)buf,
+ size, STLINK_READ_TIMEOUT) != 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;
+}
+
+/*
+ transfers block in cmdbuf
+ <size> indicates number of bytes in the following
+ data phase.
+*/
+static int stlink_usb_xfer(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;
+}
+
+/**
+ 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.
+*/
+static int stlink_usb_error_check(void *handle)
+{
+ struct stlink_usb_handle_s *h = handle;
+
+ assert(handle != NULL);
+
+ if (h->transport == HL_TRANSPORT_SWIM) {
+ switch (h->databuf[0]) {
+ case STLINK_SWIM_ERR_OK:
+ return ERROR_OK;
+ case STLINK_SWIM_BUSY:
+ return ERROR_WAIT;
+ default:
+ LOG_DEBUG("unknown/unexpected STLINK status code 0x%x", h->databuf[0]);
+ return ERROR_FAIL;
+ }
+ }
+
+ /* TODO: no error checking yet on api V1 */
+ if (h->version.jtag_api == STLINK_JTAG_API_V1)
+ h->databuf[0] = STLINK_DEBUG_ERR_OK;
+
+ switch (h->databuf[0]) {
+ case STLINK_DEBUG_ERR_OK:
+ return ERROR_OK;
+ case STLINK_DEBUG_ERR_FAULT:
+ LOG_DEBUG("SWD fault response (0x%x)", STLINK_DEBUG_ERR_FAULT);
+ return ERROR_FAIL;
+ case STLINK_SWD_AP_WAIT:
+ LOG_DEBUG("wait status SWD_AP_WAIT (0x%x)", STLINK_SWD_AP_WAIT);
+ return ERROR_WAIT;
+ case STLINK_SWD_DP_WAIT:
+ LOG_DEBUG("wait status SWD_DP_WAIT (0x%x)", STLINK_SWD_DP_WAIT);
+ return ERROR_WAIT;
+ case STLINK_JTAG_WRITE_ERROR:
+ LOG_DEBUG("Write error");
+ return ERROR_FAIL;
+ case STLINK_JTAG_WRITE_VERIF_ERROR:
+ LOG_DEBUG("Write verify error, ignoring");
+ return ERROR_OK;
+ case STLINK_SWD_AP_FAULT:
+ /* git://git.ac6.fr/openocd commit 657e3e885b9ee10
+ * returns ERROR_OK with the comment:
+ * Change in error status when reading outside RAM.
+ * This fix allows CDT plugin to visualize memory.
+ */
+ LOG_DEBUG("STLINK_SWD_AP_FAULT");
+ return ERROR_FAIL;
+ case STLINK_SWD_AP_ERROR:
+ LOG_DEBUG("STLINK_SWD_AP_ERROR");
+ return ERROR_FAIL;
+ case STLINK_SWD_AP_PARITY_ERROR:
+ LOG_DEBUG("STLINK_SWD_AP_PARITY_ERROR");
+ return ERROR_FAIL;
+ case STLINK_SWD_DP_FAULT:
+ LOG_DEBUG("STLINK_SWD_DP_FAULT");
+ return ERROR_FAIL;
+ case STLINK_SWD_DP_ERROR:
+ LOG_DEBUG("STLINK_SWD_DP_ERROR");
+ return ERROR_FAIL;
+ case STLINK_SWD_DP_PARITY_ERROR:
+ LOG_DEBUG("STLINK_SWD_DP_PARITY_ERROR");
+ return ERROR_FAIL;
+ case STLINK_SWD_AP_WDATA_ERROR:
+ LOG_DEBUG("STLINK_SWD_AP_WDATA_ERROR");
+ return ERROR_FAIL;
+ case STLINK_SWD_AP_STICKY_ERROR:
+ LOG_DEBUG("STLINK_SWD_AP_STICKY_ERROR");
+ return ERROR_FAIL;
+ case STLINK_SWD_AP_STICKYORUN_ERROR:
+ LOG_DEBUG("STLINK_SWD_AP_STICKYORUN_ERROR");
+ return ERROR_FAIL;
+ default:
+ LOG_DEBUG("unknown/unexpected STLINK status code 0x%x", h->databuf[0]);
+ return ERROR_FAIL;
+ }
+}
+
+
+/** Issue an STLINK command via USB transfer, with retries on any wait status responses.
+
+ Works for commands where the STLINK_DEBUG status is returned in the first
+ byte of the response packet. For SWIM a SWIM_READSTATUS is requested instead.
+
+ Returns an openocd result code.
+*/
+static int stlink_cmd_allow_retry(void *handle, const uint8_t *buf, int size)
+{
+ int retries = 0;
+ int res;
+ struct stlink_usb_handle_s *h = handle;
+
+ while (1) {
+ if ((h->transport != HL_TRANSPORT_SWIM) || !retries) {
+ res = stlink_usb_xfer(handle, buf, size);
+ if (res != ERROR_OK)
+ return res;
+ }
+
+ if (h->transport == HL_TRANSPORT_SWIM) {
+ res = stlink_swim_status(handle);
+ if (res != ERROR_OK)
+ return res;
+ }
+
+ res = stlink_usb_error_check(handle);
+ if (res == ERROR_WAIT && retries < MAX_WAIT_RETRIES) {
+ useconds_t delay_us = (1<<retries++) * 1000;
+ LOG_DEBUG("stlink_cmd_allow_retry ERROR_WAIT, retry %d, delaying %u microseconds", retries, delay_us);
+ usleep(delay_us);
+ continue;
+ }
+ return res;
+ }
+}
+
+/** */
+static int stlink_usb_read_trace(void *handle, const uint8_t *buf, int size)
+{
+ struct stlink_usb_handle_s *h = handle;
+
+ assert(handle != NULL);
+
+ assert(h->version.flags & STLINK_F_HAS_TRACE);
+
+ if (jtag_libusb_bulk_read(h->fd, h->trace_ep, (char *)buf,
+ size, STLINK_READ_TIMEOUT) != size) {
+ LOG_ERROR("bulk trace read failed");
+ return ERROR_FAIL;
+ }
+
+ return ERROR_OK;
+}
+
+/*
+ this function writes transfer length in
+ the right place in the cb
+*/
+static void stlink_usb_set_cbw_transfer_datalength(void *handle, uint32_t size)
+{
+ struct stlink_usb_handle_s *h = handle;
+
+ buf_set_u32(h->cmdbuf+8, 0, 32, size);
+}
+
+static void stlink_usb_xfer_v1_create_cmd(void *handle, uint8_t direction, uint32_t size)
+{
+ struct stlink_usb_handle_s *h = handle;
+
+ /* fill the send buffer */
+ strcpy((char *)h->cmdbuf, "USBC");
+ h->cmdidx += 4;
+ /* csw tag not used */
+ buf_set_u32(h->cmdbuf+h->cmdidx, 0, 32, 0);
+ h->cmdidx += 4;
+ /* cbw data transfer length (in the following data phase in or out) */
+ buf_set_u32(h->cmdbuf+h->cmdidx, 0, 32, size);
+ h->cmdidx += 4;
+ /* cbw flags */
+ h->cmdbuf[h->cmdidx++] = (direction == h->rx_ep ? ENDPOINT_IN : ENDPOINT_OUT);
+ h->cmdbuf[h->cmdidx++] = 0; /* lun */
+ /* cdb clength (is filled in at xfer) */
+ h->cmdbuf[h->cmdidx++] = 0;
+}
+
+/** */
+static void stlink_usb_init_buffer(void *handle, uint8_t direction, uint32_t size)
+{
+ struct stlink_usb_handle_s *h = handle;
+
+ h->direction = direction;
+
+ h->cmdidx = 0;
+
+ memset(h->cmdbuf, 0, STLINK_SG_SIZE);
+ memset(h->databuf, 0, STLINK_DATA_SIZE);
+
+ if (h->version.stlink == 1)
+ stlink_usb_xfer_v1_create_cmd(handle, direction, size);
+}
+
+/** */
+static int stlink_usb_version(void *handle)
+{
+ int res;
+ uint32_t flags;
+ uint16_t version;
+ uint8_t v, x, y, jtag, swim, msd, bridge = 0;
+ char v_str[5 * (1 + 3) + 1]; /* VvJjMmBbSs */
+ char *p;
+ struct stlink_usb_handle_s *h = handle;
+
+ assert(handle != NULL);
+
+ stlink_usb_init_buffer(handle, h->rx_ep, 6);
+
+ h->cmdbuf[h->cmdidx++] = STLINK_GET_VERSION;
+
+ res = stlink_usb_xfer(handle, h->databuf, 6);
+
+ if (res != ERROR_OK)
+ return res;
+
+ version = be_to_h_u16(h->databuf);
+ v = (version >> 12) & 0x0f;
+ x = (version >> 6) & 0x3f;
+ y = version & 0x3f;
+
+ h->vid = le_to_h_u16(h->databuf + 2);
+ h->pid = le_to_h_u16(h->databuf + 4);
+
+ switch (h->pid) {
+ case STLINK_V2_1_PID:
+ case STLINK_V2_1_NO_MSD_PID:
+ if ((x <= 22 && y == 7) || (x >= 25 && y >= 7 && y <= 12)) {
+ /* MxSy : STM8 V2.1 - SWIM only */
+ msd = x;
+ swim = y;
+ jtag = 0;
+ } else {
+ /* JxMy : STM32 V2.1 - JTAG/SWD only */
+ jtag = x;
+ msd = y;
+ swim = 0;
+ }
+ break;
+ default:
+ jtag = x;
+ swim = y;
+ msd = 0;
+ break;
+ }
+
+ /* STLINK-V3 requires a specific command */
+ if (v == 3 && x == 0 && y == 0) {
+ stlink_usb_init_buffer(handle, h->rx_ep, 16);
+
+ h->cmdbuf[h->cmdidx++] = STLINK_APIV3_GET_VERSION_EX;
+
+ res = stlink_usb_xfer(handle, h->databuf, 12);
+ if (res != ERROR_OK)
+ return res;
+
+ v = h->databuf[0];
+ swim = h->databuf[1];
+ jtag = h->databuf[2];
+ msd = h->databuf[3];
+ bridge = h->databuf[4];
+ h->vid = le_to_h_u16(h->databuf + 8);
+ h->pid = le_to_h_u16(h->databuf + 10);
+ }
+
+ h->version.stlink = v;
+ h->version.jtag = jtag;
+ h->version.swim = swim;
+
+ flags = 0;
+ switch (h->version.stlink) {
+ case 1:
+ /* ST-LINK/V1 from J11 switch to api-v2 (and support SWD) */
+ if (h->version.jtag >= 11)
+ h->version.jtag_api = STLINK_JTAG_API_V2;
+ else
+ h->version.jtag_api = STLINK_JTAG_API_V1;
+
+ break;
+ case 2:
+ /* all ST-LINK/V2 and ST-Link/V2.1 use api-v2 */
+ h->version.jtag_api = STLINK_JTAG_API_V2;
+
+ /* API for trace from J13 */
+ /* API for target voltage from J13 */
+ if (h->version.jtag >= 13)
+ flags |= STLINK_F_HAS_TRACE;
+
+ /* preferred API to get last R/W status from J15 */
+ if (h->version.jtag >= 15)
+ flags |= STLINK_F_HAS_GETLASTRWSTATUS2;
+
+ /* API to set SWD frequency from J22 */
+ if (h->version.jtag >= 22)
+ flags |= STLINK_F_HAS_SWD_SET_FREQ;
+
+ /* API to set JTAG frequency from J24 */
+ if (h->version.jtag >= 24)
+ flags |= STLINK_F_HAS_JTAG_SET_FREQ;
+
+ /* API to read/write memory at 16 bit from J26 */
+ if (h->version.jtag >= 26)
+ flags |= STLINK_F_HAS_MEM_16BIT;
+
+ break;
+ case 3:
+ /* all STLINK-V3 use api-v3 */
+ h->version.jtag_api = STLINK_JTAG_API_V3;
+
+ /* STLINK-V3 is a superset of ST-LINK/V2 */
+
+ /* API for trace */
+ /* API for target voltage */
+ flags |= STLINK_F_HAS_TRACE;
+
+ /* preferred API to get last R/W status */
+ flags |= STLINK_F_HAS_GETLASTRWSTATUS2;
+
+ /* API to read/write memory at 16 bit */
+ flags |= STLINK_F_HAS_MEM_16BIT;
+
+ break;
+ default:
+ break;
+ }
+ h->version.flags = flags;
+
+ p = v_str;
+ p += sprintf(p, "V%d", v);
+ if (jtag || !msd)
+ p += sprintf(p, "J%d", jtag);
+ if (msd)
+ p += sprintf(p, "M%d", msd);
+ if (bridge)
+ p += sprintf(p, "B%d", bridge);
+ if (swim || !msd)
+ sprintf(p, "S%d", swim);
+
+ LOG_INFO("STLINK %s (API v%d) VID:PID %04X:%04X",
+ v_str,
+ h->version.jtag_api,
+ h->vid,
+ h->pid);
+
+ return ERROR_OK;
+}
+
+static int stlink_usb_check_voltage(void *handle, float *target_voltage)
+{
+ struct stlink_usb_handle_s *h = handle;
+ uint32_t adc_results[2];
+
+ /* no error message, simply quit with error */
+ if (!(h->version.flags & STLINK_F_HAS_TARGET_VOLT))
+ return ERROR_COMMAND_NOTFOUND;
+
+ stlink_usb_init_buffer(handle, h->rx_ep, 8);
+
+ h->cmdbuf[h->cmdidx++] = STLINK_GET_TARGET_VOLTAGE;
+
+ int result = stlink_usb_xfer(handle, h->databuf, 8);
+
+ if (result != ERROR_OK)
+ return result;
+
+ /* convert result */
+ adc_results[0] = le_to_h_u32(h->databuf);
+ adc_results[1] = le_to_h_u32(h->databuf + 4);
+
+ *target_voltage = 0;
+
+ if (adc_results[0])
+ *target_voltage = 2 * ((float)adc_results[1]) * (float)(1.2 / adc_results[0]);
+
+ LOG_INFO("Target voltage: %f", (double)*target_voltage);
+
+ return ERROR_OK;
+}
+
+static int stlink_usb_set_swdclk(void *handle, uint16_t clk_divisor)
+{
+ struct stlink_usb_handle_s *h = handle;
+
+ assert(handle != NULL);
+
+ if (!(h->version.flags & STLINK_F_HAS_SWD_SET_FREQ))
+ return ERROR_COMMAND_NOTFOUND;
+
+ stlink_usb_init_buffer(handle, h->rx_ep, 2);
+
+ h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_COMMAND;
+ h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_APIV2_SWD_SET_FREQ;
+ h_u16_to_le(h->cmdbuf+h->cmdidx, clk_divisor);
+ h->cmdidx += 2;
+
+ int result = stlink_cmd_allow_retry(handle, h->databuf, 2);
+
+ if (result != ERROR_OK)
+ return result;
+
+ return ERROR_OK;
+}
+
+static int stlink_usb_set_jtagclk(void *handle, uint16_t clk_divisor)
+{
+ struct stlink_usb_handle_s *h = handle;
+
+ assert(handle != NULL);
+
+ if (!(h->version.flags & STLINK_F_HAS_JTAG_SET_FREQ))
+ return ERROR_COMMAND_NOTFOUND;
+
+ stlink_usb_init_buffer(handle, h->rx_ep, 2);
+
+ h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_COMMAND;
+ h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_APIV2_JTAG_SET_FREQ;
+ h_u16_to_le(h->cmdbuf+h->cmdidx, clk_divisor);
+ h->cmdidx += 2;
+
+ int result = stlink_cmd_allow_retry(handle, h->databuf, 2);
+
+ if (result != ERROR_OK)
+ return result;
+
+ return ERROR_OK;
+}
+
+/** */
+static int stlink_usb_current_mode(void *handle, uint8_t *mode)
+{
+ int res;
+ struct stlink_usb_handle_s *h = handle;
+
+ assert(handle != NULL);
+
+ stlink_usb_init_buffer(handle, h->rx_ep, 2);
+
+ h->cmdbuf[h->cmdidx++] = STLINK_GET_CURRENT_MODE;
+
+ res = stlink_usb_xfer(handle, h->databuf, 2);
+
+ if (res != ERROR_OK)
+ return res;
+
+ *mode = h->databuf[0];
+
+ return ERROR_OK;
+}
+
+/** */
+static int stlink_usb_mode_enter(void *handle, enum stlink_mode type)
+{
+ int rx_size = 0;
+ struct stlink_usb_handle_s *h = handle;
+
+ assert(handle != NULL);
+
+ /* on api V2 we are able the read the latest command
+ * status
+ * TODO: we need the test on api V1 too
+ */
+ if (h->version.jtag_api != STLINK_JTAG_API_V1)
+ rx_size = 2;
+
+ stlink_usb_init_buffer(handle, h->rx_ep, rx_size);
+
+ switch (type) {
+ case STLINK_MODE_DEBUG_JTAG:
+ h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_COMMAND;
+ if (h->version.jtag_api == STLINK_JTAG_API_V1)
+ h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_APIV1_ENTER;
+ else
+ h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_APIV2_ENTER;
+ h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_ENTER_JTAG_NO_RESET;
+ break;
+ case STLINK_MODE_DEBUG_SWD:
+ h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_COMMAND;
+ if (h->version.jtag_api == STLINK_JTAG_API_V1)
+ h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_APIV1_ENTER;
+ else
+ h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_APIV2_ENTER;
+ h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_ENTER_SWD_NO_RESET;
+ break;
+ case STLINK_MODE_DEBUG_SWIM:
+ h->cmdbuf[h->cmdidx++] = STLINK_SWIM_COMMAND;
+ h->cmdbuf[h->cmdidx++] = STLINK_SWIM_ENTER;
+ /* no answer for this function... */
+ rx_size = 0;
+ break;
+ case STLINK_MODE_DFU:
+ case STLINK_MODE_MASS:
+ default:
+ return ERROR_FAIL;
+ }
+
+ return stlink_cmd_allow_retry(handle, h->databuf, rx_size);
+}
+
+/** */
+static int stlink_usb_mode_leave(void *handle, enum stlink_mode type)
+{
+ int res;
+ struct stlink_usb_handle_s *h = handle;
+
+ assert(handle != NULL);
+
+ stlink_usb_init_buffer(handle, STLINK_NULL_EP, 0);
+
+ switch (type) {
+ case STLINK_MODE_DEBUG_JTAG:
+ case STLINK_MODE_DEBUG_SWD:
+ h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_COMMAND;
+ h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_EXIT;
+ break;
+ case STLINK_MODE_DEBUG_SWIM:
+ h->cmdbuf[h->cmdidx++] = STLINK_SWIM_COMMAND;
+ h->cmdbuf[h->cmdidx++] = STLINK_SWIM_EXIT;
+ break;
+ case STLINK_MODE_DFU:
+ h->cmdbuf[h->cmdidx++] = STLINK_DFU_COMMAND;
+ h->cmdbuf[h->cmdidx++] = STLINK_DFU_EXIT;
+ break;
+ case STLINK_MODE_MASS:
+ default:
+ return ERROR_FAIL;
+ }
+
+ res = stlink_usb_xfer(handle, 0, 0);
+
+ if (res != ERROR_OK)
+ return res;
+
+ return ERROR_OK;
+}
+
+static int stlink_usb_assert_srst(void *handle, int srst);
+
+static enum stlink_mode stlink_get_mode(enum hl_transports t)
+{
+ switch (t) {
+ case HL_TRANSPORT_SWD:
+ return STLINK_MODE_DEBUG_SWD;
+ case HL_TRANSPORT_JTAG:
+ return STLINK_MODE_DEBUG_JTAG;
+ case HL_TRANSPORT_SWIM:
+ return STLINK_MODE_DEBUG_SWIM;
+ default:
+ return STLINK_MODE_UNKNOWN;
+ }
+}
+
+/** */
+static int stlink_usb_init_mode(void *handle, bool connect_under_reset)
+{
+ int res;
+ uint8_t mode;
+ enum stlink_mode emode;
+ struct stlink_usb_handle_s *h = handle;
+
+ assert(handle != NULL);
+
+ res = stlink_usb_current_mode(handle, &mode);
+
+ if (res != ERROR_OK)
+ return res;
+
+ LOG_DEBUG("MODE: 0x%02X", mode);
+
+ /* try to exit current mode */
+ switch (mode) {
+ case STLINK_DEV_DFU_MODE:
+ emode = STLINK_MODE_DFU;
+ break;
+ case STLINK_DEV_DEBUG_MODE:
+ emode = STLINK_MODE_DEBUG_SWD;
+ break;
+ case STLINK_DEV_SWIM_MODE:
+ emode = STLINK_MODE_DEBUG_SWIM;
+ break;
+ case STLINK_DEV_BOOTLOADER_MODE:
+ case STLINK_DEV_MASS_MODE:
+ default:
+ emode = STLINK_MODE_UNKNOWN;
+ break;
+ }
+
+ if (emode != STLINK_MODE_UNKNOWN) {
+ res = stlink_usb_mode_leave(handle, emode);
+
+ if (res != ERROR_OK)
+ return res;
+ }
+
+ res = stlink_usb_current_mode(handle, &mode);
+
+ if (res != ERROR_OK)
+ return res;
+
+ /* we check the target voltage here as an aid to debugging connection problems.
+ * the stlink requires the target Vdd to be connected for reliable debugging.
+ * this cmd is supported in all modes except DFU
+ */
+ if (mode != STLINK_DEV_DFU_MODE) {
+
+ float target_voltage;
+
+ /* check target voltage (if supported) */
+ res = stlink_usb_check_voltage(h, &target_voltage);
+
+ if (res != ERROR_OK) {
+ if (res != ERROR_COMMAND_NOTFOUND)
+ LOG_ERROR("voltage check failed");
+ /* attempt to continue as it is not a catastrophic failure */
+ } else {
+ /* check for a sensible target voltage, operating range is 1.65-5.5v
+ * according to datasheet */
+ if (target_voltage < 1.5)
+ LOG_ERROR("target voltage may be too low for reliable debugging");
+ }
+ }
+
+ LOG_DEBUG("MODE: 0x%02X", mode);
+
+ /* set selected mode */
+ emode = stlink_get_mode(h->transport);
+
+ if (emode == STLINK_MODE_UNKNOWN) {
+ LOG_ERROR("selected mode (transport) not supported");
+ return ERROR_FAIL;
+ }
+
+ /* preliminary SRST assert:
+ * We want SRST is asserted before activating debug signals (mode_enter).
+ * As the required mode has not been set, the adapter may not know what pin to use.
+ * Tested firmware STLINK v2 JTAG v29 API v2 SWIM v0 uses T_NRST pin by default
+ * Tested firmware STLINK v2 JTAG v27 API v2 SWIM v6 uses T_NRST pin by default
+ * after power on, SWIM_RST stays unchanged */
+ if (connect_under_reset && emode != STLINK_MODE_DEBUG_SWIM)
+ stlink_usb_assert_srst(handle, 0);
+ /* do not check the return status here, we will
+ proceed and enter the desired mode below
+ and try asserting srst again. */
+
+ res = stlink_usb_mode_enter(handle, emode);
+ if (res != ERROR_OK)
+ return res;
+
+ /* assert SRST again: a little bit late but now the adapter knows for sure what pin to use */
+ if (connect_under_reset) {
+ res = stlink_usb_assert_srst(handle, 0);
+ if (res != ERROR_OK)
+ return res;
+ }
+
+ res = stlink_usb_current_mode(handle, &mode);
+
+ if (res != ERROR_OK)
+ return res;
+
+ LOG_DEBUG("MODE: 0x%02X", mode);
+
+ return ERROR_OK;
+}
+
+/* request status from last swim request */
+static int stlink_swim_status(void *handle)
+{
+ struct stlink_usb_handle_s *h = handle;
+ int res;
+
+ stlink_usb_init_buffer(handle, h->rx_ep, 4);
+ h->cmdbuf[h->cmdidx++] = STLINK_SWIM_COMMAND;
+ h->cmdbuf[h->cmdidx++] = STLINK_SWIM_READSTATUS;
+ res = stlink_usb_xfer(handle, h->databuf, 4);
+ if (res != ERROR_OK)
+ return res;
+ return ERROR_OK;
+}
+/*
+ the purpose of this function is unknown...
+ capabilites? anyway for swim v6 it returns
+ 0001020600000000
+*/
+__attribute__((unused))
+static int stlink_swim_cap(void *handle, uint8_t *cap)
+{
+ struct stlink_usb_handle_s *h = handle;
+ int res;
+
+ stlink_usb_init_buffer(handle, h->rx_ep, 8);
+ h->cmdbuf[h->cmdidx++] = STLINK_SWIM_COMMAND;
+ h->cmdbuf[h->cmdidx++] = STLINK_SWIM_READ_CAP;
+ h->cmdbuf[h->cmdidx++] = 0x01;
+ res = stlink_usb_xfer(handle, h->databuf, 8);
+ if (res != ERROR_OK)
+ return res;
+ memcpy(cap, h->databuf, 8);
+ return ERROR_OK;
+}
+
+/* debug dongle assert/deassert sreset line */
+static int stlink_swim_assert_reset(void *handle, int reset)
+{
+ struct stlink_usb_handle_s *h = handle;
+ int res;
+
+ stlink_usb_init_buffer(handle, h->rx_ep, 0);
+ h->cmdbuf[h->cmdidx++] = STLINK_SWIM_COMMAND;
+ if (!reset)
+ h->cmdbuf[h->cmdidx++] = STLINK_SWIM_ASSERT_RESET;
+ else
+ h->cmdbuf[h->cmdidx++] = STLINK_SWIM_DEASSERT_RESET;
+ res = stlink_cmd_allow_retry(handle, h->databuf, 0);
+ if (res != ERROR_OK)
+ return res;
+ return ERROR_OK;
+}
+
+/*
+ send swim enter seq
+ 1.3ms low then 750Hz then 1.5kHz
+*/
+static int stlink_swim_enter(void *handle)
+{
+ struct stlink_usb_handle_s *h = handle;
+ int res;
+
+ stlink_usb_init_buffer(handle, h->rx_ep, 0);
+ h->cmdbuf[h->cmdidx++] = STLINK_SWIM_COMMAND;
+ h->cmdbuf[h->cmdidx++] = STLINK_SWIM_ENTER_SEQ;
+ res = stlink_cmd_allow_retry(handle, h->databuf, 0);
+ if (res != ERROR_OK)
+ return res;
+ return ERROR_OK;
+}
+
+/* switch high/low speed swim */
+static int stlink_swim_speed(void *handle, int speed)
+{
+ struct stlink_usb_handle_s *h = handle;
+ int res;
+
+ stlink_usb_init_buffer(handle, h->rx_ep, 0);
+ h->cmdbuf[h->cmdidx++] = STLINK_SWIM_COMMAND;
+ h->cmdbuf[h->cmdidx++] = STLINK_SWIM_SPEED;
+ if (speed)
+ h->cmdbuf[h->cmdidx++] = 1;
+ else
+ h->cmdbuf[h->cmdidx++] = 0;
+ res = stlink_cmd_allow_retry(handle, h->databuf, 0);
+ if (res != ERROR_OK)
+ return res;
+ return ERROR_OK;
+}
+
+/*
+ initiate srst from swim.
+ nrst is pulled low for 50us.
+*/
+static int stlink_swim_generate_rst(void *handle)
+{
+ struct stlink_usb_handle_s *h = handle;
+ int res;
+
+ stlink_usb_init_buffer(handle, h->rx_ep, 0);
+ h->cmdbuf[h->cmdidx++] = STLINK_SWIM_COMMAND;
+ h->cmdbuf[h->cmdidx++] = STLINK_SWIM_GEN_RST;
+ res = stlink_cmd_allow_retry(handle, h->databuf, 0);
+ if (res != ERROR_OK)
+ return res;
+ return ERROR_OK;
+}
+
+/*
+ send resyncronize sequence
+ swim is pulled low for 16us
+ reply is 64 clks low
+*/
+static int stlink_swim_resync(void *handle)
+{
+ struct stlink_usb_handle_s *h = handle;
+ int res;
+
+ stlink_usb_init_buffer(handle, h->rx_ep, 0);
+ h->cmdbuf[h->cmdidx++] = STLINK_SWIM_COMMAND;
+ h->cmdbuf[h->cmdidx++] = STLINK_SWIM_RESET;
+ res = stlink_cmd_allow_retry(handle, h->databuf, 0);
+ if (res != ERROR_OK)
+ return res;
+ return ERROR_OK;
+}
+
+static int stlink_swim_writebytes(void *handle, uint32_t addr, uint32_t len, const uint8_t *data)
+{
+ struct stlink_usb_handle_s *h = handle;
+ int res;
+ unsigned int i;
+ unsigned int datalen = 0;
+ int cmdsize = STLINK_CMD_SIZE_V2;
+
+ if (len > STLINK_DATA_SIZE)
+ return ERROR_FAIL;
+
+ if (h->version.stlink == 1)
+ cmdsize = STLINK_SG_SIZE;
+
+ stlink_usb_init_buffer(handle, h->tx_ep, 0);
+ h->cmdbuf[h->cmdidx++] = STLINK_SWIM_COMMAND;
+ h->cmdbuf[h->cmdidx++] = STLINK_SWIM_WRITEMEM;
+ h_u16_to_be(h->cmdbuf+h->cmdidx, len);
+ h->cmdidx += 2;
+ h_u32_to_be(h->cmdbuf+h->cmdidx, addr);
+ h->cmdidx += 4;
+ for (i = 0; i < len; i++) {
+ if (h->cmdidx == cmdsize)
+ h->databuf[datalen++] = *(data++);
+ else
+ h->cmdbuf[h->cmdidx++] = *(data++);
+ }
+ if (h->version.stlink == 1)
+ stlink_usb_set_cbw_transfer_datalength(handle, datalen);
+
+ res = stlink_cmd_allow_retry(handle, h->databuf, datalen);
+ if (res != ERROR_OK)
+ return res;
+ return ERROR_OK;
+}
+
+static int stlink_swim_readbytes(void *handle, uint32_t addr, uint32_t len, uint8_t *data)
+{
+ struct stlink_usb_handle_s *h = handle;
+ int res;
+
+ if (len > STLINK_DATA_SIZE)
+ return ERROR_FAIL;
+
+ stlink_usb_init_buffer(handle, h->rx_ep, 0);
+ h->cmdbuf[h->cmdidx++] = STLINK_SWIM_COMMAND;
+ h->cmdbuf[h->cmdidx++] = STLINK_SWIM_READMEM;
+ h_u16_to_be(h->cmdbuf+h->cmdidx, len);
+ h->cmdidx += 2;
+ h_u32_to_be(h->cmdbuf+h->cmdidx, addr);
+ h->cmdidx += 4;
+ res = stlink_cmd_allow_retry(handle, h->databuf, 0);
+ if (res != ERROR_OK)
+ return res;
+
+ stlink_usb_init_buffer(handle, h->rx_ep, len);
+ h->cmdbuf[h->cmdidx++] = STLINK_SWIM_COMMAND;
+ h->cmdbuf[h->cmdidx++] = STLINK_SWIM_READBUF;
+ res = stlink_usb_xfer(handle, data, len);
+ if (res != ERROR_OK)
+ return res;
+
+ return ERROR_OK;
+}
+
+/** */
+static int stlink_usb_idcode(void *handle, uint32_t *idcode)
+{
+ int res;
+ struct stlink_usb_handle_s *h = handle;
+
+ assert(handle != NULL);
+
+ /* there is no swim read core id cmd */
+ if (h->transport == HL_TRANSPORT_SWIM) {
+ *idcode = 0;
+ return ERROR_OK;
+ }
+
+ stlink_usb_init_buffer(handle, h->rx_ep, 4);
+
+ h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_COMMAND;
+ h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_READCOREID;
+
+ res = stlink_usb_xfer(handle, h->databuf, 4);
+
+ if (res != ERROR_OK)
+ return res;
+
+ *idcode = le_to_h_u32(h->databuf);
+
+ LOG_DEBUG("IDCODE: 0x%08" PRIX32, *idcode);
+
+ return ERROR_OK;
+}
+
+static int stlink_usb_v2_read_debug_reg(void *handle, uint32_t addr, uint32_t *val)
+{
+ struct stlink_usb_handle_s *h = handle;
+ int res;
+
+ assert(handle != NULL);
+
+ stlink_usb_init_buffer(handle, h->rx_ep, 8);
+
+ h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_COMMAND;
+ h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_APIV2_READDEBUGREG;
+ h_u32_to_le(h->cmdbuf+h->cmdidx, addr);
+ h->cmdidx += 4;
+
+ res = stlink_cmd_allow_retry(handle, h->databuf, 8);
+ if (res != ERROR_OK)
+ return res;
+
+ *val = le_to_h_u32(h->databuf + 4);
+ return ERROR_OK;
+}
+
+static int stlink_usb_write_debug_reg(void *handle, uint32_t addr, uint32_t val)
+{
+ struct stlink_usb_handle_s *h = handle;
+
+ assert(handle != NULL);
+
+ stlink_usb_init_buffer(handle, h->rx_ep, 2);
+
+ h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_COMMAND;
+ if (h->version.jtag_api == STLINK_JTAG_API_V1)
+ h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_APIV1_WRITEDEBUGREG;
+ else
+ h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_APIV2_WRITEDEBUGREG;
+ h_u32_to_le(h->cmdbuf+h->cmdidx, addr);
+ h->cmdidx += 4;
+ h_u32_to_le(h->cmdbuf+h->cmdidx, val);
+ h->cmdidx += 4;
+
+ return stlink_cmd_allow_retry(handle, h->databuf, 2);
+}
+
+/** */
+static int stlink_usb_trace_read(void *handle, uint8_t *buf, size_t *size)
+{
+ struct stlink_usb_handle_s *h = handle;
+
+ assert(handle != NULL);
+
+ if (h->trace.enabled && (h->version.flags & STLINK_F_HAS_TRACE)) {
+ int res;
+
+ stlink_usb_init_buffer(handle, h->rx_ep, 10);
+
+ h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_COMMAND;
+ h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_APIV2_GET_TRACE_NB;
+
+ res = stlink_usb_xfer(handle, h->databuf, 2);
+ if (res != ERROR_OK)
+ return res;
+
+ size_t bytes_avail = le_to_h_u16(h->databuf);
+ *size = bytes_avail < *size ? bytes_avail : *size - 1;
+
+ if (*size > 0) {
+ res = stlink_usb_read_trace(handle, buf, *size);
+ if (res != ERROR_OK)
+ return res;
+ return ERROR_OK;
+ }
+ }
+ *size = 0;
+ return ERROR_OK;
+}
+
+static enum target_state stlink_usb_v2_get_status(void *handle)
+{
+ int result;
+ uint32_t status;
+
+ result = stlink_usb_v2_read_debug_reg(handle, DCB_DHCSR, &status);
+ if (result != ERROR_OK)
+ return TARGET_UNKNOWN;
+
+ if (status & S_HALT)
+ return TARGET_HALTED;
+ else if (status & S_RESET_ST)
+ return TARGET_RESET;
+
+ return TARGET_RUNNING;
+}
+
+/** */
+static enum target_state stlink_usb_state(void *handle)
+{
+ int res;
+ struct stlink_usb_handle_s *h = handle;
+
+ assert(handle != NULL);
+
+ if (h->transport == HL_TRANSPORT_SWIM) {
+ res = stlink_usb_mode_enter(handle, stlink_get_mode(h->transport));
+ if (res != ERROR_OK)
+ return TARGET_UNKNOWN;
+
+ res = stlink_swim_resync(handle);
+ if (res != ERROR_OK)
+ return TARGET_UNKNOWN;
+
+ return ERROR_OK;
+ }
+
+ if (h->reconnect_pending) {
+ LOG_INFO("Previous state query failed, trying to reconnect");
+ res = stlink_usb_mode_enter(handle, stlink_get_mode(h->transport));
+
+ if (res != ERROR_OK)
+ return TARGET_UNKNOWN;
+
+ h->reconnect_pending = false;
+ }
+
+ if (h->version.jtag_api != STLINK_JTAG_API_V1) {
+ res = stlink_usb_v2_get_status(handle);
+ if (res == TARGET_UNKNOWN)
+ h->reconnect_pending = true;
+ return res;
+ }
+
+ stlink_usb_init_buffer(handle, h->rx_ep, 2);
+
+ h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_COMMAND;
+ h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_GETSTATUS;
+
+ res = stlink_usb_xfer(handle, h->databuf, 2);
+
+ if (res != ERROR_OK)
+ return TARGET_UNKNOWN;
+
+ if (h->databuf[0] == STLINK_CORE_RUNNING)
+ return TARGET_RUNNING;
+ if (h->databuf[0] == STLINK_CORE_HALTED)
+ return TARGET_HALTED;
+
+ h->reconnect_pending = true;
+
+ return TARGET_UNKNOWN;
+}
+
+static int stlink_usb_assert_srst(void *handle, int srst)
+{
+ struct stlink_usb_handle_s *h = handle;
+
+ assert(handle != NULL);
+
+ if (h->transport == HL_TRANSPORT_SWIM)
+ return stlink_swim_assert_reset(handle, srst);