openocd: fix SPDX tag format for files .c
[fw/openocd] / src / jtag / drivers / stlink_usb.c
index e6fc7b624aa4b88fa76d10dc906a1f589beb8f32..5b051c1ff9dff034b3e71f9a2d1a083155bdbdff 100644 (file)
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
 /***************************************************************************
  *   Copyright (C) 2020 by Tarek Bochkati                                  *
  *   Tarek Bochkati <tarek.bouchkati@gmail.com>                            *
  *   spen@spen-soft.co.uk                                                  *
  *                                                                         *
  *   This code is based on https://github.com/texane/stlink                *
- *                                                                         *
- *   This program is free software; you can redistribute it and/or modify  *
- *   it under the terms of the GNU General Public License as published by  *
- *   the Free Software Foundation; either version 2 of the License, or     *
- *   (at your option) any later version.                                   *
- *                                                                         *
- *   This program is distributed in the hope that it will be useful,       *
- *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
- *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
- *   GNU General Public License for more details.                          *
- *                                                                         *
- *   You should have received a copy of the GNU General Public License     *
- *   along with this program.  If not, see <http://www.gnu.org/licenses/>. *
  ***************************************************************************/
 
 #ifdef HAVE_CONFIG_H
 #endif
 
 /* project specific includes */
+#include <helper/align.h>
 #include <helper/binarybuffer.h>
 #include <helper/bits.h>
 #include <helper/system.h>
+#include <helper/time_support.h>
+#include <jtag/adapter.h>
 #include <jtag/interface.h>
 #include <jtag/hla/hla_layout.h>
 #include <jtag/hla/hla_transport.h>
@@ -68,8 +60,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 +145,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 +165,8 @@ struct stlink_tcp_priv_s {
        uint8_t *send_buf;
        /** */
        uint8_t *recv_buf;
+       /** */
+       struct stlink_tcp_version version;
 };
 
 struct stlink_backend_s {
@@ -227,6 +228,7 @@ struct dap_queue {
                        unsigned int reg;
                        struct adiv5_ap *ap;
                        uint32_t data;
+                       bool changes_csw_default;
                } ap_w;
                struct mem_ap {
                        uint32_t addr;
@@ -435,6 +437,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
 
@@ -489,6 +493,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
 
 /*
@@ -512,6 +518,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)
@@ -921,22 +928,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;
                }
@@ -3339,7 +3369,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;
@@ -3507,16 +3537,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) */
@@ -3550,7 +3583,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 */
@@ -3580,27 +3614,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;
        }
@@ -3669,7 +3704,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)
@@ -3866,6 +3901,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)
@@ -3960,6 +4042,7 @@ struct hl_layout_api_s stlink_usb_layout_api = {
 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 uint32_t last_csw_default[DP_APSEL_MAX + 1];
 static int stlink_dap_error = ERROR_OK;
 
 /** */
@@ -4004,6 +4087,7 @@ static int stlink_usb_open_ap(void *handle, unsigned short apsel)
 
        LOG_DEBUG("AP %d enabled", apsel);
        set_bit(apsel, opened_ap);
+       last_csw_default[apsel] = 0;
        return ERROR_OK;
 }
 
@@ -4057,7 +4141,7 @@ static int stlink_dap_reinit_interface(void)
        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++)
+               for (unsigned int apsel = 0; apsel <= DP_APSEL_MAX; apsel++)
                        if (test_bit(apsel, opened_ap)) {
                                clear_bit(apsel, opened_ap);
                                stlink_dap_open_ap(apsel);
@@ -4087,6 +4171,8 @@ static int stlink_dap_op_connect(struct adiv5_dap *dap)
 
        dap->do_reconnect = false;
        dap_invalidate_cache(dap);
+       for (unsigned int i = 0; i <= DP_APSEL_MAX; i++)
+               last_csw_default[i] = 0;
 
        retval = dap_dp_init(dap);
        if (retval != ERROR_OK) {
@@ -4189,7 +4275,15 @@ static int stlink_dap_ap_read(struct adiv5_ap *ap, unsigned int reg, uint32_t *d
        uint32_t dummy;
        int retval;
 
-       if (reg != AP_REG_IDR) {
+       if (is_adiv6(dap)) {
+               static bool error_flagged;
+               if (!error_flagged)
+                       LOG_ERROR("ADIv6 dap not supported by stlink dap-direct mode");
+               error_flagged = true;
+               return ERROR_FAIL;
+       }
+
+       if (reg != ADIV5_AP_REG_IDR) {
                retval = stlink_dap_open_ap(ap->ap_num);
                if (retval != ERROR_OK)
                        return retval;
@@ -4207,6 +4301,14 @@ static int stlink_dap_ap_write(struct adiv5_ap *ap, unsigned int reg, uint32_t d
        struct adiv5_dap *dap = ap->dap;
        int retval;
 
+       if (is_adiv6(dap)) {
+               static bool error_flagged;
+               if (!error_flagged)
+                       LOG_ERROR("ADIv6 dap not supported by stlink dap-direct mode");
+               error_flagged = true;
+               return ERROR_FAIL;
+       }
+
        retval = stlink_dap_open_ap(ap->ap_num);
        if (retval != ERROR_OK)
                return retval;
@@ -4224,6 +4326,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);
+
+       uint32_t 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);
@@ -4284,6 +4477,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;
+       uint32_t 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);
@@ -4313,9 +4549,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;
+
+       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);
 
-       int retval = stlink_usb_buf_rw_segment(handle, q, count);
+       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;
 
@@ -4350,7 +4596,7 @@ static void stlink_dap_run_internal(struct adiv5_dap *dap)
                        break;
                case CMD_AP_WRITE:
                        /* ignore increment packed, not supported */
-                       if (q->ap_w.reg == MEM_AP_REG_CSW)
+                       if (q->ap_w.reg == ADIV5_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;
@@ -4492,20 +4738,22 @@ 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)) {
+                       (reg == ADIV5_MEM_AP_REG_DRW || reg == ADIV5_MEM_AP_REG_BD0 || reg == ADIV5_MEM_AP_REG_BD1 ||
+                        reg == ADIV5_MEM_AP_REG_BD2 || reg == ADIV5_MEM_AP_REG_BD3)) {
                /* de-queue previous write-TAR */
                struct dap_queue *prev_q = q - 1;
-               if (i && prev_q->cmd == CMD_AP_WRITE && prev_q->ap_w.ap == ap && prev_q->ap_w.reg == MEM_AP_REG_TAR) {
+               if (i && prev_q->cmd == CMD_AP_WRITE && prev_q->ap_w.ap == ap && prev_q->ap_w.reg == ADIV5_MEM_AP_REG_TAR) {
                        stlink_dap_handle->queue_index = i;
                        i--;
                        q = prev_q;
                        prev_q--;
                }
-               /* de-queue previous write-CSW */
-               if (i && prev_q->cmd == CMD_AP_WRITE && prev_q->ap_w.ap == ap && prev_q->ap_w.reg == MEM_AP_REG_CSW) {
+               /* de-queue previous write-CSW if it didn't changed ap->csw_default */
+               if (i && prev_q->cmd == CMD_AP_WRITE && prev_q->ap_w.ap == ap && prev_q->ap_w.reg == ADIV5_MEM_AP_REG_CSW &&
+                               !prev_q->ap_w.changes_csw_default) {
                        stlink_dap_handle->queue_index = i;
                        q = prev_q;
                }
@@ -4526,7 +4774,7 @@ static int stlink_dap_op_queue_ap_read(struct adiv5_ap *ap, unsigned int reg,
                        return ERROR_FAIL;
                }
 
-               q->mem_ap.addr = (reg == MEM_AP_REG_DRW) ? ap->tar_value : ((ap->tar_value & ~0x0f) | (reg & 0x0c));
+               q->mem_ap.addr = (reg == ADIV5_MEM_AP_REG_DRW) ? ap->tar_value : ((ap->tar_value & ~0x0f) | (reg & 0x0c));
                q->mem_ap.ap = ap;
                q->mem_ap.p_data = data;
                q->mem_ap.csw = ap->csw_default;
@@ -4556,20 +4804,22 @@ 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)) {
+                       (reg == ADIV5_MEM_AP_REG_DRW || reg == ADIV5_MEM_AP_REG_BD0 || reg == ADIV5_MEM_AP_REG_BD1 ||
+                        reg == ADIV5_MEM_AP_REG_BD2 || reg == ADIV5_MEM_AP_REG_BD3)) {
                /* de-queue previous write-TAR */
                struct dap_queue *prev_q = q - 1;
-               if (i && prev_q->cmd == CMD_AP_WRITE && prev_q->ap_w.ap == ap && prev_q->ap_w.reg == MEM_AP_REG_TAR) {
+               if (i && prev_q->cmd == CMD_AP_WRITE && prev_q->ap_w.ap == ap && prev_q->ap_w.reg == ADIV5_MEM_AP_REG_TAR) {
                        stlink_dap_handle->queue_index = i;
                        i--;
                        q = prev_q;
                        prev_q--;
                }
-               /* de-queue previous write-CSW */
-               if (i && prev_q->cmd == CMD_AP_WRITE && prev_q->ap_w.ap == ap && prev_q->ap_w.reg == MEM_AP_REG_CSW) {
+               /* de-queue previous write-CSW if it didn't changed ap->csw_default */
+               if (i && prev_q->cmd == CMD_AP_WRITE && prev_q->ap_w.ap == ap && prev_q->ap_w.reg == ADIV5_MEM_AP_REG_CSW &&
+                               !prev_q->ap_w.changes_csw_default) {
                        stlink_dap_handle->queue_index = i;
                        q = prev_q;
                }
@@ -4590,7 +4840,7 @@ static int stlink_dap_op_queue_ap_write(struct adiv5_ap *ap, unsigned int reg,
                        return ERROR_FAIL;
                }
 
-               q->mem_ap.addr = (reg == MEM_AP_REG_DRW) ? ap->tar_value : ((ap->tar_value & ~0x0f) | (reg & 0x0c));
+               q->mem_ap.addr = (reg == ADIV5_MEM_AP_REG_DRW) ? ap->tar_value : ((ap->tar_value & ~0x0f) | (reg & 0x0c));
                q->mem_ap.ap = ap;
                q->mem_ap.data = data;
                q->mem_ap.csw = ap->csw_default;
@@ -4603,6 +4853,13 @@ static int stlink_dap_op_queue_ap_write(struct adiv5_ap *ap, unsigned int reg,
                q->ap_w.reg = reg;
                q->ap_w.ap = ap;
                q->ap_w.data = data;
+               uint8_t ap_num = ap->ap_num;
+               if (reg == ADIV5_MEM_AP_REG_CSW && ap->csw_default != last_csw_default[ap_num]) {
+                       q->ap_w.changes_csw_default = true;
+                       last_csw_default[ap_num] = ap->csw_default;
+               } else {
+                       q->ap_w.changes_csw_default = false;
+               }
        }
 
        if (i == MAX_QUEUE_DEPTH - 1)
@@ -4688,25 +4945,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 <serial-number>\".");
-               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)
 {
@@ -4797,13 +5035,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 = "<serial_number>",
-       },
        {
                .name = "vid_pid",
                .handler = stlink_dap_vid_pid,
@@ -4884,9 +5115,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);
 }