+ 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;
+}
+
+/** */
+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;
+}
+
+/** */
+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->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) {
+ usleep((1<<retries++) * 1000);
+ 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.stlink >= 2);
+
+ 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;
+ uint16_t v;
+ 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;
+
+ v = (h->databuf[0] << 8) | h->databuf[1];
+
+ h->version.stlink = (v >> 12) & 0x0f;
+ h->version.jtag = (v >> 6) & 0x3f;
+ h->version.swim = v & 0x3f;
+ h->vid = buf_get_u32(h->databuf, 16, 16);
+ h->pid = buf_get_u32(h->databuf, 32, 16);
+
+ /* set the supported jtag api version
+ * API V2 is supported since JTAG V11
+ */
+ if (h->version.jtag >= 11)
+ h->version.jtag_api_max = STLINK_JTAG_API_V2;
+ else
+ h->version.jtag_api_max = STLINK_JTAG_API_V1;
+
+ LOG_INFO("STLINK v%d JTAG v%d API v%d SWIM v%d VID 0x%04X PID 0x%04X",
+ h->version.stlink,
+ h->version.jtag,
+ (h->version.jtag_api_max == STLINK_JTAG_API_V1) ? 1 : 2,
+ h->version.swim,
+ 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];
+
+ /* only supported by stlink/v2 and for firmware >= 13 */
+ if (h->version.stlink == 1 || h->version.jtag < 13)
+ 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);
+
+ /* only supported by stlink/v2 and for firmware >= 22 */
+ if (h->version.stlink == 1 || h->version.jtag < 22)
+ 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);
+
+ /* only supported by stlink/v2 and for firmware >= 24 */
+ if (h->version.stlink == 1 || h->version.jtag < 24)
+ 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->jtag_api == STLINK_JTAG_API_V2)
+ 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->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;
+ break;
+ case STLINK_MODE_DEBUG_SWD:
+ h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_COMMAND;
+ if (h->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;
+ 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;