drivers/stlink_usb: fix stlink_usb_read_regs() for API v2
[openocd.git] / src / jtag / drivers / stlink_usb.c
index aed30b6f7360b8fa1b038f79e2340f2bc326bedf..9ba4f4af987212360281eac2f0b8b279d94296be 100644 (file)
 
 #include "libusb_common.h"
 
+#ifdef HAVE_LIBUSB1
+#define USE_LIBUSB_ASYNCIO
+#endif
+
 #define ENDPOINT_IN  0x80
 #define ENDPOINT_OUT 0x00
 
@@ -152,6 +156,7 @@ struct stlink_usb_handle_s {
 #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
@@ -163,6 +168,8 @@ struct stlink_usb_handle_s {
 #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
@@ -351,6 +358,160 @@ 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(
+               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)
 {
@@ -384,7 +545,45 @@ static int stlink_usb_xfer_v1_get_status(void *handle)
        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;
@@ -412,6 +611,7 @@ static int stlink_usb_xfer_rw(void *handle, int cmdsize, const uint8_t *buf, int
 
        return ERROR_OK;
 }
+#endif
 
 /** */
 static int stlink_usb_xfer_v1_get_sense(void *handle)
@@ -516,6 +716,9 @@ static int stlink_usb_error_check(void *handle)
                case STLINK_SWD_DP_WAIT:
                        LOG_DEBUG("wait status SWD_DP_WAIT (0x%x)", STLINK_SWD_DP_WAIT);
                        return ERROR_WAIT;
+               case STLINK_JTAG_GET_IDCODE_ERROR:
+                       LOG_DEBUG("STLINK_JTAG_GET_IDCODE_ERROR");
+                       return ERROR_FAIL;
                case STLINK_JTAG_WRITE_ERROR:
                        LOG_DEBUG("Write error");
                        return ERROR_FAIL;
@@ -554,6 +757,9 @@ static int stlink_usb_error_check(void *handle)
                case STLINK_SWD_AP_STICKYORUN_ERROR:
                        LOG_DEBUG("STLINK_SWD_AP_STICKYORUN_ERROR");
                        return ERROR_FAIL;
+               case STLINK_BAD_AP_ERROR:
+                       LOG_DEBUG("STLINK_BAD_AP_ERROR");
+                       return ERROR_FAIL;
                default:
                        LOG_DEBUG("unknown/unexpected STLINK status code 0x%x", h->databuf[0]);
                        return ERROR_FAIL;
@@ -589,7 +795,9 @@ static int stlink_cmd_allow_retry(void *handle, const uint8_t *buf, int size)
 
                res = stlink_usb_error_check(handle);
                if (res == ERROR_WAIT && retries < MAX_WAIT_RETRIES) {
-                       usleep((1<<retries++) * 1000);
+                       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;
@@ -803,7 +1011,7 @@ static int stlink_usb_version(void *handle)
        if (bridge)
                p += sprintf(p, "B%d", bridge);
        if (swim || !msd)
-               p += sprintf(p, "S%d", swim);
+               sprintf(p, "S%d", swim);
 
        LOG_INFO("STLINK %s (API v%d) VID:PID %04X:%04X",
                v_str,
@@ -1687,20 +1895,21 @@ static int stlink_usb_read_regs(void *handle)
 
        assert(handle != NULL);
 
-       stlink_usb_init_buffer(handle, h->rx_ep, 84);
+       stlink_usb_init_buffer(handle, h->rx_ep, 88);
 
        h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_COMMAND;
-       if (h->version.jtag_api == STLINK_JTAG_API_V1)
+       if (h->version.jtag_api == STLINK_JTAG_API_V1) {
+
                h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_APIV1_READALLREGS;
-       else
+               res = stlink_usb_xfer(handle, h->databuf, 84);
+               /* regs data from offset 0 */
+       } else {
                h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_APIV2_READALLREGS;
+               res = stlink_usb_xfer(handle, h->databuf, 88);
+               /* status at offset 0, regs data from offset 4 */
+       }
 
-       res = stlink_usb_xfer(handle, h->databuf, 84);
-
-       if (res != ERROR_OK)
-               return res;
-
-       return ERROR_OK;
+       return res;
 }
 
 /** */

Linking to existing account procedure

If you already have an account and want to add another login method you MUST first sign in with your existing account and then change URL to read https://review.openocd.org/login/?link to get to this page again but this time it'll work for linking. Thank you.

SSH host keys fingerprints

1024 SHA256:YKx8b7u5ZWdcbp7/4AeXNaqElP49m6QrwfXaqQGJAOk gerrit-code-review@openocd.zylin.com (DSA)
384 SHA256:jHIbSQa4REvwCFG4cq5LBlBLxmxSqelQPem/EXIrxjk gerrit-code-review@openocd.org (ECDSA)
521 SHA256:UAOPYkU9Fjtcao0Ul/Rrlnj/OsQvt+pgdYSZ4jOYdgs gerrit-code-review@openocd.org (ECDSA)
256 SHA256:A13M5QlnozFOvTllybRZH6vm7iSt0XLxbA48yfc2yfY gerrit-code-review@openocd.org (ECDSA)
256 SHA256:spYMBqEYoAOtK7yZBrcwE8ZpYt6b68Cfh9yEVetvbXg gerrit-code-review@openocd.org (ED25519)
+--[ED25519 256]--+
|=..              |
|+o..   .         |
|*.o   . .        |
|+B . . .         |
|Bo. = o S        |
|Oo.+ + =         |
|oB=.* = . o      |
| =+=.+   + E     |
|. .=o   . o      |
+----[SHA256]-----+
2048 SHA256:0Onrb7/PHjpo6iVZ7xQX2riKN83FJ3KGU0TvI0TaFG4 gerrit-code-review@openocd.zylin.com (RSA)