diff options
Diffstat (limited to 'src/jtag/drivers/cmsis_dap.c')
-rw-r--r-- | src/jtag/drivers/cmsis_dap.c | 202 |
1 files changed, 144 insertions, 58 deletions
diff --git a/src/jtag/drivers/cmsis_dap.c b/src/jtag/drivers/cmsis_dap.c index 1e7a851..caacc9b 100644 --- a/src/jtag/drivers/cmsis_dap.c +++ b/src/jtag/drivers/cmsis_dap.c @@ -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; @@ -1003,20 +1071,19 @@ static void cmsis_dap_swd_queue_cmd(uint8_t cmd, uint32_t *dst, uint32_t data) 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); @@ -1024,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; @@ -1160,7 +1222,10 @@ static int cmsis_dap_swd_switch_seq(enum swd_special_seq seq) unsigned int s_len; int retval; - if (seq != LINE_RESET && + 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. @@ -1296,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; @@ -2108,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 */ @@ -2148,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", @@ -2172,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", @@ -2210,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, |