jtag/hla, jtag/stlink: switch to command 'adapter serial'
[fw/openocd] / src / jtag / drivers / stlink_usb.c
index 45bb5019f95fefaac71d93caa845ed7abd8f08a2..4c0e025fc5f9522097caf039d469f10de9a930b6 100644 (file)
@@ -1,4 +1,7 @@
 /***************************************************************************
+ *   Copyright (C) 2020 by Tarek Bochkati                                  *
+ *   Tarek Bochkati <tarek.bouchkati@gmail.com>                            *
+ *                                                                         *
  *   SWIM contributions by Ake Rehnman                                     *
  *   Copyright (C) 2017  Ake Rehnman                                       *
  *   ake.rehnman(at)gmail.com                                              *
 #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>
 #include <jtag/hla/hla_interface.h>
+#include <jtag/swim.h>
+#include <target/arm_adi_v5.h>
 #include <target/target.h>
 #include <transport/transport.h>
 
 #include <target/cortex_m.h>
 
+#include <helper/system.h>
+
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+
+#ifdef HAVE_NETINET_TCP_H
+#include <netinet/tcp.h>
+#endif
+
 #include "libusb_helper.h"
 
 #ifdef HAVE_LIBUSB1
@@ -63,7 +82,7 @@
 #define STLINK_V2_1_TRACE_EP  (2|ENDPOINT_IN)
 
 #define STLINK_SG_SIZE        (31)
-#define STLINK_DATA_SIZE      (4096)
+#define STLINK_DATA_SIZE      (6144)
 #define STLINK_CMD_SIZE_V2    (16)
 #define STLINK_CMD_SIZE_V1    (10)
 
 #define STLINK_V3E_PID          (0x374E)
 #define STLINK_V3S_PID          (0x374F)
 #define STLINK_V3_2VCP_PID      (0x3753)
+#define STLINK_V3E_NO_MSD_PID   (0x3754)
 
 /*
  * ST-Link/V1, ST-Link/V2 and ST-Link/V2.1 are full-speed USB devices and
  * this limits the bulk packet size and the 8bit read/writes to max 64 bytes.
  * STLINK-V3 is a high speed USB 2.0 and the limit is 512 bytes from FW V3J6.
+ *
+ * For 16 and 32bit read/writes stlink handles USB packet split and the limit
+ * is the internal buffer size of 6144 bytes.
+ * TODO: override ADIv5 layer's tar_autoincr_block that limits the transfer
+ * to 1024 or 4096 bytes
  */
-#define STLINK_MAX_RW8         (64)
-#define STLINKV3_MAX_RW8       (512)
+#define STLINK_MAX_RW8          (64)
+#define STLINKV3_MAX_RW8        (512)
+#define STLINK_MAX_RW16_32      STLINK_DATA_SIZE
+#define STLINK_SWIM_DATA_SIZE   STLINK_DATA_SIZE
 
 /* "WAIT" responses will be retried (with exponential backoff) at
  * most this many times before failing to caller.
  */
 #define MAX_WAIT_RETRIES 8
 
+/* HLA is currently limited at AP#0 and no control on CSW */
+#define STLINK_HLA_AP_NUM       0
+#define STLINK_HLA_CSW          0
+
 enum stlink_jtag_api_version {
        STLINK_JTAG_API_V1 = 1,
        STLINK_JTAG_API_V2,
        STLINK_JTAG_API_V3,
 };
 
+enum stlink_mode {
+       STLINK_MODE_UNKNOWN = 0,
+       STLINK_MODE_DFU,
+       STLINK_MODE_MASS,
+       STLINK_MODE_DEBUG_JTAG,
+       STLINK_MODE_DEBUG_SWD,
+       STLINK_MODE_DEBUG_SWIM
+};
+
 /** */
 struct stlink_usb_version {
        /** */
@@ -109,12 +149,110 @@ struct stlink_usb_version {
        uint32_t flags;
 };
 
-/** */
-struct stlink_usb_handle_s {
+struct stlink_usb_priv_s {
        /** */
        struct libusb_device_handle *fd;
        /** */
        struct libusb_transfer *trans;
+};
+
+struct stlink_tcp_priv_s {
+       /** */
+       int fd;
+       /** */
+       bool connected;
+       /** */
+       uint32_t device_id;
+       /** */
+       uint32_t connect_id;
+       /** */
+       uint8_t *send_buf;
+       /** */
+       uint8_t *recv_buf;
+};
+
+struct stlink_backend_s {
+       /** */
+       int (*open)(void *handle, struct hl_interface_param_s *param);
+       /** */
+       int (*close)(void *handle);
+       /** */
+       int (*xfer_noerrcheck)(void *handle, const uint8_t *buf, int size);
+       /** */
+       int (*read_trace)(void *handle, const uint8_t *buf, int size);
+};
+
+/* TODO: make queue size dynamic */
+/* TODO: don't allocate queue for HLA */
+#define MAX_QUEUE_DEPTH (4096)
+
+enum queue_cmd {
+       CMD_DP_READ = 1,
+       CMD_DP_WRITE,
+
+       CMD_AP_READ,
+       CMD_AP_WRITE,
+
+       /*
+        * encode the bytes size in the enum's value. This makes easy to extract it
+        * with a simple logic AND, by using the macro CMD_MEM_AP_2_SIZE() below
+        */
+       CMD_MEM_AP_READ8   = 0x10 + 1,
+       CMD_MEM_AP_READ16  = 0x10 + 2,
+       CMD_MEM_AP_READ32  = 0x10 + 4,
+
+       CMD_MEM_AP_WRITE8  = 0x20 + 1,
+       CMD_MEM_AP_WRITE16 = 0x20 + 2,
+       CMD_MEM_AP_WRITE32 = 0x20 + 4,
+};
+
+#define CMD_MEM_AP_2_SIZE(cmd) ((cmd) & 7)
+
+struct dap_queue {
+       enum queue_cmd cmd;
+       union {
+               struct dp_r {
+                       unsigned int reg;
+                       struct adiv5_dap *dap;
+                       uint32_t *p_data;
+               } dp_r;
+               struct dp_w {
+                       unsigned int reg;
+                       struct adiv5_dap *dap;
+                       uint32_t data;
+               } dp_w;
+               struct ap_r {
+                       unsigned int reg;
+                       struct adiv5_ap *ap;
+                       uint32_t *p_data;
+               } ap_r;
+               struct ap_w {
+                       unsigned int reg;
+                       struct adiv5_ap *ap;
+                       uint32_t data;
+                       bool changes_csw_default;
+               } ap_w;
+               struct mem_ap {
+                       uint32_t addr;
+                       struct adiv5_ap *ap;
+                       union {
+                               uint32_t *p_data;
+                               uint32_t data;
+                       };
+                       uint32_t csw;
+               } mem_ap;
+       };
+};
+
+/** */
+struct stlink_usb_handle_s {
+       /** */
+       struct stlink_backend_s *backend;
+       /** */
+       union {
+               struct stlink_usb_priv_s usb_backend_priv;
+               struct stlink_tcp_priv_s tcp_backend_priv;
+       };
        /** */
        uint8_t rx_ep;
        /** */
@@ -122,17 +260,17 @@ struct stlink_usb_handle_s {
        /** */
        uint8_t trace_ep;
        /** */
-       uint8_t cmdbuf[STLINK_SG_SIZE];
+       uint8_t *cmdbuf;
        /** */
        uint8_t cmdidx;
        /** */
        uint8_t direction;
        /** */
-       uint8_t databuf[STLINK_DATA_SIZE];
+       uint8_t *databuf;
        /** */
        uint32_t max_mem_packet;
        /** */
-       enum hl_transports transport;
+       enum stlink_mode st_mode;
        /** */
        struct stlink_usb_version version;
        /** */
@@ -149,8 +287,32 @@ struct stlink_usb_handle_s {
        /** reconnect is needed next time we try to query the
         * status */
        bool reconnect_pending;
+       /** queue of dap_direct operations */
+       struct dap_queue queue[MAX_QUEUE_DEPTH];
+       /** first element available in the queue */
+       unsigned int queue_index;
 };
 
+/** */
+static inline int stlink_usb_open(void *handle, struct hl_interface_param_s *param)
+{
+       struct stlink_usb_handle_s *h = handle;
+       return h->backend->open(handle, param);
+}
+
+/** */
+static inline int stlink_usb_close(void *handle)
+{
+       struct stlink_usb_handle_s *h = handle;
+       return h->backend->close(handle);
+}
+/** */
+static inline int stlink_usb_xfer_noerrcheck(void *handle, const uint8_t *buf, int size)
+{
+       struct stlink_usb_handle_s *h = handle;
+       return h->backend->xfer_noerrcheck(handle, buf, size);
+}
+
 #define STLINK_SWIM_ERR_OK             0x00
 #define STLINK_SWIM_BUSY               0x01
 #define STLINK_DEBUG_ERR_OK            0x80
@@ -208,7 +370,7 @@ struct stlink_usb_handle_s {
        uint32_t address
 
        STLINK_SWIM_RESET
-       send syncronization seq (16us low, response 64 clocks low)
+       send synchronization seq (16us low, response 64 clocks low)
 */
 #define STLINK_SWIM_ENTER                  0x00
 #define STLINK_SWIM_EXIT                   0x01
@@ -276,6 +438,12 @@ struct stlink_usb_handle_s {
 #define STLINK_DEBUG_APIV2_INIT_AP         0x4B
 #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
+
 #define STLINK_APIV3_SET_COM_FREQ           0x61
 #define STLINK_APIV3_GET_COM_FREQ           0x62
 
@@ -289,39 +457,71 @@ struct stlink_usb_handle_s {
 
 #define STLINK_TRACE_SIZE               4096
 #define STLINK_TRACE_MAX_HZ             2000000
+#define STLINK_V3_TRACE_MAX_HZ          24000000
 
 #define STLINK_V3_MAX_FREQ_NB               10
 
-/** */
-enum stlink_mode {
-       STLINK_MODE_UNKNOWN = 0,
-       STLINK_MODE_DFU,
-       STLINK_MODE_MASS,
-       STLINK_MODE_DEBUG_JTAG,
-       STLINK_MODE_DEBUG_SWD,
-       STLINK_MODE_DEBUG_SWIM
-};
-
 #define REQUEST_SENSE        0x03
 #define REQUEST_SENSE_LENGTH 18
 
+/* STLINK TCP commands */
+#define STLINK_TCP_CMD_REFRESH_DEVICE_LIST   0x00
+#define STLINK_TCP_CMD_GET_NB_DEV            0x01
+#define STLINK_TCP_CMD_GET_DEV_INFO          0x02
+#define STLINK_TCP_CMD_OPEN_DEV              0x03
+#define STLINK_TCP_CMD_CLOSE_DEV             0x04
+#define STLINK_TCP_CMD_SEND_USB_CMD          0x05
+#define STLINK_TCP_CMD_GET_SERVER_VERSION    0x06
+#define STLINK_TCP_CMD_GET_NB_OF_DEV_CLIENTS 0x07
+
+/* STLINK TCP constants */
+#define OPENOCD_STLINK_TCP_API_VERSION       1
+#define STLINK_TCP_REQUEST_WRITE             0
+#define STLINK_TCP_REQUEST_READ              1
+#define STLINK_TCP_REQUEST_READ_SWO          3
+#define STLINK_TCP_SS_SIZE                   4
+#define STLINK_TCP_USB_CMD_SIZE              32
+#define STLINK_TCP_SERIAL_SIZE               32
+#define STLINK_TCP_SEND_BUFFER_SIZE          10240
+#define STLINK_TCP_RECV_BUFFER_SIZE          10240
+
+/* STLINK TCP command status */
+#define STLINK_TCP_SS_OK                     0x00000001
+#define STLINK_TCP_SS_MEMORY_PROBLEM         0x00001000
+#define STLINK_TCP_SS_TIMEOUT                0x00001001
+#define STLINK_TCP_SS_BAD_PARAMETER          0x00001002
+#define STLINK_TCP_SS_OPEN_ERR               0x00001003
+#define STLINK_TCP_SS_TRUNCATED_DATA         0x00001052
+#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_WIN32_ERROR            0x00010000
+
 /*
  * Map the relevant features, quirks and workaround for specific firmware
  * version of stlink
  */
-#define STLINK_F_HAS_TRACE              BIT(0)
-#define STLINK_F_HAS_SWD_SET_FREQ       BIT(1)
-#define STLINK_F_HAS_JTAG_SET_FREQ      BIT(2)
-#define STLINK_F_HAS_MEM_16BIT          BIT(3)
-#define STLINK_F_HAS_GETLASTRWSTATUS2   BIT(4)
-#define STLINK_F_HAS_DAP_REG            BIT(5)
-#define STLINK_F_QUIRK_JTAG_DP_READ     BIT(6)
-#define STLINK_F_HAS_AP_INIT            BIT(7)
-#define STLINK_F_HAS_DPBANKSEL          BIT(8)
-#define STLINK_F_HAS_RW8_512BYTES       BIT(9)
+#define STLINK_F_HAS_TRACE              BIT(0)  /* v2>=j13 || v3     */
+#define STLINK_F_HAS_GETLASTRWSTATUS2   BIT(1)  /* v2>=j15 || v3     */
+#define STLINK_F_HAS_SWD_SET_FREQ       BIT(2)  /* v2>=j22           */
+#define STLINK_F_HAS_JTAG_SET_FREQ      BIT(3)  /* v2>=j24           */
+#define STLINK_F_QUIRK_JTAG_DP_READ     BIT(4)  /* v2>=j24 && v2<j32 */
+#define STLINK_F_HAS_DAP_REG            BIT(5)  /* v2>=j24 || v3     */
+#define STLINK_F_HAS_MEM_16BIT          BIT(6)  /* v2>=j26 || v3     */
+#define STLINK_F_HAS_AP_INIT            BIT(7)  /* v2>=j28 || v3     */
+#define STLINK_F_FIX_CLOSE_AP           BIT(8)  /* v2>=j29 || v3     */
+#define STLINK_F_HAS_DPBANKSEL          BIT(9)  /* v2>=j32 || v3>=j2 */
+#define STLINK_F_HAS_RW8_512BYTES       BIT(10) /*            v3>=j6 */
 
 /* aliases */
 #define STLINK_F_HAS_TARGET_VOLT        STLINK_F_HAS_TRACE
+#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)
 
 struct speed_map {
        int speed;
@@ -357,16 +557,17 @@ static const struct speed_map stlink_khz_to_speed_map_jtag[] = {
 
 static void stlink_usb_init_buffer(void *handle, uint8_t direction, uint32_t size);
 static int stlink_swim_status(void *handle);
-void stlink_dump_speed_map(const struct speed_map *map, unsigned int map_size);
+static void stlink_dump_speed_map(const struct speed_map *map, unsigned int map_size);
 static int stlink_get_com_freq(void *handle, bool is_jtag, struct speed_map *map);
 static int stlink_speed(void *handle, int khz, bool query);
+static int stlink_usb_open_ap(void *handle, unsigned short apsel);
 
 /** */
 static unsigned int stlink_usb_block(void *handle)
 {
        struct stlink_usb_handle_s *h = handle;
 
-       assert(handle != NULL);
+       assert(handle);
 
        if (h->version.flags & STLINK_F_HAS_RW8_512BYTES)
                return STLINKV3_MAX_RW8;
@@ -374,8 +575,6 @@ static unsigned int stlink_usb_block(void *handle)
                return STLINK_MAX_RW8;
 }
 
-
-
 #ifdef USE_LIBUSB_ASYNCIO
 
 static LIBUSB_CALL void sync_transfer_cb(struct libusb_transfer *transfer)
@@ -390,13 +589,8 @@ static void sync_transfer_wait_for_completion(struct libusb_transfer *transfer)
 {
        int r, *completed = transfer->user_data;
 
-       /* Assuming a single libusb context exists.  There no existing interface into this
-        * module to pass a libusb context.
-        */
-       struct libusb_context *ctx = NULL;
-
        while (!*completed) {
-               r = libusb_handle_events_completed(ctx, completed);
+               r = jtag_libusb_handle_events_completed(completed);
                if (r < 0) {
                        if (r == LIBUSB_ERROR_INTERRUPTED)
                                continue;
@@ -466,7 +660,7 @@ static int jtag_libusb_bulk_transfer_n(
                transfers[i].transfer_size = 0;
                transfers[i].transfer = libusb_alloc_transfer(0);
 
-               if (transfers[i].transfer == NULL) {
+               if (!transfers[i].transfer) {
                        for (size_t j = 0; j < i; ++j)
                                libusb_free_transfer(transfers[j].transfer);
 
@@ -534,12 +728,12 @@ static int stlink_usb_xfer_v1_get_status(void *handle)
        struct stlink_usb_handle_s *h = handle;
        int tr, ret;
 
-       assert(handle != NULL);
+       assert(handle);
 
        /* read status */
        memset(h->cmdbuf, 0, STLINK_SG_SIZE);
 
-       ret = jtag_libusb_bulk_read(h->fd, h->rx_ep, (char *)h->cmdbuf, 13,
+       ret = jtag_libusb_bulk_read(h->usb_backend_priv.fd, h->rx_ep, (char *)h->cmdbuf, 13,
                                    STLINK_READ_TIMEOUT, &tr);
        if (ret || tr != 13)
                return ERROR_FAIL;
@@ -568,7 +762,7 @@ static int stlink_usb_xfer_rw(void *handle, int cmdsize, const uint8_t *buf, int
 {
        struct stlink_usb_handle_s *h = handle;
 
-       assert(handle != NULL);
+       assert(handle);
 
        size_t n_transfers = 0;
        struct jtag_xfer transfers[2];
@@ -596,7 +790,7 @@ static int stlink_usb_xfer_rw(void *handle, int cmdsize, const uint8_t *buf, int
        }
 
        return jtag_libusb_bulk_transfer_n(
-                       h->fd,
+                       h->usb_backend_priv.fd,
                        transfers,
                        n_transfers,
                        STLINK_WRITE_TIMEOUT);
@@ -607,22 +801,22 @@ static int stlink_usb_xfer_rw(void *handle, int cmdsize, const uint8_t *buf, int
        struct stlink_usb_handle_s *h = handle;
        int tr, ret;
 
-       assert(handle != NULL);
+       assert(handle);
 
-       ret = jtag_libusb_bulk_write(h->fd, h->tx_ep, (char *)h->cmdbuf,
+       ret = jtag_libusb_bulk_write(h->usb_backend_priv.fd, h->tx_ep, (char *)h->cmdbuf,
                                     cmdsize, STLINK_WRITE_TIMEOUT, &tr);
        if (ret || tr != cmdsize)
                return ERROR_FAIL;
 
        if (h->direction == h->tx_ep && size) {
-               ret = jtag_libusb_bulk_write(h->fd, h->tx_ep, (char *)buf,
+               ret = jtag_libusb_bulk_write(h->usb_backend_priv.fd, h->tx_ep, (char *)buf,
                                             size, STLINK_WRITE_TIMEOUT, &tr);
                if (ret || tr != size) {
                        LOG_DEBUG("bulk write failed");
                        return ERROR_FAIL;
                }
        } else if (h->direction == h->rx_ep && size) {
-               ret = jtag_libusb_bulk_read(h->fd, h->rx_ep, (char *)buf,
+               ret = jtag_libusb_bulk_read(h->usb_backend_priv.fd, h->rx_ep, (char *)buf,
                                            size, STLINK_READ_TIMEOUT, &tr);
                if (ret || tr != size) {
                        LOG_DEBUG("bulk read failed");
@@ -640,7 +834,7 @@ static int stlink_usb_xfer_v1_get_sense(void *handle)
        int res;
        struct stlink_usb_handle_s *h = handle;
 
-       assert(handle != NULL);
+       assert(handle);
 
        stlink_usb_init_buffer(handle, h->rx_ep, 16);
 
@@ -661,18 +855,34 @@ static int stlink_usb_xfer_v1_get_sense(void *handle)
        return ERROR_OK;
 }
 
+/** */
+static int stlink_usb_usb_read_trace(void *handle, const uint8_t *buf, int size)
+{
+       struct stlink_usb_handle_s *h = handle;
+       int tr, ret;
+
+       ret = jtag_libusb_bulk_read(h->usb_backend_priv.fd, h->trace_ep, (char *)buf, size,
+                                   STLINK_READ_TIMEOUT, &tr);
+       if (ret || tr != size) {
+               LOG_ERROR("bulk trace read failed");
+               return ERROR_FAIL;
+       }
+
+       return ERROR_OK;
+}
+
 /*
        transfers block in cmdbuf
        <size> indicates number of bytes in the following
        data phase.
        Ignore the (eventual) error code in the received packet.
 */
-static int stlink_usb_xfer_noerrcheck(void *handle, const uint8_t *buf, int size)
+static int stlink_usb_usb_xfer_noerrcheck(void *handle, const uint8_t *buf, int size)
 {
        int err, cmdsize = STLINK_CMD_SIZE_V2;
        struct stlink_usb_handle_s *h = handle;
 
-       assert(handle != NULL);
+       assert(handle);
 
        if (h->version.stlink == 1) {
                cmdsize = STLINK_SG_SIZE;
@@ -700,6 +910,135 @@ static int stlink_usb_xfer_noerrcheck(void *handle, const uint8_t *buf, int size
        return ERROR_OK;
 }
 
+
+static int stlink_tcp_send_cmd(void *handle, int send_size, int recv_size, bool check_tcp_status)
+{
+       struct stlink_usb_handle_s *h = handle;
+
+       assert(handle);
+
+       /* send the TCP command */
+       int sent_size = send(h->tcp_backend_priv.fd, (void *)h->tcp_backend_priv.send_buf, send_size, 0);
+       if (sent_size != send_size) {
+               LOG_ERROR("failed to send USB CMD");
+               if (sent_size == -1)
+                       LOG_DEBUG("socket send error: %s (errno %d)", strerror(errno), errno);
+               else
+                       LOG_DEBUG("sent size %d (expected %d)", sent_size, send_size);
+               return ERROR_FAIL;
+       }
+
+       /* read the TCP response */
+       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);
+                       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) {
+                       LOG_ERROR("TCP error status 0x%X", tcp_ss);
+                       return ERROR_FAIL;
+               }
+       }
+
+       return ERROR_OK;
+}
+
+/** */
+static int stlink_tcp_xfer_noerrcheck(void *handle, const uint8_t *buf, int size)
+{
+       struct stlink_usb_handle_s *h = handle;
+
+       int send_size = STLINK_TCP_USB_CMD_SIZE;
+       int recv_size = STLINK_TCP_SS_SIZE;
+
+       assert(handle);
+
+       /* prepare the TCP command */
+       h->tcp_backend_priv.send_buf[0] = STLINK_TCP_CMD_SEND_USB_CMD;
+       memset(&h->tcp_backend_priv.send_buf[1], 0, 3); /* reserved for alignment and future use, must be zero */
+       h_u32_to_le(&h->tcp_backend_priv.send_buf[4], h->tcp_backend_priv.connect_id);
+       /* tcp_backend_priv.send_buf[8..23] already contains the constructed stlink command */
+       h->tcp_backend_priv.send_buf[24] = h->direction;
+       memset(&h->tcp_backend_priv.send_buf[25], 0, 3);  /* reserved for alignment and future use, must be zero */
+
+       h_u32_to_le(&h->tcp_backend_priv.send_buf[28], size);
+
+       /*
+        * if the xfer is a write request (tx_ep)
+        *  > then buf content will be copied
+        * into &cmdbuf[32].
+        * else : the xfer is a read or trace read request (rx_ep or trace_ep)
+        *  > the buf content will be filled from &databuf[4].
+        *
+        * note : if h->direction is trace_ep, h->cmdbuf is zeros.
+        */
+
+       if (h->direction == h->tx_ep) { /* STLINK_TCP_REQUEST_WRITE */
+               send_size += size;
+               if (send_size > STLINK_TCP_SEND_BUFFER_SIZE) {
+                       LOG_ERROR("STLINK_TCP command buffer overflow");
+                       return ERROR_FAIL;
+               }
+               memcpy(&h->tcp_backend_priv.send_buf[32], buf, size);
+       } else { /* STLINK_TCP_REQUEST_READ or STLINK_TCP_REQUEST_READ_SWO */
+               recv_size += size;
+               if (recv_size > STLINK_TCP_RECV_BUFFER_SIZE) {
+                       LOG_ERROR("STLINK_TCP data buffer overflow");
+                       return ERROR_FAIL;
+               }
+       }
+
+       int ret = stlink_tcp_send_cmd(h, send_size, recv_size, true);
+       if (ret != ERROR_OK)
+               return ret;
+
+       if (h->direction != h->tx_ep) {
+               /* the read data is located in tcp_backend_priv.recv_buf[4] */
+               /* most of the case it will be copying the data from tcp_backend_priv.recv_buf[4]
+                * to handle->cmd_buff which are the same, so let's avoid unnecessary copying */
+               if (buf != &h->tcp_backend_priv.recv_buf[4])
+                       memcpy((uint8_t *)buf, &h->tcp_backend_priv.recv_buf[4], size);
+       }
+
+       return ERROR_OK;
+}
+
+/** */
+static int stlink_tcp_read_trace(void *handle, const uint8_t *buf, int size)
+{
+       struct stlink_usb_handle_s *h = handle;
+
+       stlink_usb_init_buffer(h, h->trace_ep, 0);
+       return stlink_tcp_xfer_noerrcheck(handle, buf, size);
+}
+
 /**
     Converts an STLINK status code held in the first byte of a response
     to an openocd error, logs any error/wait status as debug output.
@@ -708,9 +1047,9 @@ static int stlink_usb_error_check(void *handle)
 {
        struct stlink_usb_handle_s *h = handle;
 
-       assert(handle != NULL);
+       assert(handle);
 
-       if (h->transport == HL_TRANSPORT_SWIM) {
+       if (h->st_mode == STLINK_MODE_DEBUG_SWIM) {
                switch (h->databuf[0]) {
                        case STLINK_SWIM_ERR_OK:
                                return ERROR_OK;
@@ -819,13 +1158,13 @@ static int stlink_cmd_allow_retry(void *handle, const uint8_t *buf, int size)
        struct stlink_usb_handle_s *h = handle;
 
        while (1) {
-               if ((h->transport != HL_TRANSPORT_SWIM) || !retries) {
+               if ((h->st_mode != STLINK_MODE_DEBUG_SWIM) || !retries) {
                        res = stlink_usb_xfer_noerrcheck(handle, buf, size);
                        if (res != ERROR_OK)
                                return res;
                }
 
-               if (h->transport == HL_TRANSPORT_SWIM) {
+               if (h->st_mode == STLINK_MODE_DEBUG_SWIM) {
                        res = stlink_swim_status(handle);
                        if (res != ERROR_OK)
                                return res;
@@ -846,20 +1185,12 @@ static int stlink_cmd_allow_retry(void *handle, const uint8_t *buf, int size)
 static int stlink_usb_read_trace(void *handle, const uint8_t *buf, int size)
 {
        struct stlink_usb_handle_s *h = handle;
-       int tr, ret;
 
-       assert(handle != NULL);
+       assert(handle);
 
        assert(h->version.flags & STLINK_F_HAS_TRACE);
 
-       ret = jtag_libusb_bulk_read(h->fd, h->trace_ep, (char *)buf, size,
-                                   STLINK_READ_TIMEOUT, &tr);
-       if (ret || tr != size) {
-               LOG_ERROR("bulk trace read failed");
-               return ERROR_FAIL;
-       }
-
-       return ERROR_OK;
+       return h->backend->read_trace(handle, buf, size);
 }
 
 /*
@@ -920,7 +1251,7 @@ static int stlink_usb_version(void *handle)
        char *p;
        struct stlink_usb_handle_s *h = handle;
 
-       assert(handle != NULL);
+       assert(handle);
 
        stlink_usb_init_buffer(handle, h->rx_ep, 6);
 
@@ -1023,6 +1354,7 @@ static int stlink_usb_version(void *handle)
                        flags |= STLINK_F_QUIRK_JTAG_DP_READ;
 
                /* API to read/write memory at 16 bit from J26 */
+               /* API to write memory without address increment from J26 */
                if (h->version.jtag >= 26)
                        flags |= STLINK_F_HAS_MEM_16BIT;
 
@@ -1030,7 +1362,13 @@ static int stlink_usb_version(void *handle)
                if (h->version.jtag >= 28)
                        flags |= STLINK_F_HAS_AP_INIT;
 
+               /* API required to return proper error code on close AP from J29 */
+               if (h->version.jtag >= 29)
+                       flags |= STLINK_F_FIX_CLOSE_AP;
+
                /* Banked regs (DPv1 & DPv2) support from V2J32 */
+               /* API to read memory without address increment from V2J32 */
+               /* Memory R/W supports CSW from V2J32 */
                if (h->version.jtag >= 32)
                        flags |= STLINK_F_HAS_DPBANKSEL;
 
@@ -1052,12 +1390,18 @@ static int stlink_usb_version(void *handle)
                flags |= STLINK_F_HAS_DAP_REG;
 
                /* API to read/write memory at 16 bit */
+               /* API to write memory without address increment */
                flags |= STLINK_F_HAS_MEM_16BIT;
 
                /* API required to init AP before any AP access */
                flags |= STLINK_F_HAS_AP_INIT;
 
+               /* API required to return proper error code on close AP */
+               flags |= STLINK_F_FIX_CLOSE_AP;
+
                /* Banked regs (DPv1 & DPv2) support from V3J2 */
+               /* API to read memory without address increment from V3J2 */
+               /* Memory R/W supports CSW from V3J2 */
                if (h->version.jtag >= 2)
                        flags |= STLINK_F_HAS_DPBANKSEL;
 
@@ -1127,7 +1471,7 @@ static int stlink_usb_set_swdclk(void *handle, uint16_t clk_divisor)
 {
        struct stlink_usb_handle_s *h = handle;
 
-       assert(handle != NULL);
+       assert(handle);
 
        if (!(h->version.flags & STLINK_F_HAS_SWD_SET_FREQ))
                return ERROR_COMMAND_NOTFOUND;
@@ -1151,7 +1495,7 @@ static int stlink_usb_set_jtagclk(void *handle, uint16_t clk_divisor)
 {
        struct stlink_usb_handle_s *h = handle;
 
-       assert(handle != NULL);
+       assert(handle);
 
        if (!(h->version.flags & STLINK_F_HAS_JTAG_SET_FREQ))
                return ERROR_COMMAND_NOTFOUND;
@@ -1177,7 +1521,7 @@ static int stlink_usb_current_mode(void *handle, uint8_t *mode)
        int res;
        struct stlink_usb_handle_s *h = handle;
 
-       assert(handle != NULL);
+       assert(handle);
 
        stlink_usb_init_buffer(handle, h->rx_ep, 2);
 
@@ -1199,7 +1543,7 @@ static int stlink_usb_mode_enter(void *handle, enum stlink_mode type)
        int rx_size = 0;
        struct stlink_usb_handle_s *h = handle;
 
-       assert(handle != NULL);
+       assert(handle);
 
        /* on api V2 we are able the read the latest command
         * status
@@ -1230,9 +1574,8 @@ static int stlink_usb_mode_enter(void *handle, enum stlink_mode type)
                case STLINK_MODE_DEBUG_SWIM:
                        h->cmdbuf[h->cmdidx++] = STLINK_SWIM_COMMAND;
                        h->cmdbuf[h->cmdidx++] = STLINK_SWIM_ENTER;
-                       /* no answer for this function... */
-                       rx_size = 0;
-                       break;
+                       /* swim enter does not return any response or status */
+                       return stlink_usb_xfer_noerrcheck(handle, h->databuf, 0);
                case STLINK_MODE_DFU:
                case STLINK_MODE_MASS:
                default:
@@ -1248,7 +1591,7 @@ static int stlink_usb_mode_leave(void *handle, enum stlink_mode type)
        int res;
        struct stlink_usb_handle_s *h = handle;
 
-       assert(handle != NULL);
+       assert(handle);
 
        /* command with no reply, use a valid endpoint but zero size */
        stlink_usb_init_buffer(handle, h->rx_ep, 0);
@@ -1289,22 +1632,19 @@ static enum stlink_mode stlink_get_mode(enum hl_transports t)
                return STLINK_MODE_DEBUG_SWD;
        case HL_TRANSPORT_JTAG:
                return STLINK_MODE_DEBUG_JTAG;
-       case HL_TRANSPORT_SWIM:
-               return STLINK_MODE_DEBUG_SWIM;
        default:
                return STLINK_MODE_UNKNOWN;
        }
 }
 
 /** */
-static int stlink_usb_init_mode(void *handle, bool connect_under_reset, int initial_interface_speed)
+static int stlink_usb_exit_mode(void *handle)
 {
        int res;
        uint8_t mode;
        enum stlink_mode emode;
-       struct stlink_usb_handle_s *h = handle;
 
-       assert(handle != NULL);
+       assert(handle);
 
        res = stlink_usb_current_mode(handle, &mode);
 
@@ -1331,12 +1671,25 @@ static int stlink_usb_init_mode(void *handle, bool connect_under_reset, int init
                        break;
        }
 
-       if (emode != STLINK_MODE_UNKNOWN) {
-               res = stlink_usb_mode_leave(handle, emode);
+       if (emode != STLINK_MODE_UNKNOWN)
+               return stlink_usb_mode_leave(handle, emode);
 
-               if (res != ERROR_OK)
-                       return res;
-       }
+       return ERROR_OK;
+}
+
+/** */
+static int stlink_usb_init_mode(void *handle, bool connect_under_reset, int initial_interface_speed)
+{
+       int res;
+       uint8_t mode;
+       enum stlink_mode emode;
+       struct stlink_usb_handle_s *h = handle;
+
+       assert(handle);
+
+       res = stlink_usb_exit_mode(handle);
+       if (res != ERROR_OK)
+               return res;
 
        res = stlink_usb_current_mode(handle, &mode);
 
@@ -1369,7 +1722,7 @@ static int stlink_usb_init_mode(void *handle, bool connect_under_reset, int init
        LOG_DEBUG("MODE: 0x%02X", mode);
 
        /* set selected mode */
-       emode = stlink_get_mode(h->transport);
+       emode = h->st_mode;
 
        if (emode == STLINK_MODE_UNKNOWN) {
                LOG_ERROR("selected mode (transport) not supported");
@@ -1377,22 +1730,23 @@ static int stlink_usb_init_mode(void *handle, bool connect_under_reset, int init
        }
 
        /* set the speed before entering the mode, as the chip discovery phase should be done at this speed too */
-       if (h->transport == HL_TRANSPORT_JTAG) {
+       if (emode == STLINK_MODE_DEBUG_JTAG) {
                if (h->version.flags & STLINK_F_HAS_JTAG_SET_FREQ) {
                        stlink_dump_speed_map(stlink_khz_to_speed_map_jtag, ARRAY_SIZE(stlink_khz_to_speed_map_jtag));
                        stlink_speed(h, initial_interface_speed, false);
                }
-       } else if (h->transport == HL_TRANSPORT_SWD) {
+       } else if (emode == STLINK_MODE_DEBUG_SWD) {
                if (h->version.flags & STLINK_F_HAS_SWD_SET_FREQ) {
                        stlink_dump_speed_map(stlink_khz_to_speed_map_swd, ARRAY_SIZE(stlink_khz_to_speed_map_swd));
                        stlink_speed(h, initial_interface_speed, false);
                }
        }
 
-       if (h->version.jtag_api == STLINK_JTAG_API_V3) {
+       if (h->version.jtag_api == STLINK_JTAG_API_V3 &&
+                       (emode == STLINK_MODE_DEBUG_JTAG || emode == STLINK_MODE_DEBUG_SWD)) {
                struct speed_map map[STLINK_V3_MAX_FREQ_NB];
 
-               stlink_get_com_freq(h, (h->transport == HL_TRANSPORT_JTAG), map);
+               stlink_get_com_freq(h, (emode == STLINK_MODE_DEBUG_JTAG), map);
                stlink_dump_speed_map(map, ARRAY_SIZE(map));
                stlink_speed(h, initial_interface_speed, false);
        }
@@ -1447,7 +1801,7 @@ static int stlink_swim_status(void *handle)
 }
 /*
        the purpose of this function is unknown...
-       capabilites? anyway for swim v6 it returns
+       capabilities? anyway for swim v6 it returns
        0001020600000000
 */
 __attribute__((unused))
@@ -1541,7 +1895,7 @@ static int stlink_swim_generate_rst(void *handle)
 }
 
 /*
-       send resyncronize sequence
+       send resynchronize sequence
        swim is pulled low for 16us
        reply is 64 clks low
 */
@@ -1567,7 +1921,7 @@ static int stlink_swim_writebytes(void *handle, uint32_t addr, uint32_t len, con
        unsigned int datalen = 0;
        int cmdsize = STLINK_CMD_SIZE_V2;
 
-       if (len > STLINK_DATA_SIZE)
+       if (len > STLINK_SWIM_DATA_SIZE)
                return ERROR_FAIL;
 
        if (h->version.stlink == 1)
@@ -1600,7 +1954,7 @@ static int stlink_swim_readbytes(void *handle, uint32_t addr, uint32_t len, uint
        struct stlink_usb_handle_s *h = handle;
        int res;
 
-       if (len > STLINK_DATA_SIZE)
+       if (len > STLINK_SWIM_DATA_SIZE)
                return ERROR_FAIL;
 
        stlink_usb_init_buffer(handle, h->rx_ep, 0);
@@ -1630,10 +1984,10 @@ static int stlink_usb_idcode(void *handle, uint32_t *idcode)
        int res, offset;
        struct stlink_usb_handle_s *h = handle;
 
-       assert(handle != NULL);
+       assert(handle);
 
        /* there is no swim read core id cmd */
-       if (h->transport == HL_TRANSPORT_SWIM) {
+       if (h->st_mode == STLINK_MODE_DEBUG_SWIM) {
                *idcode = 0;
                return ERROR_OK;
        }
@@ -1668,7 +2022,7 @@ static int stlink_usb_v2_read_debug_reg(void *handle, uint32_t addr, uint32_t *v
        struct stlink_usb_handle_s *h = handle;
        int res;
 
-       assert(handle != NULL);
+       assert(handle);
 
        stlink_usb_init_buffer(handle, h->rx_ep, 8);
 
@@ -1689,7 +2043,7 @@ static int stlink_usb_write_debug_reg(void *handle, uint32_t addr, uint32_t val)
 {
        struct stlink_usb_handle_s *h = handle;
 
-       assert(handle != NULL);
+       assert(handle);
 
        stlink_usb_init_buffer(handle, h->rx_ep, 2);
 
@@ -1711,7 +2065,7 @@ static int stlink_usb_trace_read(void *handle, uint8_t *buf, size_t *size)
 {
        struct stlink_usb_handle_s *h = handle;
 
-       assert(handle != NULL);
+       assert(handle);
 
        if (h->trace.enabled && (h->version.flags & STLINK_F_HAS_TRACE)) {
                int res;
@@ -1726,7 +2080,7 @@ static int stlink_usb_trace_read(void *handle, uint8_t *buf, size_t *size)
                        return res;
 
                size_t bytes_avail = le_to_h_u16(h->databuf);
-               *size = bytes_avail < *size ? bytes_avail : *size - 1;
+               *size = bytes_avail < *size ? bytes_avail : *size;
 
                if (*size > 0) {
                        res = stlink_usb_read_trace(handle, buf, *size);
@@ -1762,24 +2116,11 @@ static enum target_state stlink_usb_state(void *handle)
        int res;
        struct stlink_usb_handle_s *h = handle;
 
-       assert(handle != NULL);
-
-       if (h->transport == HL_TRANSPORT_SWIM) {
-               res = stlink_usb_mode_enter(handle, stlink_get_mode(h->transport));
-               if (res != ERROR_OK)
-                       return TARGET_UNKNOWN;
-
-               res = stlink_swim_resync(handle);
-               if (res != ERROR_OK)
-                       return TARGET_UNKNOWN;
-
-               return ERROR_OK;
-       }
+       assert(handle);
 
        if (h->reconnect_pending) {
                LOG_INFO("Previous state query failed, trying to reconnect");
-               res = stlink_usb_mode_enter(handle, stlink_get_mode(h->transport));
-
+               res = stlink_usb_mode_enter(handle, h->st_mode);
                if (res != ERROR_OK)
                        return TARGET_UNKNOWN;
 
@@ -1817,9 +2158,9 @@ static int stlink_usb_assert_srst(void *handle, int srst)
 {
        struct stlink_usb_handle_s *h = handle;
 
-       assert(handle != NULL);
+       assert(handle);
 
-       if (h->transport == HL_TRANSPORT_SWIM)
+       if (h->st_mode == STLINK_MODE_DEBUG_SWIM)
                return stlink_swim_assert_reset(handle, srst);
 
        if (h->version.stlink == 1)
@@ -1840,7 +2181,7 @@ static void stlink_usb_trace_disable(void *handle)
        int res = ERROR_OK;
        struct stlink_usb_handle_s *h = handle;
 
-       assert(handle != NULL);
+       assert(handle);
 
        assert(h->version.flags & STLINK_F_HAS_TRACE);
 
@@ -1862,7 +2203,7 @@ static int stlink_usb_trace_enable(void *handle)
        int res;
        struct stlink_usb_handle_s *h = handle;
 
-       assert(handle != NULL);
+       assert(handle);
 
        if (h->version.flags & STLINK_F_HAS_TRACE) {
                stlink_usb_init_buffer(handle, h->rx_ep, 10);
@@ -1894,10 +2235,7 @@ static int stlink_usb_reset(void *handle)
        struct stlink_usb_handle_s *h = handle;
        int retval;
 
-       assert(handle != NULL);
-
-       if (h->transport == HL_TRANSPORT_SWIM)
-               return stlink_swim_generate_rst(handle);
+       assert(handle);
 
        stlink_usb_init_buffer(handle, h->rx_ep, 2);
 
@@ -1926,7 +2264,7 @@ static int stlink_usb_run(void *handle)
        int res;
        struct stlink_usb_handle_s *h = handle;
 
-       assert(handle != NULL);
+       assert(handle);
 
        if (h->version.jtag_api != STLINK_JTAG_API_V1) {
                res = stlink_usb_write_debug_reg(handle, DCB_DHCSR, DBGKEY|C_DEBUGEN);
@@ -1948,7 +2286,7 @@ static int stlink_usb_halt(void *handle)
        int res;
        struct stlink_usb_handle_s *h = handle;
 
-       assert(handle != NULL);
+       assert(handle);
 
        if (h->version.jtag_api != STLINK_JTAG_API_V1) {
                res = stlink_usb_write_debug_reg(handle, DCB_DHCSR, DBGKEY|C_HALT|C_DEBUGEN);
@@ -1969,7 +2307,7 @@ static int stlink_usb_step(void *handle)
 {
        struct stlink_usb_handle_s *h = handle;
 
-       assert(handle != NULL);
+       assert(handle);
 
        if (h->version.jtag_api != STLINK_JTAG_API_V1) {
                /* TODO: this emulates the v1 api, it should really use a similar auto mask isr
@@ -1993,7 +2331,7 @@ static int stlink_usb_read_regs(void *handle)
        int res;
        struct stlink_usb_handle_s *h = handle;
 
-       assert(handle != NULL);
+       assert(handle);
 
        stlink_usb_init_buffer(handle, h->rx_ep, 88);
 
@@ -2013,12 +2351,21 @@ static int stlink_usb_read_regs(void *handle)
 }
 
 /** */
-static int stlink_usb_read_reg(void *handle, int num, uint32_t *val)
+static int stlink_usb_read_reg(void *handle, unsigned int regsel, uint32_t *val)
 {
        int res;
        struct stlink_usb_handle_s *h = handle;
 
-       assert(handle != NULL);
+       assert(handle);
+
+       if (STLINK_REGSEL_IS_FPU(regsel) && !(h->version.flags & STLINK_F_HAS_FPU_REG)) {
+               res = stlink_usb_write_debug_reg(h, DCB_DCRSR, regsel & 0x7f);
+               if (res != ERROR_OK)
+                       return res;
+
+               /* FIXME: poll DHCSR.S_REGRDY before read DCRDR */
+               return stlink_usb_v2_read_debug_reg(h, DCB_DCRDR, val);
+       }
 
        stlink_usb_init_buffer(handle, h->rx_ep, h->version.jtag_api == STLINK_JTAG_API_V1 ? 4 : 8);
 
@@ -2027,7 +2374,7 @@ static int stlink_usb_read_reg(void *handle, int num, uint32_t *val)
                h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_APIV1_READREG;
        else
                h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_APIV2_READREG;
-       h->cmdbuf[h->cmdidx++] = num;
+       h->cmdbuf[h->cmdidx++] = regsel;
 
        if (h->version.jtag_api == STLINK_JTAG_API_V1) {
                res = stlink_usb_xfer_noerrcheck(handle, h->databuf, 4);
@@ -2045,11 +2392,20 @@ static int stlink_usb_read_reg(void *handle, int num, uint32_t *val)
 }
 
 /** */
-static int stlink_usb_write_reg(void *handle, int num, uint32_t val)
+static int stlink_usb_write_reg(void *handle, unsigned int regsel, uint32_t val)
 {
        struct stlink_usb_handle_s *h = handle;
 
-       assert(handle != NULL);
+       assert(handle);
+
+       if (STLINK_REGSEL_IS_FPU(regsel) && !(h->version.flags & STLINK_F_HAS_FPU_REG)) {
+               int res = stlink_usb_write_debug_reg(h, DCB_DCRDR, val);
+               if (res != ERROR_OK)
+                       return res;
+
+               return stlink_usb_write_debug_reg(h, DCB_DCRSR, DCRSR_WNR | (regsel & 0x7f));
+               /* FIXME: poll DHCSR.S_REGRDY after write DCRSR */
+       }
 
        stlink_usb_init_buffer(handle, h->rx_ep, 2);
 
@@ -2058,7 +2414,7 @@ static int stlink_usb_write_reg(void *handle, int num, uint32_t val)
                h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_APIV1_WRITEREG;
        else
                h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_APIV2_WRITEREG;
-       h->cmdbuf[h->cmdidx++] = num;
+       h->cmdbuf[h->cmdidx++] = regsel;
        h_u32_to_le(h->cmdbuf+h->cmdidx, val);
        h->cmdidx += 4;
 
@@ -2069,7 +2425,7 @@ static int stlink_usb_get_rw_status(void *handle)
 {
        struct stlink_usb_handle_s *h = handle;
 
-       assert(handle != NULL);
+       assert(handle);
 
        if (h->version.jtag_api == STLINK_JTAG_API_V1)
                return ERROR_OK;
@@ -2087,14 +2443,17 @@ static int stlink_usb_get_rw_status(void *handle)
 }
 
 /** */
-static int stlink_usb_read_mem8(void *handle, uint32_t addr, uint16_t len,
-                         uint8_t *buffer)
+static int stlink_usb_read_mem8(void *handle, uint8_t ap_num, uint32_t csw,
+               uint32_t addr, uint16_t len, uint8_t *buffer)
 {
        int res;
        uint16_t read_len = len;
        struct stlink_usb_handle_s *h = handle;
 
-       assert(handle != NULL);
+       assert(handle);
+
+       if ((ap_num != 0 || csw != 0) && !(h->version.flags & STLINK_F_HAS_CSW))
+               return ERROR_COMMAND_NOTFOUND;
 
        /* max 8 bit read/write is 64 bytes or 512 bytes for v3 */
        if (len > stlink_usb_block(h)) {
@@ -2110,6 +2469,9 @@ static int stlink_usb_read_mem8(void *handle, uint32_t addr, uint16_t len,
        h->cmdidx += 4;
        h_u16_to_le(h->cmdbuf+h->cmdidx, len);
        h->cmdidx += 2;
+       h->cmdbuf[h->cmdidx++] = ap_num;
+       h_u24_to_le(h->cmdbuf + h->cmdidx, csw >> 8);
+       h->cmdidx += 3;
 
        /* we need to fix read length for single bytes */
        if (read_len == 1)
@@ -2126,13 +2488,16 @@ static int stlink_usb_read_mem8(void *handle, uint32_t addr, uint16_t len,
 }
 
 /** */
-static int stlink_usb_write_mem8(void *handle, uint32_t addr, uint16_t len,
-                          const uint8_t *buffer)
+static int stlink_usb_write_mem8(void *handle, uint8_t ap_num, uint32_t csw,
+               uint32_t addr, uint16_t len, const uint8_t *buffer)
 {
        int res;
        struct stlink_usb_handle_s *h = handle;
 
-       assert(handle != NULL);
+       assert(handle);
+
+       if ((ap_num != 0 || csw != 0) && !(h->version.flags & STLINK_F_HAS_CSW))
+               return ERROR_COMMAND_NOTFOUND;
 
        /* max 8 bit read/write is 64 bytes or 512 bytes for v3 */
        if (len > stlink_usb_block(h)) {
@@ -2148,6 +2513,9 @@ static int stlink_usb_write_mem8(void *handle, uint32_t addr, uint16_t len,
        h->cmdidx += 4;
        h_u16_to_le(h->cmdbuf+h->cmdidx, len);
        h->cmdidx += 2;
+       h->cmdbuf[h->cmdidx++] = ap_num;
+       h_u24_to_le(h->cmdbuf + h->cmdidx, csw >> 8);
+       h->cmdidx += 3;
 
        res = stlink_usb_xfer_noerrcheck(handle, buffer, len);
 
@@ -2158,17 +2526,25 @@ static int stlink_usb_write_mem8(void *handle, uint32_t addr, uint16_t len,
 }
 
 /** */
-static int stlink_usb_read_mem16(void *handle, uint32_t addr, uint16_t len,
-                         uint8_t *buffer)
+static int stlink_usb_read_mem16(void *handle, uint8_t ap_num, uint32_t csw,
+               uint32_t addr, uint16_t len, uint8_t *buffer)
 {
        int res;
        struct stlink_usb_handle_s *h = handle;
 
-       assert(handle != NULL);
+       assert(handle);
 
        if (!(h->version.flags & STLINK_F_HAS_MEM_16BIT))
                return ERROR_COMMAND_NOTFOUND;
 
+       if ((ap_num != 0 || csw != 0) && !(h->version.flags & STLINK_F_HAS_CSW))
+               return ERROR_COMMAND_NOTFOUND;
+
+       if (len > STLINK_MAX_RW16_32) {
+               LOG_DEBUG("max buffer (%d) length exceeded", STLINK_MAX_RW16_32);
+               return ERROR_FAIL;
+       }
+
        /* data must be a multiple of 2 and half-word aligned */
        if (len % 2 || addr % 2) {
                LOG_DEBUG("Invalid data alignment");
@@ -2183,6 +2559,9 @@ static int stlink_usb_read_mem16(void *handle, uint32_t addr, uint16_t len,
        h->cmdidx += 4;
        h_u16_to_le(h->cmdbuf+h->cmdidx, len);
        h->cmdidx += 2;
+       h->cmdbuf[h->cmdidx++] = ap_num;
+       h_u24_to_le(h->cmdbuf + h->cmdidx, csw >> 8);
+       h->cmdidx += 3;
 
        res = stlink_usb_xfer_noerrcheck(handle, h->databuf, len);
 
@@ -2195,17 +2574,25 @@ static int stlink_usb_read_mem16(void *handle, uint32_t addr, uint16_t len,
 }
 
 /** */
-static int stlink_usb_write_mem16(void *handle, uint32_t addr, uint16_t len,
-                          const uint8_t *buffer)
+static int stlink_usb_write_mem16(void *handle, uint8_t ap_num, uint32_t csw,
+               uint32_t addr, uint16_t len, const uint8_t *buffer)
 {
        int res;
        struct stlink_usb_handle_s *h = handle;
 
-       assert(handle != NULL);
+       assert(handle);
 
        if (!(h->version.flags & STLINK_F_HAS_MEM_16BIT))
                return ERROR_COMMAND_NOTFOUND;
 
+       if ((ap_num != 0 || csw != 0) && !(h->version.flags & STLINK_F_HAS_CSW))
+               return ERROR_COMMAND_NOTFOUND;
+
+       if (len > STLINK_MAX_RW16_32) {
+               LOG_DEBUG("max buffer (%d) length exceeded", STLINK_MAX_RW16_32);
+               return ERROR_FAIL;
+       }
+
        /* data must be a multiple of 2 and half-word aligned */
        if (len % 2 || addr % 2) {
                LOG_DEBUG("Invalid data alignment");
@@ -2220,6 +2607,9 @@ static int stlink_usb_write_mem16(void *handle, uint32_t addr, uint16_t len,
        h->cmdidx += 4;
        h_u16_to_le(h->cmdbuf+h->cmdidx, len);
        h->cmdidx += 2;
+       h->cmdbuf[h->cmdidx++] = ap_num;
+       h_u24_to_le(h->cmdbuf + h->cmdidx, csw >> 8);
+       h->cmdidx += 3;
 
        res = stlink_usb_xfer_noerrcheck(handle, buffer, len);
 
@@ -2230,13 +2620,21 @@ static int stlink_usb_write_mem16(void *handle, uint32_t addr, uint16_t len,
 }
 
 /** */
-static int stlink_usb_read_mem32(void *handle, uint32_t addr, uint16_t len,
-                         uint8_t *buffer)
+static int stlink_usb_read_mem32(void *handle, uint8_t ap_num, uint32_t csw,
+               uint32_t addr, uint16_t len, uint8_t *buffer)
 {
        int res;
        struct stlink_usb_handle_s *h = handle;
 
-       assert(handle != NULL);
+       assert(handle);
+
+       if ((ap_num != 0 || csw != 0) && !(h->version.flags & STLINK_F_HAS_CSW))
+               return ERROR_COMMAND_NOTFOUND;
+
+       if (len > STLINK_MAX_RW16_32) {
+               LOG_DEBUG("max buffer (%d) length exceeded", STLINK_MAX_RW16_32);
+               return ERROR_FAIL;
+       }
 
        /* data must be a multiple of 4 and word aligned */
        if (len % 4 || addr % 4) {
@@ -2252,6 +2650,9 @@ static int stlink_usb_read_mem32(void *handle, uint32_t addr, uint16_t len,
        h->cmdidx += 4;
        h_u16_to_le(h->cmdbuf+h->cmdidx, len);
        h->cmdidx += 2;
+       h->cmdbuf[h->cmdidx++] = ap_num;
+       h_u24_to_le(h->cmdbuf + h->cmdidx, csw >> 8);
+       h->cmdidx += 3;
 
        res = stlink_usb_xfer_noerrcheck(handle, h->databuf, len);
 
@@ -2264,13 +2665,21 @@ static int stlink_usb_read_mem32(void *handle, uint32_t addr, uint16_t len,
 }
 
 /** */
-static int stlink_usb_write_mem32(void *handle, uint32_t addr, uint16_t len,
-                          const uint8_t *buffer)
+static int stlink_usb_write_mem32(void *handle, uint8_t ap_num, uint32_t csw,
+               uint32_t addr, uint16_t len, const uint8_t *buffer)
 {
        int res;
        struct stlink_usb_handle_s *h = handle;
 
-       assert(handle != NULL);
+       assert(handle);
+
+       if ((ap_num != 0 || csw != 0) && !(h->version.flags & STLINK_F_HAS_CSW))
+               return ERROR_COMMAND_NOTFOUND;
+
+       if (len > STLINK_MAX_RW16_32) {
+               LOG_DEBUG("max buffer (%d) length exceeded", STLINK_MAX_RW16_32);
+               return ERROR_FAIL;
+       }
 
        /* data must be a multiple of 4 and word aligned */
        if (len % 4 || addr % 4) {
@@ -2286,6 +2695,9 @@ static int stlink_usb_write_mem32(void *handle, uint32_t addr, uint16_t len,
        h->cmdidx += 4;
        h_u16_to_le(h->cmdbuf+h->cmdidx, len);
        h->cmdidx += 2;
+       h->cmdbuf[h->cmdidx++] = ap_num;
+       h_u24_to_le(h->cmdbuf + h->cmdidx, csw >> 8);
+       h->cmdidx += 3;
 
        res = stlink_usb_xfer_noerrcheck(handle, buffer, len);
 
@@ -2295,6 +2707,88 @@ static int stlink_usb_write_mem32(void *handle, uint32_t addr, uint16_t len,
        return stlink_usb_get_rw_status(handle);
 }
 
+static int stlink_usb_read_mem32_noaddrinc(void *handle, uint8_t ap_num, uint32_t csw,
+               uint32_t addr, uint16_t len, uint8_t *buffer)
+{
+       struct stlink_usb_handle_s *h = handle;
+
+       assert(handle != NULL);
+
+       if (!(h->version.flags & STLINK_F_HAS_MEM_RD_NO_INC))
+               return ERROR_COMMAND_NOTFOUND;
+
+       if (len > STLINK_MAX_RW16_32) {
+               LOG_DEBUG("max buffer (%d) length exceeded", STLINK_MAX_RW16_32);
+               return ERROR_FAIL;
+       }
+
+       /* data must be a multiple of 4 and word aligned */
+       if (len % 4 || addr % 4) {
+               LOG_DEBUG("Invalid data alignment");
+               return ERROR_TARGET_UNALIGNED_ACCESS;
+       }
+
+       stlink_usb_init_buffer(handle, h->rx_ep, len);
+
+       h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_COMMAND;
+       h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_READMEM_32BIT_NO_ADDR_INC;
+       h_u32_to_le(h->cmdbuf + h->cmdidx, addr);
+       h->cmdidx += 4;
+       h_u16_to_le(h->cmdbuf + h->cmdidx, len);
+       h->cmdidx += 2;
+       h->cmdbuf[h->cmdidx++] = ap_num;
+       h_u24_to_le(h->cmdbuf + h->cmdidx, csw >> 8);
+       h->cmdidx += 3;
+
+       int retval = stlink_usb_xfer_noerrcheck(handle, h->databuf, len);
+       if (retval != ERROR_OK)
+               return retval;
+
+       memcpy(buffer, h->databuf, len);
+
+       return stlink_usb_get_rw_status(handle);
+}
+
+static int stlink_usb_write_mem32_noaddrinc(void *handle, uint8_t ap_num, uint32_t csw,
+               uint32_t addr, uint16_t len, const uint8_t *buffer)
+{
+       struct stlink_usb_handle_s *h = handle;
+
+       assert(handle != NULL);
+
+       if (!(h->version.flags & STLINK_F_HAS_MEM_WR_NO_INC))
+               return ERROR_COMMAND_NOTFOUND;
+
+       if (len > STLINK_MAX_RW16_32) {
+               LOG_DEBUG("max buffer (%d) length exceeded", STLINK_MAX_RW16_32);
+               return ERROR_FAIL;
+       }
+
+       /* data must be a multiple of 4 and word aligned */
+       if (len % 4 || addr % 4) {
+               LOG_DEBUG("Invalid data alignment");
+               return ERROR_TARGET_UNALIGNED_ACCESS;
+       }
+
+       stlink_usb_init_buffer(handle, h->tx_ep, len);
+
+       h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_COMMAND;
+       h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_WRITEMEM_32BIT_NO_ADDR_INC;
+       h_u32_to_le(h->cmdbuf + h->cmdidx, addr);
+       h->cmdidx += 4;
+       h_u16_to_le(h->cmdbuf + h->cmdidx, len);
+       h->cmdidx += 2;
+       h->cmdbuf[h->cmdidx++] = ap_num;
+       h_u24_to_le(h->cmdbuf + h->cmdidx, csw >> 8);
+       h->cmdidx += 3;
+
+       int retval = stlink_usb_xfer_noerrcheck(handle, buffer, len);
+       if (retval != ERROR_OK)
+               return retval;
+
+       return stlink_usb_get_rw_status(handle);
+}
+
 static uint32_t stlink_max_block_size(uint32_t tar_autoincr_block, uint32_t address)
 {
        uint32_t max_tar_block = (tar_autoincr_block - ((tar_autoincr_block - 1) & address));
@@ -2303,8 +2797,8 @@ static uint32_t stlink_max_block_size(uint32_t tar_autoincr_block, uint32_t addr
        return max_tar_block;
 }
 
-static int stlink_usb_read_mem(void *handle, uint32_t addr, uint32_t size,
-               uint32_t count, uint8_t *buffer)
+static int stlink_usb_read_ap_mem(void *handle, uint8_t ap_num, uint32_t csw,
+               uint32_t addr, uint32_t size, uint32_t count, uint8_t *buffer)
 {
        int retval = ERROR_OK;
        uint32_t bytes_remaining;
@@ -2319,18 +2813,12 @@ static int stlink_usb_read_mem(void *handle, uint32_t addr, uint32_t size,
                size = 1;
 
        while (count) {
-
-               bytes_remaining = (size != 1) ? \
+               bytes_remaining = (size != 1) ?
                                stlink_max_block_size(h->max_mem_packet, addr) : stlink_usb_block(h);
 
                if (count < bytes_remaining)
                        bytes_remaining = count;
 
-               if (h->transport == HL_TRANSPORT_SWIM) {
-                       retval = stlink_swim_readbytes(handle, addr, bytes_remaining, buffer);
-                       if (retval != ERROR_OK)
-                               return retval;
-               } else
                /*
                 * all stlink support 8/32bit memory read/writes and only from
                 * stlink V2J26 there is support for 16 bit memory read/write.
@@ -2338,7 +2826,6 @@ static int stlink_usb_read_mem(void *handle, uint32_t addr, uint32_t size,
                 * as 8bit access.
                 */
                if (size != 1) {
-
                        /* When in jtag mode the stlink uses the auto-increment functionality.
                         * However it expects us to pass the data correctly, this includes
                         * alignment and any page boundaries. We already do this as part of the
@@ -2349,11 +2836,10 @@ static int stlink_usb_read_mem(void *handle, uint32_t addr, uint32_t size,
 
                        /* we first need to check for any unaligned bytes */
                        if (addr & (size - 1)) {
-
                                uint32_t head_bytes = size - (addr & (size - 1));
-                               retval = stlink_usb_read_mem8(handle, addr, head_bytes, buffer);
+                               retval = stlink_usb_read_mem8(handle, ap_num, csw, addr, head_bytes, buffer);
                                if (retval == ERROR_WAIT && retries < MAX_WAIT_RETRIES) {
-                                       usleep((1<<retries++) * 1000);
+                                       usleep((1 << retries++) * 1000);
                                        continue;
                                }
                                if (retval != ERROR_OK)
@@ -2365,16 +2851,17 @@ static int stlink_usb_read_mem(void *handle, uint32_t addr, uint32_t size,
                        }
 
                        if (bytes_remaining & (size - 1))
-                               retval = stlink_usb_read_mem(handle, addr, 1, bytes_remaining, buffer);
+                               retval = stlink_usb_read_ap_mem(handle, ap_num, csw, addr, 1, bytes_remaining, buffer);
                        else if (size == 2)
-                               retval = stlink_usb_read_mem16(handle, addr, bytes_remaining, buffer);
+                               retval = stlink_usb_read_mem16(handle, ap_num, csw, addr, bytes_remaining, buffer);
                        else
-                               retval = stlink_usb_read_mem32(handle, addr, bytes_remaining, buffer);
-               } else
-                       retval = stlink_usb_read_mem8(handle, addr, bytes_remaining, buffer);
+                               retval = stlink_usb_read_mem32(handle, ap_num, csw, addr, bytes_remaining, buffer);
+               } else {
+                       retval = stlink_usb_read_mem8(handle, ap_num, csw, addr, bytes_remaining, buffer);
+               }
 
                if (retval == ERROR_WAIT && retries < MAX_WAIT_RETRIES) {
-                       usleep((1<<retries++) * 1000);
+                       usleep((1 << retries++) * 1000);
                        continue;
                }
                if (retval != ERROR_OK)
@@ -2388,8 +2875,15 @@ static int stlink_usb_read_mem(void *handle, uint32_t addr, uint32_t size,
        return retval;
 }
 
-static int stlink_usb_write_mem(void *handle, uint32_t addr, uint32_t size,
-               uint32_t count, const uint8_t *buffer)
+static int stlink_usb_read_mem(void *handle, uint32_t addr, uint32_t size,
+               uint32_t count, uint8_t *buffer)
+{
+       return stlink_usb_read_ap_mem(handle, STLINK_HLA_AP_NUM, STLINK_HLA_CSW,
+                                                                 addr, size, count, buffer);
+}
+
+static int stlink_usb_write_ap_mem(void *handle, uint8_t ap_num, uint32_t csw,
+               uint32_t addr, uint32_t size, uint32_t count, const uint8_t *buffer)
 {
        int retval = ERROR_OK;
        uint32_t bytes_remaining;
@@ -2405,17 +2899,12 @@ static int stlink_usb_write_mem(void *handle, uint32_t addr, uint32_t size,
 
        while (count) {
 
-               bytes_remaining = (size != 1) ? \
+               bytes_remaining = (size != 1) ?
                                stlink_max_block_size(h->max_mem_packet, addr) : stlink_usb_block(h);
 
                if (count < bytes_remaining)
                        bytes_remaining = count;
 
-               if (h->transport == HL_TRANSPORT_SWIM) {
-                       retval = stlink_swim_writebytes(handle, addr, bytes_remaining, buffer);
-                       if (retval != ERROR_OK)
-                               return retval;
-               } else
                /*
                 * all stlink support 8/32bit memory read/writes and only from
                 * stlink V2J26 there is support for 16 bit memory read/write.
@@ -2436,7 +2925,7 @@ static int stlink_usb_write_mem(void *handle, uint32_t addr, uint32_t size,
                        if (addr & (size - 1)) {
 
                                uint32_t head_bytes = size - (addr & (size - 1));
-                               retval = stlink_usb_write_mem8(handle, addr, head_bytes, buffer);
+                               retval = stlink_usb_write_mem8(handle, ap_num, csw, addr, head_bytes, buffer);
                                if (retval == ERROR_WAIT && retries < MAX_WAIT_RETRIES) {
                                        usleep((1<<retries++) * 1000);
                                        continue;
@@ -2450,14 +2939,14 @@ static int stlink_usb_write_mem(void *handle, uint32_t addr, uint32_t size,
                        }
 
                        if (bytes_remaining & (size - 1))
-                               retval = stlink_usb_write_mem(handle, addr, 1, bytes_remaining, buffer);
+                               retval = stlink_usb_write_ap_mem(handle, ap_num, csw, addr, 1, bytes_remaining, buffer);
                        else if (size == 2)
-                               retval = stlink_usb_write_mem16(handle, addr, bytes_remaining, buffer);
+                               retval = stlink_usb_write_mem16(handle, ap_num, csw, addr, bytes_remaining, buffer);
                        else
-                               retval = stlink_usb_write_mem32(handle, addr, bytes_remaining, buffer);
+                               retval = stlink_usb_write_mem32(handle, ap_num, csw, addr, bytes_remaining, buffer);
 
                } else
-                       retval = stlink_usb_write_mem8(handle, addr, bytes_remaining, buffer);
+                       retval = stlink_usb_write_mem8(handle, ap_num, csw, addr, bytes_remaining, buffer);
                if (retval == ERROR_WAIT && retries < MAX_WAIT_RETRIES) {
                        usleep((1<<retries++) * 1000);
                        continue;
@@ -2473,6 +2962,13 @@ static int stlink_usb_write_mem(void *handle, uint32_t addr, uint32_t size,
        return retval;
 }
 
+static int stlink_usb_write_mem(void *handle, uint32_t addr, uint32_t size,
+               uint32_t count, const uint8_t *buffer)
+{
+       return stlink_usb_write_ap_mem(handle, STLINK_HLA_AP_NUM, STLINK_HLA_CSW,
+                                                                  addr, size, count, buffer);
+}
+
 /** */
 static int stlink_usb_override_target(const char *targetname)
 {
@@ -2481,17 +2977,20 @@ static int stlink_usb_override_target(const char *targetname)
 
 static int stlink_speed_swim(void *handle, int khz, bool query)
 {
+       int retval;
+
        /*
-                       we dont care what the khz rate is
                        we only have low and high speed...
                        before changing speed the SWIM_CSR HS bit
                        must be updated
         */
-       if (khz == 0)
-               stlink_swim_speed(handle, 0);
-       else
-               stlink_swim_speed(handle, 1);
-       return khz;
+       if (!query) {
+               retval = stlink_swim_speed(handle, (khz < SWIM_FREQ_HIGH) ? 0 : 1);
+               if (retval != ERROR_OK)
+                       LOG_ERROR("Unable to set adapter speed");
+       }
+
+       return (khz < SWIM_FREQ_HIGH) ? SWIM_FREQ_LOW : SWIM_FREQ_HIGH;
 }
 
 static int stlink_match_speed_map(const struct speed_map *map, unsigned int map_size, int khz, bool query)
@@ -2529,7 +3028,7 @@ static int stlink_match_speed_map(const struct speed_map *map, unsigned int map_
                match = false;
 
        if (!match && query) {
-               LOG_INFO("Unable to match requested speed %d kHz, using %d kHz", \
+               LOG_INFO("Unable to match requested speed %d kHz, using %d kHz",
                                khz, map[speed_index].speed);
        }
 
@@ -2582,7 +3081,7 @@ static int stlink_speed_jtag(void *handle, int khz, bool query)
        return stlink_khz_to_speed_map_jtag[speed_index].speed;
 }
 
-void stlink_dump_speed_map(const struct speed_map *map, unsigned int map_size)
+static void stlink_dump_speed_map(const struct speed_map *map, unsigned int map_size)
 {
        unsigned int i;
 
@@ -2675,17 +3174,16 @@ static int stlink_speed(void *handle, int khz, bool query)
        if (!handle)
                return khz;
 
-       switch (h->transport) {
-       case HL_TRANSPORT_SWIM:
+       switch (h->st_mode) {
+       case STLINK_MODE_DEBUG_SWIM:
                return stlink_speed_swim(handle, khz, query);
-               break;
-       case HL_TRANSPORT_SWD:
+       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 HL_TRANSPORT_JTAG:
+       case STLINK_MODE_DEBUG_JTAG:
                if (h->version.jtag_api == STLINK_JTAG_API_V3)
                        return stlink_speed_v3(handle, true, khz, query);
                else
@@ -2699,49 +3197,68 @@ static int stlink_speed(void *handle, int khz, bool query)
 }
 
 /** */
-static int stlink_usb_close(void *handle)
+static int stlink_usb_usb_close(void *handle)
 {
-       int res;
-       uint8_t mode;
-       enum stlink_mode emode;
        struct stlink_usb_handle_s *h = handle;
 
-       if (h && h->fd)
-               res = stlink_usb_current_mode(handle, &mode);
-       else
-               res = ERROR_FAIL;
-       /* do not exit if return code != ERROR_OK,
-          it prevents us from closing jtag_libusb */
-
-       if (res == ERROR_OK) {
-               /* try to exit current mode */
-               switch (mode) {
-                       case STLINK_DEV_DFU_MODE:
-                               emode = STLINK_MODE_DFU;
-                               break;
-                       case STLINK_DEV_DEBUG_MODE:
-                               emode = STLINK_MODE_DEBUG_SWD;
-                               break;
-                       case STLINK_DEV_SWIM_MODE:
-                               emode = STLINK_MODE_DEBUG_SWIM;
-                               break;
-                       case STLINK_DEV_BOOTLOADER_MODE:
-                       case STLINK_DEV_MASS_MODE:
-                       default:
-                               emode = STLINK_MODE_UNKNOWN;
-                               break;
+       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 (emode != STLINK_MODE_UNKNOWN)
-                       stlink_usb_mode_leave(handle, emode);
-                       /* do not check return code, it prevent
-                       us from closing jtag_libusb */
+               if (close_socket(h->tcp_backend_priv.fd) != 0)
+                       LOG_ERROR("error closing the socket, errno: %s", strerror(errno));
        }
 
-       if (h && h->fd)
-               jtag_libusb_close(h->fd);
+       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;
 
-       free(h);
+               stlink_usb_close(handle);
+
+               free(h);
+       }
 
        return ERROR_OK;
 }
@@ -2762,7 +3279,7 @@ static int stlink_usb_close(void *handle)
  *    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
  */
-char *stlink_usb_get_alternate_serial(libusb_device_handle *device,
+static char *stlink_usb_get_alternate_serial(struct libusb_device_handle *device,
                struct libusb_device_descriptor *dev_desc)
 {
        int usb_retval;
@@ -2814,7 +3331,7 @@ char *stlink_usb_get_alternate_serial(libusb_device_handle *device,
        /* else (len == 26) => buggy ST-Link */
 
        char *alternate_serial = malloc((STLINK_SERIAL_LEN + 1) * sizeof(char));
-       if (alternate_serial == NULL)
+       if (!alternate_serial)
                return NULL;
 
        for (unsigned int i = 0; i < STLINK_SERIAL_LEN; i += 2)
@@ -2826,27 +3343,16 @@ char *stlink_usb_get_alternate_serial(libusb_device_handle *device,
 }
 
 /** */
-static int stlink_usb_open(struct hl_interface_param_s *param, void **fd)
+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;
-       struct stlink_usb_handle_s *h;
 
-       LOG_DEBUG("stlink_usb_open");
+       h->cmdbuf = malloc(STLINK_SG_SIZE);
+       h->databuf = malloc(STLINK_DATA_SIZE);
 
-       h = calloc(1, sizeof(struct stlink_usb_handle_s));
-
-       if (h == 0) {
-               LOG_DEBUG("malloc failed");
+       if (!h->cmdbuf || !h->databuf)
                return ERROR_FAIL;
-       }
-
-       h->transport = param->transport;
-
-       for (unsigned i = 0; param->vid[i]; i++) {
-               LOG_DEBUG("transport: %d vid: 0x%04x pid: 0x%04x serial: %s",
-                         param->transport, param->vid[i], param->pid[i],
-                         param->serial ? param->serial : "");
-       }
 
        /*
          On certain host USB configurations(e.g. MacBook Air)
@@ -2858,26 +3364,26 @@ static int stlink_usb_open(struct hl_interface_param_s *param, void **fd)
          in order to become operational.
         */
        do {
-               if (jtag_libusb_open(param->vid, param->pid, param->serial,
-                               &h->fd, stlink_usb_get_alternate_serial) != ERROR_OK) {
+               if (jtag_libusb_open(param->vid, param->pid, adapter_get_required_serial(),
+                               &h->usb_backend_priv.fd, stlink_usb_get_alternate_serial) != ERROR_OK) {
                        LOG_ERROR("open failed");
-                       goto error_open;
+                       return ERROR_FAIL;
                }
 
-               jtag_libusb_set_configuration(h->fd, 0);
+               jtag_libusb_set_configuration(h->usb_backend_priv.fd, 0);
 
-               if (libusb_claim_interface(h->fd, 0) != ERROR_OK) {
+               if (libusb_claim_interface(h->usb_backend_priv.fd, 0) != ERROR_OK) {
                        LOG_DEBUG("claim interface failed");
-                       goto error_open;
+                       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->fd), &pid) != ERROR_OK) {
+               if (jtag_libusb_get_pid(libusb_get_device(h->usb_backend_priv.fd), &pid) != ERROR_OK) {
                        LOG_DEBUG("libusb_get_pid failed");
-                       goto error_open;
+                       return ERROR_FAIL;
                }
 
                /* wrap version for first read */
@@ -2890,6 +3396,7 @@ static int stlink_usb_open(struct hl_interface_param_s *param, void **fd)
                        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;
@@ -2917,21 +3424,21 @@ static int stlink_usb_open(struct hl_interface_param_s *param, void **fd)
                } else if (h->version.stlink == 1 ||
                           retry_count == 0) {
                        LOG_ERROR("read version failed");
-                       goto error_open;
+                       return ERROR_FAIL;
                } else {
-                       err = libusb_release_interface(h->fd, 0);
+                       err = libusb_release_interface(h->usb_backend_priv.fd, 0);
                        if (err != ERROR_OK) {
                                LOG_ERROR("release interface failed");
-                               goto error_open;
+                               return ERROR_FAIL;
                        }
 
-                       err = libusb_reset_device(h->fd);
+                       err = libusb_reset_device(h->usb_backend_priv.fd);
                        if (err != ERROR_OK) {
                                LOG_ERROR("reset device failed");
-                               goto error_open;
+                               return ERROR_FAIL;
                        }
 
-                       jtag_libusb_close(h->fd);
+                       jtag_libusb_close(h->usb_backend_priv.fd);
                        /*
                          Give the device one second to settle down and
                          reenumerate.
@@ -2941,150 +3448,433 @@ static int stlink_usb_open(struct hl_interface_param_s *param, void **fd)
                }
        } while (1);
 
-       /* check if mode is supported */
-       err = ERROR_OK;
-
-       switch (h->transport) {
-               case HL_TRANSPORT_SWD:
-                       if (h->version.jtag_api == STLINK_JTAG_API_V1)
-                               err = ERROR_FAIL;
-                       /* fall-through */
-               case HL_TRANSPORT_JTAG:
-                       if (h->version.jtag == 0)
-                               err = ERROR_FAIL;
-                       break;
-               case HL_TRANSPORT_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);
+       return ERROR_OK;
+}
 
-       if (err != ERROR_OK) {
-               LOG_ERROR("init mode failed (unable to connect to the target)");
-               goto error_open;
-       }
+/** */
+static int stlink_tcp_open(void *handle, struct hl_interface_param_s *param)
+{
+       struct stlink_usb_handle_s *h = handle;
+       int ret;
 
-       if (h->transport == HL_TRANSPORT_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_DATA_SIZE;
-               return ERROR_OK;
+       /* 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;
        }
 
-       /* get cpuid, so we can determine the max page size
-        * start with a safe default */
-       h->max_mem_packet = (1 << 10);
+       h->tcp_backend_priv.send_buf = malloc(STLINK_TCP_SEND_BUFFER_SIZE);
+       h->tcp_backend_priv.recv_buf = malloc(STLINK_TCP_RECV_BUFFER_SIZE);
 
-       uint8_t buffer[4];
-       err = stlink_usb_read_mem32(h, 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);
-               }
-       }
+       if (!h->tcp_backend_priv.send_buf || !h->tcp_backend_priv.recv_buf)
+               return ERROR_FAIL;
 
-       LOG_DEBUG("Using TAR autoincrement: %" PRIu32, h->max_mem_packet);
+       h->cmdbuf = &h->tcp_backend_priv.send_buf[8];
+       h->databuf = &h->tcp_backend_priv.recv_buf[4];
 
-       *fd = h;
+       /* configure directions */
+       h->rx_ep = STLINK_TCP_REQUEST_READ;
+       h->tx_ep = STLINK_TCP_REQUEST_WRITE;
+       h->trace_ep = STLINK_TCP_REQUEST_READ_SWO;
 
-       return ERROR_OK;
+       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;
 
-error_open:
-       stlink_usb_close(h);
+       if (h->tcp_backend_priv.fd == -1) {
+               LOG_ERROR("error creating the socket, errno: %s", strerror(errno));
+               return ERROR_FAIL;
+       }
 
-       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");
 
-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;
-       uint16_t presc;
+       LOG_DEBUG("socket : %x", h->tcp_backend_priv.fd);
 
-       if (enabled && (!(h->version.flags & STLINK_F_HAS_TRACE) ||
-                       pin_protocol != TPIU_PIN_PROTOCOL_ASYNC_UART)) {
-               LOG_ERROR("The attached ST-LINK version doesn't support this trace mode");
+       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;
        }
 
-       if (!enabled) {
-               stlink_usb_trace_disable(h);
-               return ERROR_OK;
+       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;
        }
 
-       if (*trace_freq > STLINK_TRACE_MAX_HZ) {
-               LOG_ERROR("ST-LINK doesn't support SWO frequency higher than %u",
-                         STLINK_TRACE_MAX_HZ);
+       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;
        }
 
-       stlink_usb_trace_disable(h);
+       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;
+       }
 
-       if (!*trace_freq)
-               *trace_freq = STLINK_TRACE_MAX_HZ;
+       h->tcp_backend_priv.connected = true;
 
-       presc = traceclkin_freq / *trace_freq;
+       LOG_INFO("connected to stlink-server");
 
-       if (traceclkin_freq % *trace_freq > 0)
-               presc++;
+       /* 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;
+       }
 
-       if (presc > TPIU_ACPR_MAX_SWOSCALER) {
-               LOG_ERROR("SWO frequency is not suitable. Please choose a different "
-                       "frequency.");
+       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;
        }
 
-       *prescaler = presc;
-       h->trace.source_hz = *trace_freq;
+       LOG_DEBUG("%d ST-LINK detected", connected_stlinks);
 
-       return stlink_usb_trace_enable(h);
-}
+       if (connected_stlinks > 255) {
+               LOG_WARNING("STLink server cannot handle more than 255 ST-LINK connected");
+               connected_stlinks = 255;
+       }
 
-/** */
-static int stlink_usb_init_access_port(void *handle, unsigned char ap_num)
-{
-       struct stlink_usb_handle_s *h = handle;
+       /* 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;
+       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 */
+               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;
+                       }
+               }
 
-       assert(handle != NULL);
+               if (!stlink_id_matched)
+                       continue;
 
-       if (!(h->version.flags & STLINK_F_HAS_AP_INIT))
-               return ERROR_COMMAND_NOTFOUND;
+               /* check the serial if specified */
+               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(adapter_serial) == STLINK_SERIAL_LEN / 2) {
+                               char fixed_serial[STLINK_SERIAL_LEN + 1];
 
-       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;
+                               for (unsigned int i = 0; i < STLINK_SERIAL_LEN; i += 2)
+                                       sprintf(fixed_serial + i, "%02X", adapter_serial[i / 2]);
 
-       return stlink_usb_xfer_errcheck(handle, h->databuf, 2);
-}
+                               fixed_serial[STLINK_SERIAL_LEN] = '\0';
+
+                               stlink_serial_matched = strcmp(fixed_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, adapter_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],
+                         adapter_get_required_serial() ? adapter_get_required_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 != NULL);
+       assert(handle);
 
        if (!(h->version.flags & STLINK_F_HAS_AP_INIT))
                return ERROR_COMMAND_NOTFOUND;
@@ -3095,7 +3885,59 @@ static int stlink_usb_close_access_port(void *handle, unsigned char ap_num)
        h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_APIV2_CLOSE_AP_DBG;
        h->cmdbuf[h->cmdidx++] = ap_num;
 
-       return stlink_usb_xfer_errcheck(handle, h->databuf, 2);
+       /* 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_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;
 }
 
 /** */
@@ -3105,7 +3947,7 @@ static int stlink_read_dap_register(void *handle, unsigned short dap_port,
        struct stlink_usb_handle_s *h = handle;
        int retval;
 
-       assert(handle != NULL);
+       assert(handle);
 
        if (!(h->version.flags & STLINK_F_HAS_DAP_REG))
                return ERROR_COMMAND_NOTFOUND;
@@ -3118,7 +3960,7 @@ static int stlink_read_dap_register(void *handle, unsigned short dap_port,
 
        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%x", dap_port, addr, *val);
+       LOG_DEBUG_IO("dap_port_read = %d, addr =  0x%x, value = 0x%" PRIx32, dap_port, addr, *val);
        return retval;
 }
 
@@ -3128,12 +3970,12 @@ static int stlink_write_dap_register(void *handle, unsigned short dap_port,
 {
        struct stlink_usb_handle_s *h = handle;
 
-       assert(handle != NULL);
+       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%x", dap_port, addr, val);
+       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;
@@ -3146,9 +3988,9 @@ static int stlink_write_dap_register(void *handle, unsigned short dap_port,
 /** */
 struct hl_layout_api_s stlink_usb_layout_api = {
        /** */
-       .open = stlink_usb_open,
+       .open = stlink_usb_hl_open,
        /** */
-       .close = stlink_usb_close,
+       .close = stlink_close,
        /** */
        .idcode = stlink_usb_idcode,
        /** */
@@ -3192,11 +4034,9 @@ 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;
 
-static int stlink_dap_op_queue_dp_read(struct adiv5_dap *dap, unsigned reg,
-               uint32_t *data);
-
 /** */
 static int stlink_dap_record_error(int error)
 {
@@ -3213,13 +4053,18 @@ static int stlink_dap_get_and_clear_error(void)
        return retval;
 }
 
-/** */
-static int stlink_dap_open_ap(unsigned short apsel)
+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 (!(stlink_dap_handle->version.flags & STLINK_F_HAS_AP_INIT))
+       if (!(h->version.flags & STLINK_F_HAS_AP_INIT))
                return ERROR_OK;
 
        if (apsel > DP_APSEL_MAX)
@@ -3228,15 +4073,21 @@ static int stlink_dap_open_ap(unsigned short apsel)
        if (test_bit(apsel, opened_ap))
                return ERROR_OK;
 
-       retval = stlink_usb_init_access_port(stlink_dap_handle, apsel);
+       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);
+       last_csw_default[apsel] = 0;
        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)
 {
@@ -3261,7 +4112,6 @@ static int stlink_dap_closeall_ap(void)
 static int stlink_dap_reinit_interface(void)
 {
        int retval;
-       enum stlink_mode mode;
 
        /*
         * On JTAG only, it should be enough to call stlink_usb_reset(). But on
@@ -3271,13 +4121,12 @@ static int stlink_dap_reinit_interface(void)
         * select the mode again.
         */
 
-       mode = stlink_get_mode(stlink_dap_param.transport);
        if (!stlink_dap_handle->reconnect_pending) {
                stlink_dap_handle->reconnect_pending = true;
-               stlink_usb_mode_leave(stlink_dap_handle, mode);
+               stlink_usb_mode_leave(stlink_dap_handle, stlink_dap_handle->st_mode);
        }
 
-       retval = stlink_usb_mode_enter(stlink_dap_handle, mode);
+       retval = stlink_usb_mode_enter(stlink_dap_handle, stlink_dap_handle->st_mode);
        if (retval != ERROR_OK)
                return retval;
 
@@ -3314,6 +4163,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) {
@@ -3324,7 +4175,7 @@ static int stlink_dap_op_connect(struct adiv5_dap *dap)
        retval = stlink_usb_idcode(stlink_dap_handle, &idcode);
        if (retval == ERROR_OK)
                LOG_INFO("%s %#8.8" PRIx32,
-                       (stlink_dap_handle->transport == HL_TRANSPORT_JTAG) ? "JTAG IDCODE" : "SWD DPIDR",
+                       (stlink_dap_handle->st_mode == STLINK_MODE_DEBUG_JTAG) ? "JTAG IDCODE" : "SWD DPIDR",
                        idcode);
        else
                dap->do_reconnect = true;
@@ -3355,8 +4206,7 @@ static int stlink_dap_op_send_sequence(struct adiv5_dap *dap, enum swd_special_s
 }
 
 /** */
-static int stlink_dap_op_queue_dp_read(struct adiv5_dap *dap, unsigned reg,
-               uint32_t *data)
+static int stlink_dap_dp_read(struct adiv5_dap *dap, unsigned int reg, uint32_t *data)
 {
        uint32_t dummy;
        int retval;
@@ -3367,13 +4217,9 @@ static int stlink_dap_op_queue_dp_read(struct adiv5_dap *dap, unsigned reg,
                        return ERROR_COMMAND_NOTFOUND;
                }
 
-       retval = stlink_dap_check_reconnect(dap);
-       if (retval != ERROR_OK)
-               return retval;
-
-       data = data ? : &dummy;
+       data = data ? data : &dummy;
        if (stlink_dap_handle->version.flags & STLINK_F_QUIRK_JTAG_DP_READ
-               && stlink_dap_handle->transport == HL_TRANSPORT_JTAG) {
+               && 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);
@@ -3385,12 +4231,11 @@ static int stlink_dap_op_queue_dp_read(struct adiv5_dap *dap, unsigned reg,
                                        STLINK_DEBUG_PORT_ACCESS, reg, data);
        }
 
-       return stlink_dap_record_error(retval);
+       return retval;
 }
 
 /** */
-static int stlink_dap_op_queue_dp_write(struct adiv5_dap *dap, unsigned reg,
-               uint32_t data)
+static int stlink_dap_dp_write(struct adiv5_dap *dap, unsigned int reg, uint32_t data)
 {
        int retval;
 
@@ -3406,54 +4251,40 @@ static int stlink_dap_op_queue_dp_write(struct adiv5_dap *dap, unsigned reg,
                data &= ~DP_SELECT_DPBANK;
        }
 
-       retval = stlink_dap_check_reconnect(dap);
-       if (retval != ERROR_OK)
-               return retval;
-
        /* 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 stlink_dap_record_error(retval);
+       return retval;
 }
 
 /** */
-static int stlink_dap_op_queue_ap_read(struct adiv5_ap *ap, unsigned reg,
-               uint32_t *data)
+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;
 
-       retval = stlink_dap_check_reconnect(dap);
-       if (retval != ERROR_OK)
-               return retval;
-
        if (reg != AP_REG_IDR) {
                retval = stlink_dap_open_ap(ap->ap_num);
                if (retval != ERROR_OK)
                        return retval;
        }
-       data = data ? : &dummy;
+       data = data ? data : &dummy;
        retval = stlink_read_dap_register(stlink_dap_handle, ap->ap_num, reg,
                                 data);
        dap->stlink_flush_ap_write = false;
-       return stlink_dap_record_error(retval);
+       return retval;
 }
 
 /** */
-static int stlink_dap_op_queue_ap_write(struct adiv5_ap *ap, unsigned reg,
-               uint32_t data)
+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_check_reconnect(dap);
-       if (retval != ERROR_OK)
-               return retval;
-
        retval = stlink_dap_open_ap(ap->ap_num);
        if (retval != ERROR_OK)
                return retval;
@@ -3461,7 +4292,7 @@ static int stlink_dap_op_queue_ap_write(struct adiv5_ap *ap, unsigned reg,
        retval = stlink_write_dap_register(stlink_dap_handle, ap->ap_num, reg,
                                data);
        dap->stlink_flush_ap_write = true;
-       return stlink_dap_record_error(retval);
+       return retval;
 }
 
 /** */
@@ -3471,37 +4302,328 @@ static int stlink_dap_op_queue_ap_abort(struct adiv5_dap *dap, uint8_t *ack)
        return ERROR_OK;
 }
 
-/** */
-static int stlink_dap_op_run(struct adiv5_dap *dap)
-{
-       uint32_t ctrlstat, pwrmask;
-       int retval, saved_retval;
-
-       /* Here no LOG_DEBUG. This is called continuously! */
+#define RW_MISC_CMD_ADDRESS     1
+#define RW_MISC_CMD_WRITE       2
+#define RW_MISC_CMD_READ        3
+#define RW_MISC_CMD_APNUM       5
 
-       /*
-        * ST-Link returns immediately after a DAP write, without waiting for it
-        * to complete.
-        * Run a dummy read to DP_RDBUFF, as suggested in
-        * http://infocenter.arm.com/help/topic/com.arm.doc.faqs/ka16363.html
-        */
-       if (dap->stlink_flush_ap_write) {
-               dap->stlink_flush_ap_write = false;
-               retval = stlink_dap_op_queue_dp_read(dap, DP_RDBUFF, NULL);
-               if (retval != ERROR_OK) {
-                       dap->do_reconnect = true;
-                       return retval;
+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;
                }
-       }
-
-       saved_retval = stlink_dap_get_and_clear_error();
 
-       retval = stlink_dap_op_queue_dp_read(dap, DP_CTRL_STAT, &ctrlstat);
-       if (retval != ERROR_OK) {
-               dap->do_reconnect = true;
+               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);
+       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;
+       };
+}
+
+/* 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;
+       /*
+        * 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)
+               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);
+       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, 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);
+
+       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;
+
+       *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;
+}
+
+/** */
+static int stlink_dap_run_finalize(struct adiv5_dap *dap)
+{
+       uint32_t ctrlstat, pwrmask;
+       int retval, saved_retval;
+
+       /* Here no LOG_DEBUG. This is called continuously! */
+
+       /*
+        * ST-Link returns immediately after a DAP write, without waiting for it
+        * to complete.
+        * Run a dummy read to DP_RDBUFF, as suggested in
+        * http://infocenter.arm.com/help/topic/com.arm.doc.faqs/ka16363.html
+        */
+       if (dap->stlink_flush_ap_write) {
+               dap->stlink_flush_ap_write = false;
+               retval = stlink_dap_dp_read(dap, DP_RDBUFF, NULL);
+               if (retval != ERROR_OK) {
+                       dap->do_reconnect = true;
+                       return retval;
+               }
        }
-       retval = stlink_dap_get_and_clear_error();
+
+       saved_retval = stlink_dap_get_and_clear_error();
+
+       retval = stlink_dap_dp_read(dap, DP_CTRL_STAT, &ctrlstat);
        if (retval != ERROR_OK) {
                LOG_ERROR("Fail reading CTRL/STAT register. Force reconnect");
                dap->do_reconnect = true;
@@ -3509,16 +4631,11 @@ static int stlink_dap_op_run(struct adiv5_dap *dap)
        }
 
        if (ctrlstat & SSTICKYERR) {
-               if (stlink_dap_param.transport == HL_TRANSPORT_JTAG)
-                       retval = stlink_dap_op_queue_dp_write(dap, DP_CTRL_STAT,
+               if (stlink_dap_handle->st_mode == STLINK_MODE_DEBUG_JTAG)
+                       retval = stlink_dap_dp_write(dap, DP_CTRL_STAT,
                                        ctrlstat & (dap->dp_ctrl_stat | SSTICKYERR));
                else
-                       retval = stlink_dap_op_queue_dp_write(dap, DP_ABORT, STKERRCLR);
-               if (retval != ERROR_OK) {
-                       dap->do_reconnect = true;
-                       return retval;
-               }
-               retval = stlink_dap_get_and_clear_error();
+                       retval = stlink_dap_dp_write(dap, DP_ABORT, STKERRCLR);
                if (retval != ERROR_OK) {
                        dap->do_reconnect = true;
                        return retval;
@@ -3533,6 +4650,12 @@ static int stlink_dap_op_run(struct adiv5_dap *dap)
        return saved_retval;
 }
 
+static int stlink_dap_op_queue_run(struct adiv5_dap *dap)
+{
+       stlink_dap_run_internal(dap);
+       return stlink_dap_run_finalize(dap);
+}
+
 /** */
 static void stlink_dap_op_quit(struct adiv5_dap *dap)
 {
@@ -3543,40 +4666,259 @@ static void stlink_dap_op_quit(struct adiv5_dap *dap)
                LOG_ERROR("Error closing APs");
 }
 
-static int stlink_dap_config_trace(bool enabled,
-               enum tpiu_pin_protocol pin_protocol, uint32_t port_size,
-               unsigned int *trace_freq, unsigned int traceclkin_freq,
-               uint16_t *prescaler)
+static int stlink_dap_op_queue_dp_read(struct adiv5_dap *dap, unsigned int reg,
+       uint32_t *data)
 {
-       return stlink_config_trace(stlink_dap_handle, enabled, pin_protocol,
-                                                          port_size, trace_freq, traceclkin_freq,
-                                                          prescaler);
+       if (stlink_dap_get_error() != ERROR_OK)
+               return ERROR_OK;
+
+       unsigned int i = stlink_dap_handle->queue_index++;
+       struct dap_queue *q = &stlink_dap_handle->queue[i];
+       q->cmd = CMD_DP_READ;
+       q->dp_r.reg = reg;
+       q->dp_r.dap = dap;
+       q->dp_r.p_data = data;
+
+       if (i == MAX_QUEUE_DEPTH - 1)
+               stlink_dap_run_internal(dap);
+
+       return ERROR_OK;
 }
 
-static int stlink_dap_trace_read(uint8_t *buf, size_t *size)
+static int stlink_dap_op_queue_dp_write(struct adiv5_dap *dap, unsigned int reg,
+       uint32_t data)
 {
-       return stlink_usb_trace_read(stlink_dap_handle, buf, size);
+       if (stlink_dap_get_error() != ERROR_OK)
+               return ERROR_OK;
+
+       unsigned int i = stlink_dap_handle->queue_index++;
+       struct dap_queue *q = &stlink_dap_handle->queue[i];
+       q->cmd = CMD_DP_WRITE;
+       q->dp_w.reg = reg;
+       q->dp_w.dap = dap;
+       q->dp_w.data = data;
+
+       if (i == MAX_QUEUE_DEPTH - 1)
+               stlink_dap_run_internal(dap);
+
+       return ERROR_OK;
 }
 
-/** */
-COMMAND_HANDLER(stlink_dap_serial_command)
+static int stlink_dap_op_queue_ap_read(struct adiv5_ap *ap, unsigned int reg,
+       uint32_t *data)
 {
-       LOG_DEBUG("stlink_dap_serial_command");
+       if (stlink_dap_get_error() != ERROR_OK)
+               return ERROR_OK;
 
-       if (CMD_ARGC != 1) {
-               LOG_ERROR("Expected exactly one argument for \"st-link serial <serial-number>\".");
-               return ERROR_COMMAND_SYNTAX_ERROR;
+       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
+        * 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)) {
+               /* 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) {
+                       stlink_dap_handle->queue_index = i;
+                       i--;
+                       q = prev_q;
+                       prev_q--;
+               }
+               /* 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 == MEM_AP_REG_CSW &&
+                               !prev_q->ap_w.changes_csw_default) {
+                       stlink_dap_handle->queue_index = i;
+                       q = prev_q;
+               }
+
+               switch (ap->csw_value & CSW_SIZE_MASK) {
+               case CSW_8BIT:
+                       q->cmd = CMD_MEM_AP_READ8;
+                       break;
+               case CSW_16BIT:
+                       q->cmd = CMD_MEM_AP_READ16;
+                       break;
+               case CSW_32BIT:
+                       q->cmd = CMD_MEM_AP_READ32;
+                       break;
+               default:
+                       LOG_ERROR("ST-Link: Unsupported CSW size %d", ap->csw_value & CSW_SIZE_MASK);
+                       stlink_dap_record_error(ERROR_FAIL);
+                       return ERROR_FAIL;
+               }
+
+               q->mem_ap.addr = (reg == 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;
+
+               /* force TAR and CSW update */
+               ap->tar_valid = false;
+               ap->csw_value = 0;
+       } else {
+               q->cmd = CMD_AP_READ;
+               q->ap_r.reg = reg;
+               q->ap_r.ap = ap;
+               q->ap_r.p_data = data;
+       }
+
+       if (i == MAX_QUEUE_DEPTH - 1)
+               stlink_dap_run_internal(ap->dap);
+
+       return ERROR_OK;
+}
+
+static int stlink_dap_op_queue_ap_write(struct adiv5_ap *ap, unsigned int reg,
+       uint32_t data)
+{
+       if (stlink_dap_get_error() != ERROR_OK)
+               return ERROR_OK;
+
+       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
+        * 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)) {
+               /* 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) {
+                       stlink_dap_handle->queue_index = i;
+                       i--;
+                       q = prev_q;
+                       prev_q--;
+               }
+               /* 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 == MEM_AP_REG_CSW &&
+                               !prev_q->ap_w.changes_csw_default) {
+                       stlink_dap_handle->queue_index = i;
+                       q = prev_q;
+               }
+
+               switch (ap->csw_value & CSW_SIZE_MASK) {
+               case CSW_8BIT:
+                       q->cmd = CMD_MEM_AP_WRITE8;
+                       break;
+               case CSW_16BIT:
+                       q->cmd = CMD_MEM_AP_WRITE16;
+                       break;
+               case CSW_32BIT:
+                       q->cmd = CMD_MEM_AP_WRITE32;
+                       break;
+               default:
+                       LOG_ERROR("ST-Link: Unsupported CSW size %d", ap->csw_value & CSW_SIZE_MASK);
+                       stlink_dap_record_error(ERROR_FAIL);
+                       return ERROR_FAIL;
+               }
+
+               q->mem_ap.addr = (reg == 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;
+
+               /* force TAR and CSW update */
+               ap->tar_valid = false;
+               ap->csw_value = 0;
+       } else {
+               q->cmd = CMD_AP_WRITE;
+               q->ap_w.reg = reg;
+               q->ap_w.ap = ap;
+               q->ap_w.data = data;
+               if (reg == MEM_AP_REG_CSW && ap->csw_default != last_csw_default[ap->ap_num]) {
+                       q->ap_w.changes_csw_default = true;
+                       last_csw_default[ap->ap_num] = ap->csw_default;
+               } else {
+                       q->ap_w.changes_csw_default = false;
+               }
        }
 
-       if (stlink_dap_param.serial) {
-               LOG_WARNING("Command \"st-link serial\" already used. Replacing previous value");
-               free((void *)stlink_dap_param.serial);
+       if (i == MAX_QUEUE_DEPTH - 1)
+               stlink_dap_run_internal(ap->dap);
+
+       return ERROR_OK;
+}
+
+static int stlink_swim_op_srst(void)
+{
+       return stlink_swim_generate_rst(stlink_dap_handle);
+}
+
+static int stlink_swim_op_read_mem(uint32_t addr, uint32_t size,
+                                                                  uint32_t count, uint8_t *buffer)
+{
+       int retval;
+       uint32_t bytes_remaining;
+
+       LOG_DEBUG_IO("read at 0x%08" PRIx32 " len %" PRIu32 "*0x%08" PRIx32, addr, size, count);
+       count *= size;
+
+       while (count) {
+               bytes_remaining = (count > STLINK_SWIM_DATA_SIZE) ? STLINK_SWIM_DATA_SIZE : count;
+               retval = stlink_swim_readbytes(stlink_dap_handle, addr, bytes_remaining, buffer);
+               if (retval != ERROR_OK)
+                       return retval;
+
+               buffer += bytes_remaining;
+               addr += bytes_remaining;
+               count -= bytes_remaining;
        }
 
-       stlink_dap_param.serial = strdup(CMD_ARGV[0]);
        return ERROR_OK;
 }
 
+static int stlink_swim_op_write_mem(uint32_t addr, uint32_t size,
+                                                                       uint32_t count, const uint8_t *buffer)
+{
+       int retval;
+       uint32_t bytes_remaining;
+
+       LOG_DEBUG_IO("write at 0x%08" PRIx32 " len %" PRIu32 "*0x%08" PRIx32, addr, size, count);
+       count *= size;
+
+       while (count) {
+               bytes_remaining = (count > STLINK_SWIM_DATA_SIZE) ? STLINK_SWIM_DATA_SIZE : count;
+               retval = stlink_swim_writebytes(stlink_dap_handle, addr, bytes_remaining, buffer);
+               if (retval != ERROR_OK)
+                       return retval;
+
+               buffer += bytes_remaining;
+               addr += bytes_remaining;
+               count -= bytes_remaining;
+       }
+
+       return ERROR_OK;
+}
+
+static int stlink_swim_op_reconnect(void)
+{
+       int retval;
+
+       retval = stlink_usb_mode_enter(stlink_dap_handle, STLINK_MODE_DEBUG_SWIM);
+       if (retval != ERROR_OK)
+               return retval;
+
+       return stlink_swim_resync(stlink_dap_handle);
+}
+
+static int stlink_dap_config_trace(bool enabled,
+               enum tpiu_pin_protocol pin_protocol, uint32_t port_size,
+               unsigned int *trace_freq, unsigned int traceclkin_freq,
+               uint16_t *prescaler)
+{
+       return stlink_config_trace(stlink_dap_handle, enabled, pin_protocol,
+                                                          port_size, trace_freq, traceclkin_freq,
+                                                          prescaler);
+}
+
+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_vid_pid)
 {
@@ -3602,15 +4944,71 @@ COMMAND_HANDLER(stlink_dap_vid_pid)
        return ERROR_OK;
 }
 
+/** */
+COMMAND_HANDLER(stlink_dap_backend_command)
+{
+       /* default values */
+       bool use_stlink_tcp = false;
+       uint16_t stlink_tcp_port = 7184;
+
+       if (CMD_ARGC == 0 || CMD_ARGC > 2)
+               return ERROR_COMMAND_SYNTAX_ERROR;
+       else if (strcmp(CMD_ARGV[0], "usb") == 0) {
+               if (CMD_ARGC > 1)
+                       return ERROR_COMMAND_SYNTAX_ERROR;
+               /* else use_stlink_tcp = false (already the case ) */
+       } else if (strcmp(CMD_ARGV[0], "tcp") == 0) {
+               use_stlink_tcp = true;
+               if (CMD_ARGC == 2)
+                       COMMAND_PARSE_NUMBER(u16, CMD_ARGV[1], stlink_tcp_port);
+       } else
+               return ERROR_COMMAND_SYNTAX_ERROR;
+
+       stlink_dap_param.use_stlink_tcp = use_stlink_tcp;
+       stlink_dap_param.stlink_tcp_port = stlink_tcp_port;
+
+       return ERROR_OK;
+}
+
+#define BYTES_PER_LINE 16
+COMMAND_HANDLER(stlink_dap_cmd_command)
+{
+       unsigned int rx_n, tx_n;
+       struct stlink_usb_handle_s *h = stlink_dap_handle;
+
+       if (CMD_ARGC < 2)
+               return ERROR_COMMAND_SYNTAX_ERROR;
+
+       COMMAND_PARSE_NUMBER(uint, CMD_ARGV[0], rx_n);
+       tx_n = CMD_ARGC - 1;
+       if (tx_n > STLINK_SG_SIZE || rx_n > STLINK_DATA_SIZE) {
+               LOG_ERROR("max %x byte sent and %d received", STLINK_SG_SIZE, STLINK_DATA_SIZE);
+               return ERROR_COMMAND_SYNTAX_ERROR;
+       }
+
+       stlink_usb_init_buffer(h, h->rx_ep, rx_n);
+
+       for (unsigned int i = 0; i < tx_n; i++) {
+               uint8_t byte;
+               COMMAND_PARSE_NUMBER(u8, CMD_ARGV[i + 1], byte);
+               h->cmdbuf[h->cmdidx++] = byte;
+       }
+
+       int retval = stlink_usb_xfer_noerrcheck(h, h->databuf, rx_n);
+       if (retval != ERROR_OK) {
+               LOG_ERROR("Error %d", retval);
+               return retval;
+       }
+
+       for (unsigned int i = 0; i < rx_n; i++)
+               command_print_sameline(CMD, "0x%02x%c", h->databuf[i],
+                       ((i == (rx_n - 1)) || ((i % BYTES_PER_LINE) == (BYTES_PER_LINE - 1))) ? '\n' : ' ');
+
+       return ERROR_OK;
+}
+
 /** */
 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,
@@ -3618,6 +5016,20 @@ static const struct command_registration stlink_dap_subcommand_handlers[] = {
                .help = "USB VID and PID of the adapter",
                .usage = "(vid pid)+",
        },
+       {
+               .name = "backend",
+               .handler = &stlink_dap_backend_command,
+               .mode = COMMAND_CONFIG,
+               .help = "select which ST-Link backend to use",
+               .usage = "usb | tcp [port]",
+       },
+       {
+               .name = "cmd",
+               .handler = stlink_dap_cmd_command,
+               .mode = COMMAND_EXEC,
+               .help = "send arbitrary command",
+               .usage = "rx_n (tx_byte)+",
+       },
        COMMAND_REGISTRATION_DONE
 };
 
@@ -3637,6 +5049,7 @@ static const struct command_registration stlink_dap_command_handlers[] = {
 static int stlink_dap_init(void)
 {
        enum reset_types jtag_reset_config = jtag_get_reset_config();
+       enum stlink_mode mode;
        int retval;
 
        LOG_DEBUG("stlink_dap_init()");
@@ -3649,19 +5062,22 @@ static int stlink_dap_init(void)
        }
 
        if (transport_is_dapdirect_swd())
-               stlink_dap_param.transport = HL_TRANSPORT_SWD;
+               mode = STLINK_MODE_DEBUG_SWD;
        else if (transport_is_dapdirect_jtag())
-               stlink_dap_param.transport = HL_TRANSPORT_JTAG;
+               mode = STLINK_MODE_DEBUG_JTAG;
+       else if (transport_is_swim())
+               mode = STLINK_MODE_DEBUG_SWIM;
        else {
                LOG_ERROR("Unsupported transport");
                return ERROR_FAIL;
        }
 
-       retval = stlink_usb_open(&stlink_dap_param, (void **)&stlink_dap_handle);
+       retval = stlink_open(&stlink_dap_param, mode, (void **)&stlink_dap_handle);
        if (retval != ERROR_OK)
                return retval;
 
-       if (!(stlink_dap_handle->version.flags & STLINK_F_HAS_DAP_REG)) {
+       if ((mode != STLINK_MODE_DEBUG_SWIM) &&
+               !(stlink_dap_handle->version.flags & STLINK_F_HAS_DAP_REG)) {
                LOG_ERROR("ST-Link version does not support DAP direct transport");
                return ERROR_FAIL;
        }
@@ -3673,10 +5089,7 @@ static int stlink_dap_quit(void)
 {
        LOG_DEBUG("stlink_dap_quit()");
 
-       free((void *)stlink_dap_param.serial);
-       stlink_dap_param.serial = NULL;
-
-       return stlink_usb_close(stlink_dap_handle);
+       return stlink_close(stlink_dap_handle);
 }
 
 /** */
@@ -3728,12 +5141,19 @@ static const struct dap_ops stlink_dap_ops = {
        .queue_ap_read = stlink_dap_op_queue_ap_read,
        .queue_ap_write = stlink_dap_op_queue_ap_write,
        .queue_ap_abort = stlink_dap_op_queue_ap_abort,
-       .run = stlink_dap_op_run,
+       .run = stlink_dap_op_queue_run,
        .sync = NULL, /* optional */
        .quit = stlink_dap_op_quit, /* optional */
 };
 
-static const char *const stlink_dap_transport[] = { "dapdirect_jtag", "dapdirect_swd", NULL };
+static const struct swim_driver stlink_swim_ops = {
+       .srst = stlink_swim_op_srst,
+       .read_mem = stlink_swim_op_read_mem,
+       .write_mem = stlink_swim_op_write_mem,
+       .reconnect = stlink_swim_op_reconnect,
+};
+
+static const char *const stlink_dap_transport[] = { "dapdirect_swd", "dapdirect_jtag", "swim", NULL };
 
 struct adapter_driver stlink_dap_adapter_driver = {
        .name = "st-link",
@@ -3751,4 +5171,5 @@ struct adapter_driver stlink_dap_adapter_driver = {
 
        .dap_jtag_ops = &stlink_dap_ops,
        .dap_swd_ops = &stlink_dap_ops,
+       .swim_ops = &stlink_swim_ops,
 };