jtag/mpsse: mpsse_flush should not treat LIBUSB_ERROR_INTERRUPTED as an error
[openocd.git] / src / jtag / drivers / cmsis_dap.c
index d2c30cc7964b223ff21c5da8f57dfe634e3bc072..caacc9b91602ea543d956ebda8ab9323fee3d59f 100644 (file)
@@ -225,6 +225,12 @@ struct pending_scan_result {
        unsigned int buffer_offset;
 };
 
+/* Read mode */
+enum cmsis_dap_blocking {
+       CMSIS_DAP_NON_BLOCKING,
+       CMSIS_DAP_BLOCKING
+};
+
 /* Each block in FIFO can contain up to pending_queue_len transfers */
 static unsigned int pending_queue_len;
 static unsigned int tfer_max_command_size;
@@ -315,7 +321,7 @@ static void cmsis_dap_flush_read(struct cmsis_dap *dap)
         * USB close/open so we need to flush up to 64 old packets
         * to be sure all buffers are empty */
        for (i = 0; i < 64; i++) {
-               int retval = dap->backend->read(dap, 10);
+               int retval = dap->backend->read(dap, 10, NULL);
                if (retval == ERROR_TIMEOUT_REACHED)
                        break;
        }
@@ -326,10 +332,13 @@ static void cmsis_dap_flush_read(struct cmsis_dap *dap)
 /* Send a message and receive the reply */
 static int cmsis_dap_xfer(struct cmsis_dap *dap, int txlen)
 {
+       if (dap->write_count + dap->read_count) {
+               LOG_ERROR("internal: queue not empty before xfer");
+       }
        if (dap->pending_fifo_block_count) {
                LOG_ERROR("pending %u blocks, flushing", dap->pending_fifo_block_count);
                while (dap->pending_fifo_block_count) {
-                       dap->backend->read(dap, 10);
+                       dap->backend->read(dap, 10, NULL);
                        dap->pending_fifo_block_count--;
                }
                dap->pending_fifo_put_idx = 0;
@@ -342,7 +351,7 @@ static int cmsis_dap_xfer(struct cmsis_dap *dap, int txlen)
                return retval;
 
        /* get reply */
-       retval = dap->backend->read(dap, LIBUSB_TIMEOUT_MS);
+       retval = dap->backend->read(dap, LIBUSB_TIMEOUT_MS, NULL);
        if (retval < 0)
                return retval;
 
@@ -356,6 +365,7 @@ static int cmsis_dap_xfer(struct cmsis_dap *dap, int txlen)
                LOG_ERROR("CMSIS-DAP command mismatch. Sent 0x%" PRIx8
                         " received 0x%" PRIx8, current_cmd, resp[0]);
 
+               dap->backend->cancel_all(dap);
                cmsis_dap_flush_read(dap);
                return ERROR_FAIL;
        }
@@ -749,6 +759,22 @@ static int cmsis_dap_cmd_dap_swo_data(
        return ERROR_OK;
 }
 
+static void cmsis_dap_swd_discard_all_pending(struct cmsis_dap *dap)
+{
+       for (unsigned int i = 0; i < MAX_PENDING_REQUESTS; i++)
+               dap->pending_fifo[i].transfer_count = 0;
+
+       dap->pending_fifo_put_idx = 0;
+       dap->pending_fifo_get_idx = 0;
+       dap->pending_fifo_block_count = 0;
+}
+
+static void cmsis_dap_swd_cancel_transfers(struct cmsis_dap *dap)
+{
+       dap->backend->cancel_all(dap);
+       cmsis_dap_flush_read(dap);
+       cmsis_dap_swd_discard_all_pending(dap);
+}
 
 static void cmsis_dap_swd_write_from_queue(struct cmsis_dap *dap)
 {
@@ -770,8 +796,10 @@ static void cmsis_dap_swd_write_from_queue(struct cmsis_dap *dap)
                goto skip;
        }
 
-       if (block->transfer_count == 0)
+       if (block->transfer_count == 0) {
+               LOG_ERROR("internal: write an empty queue?!");
                goto skip;
+       }
 
        bool block_cmd = !cmsis_dap_handle->swd_cmds_differ
                                         && block->transfer_count >= CMD_DAP_TFER_BLOCK_MIN_OPS;
@@ -831,14 +859,13 @@ static void cmsis_dap_swd_write_from_queue(struct cmsis_dap *dap)
        if (retval < 0) {
                queued_retval = retval;
                goto skip;
-       } else {
-               queued_retval = ERROR_OK;
        }
 
-       dap->pending_fifo_put_idx = (dap->pending_fifo_put_idx + 1) % dap->packet_count;
+       unsigned int packet_count = dap->quirk_mode ? 1 : dap->packet_count;
+       dap->pending_fifo_put_idx = (dap->pending_fifo_put_idx + 1) % packet_count;
        dap->pending_fifo_block_count++;
-       if (dap->pending_fifo_block_count > dap->packet_count)
-               LOG_ERROR("too much pending writes %u", dap->pending_fifo_block_count);
+       if (dap->pending_fifo_block_count > packet_count)
+               LOG_ERROR("internal: too much pending writes %u", dap->pending_fifo_block_count);
 
        return;
 
@@ -846,21 +873,47 @@ skip:
        block->transfer_count = 0;
 }
 
-static void cmsis_dap_swd_read_process(struct cmsis_dap *dap, int timeout_ms)
+static void cmsis_dap_swd_read_process(struct cmsis_dap *dap, enum cmsis_dap_blocking blocking)
 {
+       int retval;
        struct pending_request_block *block = &dap->pending_fifo[dap->pending_fifo_get_idx];
 
-       if (dap->pending_fifo_block_count == 0)
-               LOG_ERROR("no pending write");
+       if (dap->pending_fifo_block_count == 0) {
+               LOG_ERROR("internal: no pending write when reading?!");
+               return;
+       }
+
+       if (queued_retval != ERROR_OK) {
+               /* keep reading blocks until the pipeline is empty */
+               retval = dap->backend->read(dap, 10, NULL);
+               if (retval == ERROR_TIMEOUT_REACHED || retval == 0) {
+                       /* timeout means that we flushed the pipeline,
+                        * we can safely discard remaining pending requests */
+                       cmsis_dap_swd_discard_all_pending(dap);
+                       return;
+               }
+               goto skip;
+       }
 
        /* get reply */
-       int retval = dap->backend->read(dap, timeout_ms);
-       if (retval == ERROR_TIMEOUT_REACHED && timeout_ms < LIBUSB_TIMEOUT_MS)
+       struct timeval tv = {
+               .tv_sec = 0,
+               .tv_usec = 0
+       };
+       retval = dap->backend->read(dap, LIBUSB_TIMEOUT_MS, blocking ? NULL : &tv);
+       bool timeout = (retval == ERROR_TIMEOUT_REACHED || retval == 0);
+       if (timeout && blocking == CMSIS_DAP_NON_BLOCKING)
                return;
 
        if (retval <= 0) {
-               LOG_DEBUG("error reading data");
+               LOG_DEBUG("error reading adapter response");
                queued_retval = ERROR_FAIL;
+               if (timeout) {
+                       /* timeout means that we flushed the pipeline,
+                        * we can safely discard remaining pending requests */
+                       cmsis_dap_swd_discard_all_pending(dap);
+                       return;
+               }
                goto skip;
        }
 
@@ -868,8 +921,9 @@ static void cmsis_dap_swd_read_process(struct cmsis_dap *dap, int timeout_ms)
        if (resp[0] != block->command) {
                LOG_ERROR("CMSIS-DAP command mismatch. Expected 0x%x received 0x%" PRIx8,
                        block->command, resp[0]);
+               cmsis_dap_swd_cancel_transfers(dap);
                queued_retval = ERROR_FAIL;
-               goto skip;
+               return;
        }
 
        unsigned int transfer_count;
@@ -895,12 +949,17 @@ static void cmsis_dap_swd_read_process(struct cmsis_dap *dap, int timeout_ms)
                goto skip;
        }
 
-       if (block->transfer_count != transfer_count)
+       if (block->transfer_count != transfer_count) {
                LOG_ERROR("CMSIS-DAP transfer count mismatch: expected %d, got %d",
                          block->transfer_count, transfer_count);
+               cmsis_dap_swd_cancel_transfers(dap);
+               queued_retval = ERROR_FAIL;
+               return;
+       }
 
-       LOG_DEBUG_IO("Received results of %d queued transactions FIFO index %u timeout %i",
-                transfer_count, dap->pending_fifo_get_idx, timeout_ms);
+       LOG_DEBUG_IO("Received results of %d queued transactions FIFO index %u, %s mode",
+                                transfer_count, dap->pending_fifo_get_idx,
+                                blocking ? "blocking" : "nonblocking");
 
        for (unsigned int i = 0; i < transfer_count; i++) {
                struct pending_transfer_result *transfer = &(block->transfers[i]);
@@ -926,19 +985,22 @@ static void cmsis_dap_swd_read_process(struct cmsis_dap *dap, int timeout_ms)
 
 skip:
        block->transfer_count = 0;
-       dap->pending_fifo_get_idx = (dap->pending_fifo_get_idx + 1) % dap->packet_count;
+       if (!dap->quirk_mode && dap->packet_count > 1)
+               dap->pending_fifo_get_idx = (dap->pending_fifo_get_idx + 1) % dap->packet_count;
        dap->pending_fifo_block_count--;
 }
 
 static int cmsis_dap_swd_run_queue(void)
 {
-       if (cmsis_dap_handle->pending_fifo_block_count)
-               cmsis_dap_swd_read_process(cmsis_dap_handle, 0);
+       if (cmsis_dap_handle->write_count + cmsis_dap_handle->read_count) {
+               if (cmsis_dap_handle->pending_fifo_block_count)
+                       cmsis_dap_swd_read_process(cmsis_dap_handle, CMSIS_DAP_NON_BLOCKING);
 
-       cmsis_dap_swd_write_from_queue(cmsis_dap_handle);
+               cmsis_dap_swd_write_from_queue(cmsis_dap_handle);
+       }
 
        while (cmsis_dap_handle->pending_fifo_block_count)
-               cmsis_dap_swd_read_process(cmsis_dap_handle, LIBUSB_TIMEOUT_MS);
+               cmsis_dap_swd_read_process(cmsis_dap_handle, CMSIS_DAP_BLOCKING);
 
        cmsis_dap_handle->pending_fifo_put_idx = 0;
        cmsis_dap_handle->pending_fifo_get_idx = 0;
@@ -979,10 +1041,16 @@ static unsigned int cmsis_dap_tfer_resp_size(unsigned int write_count,
 
 static void cmsis_dap_swd_queue_cmd(uint8_t cmd, uint32_t *dst, uint32_t data)
 {
+       /* TARGETSEL register write cannot be queued */
+       if (swd_cmd(false, false, DP_TARGETSEL) == cmd) {
+               queued_retval = cmsis_dap_swd_run_queue();
+
+               cmsis_dap_metacmd_targetsel(data);
+               return;
+       }
+
        /* Compute sizes of the DAP Transfer command and the expected response
         * for all queued and this operation */
-       bool targetsel_cmd = swd_cmd(false, false, DP_TARGETSEL) == cmd;
-
        unsigned int write_count = cmsis_dap_handle->write_count;
        unsigned int read_count = cmsis_dap_handle->read_count;
        bool block_cmd;
@@ -1001,20 +1069,21 @@ static void cmsis_dap_swd_queue_cmd(uint8_t cmd, uint32_t *dst, uint32_t data)
                                                                                                        block_cmd);
        unsigned int resp_size = cmsis_dap_tfer_resp_size(write_count, read_count,
                                                                                                        block_cmd);
+       unsigned int max_transfer_count = block_cmd ? 65535 : 255;
 
-       /* Does the DAP Transfer command and the expected response fit into one packet?
-        * Run the queue also before a targetsel - it cannot be queued */
+       /* Does the DAP Transfer command and also its expected response fit into one packet? */
        if (cmd_size > tfer_max_command_size
                        || resp_size > tfer_max_response_size
-                       || targetsel_cmd) {
+                       || write_count + read_count > max_transfer_count) {
                if (cmsis_dap_handle->pending_fifo_block_count)
-                       cmsis_dap_swd_read_process(cmsis_dap_handle, 0);
+                       cmsis_dap_swd_read_process(cmsis_dap_handle, CMSIS_DAP_NON_BLOCKING);
 
                /* Not enough room in the queue. Run the queue. */
                cmsis_dap_swd_write_from_queue(cmsis_dap_handle);
 
-               if (cmsis_dap_handle->pending_fifo_block_count >= cmsis_dap_handle->packet_count)
-                       cmsis_dap_swd_read_process(cmsis_dap_handle, LIBUSB_TIMEOUT_MS);
+               unsigned int packet_count = cmsis_dap_handle->quirk_mode ? 1 : cmsis_dap_handle->packet_count;
+               if (cmsis_dap_handle->pending_fifo_block_count >= packet_count)
+                       cmsis_dap_swd_read_process(cmsis_dap_handle, CMSIS_DAP_BLOCKING);
        }
 
        assert(cmsis_dap_handle->pending_fifo[cmsis_dap_handle->pending_fifo_put_idx].transfer_count < pending_queue_len);
@@ -1022,11 +1091,6 @@ static void cmsis_dap_swd_queue_cmd(uint8_t cmd, uint32_t *dst, uint32_t data)
        if (queued_retval != ERROR_OK)
                return;
 
-       if (targetsel_cmd) {
-               cmsis_dap_metacmd_targetsel(data);
-               return;
-       }
-
        struct pending_request_block *block = &cmsis_dap_handle->pending_fifo[cmsis_dap_handle->pending_fifo_put_idx];
        struct pending_transfer_result *transfer = &(block->transfers[block->transfer_count]);
        transfer->data = data;
@@ -1158,7 +1222,12 @@ static int cmsis_dap_swd_switch_seq(enum swd_special_seq seq)
        unsigned int s_len;
        int retval;
 
-       if ((output_pins & (SWJ_PIN_SRST | SWJ_PIN_TRST)) == (SWJ_PIN_SRST | SWJ_PIN_TRST)) {
+       if (swd_mode)
+               queued_retval = cmsis_dap_swd_run_queue();
+
+       if (cmsis_dap_handle->quirk_mode && seq != LINE_RESET &&
+                       (output_pins & (SWJ_PIN_SRST | SWJ_PIN_TRST))
+                               == (SWJ_PIN_SRST | SWJ_PIN_TRST)) {
                /* Following workaround deasserts reset on most adapters.
                 * Do not reconnect if a reset line is active!
                 * Reconnecting would break connecting under reset. */
@@ -1292,7 +1361,7 @@ static int cmsis_dap_init(void)
        if (data[0] == 2) {  /* short */
                uint16_t pkt_sz = data[1] + (data[2] << 8);
                if (pkt_sz != cmsis_dap_handle->packet_size) {
-                       free(cmsis_dap_handle->packet_buffer);
+                       cmsis_dap_handle->backend->packet_buffer_free(cmsis_dap_handle);
                        retval = cmsis_dap_handle->backend->packet_buffer_alloc(cmsis_dap_handle, pkt_sz);
                        if (retval != ERROR_OK)
                                goto init_err;
@@ -2104,12 +2173,12 @@ COMMAND_HANDLER(cmsis_dap_handle_cmd_command)
 COMMAND_HANDLER(cmsis_dap_handle_vid_pid_command)
 {
        if (CMD_ARGC > MAX_USB_IDS * 2) {
-               LOG_WARNING("ignoring extra IDs in cmsis_dap_vid_pid "
+               LOG_WARNING("ignoring extra IDs in cmsis-dap vid_pid "
                        "(maximum is %d pairs)", MAX_USB_IDS);
                CMD_ARGC = MAX_USB_IDS * 2;
        }
        if (CMD_ARGC < 2 || (CMD_ARGC & 1)) {
-               LOG_WARNING("incomplete cmsis_dap_vid_pid configuration directive");
+               LOG_WARNING("incomplete cmsis-dap vid_pid configuration directive");
                if (CMD_ARGC < 2)
                        return ERROR_COMMAND_SYNTAX_ERROR;
                /* remove the incomplete trailing id */
@@ -2144,15 +2213,29 @@ COMMAND_HANDLER(cmsis_dap_handle_backend_command)
                                }
                        }
 
-                       LOG_ERROR("invalid backend argument to cmsis_dap_backend <backend>");
+                       command_print(CMD, "invalid backend argument to cmsis-dap backend <backend>");
+                       return ERROR_COMMAND_ARGUMENT_INVALID;
                }
        } else {
-               LOG_ERROR("expected exactly one argument to cmsis_dap_backend <backend>");
+               return ERROR_COMMAND_SYNTAX_ERROR;
        }
 
        return ERROR_OK;
 }
 
+COMMAND_HANDLER(cmsis_dap_handle_quirk_command)
+{
+       if (CMD_ARGC > 1)
+               return ERROR_COMMAND_SYNTAX_ERROR;
+
+       if (CMD_ARGC == 1)
+               COMMAND_PARSE_ENABLE(CMD_ARGV[0], cmsis_dap_handle->quirk_mode);
+
+       command_print(CMD, "CMSIS-DAP quirk workarounds %s",
+                                 cmsis_dap_handle->quirk_mode ? "enabled" : "disabled");
+       return ERROR_OK;
+}
+
 static const struct command_registration cmsis_dap_subcommand_handlers[] = {
        {
                .name = "info",
@@ -2168,35 +2251,30 @@ static const struct command_registration cmsis_dap_subcommand_handlers[] = {
                .usage = "",
                .help = "issue cmsis-dap command",
        },
-       COMMAND_REGISTRATION_DONE
-};
-
-
-static const struct command_registration cmsis_dap_command_handlers[] = {
-       {
-               .name = "cmsis-dap",
-               .mode = COMMAND_ANY,
-               .help = "perform CMSIS-DAP management",
-               .usage = "<cmd>",
-               .chain = cmsis_dap_subcommand_handlers,
-       },
        {
-               .name = "cmsis_dap_vid_pid",
+               .name = "vid_pid",
                .handler = &cmsis_dap_handle_vid_pid_command,
                .mode = COMMAND_CONFIG,
                .help = "the vendor ID and product ID of the CMSIS-DAP device",
                .usage = "(vid pid)*",
        },
        {
-               .name = "cmsis_dap_backend",
+               .name = "backend",
                .handler = &cmsis_dap_handle_backend_command,
                .mode = COMMAND_CONFIG,
                .help = "set the communication backend to use (USB bulk or HID).",
                .usage = "(auto | usb_bulk | hid)",
        },
+       {
+               .name = "quirk",
+               .handler = &cmsis_dap_handle_quirk_command,
+               .mode = COMMAND_ANY,
+               .help = "allow expensive workarounds of known adapter quirks.",
+               .usage = "[enable | disable]",
+       },
 #if BUILD_CMSIS_DAP_USB
        {
-               .name = "cmsis_dap_usb",
+               .name = "usb",
                .chain = cmsis_dap_usb_subcommand_handlers,
                .mode = COMMAND_ANY,
                .help = "USB bulk backend-specific commands",
@@ -2206,6 +2284,18 @@ static const struct command_registration cmsis_dap_command_handlers[] = {
        COMMAND_REGISTRATION_DONE
 };
 
+
+static const struct command_registration cmsis_dap_command_handlers[] = {
+       {
+               .name = "cmsis-dap",
+               .mode = COMMAND_ANY,
+               .help = "perform CMSIS-DAP management",
+               .usage = "<cmd>",
+               .chain = cmsis_dap_subcommand_handlers,
+       },
+       COMMAND_REGISTRATION_DONE
+};
+
 static const struct swd_driver cmsis_dap_swd_driver = {
        .init = cmsis_dap_swd_init,
        .switch_seq = cmsis_dap_swd_switch_seq,

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)