X-Git-Url: https://review.openocd.org/gitweb?p=openocd.git;a=blobdiff_plain;f=src%2Fjtag%2Fdrivers%2Fstlink_usb.c;h=bb2c8174a5d8d50deb05642d068be6ecc3fd4c48;hp=92ce1e8015f6b65c14d07ea6fea66eb2f73ba1e5;hb=4a20a1d1c01f0abb8cc8aabecb19c1f900582136;hpb=22c76a4fd536ba53f563d72d2b3402c45f925f85 diff --git a/src/jtag/drivers/stlink_usb.c b/src/jtag/drivers/stlink_usb.c index 92ce1e8015..bb2c8174a5 100644 --- a/src/jtag/drivers/stlink_usb.c +++ b/src/jtag/drivers/stlink_usb.c @@ -33,9 +33,12 @@ #endif /* project specific includes */ +#include #include #include #include +#include +#include #include #include #include @@ -68,8 +71,8 @@ #define ENDPOINT_IN 0x80 #define ENDPOINT_OUT 0x00 -#define STLINK_WRITE_TIMEOUT 1000 -#define STLINK_READ_TIMEOUT 1000 +#define STLINK_WRITE_TIMEOUT (LIBUSB_TIMEOUT_MS) +#define STLINK_READ_TIMEOUT (LIBUSB_TIMEOUT_MS) #define STLINK_RX_EP (1|ENDPOINT_IN) #define STLINK_TX_EP (2|ENDPOINT_OUT) @@ -153,6 +156,13 @@ struct stlink_usb_priv_s { struct libusb_transfer *trans; }; +struct stlink_tcp_version { + uint32_t api; + uint32_t major; + uint32_t minor; + uint32_t build; +}; + struct stlink_tcp_priv_s { /** */ int fd; @@ -166,6 +176,8 @@ struct stlink_tcp_priv_s { uint8_t *send_buf; /** */ uint8_t *recv_buf; + /** */ + struct stlink_tcp_version version; }; struct stlink_backend_s { @@ -436,6 +448,8 @@ static inline int stlink_usb_xfer_noerrcheck(void *handle, const uint8_t *buf, i #define STLINK_DEBUG_APIV2_CLOSE_AP_DBG 0x4C #define STLINK_DEBUG_WRITEMEM_32BIT_NO_ADDR_INC 0x50 +#define STLINK_DEBUG_APIV2_RW_MISC_OUT 0x51 +#define STLINK_DEBUG_APIV2_RW_MISC_IN 0x52 #define STLINK_DEBUG_READMEM_32BIT_NO_ADDR_INC 0x54 @@ -490,6 +504,8 @@ static inline int stlink_usb_xfer_noerrcheck(void *handle, const uint8_t *buf, i #define STLINK_TCP_SS_CMD_NOT_AVAILABLE 0x00001053 #define STLINK_TCP_SS_TCP_ERROR 0x00002001 #define STLINK_TCP_SS_TCP_CANT_CONNECT 0x00002002 +#define STLINK_TCP_SS_TCP_CLOSE_ERROR 0x00002003 +#define STLINK_TCP_SS_TCP_BUSY 0x00002004 #define STLINK_TCP_SS_WIN32_ERROR 0x00010000 /* @@ -513,6 +529,7 @@ static inline int stlink_usb_xfer_noerrcheck(void *handle, const uint8_t *buf, i #define STLINK_F_HAS_FPU_REG STLINK_F_HAS_GETLASTRWSTATUS2 #define STLINK_F_HAS_MEM_WR_NO_INC STLINK_F_HAS_MEM_16BIT #define STLINK_F_HAS_MEM_RD_NO_INC STLINK_F_HAS_DPBANKSEL +#define STLINK_F_HAS_RW_MISC STLINK_F_HAS_DPBANKSEL #define STLINK_F_HAS_CSW STLINK_F_HAS_DPBANKSEL #define STLINK_REGSEL_IS_FPU(x) ((x) > 0x1F) @@ -922,22 +939,45 @@ static int stlink_tcp_send_cmd(void *handle, int send_size, int recv_size, bool return ERROR_FAIL; } - keep_alive(); - /* read the TCP response */ - int received_size = recv(h->tcp_backend_priv.fd, (void *)h->tcp_backend_priv.recv_buf, recv_size, 0); - if (received_size != recv_size) { - LOG_ERROR("failed to receive USB CMD response"); - if (received_size == -1) + int retval = ERROR_OK; + int remaining_bytes = recv_size; + uint8_t *recv_buf = h->tcp_backend_priv.recv_buf; + const int64_t timeout = timeval_ms() + 1000; /* 1 second */ + + while (remaining_bytes > 0) { + if (timeval_ms() > timeout) { + LOG_DEBUG("received size %d (expected %d)", recv_size - remaining_bytes, recv_size); + retval = ERROR_TIMEOUT_REACHED; + break; + } + + keep_alive(); + int received = recv(h->tcp_backend_priv.fd, (void *)recv_buf, remaining_bytes, 0); + + if (received == -1) { LOG_DEBUG("socket recv error: %s (errno %d)", strerror(errno), errno); - else - LOG_DEBUG("received size %d (expected %d)", received_size, recv_size); - return ERROR_FAIL; + retval = ERROR_FAIL; + break; + } + + recv_buf += received; + remaining_bytes -= received; + } + + if (retval != ERROR_OK) { + LOG_ERROR("failed to receive USB CMD response"); + return retval; } if (check_tcp_status) { uint32_t tcp_ss = le_to_h_u32(h->tcp_backend_priv.recv_buf); if (tcp_ss != STLINK_TCP_SS_OK) { + if (tcp_ss == STLINK_TCP_SS_TCP_BUSY) { + LOG_DEBUG("TCP busy"); + return ERROR_WAIT; + } + LOG_ERROR("TCP error status 0x%X", tcp_ss); return ERROR_FAIL; } @@ -3340,7 +3380,7 @@ static int stlink_usb_usb_open(void *handle, struct hl_interface_param_s *param) in order to become operational. */ do { - if (jtag_libusb_open(param->vid, param->pid, param->serial, + if (jtag_libusb_open(param->vid, param->pid, &h->usb_backend_priv.fd, stlink_usb_get_alternate_serial) != ERROR_OK) { LOG_ERROR("open failed"); return ERROR_FAIL; @@ -3508,16 +3548,19 @@ static int stlink_tcp_open(void *handle, struct hl_interface_param_s *param) return ERROR_FAIL; } - uint32_t api_ver = le_to_h_u32(&h->tcp_backend_priv.recv_buf[0]); - uint32_t ver_major = le_to_h_u32(&h->tcp_backend_priv.recv_buf[4]); - uint32_t ver_minor = le_to_h_u32(&h->tcp_backend_priv.recv_buf[8]); - uint32_t ver_build = le_to_h_u32(&h->tcp_backend_priv.recv_buf[12]); + h->tcp_backend_priv.version.api = le_to_h_u32(&h->tcp_backend_priv.recv_buf[0]); + h->tcp_backend_priv.version.major = le_to_h_u32(&h->tcp_backend_priv.recv_buf[4]); + h->tcp_backend_priv.version.minor = le_to_h_u32(&h->tcp_backend_priv.recv_buf[8]); + h->tcp_backend_priv.version.build = le_to_h_u32(&h->tcp_backend_priv.recv_buf[12]); LOG_INFO("stlink-server API v%d, version %d.%d.%d", - api_ver, ver_major, ver_minor, ver_build); + h->tcp_backend_priv.version.api, + h->tcp_backend_priv.version.major, + h->tcp_backend_priv.version.minor, + h->tcp_backend_priv.version.build); /* in stlink-server API v1 sending more than 1428 bytes will cause stlink-server * to crash in windows: select a safe default value (1K) */ - if (api_ver < 2) + if (h->tcp_backend_priv.version.api < 2) h->max_mem_packet = (1 << 10); /* refresh stlink list (re-enumerate) */ @@ -3551,7 +3594,8 @@ static int stlink_tcp_open(void *handle, struct hl_interface_param_s *param) char serial[STLINK_TCP_SERIAL_SIZE + 1] = {0}; uint8_t stlink_used; bool stlink_id_matched = false; - bool stlink_serial_matched = (!param->serial); + const char *adapter_serial = adapter_get_required_serial(); + bool stlink_serial_matched = !adapter_serial; for (uint32_t stlink_id = 0; stlink_id < connected_stlinks; stlink_id++) { /* get the stlink info */ @@ -3581,27 +3625,28 @@ static int stlink_tcp_open(void *handle, struct hl_interface_param_s *param) continue; /* check the serial if specified */ - if (param->serial) { + if (adapter_serial) { /* ST-Link server fixes the buggy serial returned by old ST-Link DFU * for further details refer to stlink_usb_get_alternate_serial * so if the user passes the buggy serial, we need to fix it before * comparing with the serial returned by ST-Link server */ - if (strlen(param->serial) == STLINK_SERIAL_LEN / 2) { + if (strlen(adapter_serial) == STLINK_SERIAL_LEN / 2) { char fixed_serial[STLINK_SERIAL_LEN + 1]; for (unsigned int i = 0; i < STLINK_SERIAL_LEN; i += 2) - sprintf(fixed_serial + i, "%02X", param->serial[i / 2]); + sprintf(fixed_serial + i, "%02X", adapter_serial[i / 2]); fixed_serial[STLINK_SERIAL_LEN] = '\0'; stlink_serial_matched = strcmp(fixed_serial, serial) == 0; - } else - stlink_serial_matched = strcmp(param->serial, serial) == 0; + } else { + stlink_serial_matched = strcmp(adapter_serial, serial) == 0; + } } if (!stlink_serial_matched) LOG_DEBUG("Device serial number '%s' doesn't match requested serial '%s'", - serial, param->serial); + serial, adapter_serial); else /* exit the search loop if there is match */ break; } @@ -3670,7 +3715,7 @@ static int stlink_open(struct hl_interface_param_s *param, enum stlink_mode mode for (unsigned i = 0; param->vid[i]; i++) { LOG_DEBUG("transport: %d vid: 0x%04x pid: 0x%04x serial: %s", h->st_mode, param->vid[i], param->pid[i], - param->serial ? param->serial : ""); + adapter_get_required_serial() ? adapter_get_required_serial() : ""); } if (param->use_stlink_tcp) @@ -3867,6 +3912,53 @@ static int stlink_usb_close_access_port(void *handle, unsigned char ap_num) } +static int stlink_usb_rw_misc_out(void *handle, uint32_t items, const uint8_t *buffer) +{ + struct stlink_usb_handle_s *h = handle; + unsigned int buflen = ALIGN_UP(items, 4) + 4 * items; + + LOG_DEBUG_IO("%s(%" PRIu32 ")", __func__, items); + + assert(handle != NULL); + + if (!(h->version.flags & STLINK_F_HAS_RW_MISC)) + return ERROR_COMMAND_NOTFOUND; + + stlink_usb_init_buffer(handle, h->tx_ep, buflen); + + h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_COMMAND; + h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_APIV2_RW_MISC_OUT; + h_u32_to_le(&h->cmdbuf[2], items); + + return stlink_usb_xfer_noerrcheck(handle, buffer, buflen); +} + +static int stlink_usb_rw_misc_in(void *handle, uint32_t items, uint8_t *buffer) +{ + struct stlink_usb_handle_s *h = handle; + unsigned int buflen = 2 * 4 * items; + + LOG_DEBUG_IO("%s(%" PRIu32 ")", __func__, items); + + assert(handle != NULL); + + if (!(h->version.flags & STLINK_F_HAS_RW_MISC)) + return ERROR_COMMAND_NOTFOUND; + + stlink_usb_init_buffer(handle, h->rx_ep, buflen); + + h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_COMMAND; + h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_APIV2_RW_MISC_IN; + + int res = stlink_usb_xfer_noerrcheck(handle, h->databuf, buflen); + if (res != ERROR_OK) + return res; + + memcpy(buffer, h->databuf, buflen); + + return ERROR_OK; +} + /** */ static int stlink_read_dap_register(void *handle, unsigned short dap_port, unsigned short addr, uint32_t *val) @@ -4229,6 +4321,97 @@ static int stlink_dap_op_queue_ap_abort(struct adiv5_dap *dap, uint8_t *ack) return ERROR_OK; } +#define RW_MISC_CMD_ADDRESS 1 +#define RW_MISC_CMD_WRITE 2 +#define RW_MISC_CMD_READ 3 +#define RW_MISC_CMD_APNUM 5 + +static int stlink_usb_misc_rw_segment(void *handle, const struct dap_queue *q, unsigned int len, unsigned int items) +{ + uint8_t buf[2 * 4 * items]; + + LOG_DEBUG("Queue: %u commands in %u items", len, items); + + int ap_num = DP_APSEL_INVALID; + unsigned int cmd_index = 0; + unsigned int val_index = ALIGN_UP(items, 4); + for (unsigned int i = 0; i < len; i++) { + if (ap_num != q[i].mem_ap.ap->ap_num) { + ap_num = q[i].mem_ap.ap->ap_num; + buf[cmd_index++] = RW_MISC_CMD_APNUM; + h_u32_to_le(&buf[val_index], ap_num); + val_index += 4; + } + + switch (q[i].cmd) { + case CMD_MEM_AP_READ32: + buf[cmd_index++] = RW_MISC_CMD_READ; + h_u32_to_le(&buf[val_index], q[i].mem_ap.addr); + val_index += 4; + break; + case CMD_MEM_AP_WRITE32: + buf[cmd_index++] = RW_MISC_CMD_ADDRESS; + h_u32_to_le(&buf[val_index], q[i].mem_ap.addr); + val_index += 4; + buf[cmd_index++] = RW_MISC_CMD_WRITE; + h_u32_to_le(&buf[val_index], q[i].mem_ap.data); + val_index += 4; + break; + default: + /* Not supposed to happen */ + return ERROR_FAIL; + } + } + /* pad after last command */ + while (!IS_ALIGNED(cmd_index, 4)) + buf[cmd_index++] = 0; + + int retval = stlink_usb_rw_misc_out(handle, items, buf); + if (retval != ERROR_OK) + return retval; + + retval = stlink_usb_rw_misc_in(handle, items, buf); + if (retval != ERROR_OK) + return retval; + + ap_num = DP_APSEL_INVALID; + val_index = 0; + unsigned int err_index = 4 * items; + for (unsigned int i = 0; i < len; i++) { + uint32_t errcode = le_to_h_u32(&buf[err_index]); + if (errcode != STLINK_DEBUG_ERR_OK) { + LOG_ERROR("unknown/unexpected STLINK status code 0x%x", errcode); + return ERROR_FAIL; + } + if (ap_num != q[i].mem_ap.ap->ap_num) { + ap_num = q[i].mem_ap.ap->ap_num; + err_index += 4; + val_index += 4; + errcode = le_to_h_u32(&buf[err_index]); + if (errcode != STLINK_DEBUG_ERR_OK) { + LOG_ERROR("unknown/unexpected STLINK status code 0x%x", errcode); + return ERROR_FAIL; + } + } + + if (q[i].cmd == CMD_MEM_AP_READ32) { + *q[i].mem_ap.p_data = le_to_h_u32(&buf[val_index]); + } else { /* q[i]->cmd == CMD_MEM_AP_WRITE32 */ + err_index += 4; + val_index += 4; + errcode = le_to_h_u32(&buf[err_index]); + if (errcode != STLINK_DEBUG_ERR_OK) { + LOG_ERROR("unknown/unexpected STLINK status code 0x%x", errcode); + return ERROR_FAIL; + } + } + err_index += 4; + val_index += 4; + } + + return ERROR_OK; +} + static int stlink_usb_buf_rw_segment(void *handle, const struct dap_queue *q, unsigned int count) { uint32_t bufsize = count * CMD_MEM_AP_2_SIZE(q[0].cmd); @@ -4289,6 +4472,49 @@ static int stlink_usb_buf_rw_segment(void *handle, const struct dap_queue *q, un }; } +/* TODO: recover these values with cmd STLINK_DEBUG_APIV2_RW_MISC_GET_MAX (0x53) */ +#define STLINK_V2_RW_MISC_SIZE (64) +#define STLINK_V3_RW_MISC_SIZE (1227) + +static int stlink_usb_count_misc_rw_queue(void *handle, const struct dap_queue *q, unsigned int len, + unsigned int *pkt_items) +{ + struct stlink_usb_handle_s *h = handle; + unsigned int i, items = 0; + int ap_num = DP_APSEL_INVALID; + unsigned int misc_max_items = (h->version.stlink == 2) ? STLINK_V2_RW_MISC_SIZE : STLINK_V3_RW_MISC_SIZE; + + if (!(h->version.flags & STLINK_F_HAS_RW_MISC)) + return 0; + /* + * Before stlink-server API v3, RW_MISC sequence doesn't lock the st-link, + * so are not safe in shared mode. + * Don't use it with TCP backend to prevent any issue in case of sharing. + * This further degrades the performance, on top of TCP server overhead. + */ + if (h->backend == &stlink_tcp_backend && h->tcp_backend_priv.version.api < 3) + return 0; + + for (i = 0; i < len; i++) { + if (q[i].cmd != CMD_MEM_AP_READ32 && q[i].cmd != CMD_MEM_AP_WRITE32) + break; + unsigned int count = 1; + if (ap_num != q[i].mem_ap.ap->ap_num) { + count++; + ap_num = q[i].mem_ap.ap->ap_num; + } + if (q[i].cmd == CMD_MEM_AP_WRITE32) + count++; + if (items + count > misc_max_items) + break; + items += count; + } + + *pkt_items = items; + + return i; +} + static int stlink_usb_count_buf_rw_queue(const struct dap_queue *q, unsigned int len) { uint32_t incr = CMD_MEM_AP_2_SIZE(q[0].cmd); @@ -4318,9 +4544,19 @@ static int stlink_usb_count_buf_rw_queue(const struct dap_queue *q, unsigned int static int stlink_usb_mem_rw_queue(void *handle, const struct dap_queue *q, unsigned int len, unsigned int *skip) { - unsigned int count = stlink_usb_count_buf_rw_queue(q, len); + unsigned int count, misc_items = 0; + int retval; - int retval = stlink_usb_buf_rw_segment(handle, q, count); + unsigned int count_misc = stlink_usb_count_misc_rw_queue(handle, q, len, &misc_items); + unsigned int count_buf = stlink_usb_count_buf_rw_queue(q, len); + + if (count_misc > count_buf) { + count = count_misc; + retval = stlink_usb_misc_rw_segment(handle, q, count, misc_items); + } else { + count = count_buf; + retval = stlink_usb_buf_rw_segment(handle, q, count_buf); + } if (retval != ERROR_OK) return retval; @@ -4497,7 +4733,8 @@ static int stlink_dap_op_queue_ap_read(struct adiv5_ap *ap, unsigned int reg, unsigned int i = stlink_dap_handle->queue_index++; struct dap_queue *q = &stlink_dap_handle->queue[i]; - /* test STLINK_F_HAS_CSW implicitly tests STLINK_F_HAS_MEM_16BIT, STLINK_F_HAS_MEM_RD_NO_INC */ + /* test STLINK_F_HAS_CSW implicitly tests STLINK_F_HAS_MEM_16BIT, STLINK_F_HAS_MEM_RD_NO_INC + * and STLINK_F_HAS_RW_MISC */ if ((stlink_dap_handle->version.flags & STLINK_F_HAS_CSW) && (reg == MEM_AP_REG_DRW || reg == MEM_AP_REG_BD0 || reg == MEM_AP_REG_BD1 || reg == MEM_AP_REG_BD2 || reg == MEM_AP_REG_BD3)) { @@ -4562,7 +4799,8 @@ static int stlink_dap_op_queue_ap_write(struct adiv5_ap *ap, unsigned int reg, unsigned int i = stlink_dap_handle->queue_index++; struct dap_queue *q = &stlink_dap_handle->queue[i]; - /* test STLINK_F_HAS_CSW implicitly tests STLINK_F_HAS_MEM_16BIT, STLINK_F_HAS_MEM_WR_NO_INC */ + /* test STLINK_F_HAS_CSW implicitly tests STLINK_F_HAS_MEM_16BIT, STLINK_F_HAS_MEM_WR_NO_INC + * and STLINK_F_HAS_RW_MISC */ if ((stlink_dap_handle->version.flags & STLINK_F_HAS_CSW) && (reg == MEM_AP_REG_DRW || reg == MEM_AP_REG_BD0 || reg == MEM_AP_REG_BD1 || reg == MEM_AP_REG_BD2 || reg == MEM_AP_REG_BD3)) { @@ -4701,25 +4939,6 @@ static int stlink_dap_trace_read(uint8_t *buf, size_t *size) return stlink_usb_trace_read(stlink_dap_handle, buf, size); } -/** */ -COMMAND_HANDLER(stlink_dap_serial_command) -{ - LOG_DEBUG("stlink_dap_serial_command"); - - if (CMD_ARGC != 1) { - LOG_ERROR("Expected exactly one argument for \"st-link serial \"."); - return ERROR_COMMAND_SYNTAX_ERROR; - } - - if (stlink_dap_param.serial) { - LOG_WARNING("Command \"st-link serial\" already used. Replacing previous value"); - free((void *)stlink_dap_param.serial); - } - - stlink_dap_param.serial = strdup(CMD_ARGV[0]); - return ERROR_OK; -} - /** */ COMMAND_HANDLER(stlink_dap_vid_pid) { @@ -4810,13 +5029,6 @@ COMMAND_HANDLER(stlink_dap_cmd_command) /** */ static const struct command_registration stlink_dap_subcommand_handlers[] = { - { - .name = "serial", - .handler = stlink_dap_serial_command, - .mode = COMMAND_CONFIG, - .help = "set the serial number of the adapter", - .usage = "", - }, { .name = "vid_pid", .handler = stlink_dap_vid_pid, @@ -4897,9 +5109,6 @@ static int stlink_dap_quit(void) { LOG_DEBUG("stlink_dap_quit()"); - free((void *)stlink_dap_param.serial); - stlink_dap_param.serial = NULL; - return stlink_close(stlink_dap_handle); }