aboutsummaryrefslogtreecommitdiff
path: root/src/jtag/drivers/cmsis_dap.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/jtag/drivers/cmsis_dap.c')
-rw-r--r--src/jtag/drivers/cmsis_dap.c202
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,