+ return stlink_khz_to_speed_map_jtag[speed_index].speed;
+}
+
+static void stlink_dump_speed_map(const struct speed_map *map, unsigned int map_size)
+{
+ unsigned int i;
+
+ LOG_DEBUG("Supported clock speeds are:");
+ for (i = 0; i < map_size; i++)
+ if (map[i].speed)
+ LOG_DEBUG("%d kHz", map[i].speed);
+}
+
+static int stlink_get_com_freq(void *handle, bool is_jtag, struct speed_map *map)
+{
+ struct stlink_usb_handle_s *h = handle;
+ int i;
+
+ if (h->version.jtag_api != STLINK_JTAG_API_V3) {
+ LOG_ERROR("Unknown command");
+ return 0;
+ }
+
+ stlink_usb_init_buffer(handle, h->rx_ep, 16);
+
+ h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_COMMAND;
+ h->cmdbuf[h->cmdidx++] = STLINK_APIV3_GET_COM_FREQ;
+ h->cmdbuf[h->cmdidx++] = is_jtag ? 1 : 0;
+
+ int res = stlink_usb_xfer_errcheck(handle, h->databuf, 52);
+
+ int size = h->databuf[8];
+
+ if (size > STLINK_V3_MAX_FREQ_NB)
+ size = STLINK_V3_MAX_FREQ_NB;
+
+ for (i = 0; i < size; i++) {
+ map[i].speed = le_to_h_u32(&h->databuf[12 + 4 * i]);
+ map[i].speed_divisor = i;
+ }
+
+ /* set to zero all the next entries */
+ for (i = size; i < STLINK_V3_MAX_FREQ_NB; i++)
+ map[i].speed = 0;
+
+ return res;
+}
+
+static int stlink_set_com_freq(void *handle, bool is_jtag, unsigned int frequency)
+{
+ struct stlink_usb_handle_s *h = handle;
+
+ if (h->version.jtag_api != STLINK_JTAG_API_V3) {
+ LOG_ERROR("Unknown command");
+ return 0;
+ }
+
+ stlink_usb_init_buffer(handle, h->rx_ep, 16);
+
+ h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_COMMAND;
+ h->cmdbuf[h->cmdidx++] = STLINK_APIV3_SET_COM_FREQ;
+ h->cmdbuf[h->cmdidx++] = is_jtag ? 1 : 0;
+ h->cmdbuf[h->cmdidx++] = 0;
+
+ h_u32_to_le(&h->cmdbuf[4], frequency);
+
+ return stlink_usb_xfer_errcheck(handle, h->databuf, 8);
+}
+
+static int stlink_speed_v3(void *handle, bool is_jtag, int khz, bool query)
+{
+ struct stlink_usb_handle_s *h = handle;
+ int speed_index;
+ struct speed_map map[STLINK_V3_MAX_FREQ_NB];
+
+ stlink_get_com_freq(h, is_jtag, map);
+
+ speed_index = stlink_match_speed_map(map, ARRAY_SIZE(map), khz, query);
+
+ if (!query) {
+ int result = stlink_set_com_freq(h, is_jtag, map[speed_index].speed);
+ if (result != ERROR_OK) {
+ LOG_ERROR("Unable to set adapter speed");
+ return khz;
+ }
+ }
+ return map[speed_index].speed;
+}
+
+static int stlink_speed(void *handle, int khz, bool query)
+{
+ struct stlink_usb_handle_s *h = handle;
+
+ if (!handle)
+ return khz;
+
+ switch (h->st_mode) {
+ case STLINK_MODE_DEBUG_SWIM:
+ return stlink_speed_swim(handle, khz, query);
+ case STLINK_MODE_DEBUG_SWD:
+ if (h->version.jtag_api == STLINK_JTAG_API_V3)
+ return stlink_speed_v3(handle, false, khz, query);
+ else
+ return stlink_speed_swd(handle, khz, query);
+ break;
+ case STLINK_MODE_DEBUG_JTAG:
+ if (h->version.jtag_api == STLINK_JTAG_API_V3)
+ return stlink_speed_v3(handle, true, khz, query);
+ else
+ return stlink_speed_jtag(handle, khz, query);
+ break;
+ default:
+ break;
+ }
+
+ return khz;
+}
+
+/** */
+static int stlink_usb_usb_close(void *handle)
+{
+ struct stlink_usb_handle_s *h = handle;
+
+ if (!h)
+ return ERROR_OK;
+
+ if (h->usb_backend_priv.fd) {
+ stlink_usb_exit_mode(h);
+ /* do not check return code, it prevent
+ us from closing jtag_libusb */
+ jtag_libusb_close(h->usb_backend_priv.fd);
+ }
+
+ free(h->cmdbuf);
+ free(h->databuf);
+
+ return ERROR_OK;
+}
+
+/** */
+static int stlink_tcp_close(void *handle)
+{
+ struct stlink_usb_handle_s *h = handle;
+
+ if (!h)
+ return ERROR_OK;
+
+ int ret = ERROR_OK;
+ if (h->tcp_backend_priv.connected) {
+ if (h->tcp_backend_priv.connect_id) {
+ stlink_usb_exit_mode(h);
+
+ /* close the stlink */
+ h->tcp_backend_priv.send_buf[0] = STLINK_TCP_CMD_CLOSE_DEV;
+ memset(&h->tcp_backend_priv.send_buf[1], 0, 4); /* reserved */
+ h_u32_to_le(&h->tcp_backend_priv.send_buf[4], h->tcp_backend_priv.connect_id);
+ ret = stlink_tcp_send_cmd(h, 8, 4, true);
+ if (ret != ERROR_OK)
+ LOG_ERROR("cannot close the STLINK");
+ }
+
+ if (close_socket(h->tcp_backend_priv.fd) != 0)
+ LOG_ERROR("error closing the socket, errno: %s", strerror(errno));
+ }
+
+ free(h->tcp_backend_priv.send_buf);
+ free(h->tcp_backend_priv.recv_buf);
+
+ return ret;
+}
+
+/** */
+static int stlink_close(void *handle)
+{
+ if (handle) {
+ struct stlink_usb_handle_s *h = handle;
+
+ stlink_usb_close(handle);
+
+ free(h);
+ }
+
+ return ERROR_OK;
+}
+
+/* Compute ST-Link serial number from the device descriptor
+ * this function will help to work-around a bug in old ST-Link/V2 DFU
+ * the buggy DFU returns an incorrect serial in the USB descriptor
+ * example for the following serial "57FF72067265575742132067"
+ * - the correct descriptor serial is:
+ * 0x32, 0x03, 0x35, 0x00, 0x37, 0x00, 0x46, 0x00, 0x46, 0x00, 0x37, 0x00, 0x32, 0x00 ...
+ * this contains the length (0x32 = 50), the type (0x3 = DT_STRING) and the serial in unicode format
+ * the serial part is: 0x0035, 0x0037, 0x0046, 0x0046, 0x0037, 0x0032 ... >> 57FF72 ...
+ * this format could be read correctly by 'libusb_get_string_descriptor_ascii'
+ * so this case is managed by libusb_helper::string_descriptor_equal
+ * - the buggy DFU is not doing any unicode conversion and returns a raw serial data in the descriptor
+ * 0x1a, 0x03, 0x57, 0x00, 0xFF, 0x00, 0x72, 0x00 ...
+ * >> 57 FF 72 ...
+ * based on the length (0x1a = 26) we could easily decide if we have to fixup the serial
+ * and then we have just to convert the raw data into printable characters using sprintf
+ */
+static char *stlink_usb_get_alternate_serial(struct libusb_device_handle *device,
+ struct libusb_device_descriptor *dev_desc)
+{
+ int usb_retval;
+ unsigned char desc_serial[(STLINK_SERIAL_LEN + 1) * 2];
+
+ if (dev_desc->iSerialNumber == 0)
+ return NULL;
+
+ /* get the LANGID from String Descriptor Zero */
+ usb_retval = libusb_get_string_descriptor(device, 0, 0, desc_serial,
+ sizeof(desc_serial));
+
+ if (usb_retval < LIBUSB_SUCCESS) {
+ LOG_ERROR("libusb_get_string_descriptor() failed: %s(%d)",
+ libusb_error_name(usb_retval), usb_retval);
+ return NULL;
+ } else if (usb_retval < 4) {
+ /* the size should be least 4 bytes to contain a minimum of 1 supported LANGID */
+ LOG_ERROR("could not get the LANGID");
+ return NULL;
+ }
+
+ uint32_t langid = desc_serial[2] | (desc_serial[3] << 8);
+
+ /* get the serial */
+ usb_retval = libusb_get_string_descriptor(device, dev_desc->iSerialNumber,
+ langid, desc_serial, sizeof(desc_serial));
+
+ unsigned char len = desc_serial[0];
+
+ if (usb_retval < LIBUSB_SUCCESS) {
+ LOG_ERROR("libusb_get_string_descriptor() failed: %s(%d)",
+ libusb_error_name(usb_retval), usb_retval);
+ return NULL;
+ } else if (desc_serial[1] != LIBUSB_DT_STRING || len > usb_retval) {
+ LOG_ERROR("invalid string in ST-LINK USB serial descriptor");
+ return NULL;
+ }
+
+ if (len == ((STLINK_SERIAL_LEN + 1) * 2)) {
+ /* good ST-Link adapter, this case is managed by
+ * libusb::libusb_get_string_descriptor_ascii */
+ return NULL;
+ } else if (len != ((STLINK_SERIAL_LEN / 2 + 1) * 2)) {
+ LOG_ERROR("unexpected serial length (%d) in descriptor", len);
+ return NULL;
+ }
+
+ /* else (len == 26) => buggy ST-Link */
+
+ char *alternate_serial = malloc((STLINK_SERIAL_LEN + 1) * sizeof(char));
+ if (!alternate_serial)
+ return NULL;
+
+ for (unsigned int i = 0; i < STLINK_SERIAL_LEN; i += 2)
+ sprintf(alternate_serial + i, "%02X", desc_serial[i + 2]);
+
+ alternate_serial[STLINK_SERIAL_LEN] = '\0';
+
+ return alternate_serial;
+}
+
+/** */
+static int stlink_usb_usb_open(void *handle, struct hl_interface_param_s *param)
+{
+ struct stlink_usb_handle_s *h = handle;
+ int err, retry_count = 1;
+
+ h->cmdbuf = malloc(STLINK_SG_SIZE);
+ h->databuf = malloc(STLINK_DATA_SIZE);
+
+ if (!h->cmdbuf || !h->databuf)
+ return ERROR_FAIL;
+
+ /*
+ On certain host USB configurations(e.g. MacBook Air)
+ STLINKv2 dongle seems to have its FW in a funky state if,
+ after plugging it in, you try to use openocd with it more
+ then once (by launching and closing openocd). In cases like
+ that initial attempt to read the FW info via
+ stlink_usb_version will fail and the device has to be reset
+ in order to become operational.
+ */
+ do {
+ if (jtag_libusb_open(param->vid, param->pid, param->serial,
+ &h->usb_backend_priv.fd, stlink_usb_get_alternate_serial) != ERROR_OK) {
+ LOG_ERROR("open failed");
+ return ERROR_FAIL;
+ }
+
+ jtag_libusb_set_configuration(h->usb_backend_priv.fd, 0);
+
+ if (libusb_claim_interface(h->usb_backend_priv.fd, 0) != ERROR_OK) {
+ LOG_DEBUG("claim interface failed");
+ return ERROR_FAIL;
+ }
+
+ /* RX EP is common for all versions */
+ h->rx_ep = STLINK_RX_EP;
+
+ uint16_t pid;
+ if (jtag_libusb_get_pid(libusb_get_device(h->usb_backend_priv.fd), &pid) != ERROR_OK) {
+ LOG_DEBUG("libusb_get_pid failed");
+ return ERROR_FAIL;
+ }
+
+ /* wrap version for first read */
+ switch (pid) {
+ case STLINK_V1_PID:
+ h->version.stlink = 1;
+ h->tx_ep = STLINK_TX_EP;
+ break;
+ case STLINK_V3_USBLOADER_PID:
+ case STLINK_V3E_PID:
+ case STLINK_V3S_PID:
+ case STLINK_V3_2VCP_PID:
+ case STLINK_V3E_NO_MSD_PID:
+ h->version.stlink = 3;
+ h->tx_ep = STLINK_V2_1_TX_EP;
+ h->trace_ep = STLINK_V2_1_TRACE_EP;
+ break;
+ case STLINK_V2_1_PID:
+ case STLINK_V2_1_NO_MSD_PID:
+ h->version.stlink = 2;
+ h->tx_ep = STLINK_V2_1_TX_EP;
+ h->trace_ep = STLINK_V2_1_TRACE_EP;
+ break;
+ default:
+ /* fall through - we assume V2 to be the default version*/
+ case STLINK_V2_PID:
+ h->version.stlink = 2;
+ h->tx_ep = STLINK_TX_EP;
+ h->trace_ep = STLINK_TRACE_EP;
+ break;
+ }
+
+ /* get the device version */
+ err = stlink_usb_version(h);
+
+ if (err == ERROR_OK) {
+ break;
+ } else if (h->version.stlink == 1 ||
+ retry_count == 0) {
+ LOG_ERROR("read version failed");
+ return ERROR_FAIL;
+ } else {
+ err = libusb_release_interface(h->usb_backend_priv.fd, 0);
+ if (err != ERROR_OK) {
+ LOG_ERROR("release interface failed");
+ return ERROR_FAIL;
+ }
+
+ err = libusb_reset_device(h->usb_backend_priv.fd);
+ if (err != ERROR_OK) {
+ LOG_ERROR("reset device failed");
+ return ERROR_FAIL;
+ }
+
+ jtag_libusb_close(h->usb_backend_priv.fd);
+ /*
+ Give the device one second to settle down and
+ reenumerate.
+ */
+ usleep(1 * 1000 * 1000);
+ retry_count--;
+ }
+ } while (1);
+
+ return ERROR_OK;
+}
+
+/** */
+static int stlink_tcp_open(void *handle, struct hl_interface_param_s *param)
+{
+ struct stlink_usb_handle_s *h = handle;
+ int ret;
+
+ /* SWIM is not supported using stlink-server */
+ if (h->st_mode == STLINK_MODE_DEBUG_SWIM) {
+ LOG_ERROR("stlink-server does not support SWIM mode");
+ return ERROR_FAIL;
+ }
+
+ h->tcp_backend_priv.send_buf = malloc(STLINK_TCP_SEND_BUFFER_SIZE);
+ h->tcp_backend_priv.recv_buf = malloc(STLINK_TCP_RECV_BUFFER_SIZE);
+
+ if (!h->tcp_backend_priv.send_buf || !h->tcp_backend_priv.recv_buf)
+ return ERROR_FAIL;
+
+ h->cmdbuf = &h->tcp_backend_priv.send_buf[8];
+ h->databuf = &h->tcp_backend_priv.recv_buf[4];
+
+ /* configure directions */
+ h->rx_ep = STLINK_TCP_REQUEST_READ;
+ h->tx_ep = STLINK_TCP_REQUEST_WRITE;
+ h->trace_ep = STLINK_TCP_REQUEST_READ_SWO;
+
+ h->tcp_backend_priv.fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ h->tcp_backend_priv.connected = false;
+ h->tcp_backend_priv.device_id = 0;
+ h->tcp_backend_priv.connect_id = 0;
+
+ if (h->tcp_backend_priv.fd == -1) {
+ LOG_ERROR("error creating the socket, errno: %s", strerror(errno));
+ return ERROR_FAIL;
+ }
+
+ struct sockaddr_in serv;
+ memset(&serv, 0, sizeof(struct sockaddr_in));
+ serv.sin_family = AF_INET;
+ serv.sin_port = htons(param->stlink_tcp_port);
+ serv.sin_addr.s_addr = inet_addr("127.0.0.1");
+
+ LOG_DEBUG("socket : %x", h->tcp_backend_priv.fd);
+
+ int optval = 1;
+ if (setsockopt(h->tcp_backend_priv.fd, IPPROTO_TCP, TCP_NODELAY, (const void *)&optval, sizeof(int)) == -1) {
+ LOG_ERROR("cannot set sock option 'TCP_NODELAY', errno: %s", strerror(errno));
+ return ERROR_FAIL;
+ }
+
+ optval = STLINK_TCP_RECV_BUFFER_SIZE;
+ if (setsockopt(h->tcp_backend_priv.fd, SOL_SOCKET, SO_RCVBUF, (const void *)&optval, sizeof(int)) == -1) {
+ LOG_ERROR("cannot set sock option 'SO_RCVBUF', errno: %s", strerror(errno));
+ return ERROR_FAIL;
+ }
+
+ optval = STLINK_TCP_SEND_BUFFER_SIZE;
+ if (setsockopt(h->tcp_backend_priv.fd, SOL_SOCKET, SO_SNDBUF, (const void *)&optval, sizeof(int)) == -1) {
+ LOG_ERROR("cannot set sock option 'SO_SNDBUF', errno: %s", strerror(errno));
+ return ERROR_FAIL;
+ }
+
+ if (connect(h->tcp_backend_priv.fd, (const struct sockaddr *)&serv, sizeof(serv)) == -1) {
+ LOG_ERROR("cannot connect to stlink server, errno: %s", strerror(errno));
+ return ERROR_FAIL;
+ }
+
+ h->tcp_backend_priv.connected = true;
+
+ LOG_INFO("connected to stlink-server");
+
+ /* print stlink-server version */
+ h->tcp_backend_priv.send_buf[0] = STLINK_TCP_CMD_GET_SERVER_VERSION;
+ h->tcp_backend_priv.send_buf[1] = OPENOCD_STLINK_TCP_API_VERSION;
+ memset(&h->tcp_backend_priv.send_buf[2], 0, 2); /* reserved */
+ ret = stlink_tcp_send_cmd(h, 4, 16, false);
+ if (ret != ERROR_OK) {
+ LOG_ERROR("cannot get the stlink-server version");
+ 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]);
+ LOG_INFO("stlink-server API v%d, version %d.%d.%d",
+ api_ver, ver_major, ver_minor, ver_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)
+ h->max_mem_packet = (1 << 10);
+
+ /* refresh stlink list (re-enumerate) */
+ h->tcp_backend_priv.send_buf[0] = STLINK_TCP_CMD_REFRESH_DEVICE_LIST;
+ h->tcp_backend_priv.send_buf[1] = 0; /* don't clear the list, just refresh it */
+ ret = stlink_tcp_send_cmd(h, 2, 4, true);
+ if (ret != ERROR_OK)
+ return ret;
+
+ /* get the number of connected stlinks */
+ h->tcp_backend_priv.send_buf[0] = STLINK_TCP_CMD_GET_NB_DEV;
+ ret = stlink_tcp_send_cmd(h, 1, 4, false);
+ if (ret != ERROR_OK)
+ return ret;
+
+ uint32_t connected_stlinks = le_to_h_u32(h->tcp_backend_priv.recv_buf);
+
+ if (connected_stlinks == 0) {
+ LOG_ERROR("no ST-LINK detected");
+ return ERROR_FAIL;
+ }
+
+ LOG_DEBUG("%d ST-LINK detected", connected_stlinks);
+
+ if (connected_stlinks > 255) {
+ LOG_WARNING("STLink server cannot handle more than 255 ST-LINK connected");
+ connected_stlinks = 255;
+ }
+
+ /* list all connected ST-Link and seek for the requested vid:pid and serial */
+ char serial[STLINK_TCP_SERIAL_SIZE + 1] = {0};
+ uint8_t stlink_used;
+ bool stlink_id_matched = false;
+ bool stlink_serial_matched = (!param->serial);
+
+ for (uint32_t stlink_id = 0; stlink_id < connected_stlinks; stlink_id++) {
+ /* get the stlink info */
+ h->tcp_backend_priv.send_buf[0] = STLINK_TCP_CMD_GET_DEV_INFO;
+ h->tcp_backend_priv.send_buf[1] = (uint8_t)stlink_id;
+ memset(&h->tcp_backend_priv.send_buf[2], 0, 2); /* reserved */
+ h_u32_to_le(&h->tcp_backend_priv.send_buf[4], 41); /* size of TDeviceInfo2 */
+ ret = stlink_tcp_send_cmd(h, 8, 45, true);
+ if (ret != ERROR_OK)
+ return ret;
+
+ h->tcp_backend_priv.device_id = le_to_h_u32(&h->tcp_backend_priv.recv_buf[4]);
+ memcpy(serial, &h->tcp_backend_priv.recv_buf[8], STLINK_TCP_SERIAL_SIZE);
+ h->vid = le_to_h_u16(&h->tcp_backend_priv.recv_buf[40]);
+ h->pid = le_to_h_u16(&h->tcp_backend_priv.recv_buf[42]);
+ stlink_used = h->tcp_backend_priv.recv_buf[44];
+
+ /* check the vid:pid */
+ for (int i = 0; param->vid[i]; i++) {
+ if (param->vid[i] == h->vid && param->pid[i] == h->pid) {
+ stlink_id_matched = true;
+ break;
+ }
+ }
+
+ if (!stlink_id_matched)
+ continue;
+
+ /* check the serial if specified */
+ if (param->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) {
+ 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]);
+
+ fixed_serial[STLINK_SERIAL_LEN] = '\0';
+
+ stlink_serial_matched = strcmp(fixed_serial, serial) == 0;
+ } else
+ stlink_serial_matched = strcmp(param->serial, serial) == 0;
+ }
+
+ if (!stlink_serial_matched)
+ LOG_DEBUG("Device serial number '%s' doesn't match requested serial '%s'",
+ serial, param->serial);
+ else /* exit the search loop if there is match */
+ break;
+ }
+
+ if (!stlink_id_matched) {
+ LOG_ERROR("ST-LINK open failed (vid/pid mismatch)");
+ return ERROR_FAIL;
+ }
+
+ if (!stlink_serial_matched) {
+ LOG_ERROR("ST-LINK open failed (serial mismatch)");
+ return ERROR_FAIL;
+ }
+
+ /* check if device is 'exclusively' used by another application */
+ if (stlink_used) {
+ LOG_ERROR("the selected device is already used");
+ return ERROR_FAIL;
+ }
+
+ LOG_DEBUG("transport: vid: 0x%04x pid: 0x%04x serial: %s", h->vid, h->pid, serial);
+
+ /* now let's open the stlink */
+ h->tcp_backend_priv.send_buf[0] = STLINK_TCP_CMD_OPEN_DEV;
+ memset(&h->tcp_backend_priv.send_buf[1], 0, 4); /* reserved */
+ h_u32_to_le(&h->tcp_backend_priv.send_buf[4], h->tcp_backend_priv.device_id);
+ ret = stlink_tcp_send_cmd(h, 8, 8, true);
+ if (ret != ERROR_OK)
+ return ret;
+
+ h->tcp_backend_priv.connect_id = le_to_h_u32(&h->tcp_backend_priv.recv_buf[4]);
+
+ /* get stlink version */
+ return stlink_usb_version(h);
+}
+
+static struct stlink_backend_s stlink_usb_backend = {
+ .open = stlink_usb_usb_open,
+ .close = stlink_usb_usb_close,
+ .xfer_noerrcheck = stlink_usb_usb_xfer_noerrcheck,
+ .read_trace = stlink_usb_usb_read_trace,
+};
+
+static struct stlink_backend_s stlink_tcp_backend = {
+ .open = stlink_tcp_open,
+ .close = stlink_tcp_close,
+ .xfer_noerrcheck = stlink_tcp_xfer_noerrcheck,
+ .read_trace = stlink_tcp_read_trace,
+};
+
+static int stlink_open(struct hl_interface_param_s *param, enum stlink_mode mode, void **fd)
+{
+ struct stlink_usb_handle_s *h;
+
+ LOG_DEBUG("stlink_open");
+
+ h = calloc(1, sizeof(struct stlink_usb_handle_s));
+
+ if (h == 0) {
+ LOG_DEBUG("malloc failed");
+ return ERROR_FAIL;
+ }
+
+ h->st_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 : "");
+ }
+
+ if (param->use_stlink_tcp)
+ h->backend = &stlink_tcp_backend;
+ else
+ h->backend = &stlink_usb_backend;
+
+ if (stlink_usb_open(h, param) != ERROR_OK)
+ goto error_open;
+
+ /* check if mode is supported */
+ int err = ERROR_OK;
+
+ switch (h->st_mode) {
+ case STLINK_MODE_DEBUG_SWD:
+ if (h->version.jtag_api == STLINK_JTAG_API_V1)
+ err = ERROR_FAIL;
+ /* fall-through */
+ case STLINK_MODE_DEBUG_JTAG:
+ if (h->version.jtag == 0)
+ err = ERROR_FAIL;
+ break;
+ case STLINK_MODE_DEBUG_SWIM:
+ if (h->version.swim == 0)
+ err = ERROR_FAIL;
+ break;
+ default:
+ err = ERROR_FAIL;
+ break;
+ }
+
+ if (err != ERROR_OK) {
+ LOG_ERROR("mode (transport) not supported by device");
+ goto error_open;
+ }
+
+ /* initialize the debug hardware */
+ err = stlink_usb_init_mode(h, param->connect_under_reset, param->initial_interface_speed);
+
+ if (err != ERROR_OK) {
+ LOG_ERROR("init mode failed (unable to connect to the target)");
+ goto error_open;
+ }
+
+ if (h->st_mode == STLINK_MODE_DEBUG_SWIM) {
+ err = stlink_swim_enter(h);
+ if (err != ERROR_OK) {
+ LOG_ERROR("stlink_swim_enter_failed (unable to connect to the target)");
+ goto error_open;
+ }
+ *fd = h;
+ h->max_mem_packet = STLINK_SWIM_DATA_SIZE;
+ return ERROR_OK;
+ }
+
+ /* set max_mem_packet if it was not set by the low-level interface */
+ if (h->max_mem_packet == 0) {
+ /* get cpuid, so we can determine the max page size
+ * start with a safe default */
+ h->max_mem_packet = (1 << 10);
+
+ uint8_t buffer[4];
+ stlink_usb_open_ap(h, STLINK_HLA_AP_NUM);
+ err = stlink_usb_read_mem32(h, STLINK_HLA_AP_NUM, STLINK_HLA_CSW, CPUID, 4, buffer);
+ if (err == ERROR_OK) {
+ uint32_t cpuid = le_to_h_u32(buffer);
+ int i = (cpuid >> 4) & 0xf;
+ if (i == 4 || i == 3) {
+ /* Cortex-M3/M4 has 4096 bytes autoincrement range */
+ h->max_mem_packet = (1 << 12);
+ }
+ }
+
+ LOG_DEBUG("Using TAR autoincrement: %" PRIu32, h->max_mem_packet);
+ }
+
+ *fd = h;
+
+ return ERROR_OK;
+
+error_open:
+ stlink_close(h);
+ return ERROR_FAIL;
+}
+
+static int stlink_usb_hl_open(struct hl_interface_param_s *param, void **fd)
+{
+ return stlink_open(param, stlink_get_mode(param->transport), fd);
+}
+
+static int stlink_config_trace(void *handle, bool enabled,
+ enum tpiu_pin_protocol pin_protocol, uint32_t port_size,
+ unsigned int *trace_freq, unsigned int traceclkin_freq,
+ uint16_t *prescaler)
+{
+ struct stlink_usb_handle_s *h = handle;
+
+ if (!(h->version.flags & STLINK_F_HAS_TRACE)) {
+ LOG_ERROR("The attached ST-LINK version doesn't support trace");
+ return ERROR_FAIL;
+ }
+
+ if (!enabled) {
+ stlink_usb_trace_disable(h);
+ return ERROR_OK;
+ }
+
+ assert(trace_freq);
+ assert(prescaler);
+
+ if (pin_protocol != TPIU_PIN_PROTOCOL_ASYNC_UART) {
+ LOG_ERROR("The attached ST-LINK version doesn't support this trace mode");
+ return ERROR_FAIL;
+ }
+
+ unsigned int max_trace_freq = (h->version.stlink == 3) ?
+ STLINK_V3_TRACE_MAX_HZ : STLINK_TRACE_MAX_HZ;
+
+ /* Only concern ourselves with the frequency if the STlink is processing it. */
+ if (*trace_freq > max_trace_freq) {
+ LOG_ERROR("ST-LINK doesn't support SWO frequency higher than %u",
+ max_trace_freq);
+ return ERROR_FAIL;
+ }
+
+ if (!*trace_freq)
+ *trace_freq = max_trace_freq;
+
+ unsigned int presc = (traceclkin_freq + *trace_freq / 2) / *trace_freq;
+ if (presc == 0 || presc > TPIU_ACPR_MAX_SWOSCALER + 1) {
+ LOG_ERROR("SWO frequency is not suitable. Please choose a different "
+ "frequency.");
+ return ERROR_FAIL;
+ }
+
+ /* Probe's UART speed must be within 3% of the TPIU's SWO baud rate. */
+ unsigned int max_deviation = (traceclkin_freq * 3) / 100;
+ if (presc * *trace_freq < traceclkin_freq - max_deviation ||
+ presc * *trace_freq > traceclkin_freq + max_deviation) {
+ LOG_ERROR("SWO frequency is not suitable. Please choose a different "
+ "frequency.");
+ return ERROR_FAIL;
+ }
+
+ *prescaler = presc;
+
+ stlink_usb_trace_disable(h);
+
+ h->trace.source_hz = *trace_freq;
+
+ return stlink_usb_trace_enable(h);
+}
+
+/** */
+static int stlink_usb_init_access_port(void *handle, unsigned char ap_num)
+{
+ struct stlink_usb_handle_s *h = handle;
+
+ assert(handle);
+
+ if (!(h->version.flags & STLINK_F_HAS_AP_INIT))
+ return ERROR_COMMAND_NOTFOUND;
+
+ LOG_DEBUG_IO("init ap_num = %d", ap_num);
+ stlink_usb_init_buffer(handle, h->rx_ep, 16);
+ h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_COMMAND;
+ h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_APIV2_INIT_AP;
+ h->cmdbuf[h->cmdidx++] = ap_num;
+
+ return stlink_usb_xfer_errcheck(handle, h->databuf, 2);
+}
+
+/** */
+static int stlink_usb_close_access_port(void *handle, unsigned char ap_num)
+{
+ struct stlink_usb_handle_s *h = handle;
+
+ assert(handle);
+
+ if (!(h->version.flags & STLINK_F_HAS_AP_INIT))
+ return ERROR_COMMAND_NOTFOUND;
+
+ LOG_DEBUG_IO("close ap_num = %d", ap_num);
+ stlink_usb_init_buffer(handle, h->rx_ep, 16);
+ h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_COMMAND;
+ h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_APIV2_CLOSE_AP_DBG;
+ h->cmdbuf[h->cmdidx++] = ap_num;
+
+ /* ignore incorrectly returned error on bogus FW */
+ if (h->version.flags & STLINK_F_FIX_CLOSE_AP)
+ return stlink_usb_xfer_errcheck(handle, h->databuf, 2);
+ else
+ return stlink_usb_xfer_noerrcheck(handle, h->databuf, 2);
+
+}
+
+/** */
+static int stlink_read_dap_register(void *handle, unsigned short dap_port,
+ unsigned short addr, uint32_t *val)
+{
+ struct stlink_usb_handle_s *h = handle;
+ int retval;
+
+ assert(handle);
+
+ if (!(h->version.flags & STLINK_F_HAS_DAP_REG))
+ return ERROR_COMMAND_NOTFOUND;
+
+ stlink_usb_init_buffer(handle, h->rx_ep, 16);
+ h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_COMMAND;
+ h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_APIV2_READ_DAP_REG;
+ h_u16_to_le(&h->cmdbuf[2], dap_port);
+ h_u16_to_le(&h->cmdbuf[4], addr);
+
+ retval = stlink_usb_xfer_errcheck(handle, h->databuf, 8);
+ *val = le_to_h_u32(h->databuf + 4);
+ LOG_DEBUG_IO("dap_port_read = %d, addr = 0x%x, value = 0x%" PRIx32, dap_port, addr, *val);
+ return retval;
+}
+
+/** */
+static int stlink_write_dap_register(void *handle, unsigned short dap_port,
+ unsigned short addr, uint32_t val)
+{
+ struct stlink_usb_handle_s *h = handle;
+
+ assert(handle);
+
+ if (!(h->version.flags & STLINK_F_HAS_DAP_REG))
+ return ERROR_COMMAND_NOTFOUND;
+
+ LOG_DEBUG_IO("dap_write port = %d, addr = 0x%x, value = 0x%" PRIx32, dap_port, addr, val);
+ stlink_usb_init_buffer(handle, h->rx_ep, 16);
+ h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_COMMAND;
+ h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_APIV2_WRITE_DAP_REG;
+ h_u16_to_le(&h->cmdbuf[2], dap_port);
+ h_u16_to_le(&h->cmdbuf[4], addr);
+ h_u32_to_le(&h->cmdbuf[6], val);
+ return stlink_usb_xfer_errcheck(handle, h->databuf, 2);
+}
+
+/** */
+struct hl_layout_api_s stlink_usb_layout_api = {
+ /** */
+ .open = stlink_usb_hl_open,
+ /** */
+ .close = stlink_close,
+ /** */
+ .idcode = stlink_usb_idcode,
+ /** */
+ .state = stlink_usb_state,
+ /** */
+ .reset = stlink_usb_reset,
+ /** */
+ .assert_srst = stlink_usb_assert_srst,
+ /** */
+ .run = stlink_usb_run,
+ /** */
+ .halt = stlink_usb_halt,
+ /** */
+ .step = stlink_usb_step,
+ /** */
+ .read_regs = stlink_usb_read_regs,
+ /** */
+ .read_reg = stlink_usb_read_reg,
+ /** */
+ .write_reg = stlink_usb_write_reg,
+ /** */
+ .read_mem = stlink_usb_read_mem,
+ /** */
+ .write_mem = stlink_usb_write_mem,
+ /** */
+ .write_debug_reg = stlink_usb_write_debug_reg,
+ /** */
+ .override_target = stlink_usb_override_target,
+ /** */
+ .speed = stlink_speed,
+ /** */
+ .config_trace = stlink_config_trace,
+ /** */
+ .poll_trace = stlink_usb_trace_read,
+};
+
+/*****************************************************************************
+ * DAP direct interface
+ */
+
+static struct stlink_usb_handle_s *stlink_dap_handle;
+static struct hl_interface_param_s stlink_dap_param;
+static DECLARE_BITMAP(opened_ap, DP_APSEL_MAX + 1);
+static int stlink_dap_error = ERROR_OK;
+
+/** */
+static int stlink_dap_record_error(int error)
+{
+ if (stlink_dap_error == ERROR_OK)
+ stlink_dap_error = error;
+ return ERROR_OK;
+}
+
+/** */
+static int stlink_dap_get_and_clear_error(void)
+{
+ int retval = stlink_dap_error;
+ stlink_dap_error = ERROR_OK;
+ return retval;
+}
+
+static int stlink_dap_get_error(void)
+{
+ return stlink_dap_error;
+}
+
+static int stlink_usb_open_ap(void *handle, unsigned short apsel)
+{
+ struct stlink_usb_handle_s *h = handle;
+ int retval;
+
+ /* nothing to do on old versions */
+ if (!(h->version.flags & STLINK_F_HAS_AP_INIT))
+ return ERROR_OK;
+
+ if (apsel > DP_APSEL_MAX)
+ return ERROR_FAIL;
+
+ if (test_bit(apsel, opened_ap))
+ return ERROR_OK;
+
+ retval = stlink_usb_init_access_port(h, apsel);
+ if (retval != ERROR_OK)
+ return retval;
+
+ LOG_DEBUG("AP %d enabled", apsel);
+ set_bit(apsel, opened_ap);
+ return ERROR_OK;
+}
+
+static int stlink_dap_open_ap(unsigned short apsel)
+{
+ return stlink_usb_open_ap(stlink_dap_handle, apsel);
+}
+
+/** */
+static int stlink_dap_closeall_ap(void)
+{
+ int retval, apsel;
+
+ /* nothing to do on old versions */
+ if (!(stlink_dap_handle->version.flags & STLINK_F_HAS_AP_INIT))
+ return ERROR_OK;
+
+ for (apsel = 0; apsel <= DP_APSEL_MAX; apsel++) {
+ if (!test_bit(apsel, opened_ap))
+ continue;
+ retval = stlink_usb_close_access_port(stlink_dap_handle, apsel);
+ if (retval != ERROR_OK)
+ return retval;
+ clear_bit(apsel, opened_ap);
+ }
+ return ERROR_OK;
+}
+
+/** */
+static int stlink_dap_reinit_interface(void)
+{
+ int retval;
+
+ /*
+ * On JTAG only, it should be enough to call stlink_usb_reset(). But on
+ * some firmware version it does not work as expected, and there is no
+ * equivalent for SWD.
+ * At least for now, to reset the interface quit from JTAG/SWD mode then
+ * select the mode again.
+ */
+
+ if (!stlink_dap_handle->reconnect_pending) {
+ stlink_dap_handle->reconnect_pending = true;
+ stlink_usb_mode_leave(stlink_dap_handle, stlink_dap_handle->st_mode);
+ }
+
+ retval = stlink_usb_mode_enter(stlink_dap_handle, stlink_dap_handle->st_mode);
+ if (retval != ERROR_OK)
+ return retval;
+
+ stlink_dap_handle->reconnect_pending = false;
+ /* on new FW, calling mode-leave closes all the opened AP; reopen them! */
+ if (stlink_dap_handle->version.flags & STLINK_F_HAS_AP_INIT)
+ for (int apsel = 0; apsel <= DP_APSEL_MAX; apsel++)
+ if (test_bit(apsel, opened_ap)) {
+ clear_bit(apsel, opened_ap);
+ stlink_dap_open_ap(apsel);
+ }
+ return ERROR_OK;
+}
+
+/** */
+static int stlink_dap_op_connect(struct adiv5_dap *dap)
+{
+ uint32_t idcode;
+ int retval;
+
+ LOG_INFO("stlink_dap_op_connect(%sconnect)", dap->do_reconnect ? "re" : "");
+
+ /* Check if we should reset srst already when connecting, but not if reconnecting. */
+ if (!dap->do_reconnect) {
+ enum reset_types jtag_reset_config = jtag_get_reset_config();
+
+ if (jtag_reset_config & RESET_CNCT_UNDER_SRST) {
+ if (jtag_reset_config & RESET_SRST_NO_GATING)
+ adapter_assert_reset();
+ else
+ LOG_WARNING("\'srst_nogate\' reset_config option is required");
+ }
+ }
+
+ dap->do_reconnect = false;
+ dap_invalidate_cache(dap);
+
+ retval = dap_dp_init(dap);
+ if (retval != ERROR_OK) {
+ dap->do_reconnect = true;
+ return retval;
+ }
+
+ retval = stlink_usb_idcode(stlink_dap_handle, &idcode);
+ if (retval == ERROR_OK)
+ LOG_INFO("%s %#8.8" PRIx32,
+ (stlink_dap_handle->st_mode == STLINK_MODE_DEBUG_JTAG) ? "JTAG IDCODE" : "SWD DPIDR",
+ idcode);
+ else
+ dap->do_reconnect = true;
+
+ return retval;
+}
+
+/** */
+static int stlink_dap_check_reconnect(struct adiv5_dap *dap)
+{
+ int retval;
+
+ if (!dap->do_reconnect)
+ return ERROR_OK;
+
+ retval = stlink_dap_reinit_interface();
+ if (retval != ERROR_OK)
+ return retval;
+
+ return stlink_dap_op_connect(dap);
+}
+
+/** */
+static int stlink_dap_op_send_sequence(struct adiv5_dap *dap, enum swd_special_seq seq)
+{
+ /* Ignore the request */
+ return ERROR_OK;
+}
+
+/** */
+static int stlink_dap_dp_read(struct adiv5_dap *dap, unsigned int reg, uint32_t *data)
+{
+ uint32_t dummy;
+ int retval;
+
+ if (!(stlink_dap_handle->version.flags & STLINK_F_HAS_DPBANKSEL))
+ if (reg & 0x000000F0) {
+ LOG_ERROR("Banked DP registers not supported in current STLink FW");
+ return ERROR_COMMAND_NOTFOUND;
+ }
+
+ data = data ? data : &dummy;
+ if (stlink_dap_handle->version.flags & STLINK_F_QUIRK_JTAG_DP_READ
+ && stlink_dap_handle->st_mode == STLINK_MODE_DEBUG_JTAG) {
+ /* Quirk required in JTAG. Read RDBUFF to get the data */
+ retval = stlink_read_dap_register(stlink_dap_handle,
+ STLINK_DEBUG_PORT_ACCESS, reg, &dummy);
+ if (retval == ERROR_OK)
+ retval = stlink_read_dap_register(stlink_dap_handle,
+ STLINK_DEBUG_PORT_ACCESS, DP_RDBUFF, data);
+ } else {
+ retval = stlink_read_dap_register(stlink_dap_handle,
+ STLINK_DEBUG_PORT_ACCESS, reg, data);
+ }
+
+ return retval;
+}
+
+/** */
+static int stlink_dap_dp_write(struct adiv5_dap *dap, unsigned int reg, uint32_t data)
+{
+ int retval;
+
+ if (!(stlink_dap_handle->version.flags & STLINK_F_HAS_DPBANKSEL))
+ if (reg & 0x000000F0) {
+ LOG_ERROR("Banked DP registers not supported in current STLink FW");
+ return ERROR_COMMAND_NOTFOUND;
+ }
+
+ if (reg == DP_SELECT && (data & DP_SELECT_DPBANK) != 0) {
+ /* ignored if STLINK_F_HAS_DPBANKSEL, not properly managed otherwise */
+ LOG_DEBUG("Ignoring DPBANKSEL while write SELECT");
+ data &= ~DP_SELECT_DPBANK;
+ }
+
+ /* ST-Link does not like that we set CORUNDETECT */
+ if (reg == DP_CTRL_STAT)
+ data &= ~CORUNDETECT;
+
+ retval = stlink_write_dap_register(stlink_dap_handle,
+ STLINK_DEBUG_PORT_ACCESS, reg, data);
+ return retval;
+}
+
+/** */
+static int stlink_dap_ap_read(struct adiv5_ap *ap, unsigned int reg, uint32_t *data)
+{
+ struct adiv5_dap *dap = ap->dap;
+ uint32_t dummy;
+ int retval;
+
+ if (reg != AP_REG_IDR) {
+ retval = stlink_dap_open_ap(ap->ap_num);
+ if (retval != ERROR_OK)
+ return retval;
+ }
+ data = data ? data : &dummy;
+ retval = stlink_read_dap_register(stlink_dap_handle, ap->ap_num, reg,
+ data);
+ dap->stlink_flush_ap_write = false;
+ return retval;
+}
+
+/** */
+static int stlink_dap_ap_write(struct adiv5_ap *ap, unsigned int reg, uint32_t data)
+{
+ struct adiv5_dap *dap = ap->dap;
+ int retval;
+
+ retval = stlink_dap_open_ap(ap->ap_num);
+ if (retval != ERROR_OK)
+ return retval;
+
+ retval = stlink_write_dap_register(stlink_dap_handle, ap->ap_num, reg,
+ data);
+ dap->stlink_flush_ap_write = true;
+ return retval;
+}
+
+/** */
+static int stlink_dap_op_queue_ap_abort(struct adiv5_dap *dap, uint8_t *ack)
+{
+ LOG_WARNING("stlink_dap_op_queue_ap_abort()");
+ 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);
+ uint8_t buf[bufsize];
+ uint8_t ap_num = q[0].mem_ap.ap->ap_num;
+ uint32_t addr = q[0].mem_ap.addr;
+ uint32_t csw = q[0].mem_ap.csw;
+
+ int retval = stlink_dap_open_ap(ap_num);
+ if (retval != ERROR_OK)
+ return retval;
+
+ switch (q[0].cmd) {
+ case CMD_MEM_AP_WRITE8:
+ for (unsigned int i = 0; i < count; i++)
+ buf[i] = q[i].mem_ap.data >> 8 * (q[i].mem_ap.addr & 3);
+ return stlink_usb_write_mem8(stlink_dap_handle, ap_num, csw, addr, bufsize, buf);
+
+ case CMD_MEM_AP_WRITE16:
+ for (unsigned int i = 0; i < count; i++)
+ h_u16_to_le(&buf[2 * i], q[i].mem_ap.data >> 8 * (q[i].mem_ap.addr & 2));
+ return stlink_usb_write_mem16(stlink_dap_handle, ap_num, csw, addr, bufsize, buf);
+
+ case CMD_MEM_AP_WRITE32:
+ for (unsigned int i = 0; i < count; i++)
+ h_u32_to_le(&buf[4 * i], q[i].mem_ap.data);
+ if (count > 1 && q[0].mem_ap.addr == q[1].mem_ap.addr)
+ return stlink_usb_write_mem32_noaddrinc(stlink_dap_handle, ap_num, csw, addr, bufsize, buf);
+ else
+ return stlink_usb_write_mem32(stlink_dap_handle, ap_num, csw, addr, bufsize, buf);
+
+ case CMD_MEM_AP_READ8:
+ retval = stlink_usb_read_mem8(stlink_dap_handle, ap_num, csw, addr, bufsize, buf);
+ if (retval == ERROR_OK)
+ for (unsigned int i = 0; i < count; i++)
+ *q[i].mem_ap.p_data = buf[i] << 8 * (q[i].mem_ap.addr & 3);
+ return retval;
+
+ case CMD_MEM_AP_READ16:
+ retval = stlink_usb_read_mem16(stlink_dap_handle, ap_num, csw, addr, bufsize, buf);
+ if (retval == ERROR_OK)
+ for (unsigned int i = 0; i < count; i++)
+ *q[i].mem_ap.p_data = le_to_h_u16(&buf[2 * i]) << 8 * (q[i].mem_ap.addr & 2);
+ return retval;
+
+ case CMD_MEM_AP_READ32:
+ if (count > 1 && q[0].mem_ap.addr == q[1].mem_ap.addr)
+ retval = stlink_usb_read_mem32_noaddrinc(stlink_dap_handle, ap_num, csw, addr, bufsize, buf);
+ else
+ retval = stlink_usb_read_mem32(stlink_dap_handle, ap_num, csw, addr, bufsize, buf);
+ if (retval == ERROR_OK)
+ for (unsigned int i = 0; i < count; i++)
+ *q[i].mem_ap.p_data = le_to_h_u32(&buf[4 * i]);
+ return retval;
+
+ default:
+ return ERROR_FAIL;
+ };
+}
+
+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);
+ unsigned int len_max;
+
+ if (incr == 1)
+ len_max = stlink_usb_block(stlink_dap_handle);
+ else
+ len_max = STLINK_MAX_RW16_32 / incr;
+
+ /* check for no address increment, 32 bits only */
+ if (len > 1 && incr == 4 && q[0].mem_ap.addr == q[1].mem_ap.addr)
+ incr = 0;
+
+ if (len > len_max)
+ len = len_max;
+
+ for (unsigned int i = 1; i < len; i++)
+ if (q[i].cmd != q[0].cmd ||
+ q[i].mem_ap.ap != q[0].mem_ap.ap ||
+ q[i].mem_ap.csw != q[0].mem_ap.csw ||
+ q[i].mem_ap.addr != q[i - 1].mem_ap.addr + incr)
+ return i;
+
+ return len;
+}
+
+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);
+
+ int retval = stlink_usb_buf_rw_segment(handle, q, count);
+ if (retval != ERROR_OK)
+ return retval;
+
+ *skip = count;
+ return ERROR_OK;
+}
+
+static void stlink_dap_run_internal(struct adiv5_dap *dap)
+{
+ int retval = stlink_dap_check_reconnect(dap);
+ if (retval != ERROR_OK) {
+ stlink_dap_handle->queue_index = 0;
+ stlink_dap_record_error(retval);
+ return;
+ }
+
+ unsigned int i = stlink_dap_handle->queue_index;
+ struct dap_queue *q = &stlink_dap_handle->queue[0];
+
+ while (i && stlink_dap_get_error() == ERROR_OK) {
+ unsigned int skip = 1;
+
+ switch (q->cmd) {
+ case CMD_DP_READ:
+ retval = stlink_dap_dp_read(q->dp_r.dap, q->dp_r.reg, q->dp_r.p_data);
+ break;
+ case CMD_DP_WRITE:
+ retval = stlink_dap_dp_write(q->dp_w.dap, q->dp_w.reg, q->dp_w.data);
+ break;
+ case CMD_AP_READ:
+ retval = stlink_dap_ap_read(q->ap_r.ap, q->ap_r.reg, q->ap_r.p_data);
+ break;
+ case CMD_AP_WRITE:
+ /* ignore increment packed, not supported */
+ if (q->ap_w.reg == MEM_AP_REG_CSW)
+ q->ap_w.data &= ~CSW_ADDRINC_PACKED;
+ retval = stlink_dap_ap_write(q->ap_w.ap, q->ap_w.reg, q->ap_w.data);
+ break;
+
+ case CMD_MEM_AP_READ8:
+ case CMD_MEM_AP_READ16:
+ case CMD_MEM_AP_READ32:
+ case CMD_MEM_AP_WRITE8:
+ case CMD_MEM_AP_WRITE16:
+ case CMD_MEM_AP_WRITE32:
+ retval = stlink_usb_mem_rw_queue(stlink_dap_handle, q, i, &skip);
+ break;
+
+ default:
+ LOG_ERROR("ST-Link: Unknown queue command %d", q->cmd);
+ retval = ERROR_FAIL;
+ break;
+ }
+ stlink_dap_record_error(retval);
+ q += skip;
+ i -= skip;
+ }
+
+ stlink_dap_handle->queue_index = 0;