This is an automated email from Gerrit.

"Brian Kuschak <bkusc...@gmail.com>" just uploaded a new patch set to Gerrit, 
which you can find at https://review.openocd.org/c/openocd/+/8973

-- gerrit

commit 76734b1388535aeff1a87715880b0c6e3945970c
Author: Brian Kuschak <bkusc...@gmail.com>
Date:   Wed Jun 11 12:41:04 2025 +0800

    jtag/drivers/cmsis_dap: add new backend cmsis_dap_tcp
    
    Create a new backend for cmsis_dap driver that allows CMSIS-DAP protocol
    to run over TCP/IP instead of USB.
    
    An implementation of the firmware for an SWD programmer that uses this
    cmsis_dap_tcp protocol can be found at the link below.
    https://github.com/bkuschak/cmsis_dap_tcp_esp32
    
    Using this cmsis_dap_tcp backend with the firmware above, flashing a
    64KB image to an STM32 completes in about 8 seconds.
    
    Change-Id: I6e3e45016bd16ef2259561b1046788f5536b0687
    Signed-off-by: Brian Kuschak <bkusc...@gmail.com>

diff --git a/configure.ac b/configure.ac
index 4b94716299..22e5e7bd05 100644
--- a/configure.ac
+++ b/configure.ac
@@ -206,6 +206,9 @@ m4_define([HOST_ARM_OR_AARCH64_BITBANG_ADAPTERS],
        [[imx_gpio], [Bitbanging on NXP IMX processors], [IMX_GPIO]],
        [[am335xgpio], [Bitbanging on AM335x (as found in Beaglebones)], 
[AM335XGPIO]]])
 
+m4_define([CMSIS_DAP_TCP_ADAPTER],
+       [[[cmsis_dap_tcp], [CMSIS-DAP v2 compliant dongle (TCP)], 
[CMSIS_DAP_TCP]]])
+
 # The word 'Adapter' in "Dummy Adapter" below must begin with a capital letter
 # because there is an M4 macro called 'adapter'.
 m4_define([DUMMY_ADAPTER],
@@ -338,7 +341,8 @@ AC_ARG_ADAPTERS([
   JTAG_VPI_ADAPTER,
   RSHIM_ADAPTER,
   PCIE_ADAPTERS,
-  LIBJAYLINK_ADAPTERS
+  LIBJAYLINK_ADAPTERS,
+  CMSIS_DAP_TCP_ADAPTER
   ],[auto])
 
 AC_ARG_ADAPTERS([
@@ -648,6 +652,7 @@ PROCESS_ADAPTERS([LIBFTDI_USB1_ADAPTERS], ["x$use_libftdi" 
= "xyes" -a "x$use_li
 PROCESS_ADAPTERS([LIBGPIOD_ADAPTERS], ["x$use_libgpiod" = "xyes"], [Linux 
libgpiod])
 PROCESS_ADAPTERS([SYSFSGPIO_ADAPTER], ["x$is_linux" = "xyes"], [Linux sysfs])
 PROCESS_ADAPTERS([REMOTE_BITBANG_ADAPTER], [true], [unused])
+PROCESS_ADAPTERS([CMSIS_DAP_TCP_ADAPTER], [true], [unused])
 PROCESS_ADAPTERS([LIBJAYLINK_ADAPTERS], ["x$use_internal_libjaylink" = "xyes" 
-o "x$use_libjaylink" = "xyes"], [libjaylink-0.2])
 PROCESS_ADAPTERS([PCIE_ADAPTERS], ["x$is_linux" = "xyes" -a 
"x$ac_cv_header_linux_pci_h" = "xyes"], [Linux build])
 PROCESS_ADAPTERS([SERIAL_PORT_ADAPTERS], ["x$can_build_buspirate" = "xyes"],
@@ -854,6 +859,7 @@ m4_foreach([adapter], [USB1_ADAPTERS,
        AMTJTAGACCEL_ADAPTER,
        HOST_ARM_BITBANG_ADAPTERS,
        HOST_ARM_OR_AARCH64_BITBANG_ADAPTERS,
+       CMSIS_DAP_TCP_ADAPTER,
        DUMMY_ADAPTER,
        OPTIONAL_LIBRARIES,
        COVERAGE],
diff --git a/src/helper/replacements.h b/src/helper/replacements.h
index ecc0e5e955..95322e96ac 100644
--- a/src/helper/replacements.h
+++ b/src/helper/replacements.h
@@ -225,6 +225,20 @@ static inline int socket_select(int max_fd,
 #endif
 }
 
+static inline int socket_timeout(int fd, unsigned long timeout_msec)
+{
+#ifdef _WIN32
+    DWORD timeout = timeout_msec;
+    return setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&timeout,
+            sizeof(timeout));
+#else
+    struct timeval tv;
+    tv.tv_sec = timeout_msec / 1000;
+    tv.tv_usec = (timeout_msec % 1000) * 1000;
+    return setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, 
sizeof(tv));
+#endif
+}
+
 #ifndef HAVE_ELF_H
 
 typedef uint32_t Elf32_Addr;
diff --git a/src/jtag/drivers/Makefile.am b/src/jtag/drivers/Makefile.am
index b0dd8e3ad1..0942777145 100644
--- a/src/jtag/drivers/Makefile.am
+++ b/src/jtag/drivers/Makefile.am
@@ -198,6 +198,10 @@ if !CMSIS_DAP_HID
 DRIVERFILES += %D%/cmsis_dap.c
 endif
 endif
+if CMSIS_DAP_TCP
+DRIVERFILES += %D%/cmsis_dap_tcp.c
+DRIVERFILES += %D%/cmsis_dap.c
+endif
 if IMX_GPIO
 DRIVERFILES += %D%/imx_gpio.c
 endif
diff --git a/src/jtag/drivers/cmsis_dap.c b/src/jtag/drivers/cmsis_dap.c
index 2bfcfcc2b0..7f8eabeb43 100644
--- a/src/jtag/drivers/cmsis_dap.c
+++ b/src/jtag/drivers/cmsis_dap.c
@@ -52,9 +52,16 @@ const struct cmsis_dap_backend cmsis_dap_hid_backend = {
 };
 #endif
 
+#if BUILD_CMSIS_DAP_TCP == 0
+const struct cmsis_dap_backend cmsis_dap_tcp_backend = {
+       .name = "tcp"
+};
+#endif
+
 static const struct cmsis_dap_backend *const cmsis_dap_backends[] = {
        &cmsis_dap_usb_backend,
        &cmsis_dap_hid_backend,
+       &cmsis_dap_tcp_backend,
 };
 
 /* USB Config */
@@ -2272,8 +2279,8 @@ static const struct command_registration 
cmsis_dap_subcommand_handlers[] = {
                .name = "backend",
                .handler = &cmsis_dap_handle_backend_command,
                .mode = COMMAND_CONFIG,
-               .help = "set the communication backend to use (USB bulk or 
HID).",
-               .usage = "(auto | usb_bulk | hid)",
+               .help = "set the communication backend to use (USB bulk or HID, 
or TCP).",
+               .usage = "(auto | usb_bulk | hid | tcp)",
        },
        {
                .name = "quirk",
@@ -2290,6 +2297,15 @@ static const struct command_registration 
cmsis_dap_subcommand_handlers[] = {
                .help = "USB bulk backend-specific commands",
                .usage = "<cmd>",
        },
+#endif
+#if BUILD_CMSIS_DAP_TCP
+       {
+               .name = "tcp",
+               .chain = cmsis_dap_tcp_subcommand_handlers,
+               .mode = COMMAND_ANY,
+               .help = "TCP backend-specific commands",
+               .usage = "<cmd>",
+       },
 #endif
        COMMAND_REGISTRATION_DONE
 };
diff --git a/src/jtag/drivers/cmsis_dap.h b/src/jtag/drivers/cmsis_dap.h
index aded0e54a3..1052bd3589 100644
--- a/src/jtag/drivers/cmsis_dap.h
+++ b/src/jtag/drivers/cmsis_dap.h
@@ -78,7 +78,9 @@ struct cmsis_dap_backend {
 
 extern const struct cmsis_dap_backend cmsis_dap_hid_backend;
 extern const struct cmsis_dap_backend cmsis_dap_usb_backend;
+extern const struct cmsis_dap_backend cmsis_dap_tcp_backend;
 extern const struct command_registration cmsis_dap_usb_subcommand_handlers[];
+extern const struct command_registration cmsis_dap_tcp_subcommand_handlers[];
 
 #define REPORT_ID_SIZE   1
 
diff --git a/src/jtag/drivers/cmsis_dap_tcp.c b/src/jtag/drivers/cmsis_dap_tcp.c
new file mode 100644
index 0000000000..b44df9a10e
--- /dev/null
+++ b/src/jtag/drivers/cmsis_dap_tcp.c
@@ -0,0 +1,351 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+/***************************************************************************
+ *   Provides CMSIS-DAP protocol over a TCP/IP socket.                     *
+ *   UART and SWO are currently unsupported.                               *
+ *                                                                         *
+ *   Copyright (C) 2025 by Brian Kuschak <bkusc...@gmail.com>              *
+ *                                                                         *
+ *   Adapted from cmsis_dap_usb_hid.c. Copyright (C) 2013-2018 by:         *
+ *     Mickaƫl Thomas <micka...@gmail.com>                                 *
+ *     Maksym Hilliaka <o...@frozen-team.com>                              *
+ *     Phillip Pearson <p...@myelin.co.nz>                                   *
+ *     Paul Fertser <fercer...@gmail.com>                                  *
+ *     mike brown <m...@theshedworks.org.uk>                               *
+ *     Spencer Oliver <s...@spen-soft.co.uk>                               *
+ *                                                                         *
+ *   Example usage:                                                        *
+ *     adapter driver cmsis-dap                                            *
+ *     cmsis-dap backend tcp                                               *
+ *     cmsis-dap tcp host 192.168.1.4                                      *
+ *     cmsis-dap tcp dap_port 4441                                         *
+ *     transport select swd                                                *
+ *     adapter speed 2000                                                  *
+ *                                                                         *
+ ***************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <hidapi.h>
+#include <netdb.h>
+#include <netinet/tcp.h>
+#include <stdbool.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+
+#include "helper/command.h"
+#include "helper/log.h"
+#include "helper/replacements.h"
+#include "helper/system.h"
+#include "cmsis_dap.h"
+
+#define STRINGIFY(x) #x
+
+// If the protocol changes in the future, the SIGNATURE should also be changed.
+#define DAP_PKT_HDR_SIGNATURE   0x00504144  // "DAP"
+#define DAP_PKT_TYPE_REQUEST    0x01
+#define DAP_PKT_TYPE_RESPONSE   0x02
+
+#define CMSIS_DAP_TCP_PORT      4441        // Default. Can be overridden.
+#define CMSIS_DAP_PACKET_SIZE   1024        // Max payload size not including
+                                            // header.
+
+// CMSIS-DAP requests are variable length. With CMSIS-DAP over USB, the
+// transfer sizes are preserved by the USB stack. However, TCP/IP is stream
+// oriented so we perform our own packetization to preserve the boundaries
+// between each request. This short header is prepended to each CMSIS-DAP
+// request and response before being sent over the socket. Little endian format
+// is used for multibyte values.
+struct cmsis_dap_tcp_packet_hdr {
+    uint32_t signature;     // "DAP"
+    uint16_t length;        // Not including header length.
+    uint8_t packet_type;
+    uint8_t reserved;       // Reserved for future use.
+};
+
+struct cmsis_dap_backend_data {
+    int sockfd;
+};
+
+static char *cmsis_dap_tcp_host;
+static char *cmsis_dap_tcp_dap_port = STRINGIFY(CMSIS_DAP_TCP_PORT);
+
+static void cmsis_dap_tcp_close(struct cmsis_dap *dap);
+static int cmsis_dap_tcp_alloc(struct cmsis_dap *dap, unsigned int pkt_sz);
+static void cmsis_dap_tcp_free(struct cmsis_dap *dap);
+
+static int cmsis_dap_tcp_open(struct cmsis_dap *dap, uint16_t vids[], uint16_t
+        pids[], const char *serial)
+{
+    // Ignore vids, pids, serial. We use host and port subcommands instead.
+    (void)vids;
+    (void)pids;
+    (void)serial;
+
+    dap->bdata = malloc(sizeof(struct cmsis_dap_backend_data));
+    if (!dap->bdata) {
+        LOG_ERROR("CMSIS-DAP: unable to allocate memory");
+        return ERROR_FAIL;
+    }
+
+    struct addrinfo hints = {
+        .ai_family = AF_UNSPEC,
+        .ai_socktype = SOCK_STREAM
+    };
+    struct addrinfo *result, *rp;
+    int fd = 0;
+
+    LOG_INFO("CMSIS-DAP: Connecting to %s:%s using TCP backend",
+            cmsis_dap_tcp_host ? cmsis_dap_tcp_host : "localhost",
+            cmsis_dap_tcp_dap_port);
+
+    /* Some of the following code was taken from remote_bitbang.c */
+    /* Obtain address(es) matching host/port */
+    int s = getaddrinfo(cmsis_dap_tcp_host, cmsis_dap_tcp_dap_port, &hints,
+            &result);
+    if (s != 0) {
+        LOG_ERROR("CMSIS-DAP: getaddrinfo: %s\n", gai_strerror(s));
+        free(dap->bdata);
+        return ERROR_FAIL;
+    }
+
+    /* getaddrinfo() returns a list of address structures.
+     Try each address until we successfully connect(2).
+     If socket(2) (or connect(2)) fails, we (close the socket
+     and) try the next address. */
+
+    for (rp = result; rp ; rp = rp->ai_next) {
+        fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
+        if (fd == -1)
+            continue;
+
+        if (connect(fd, rp->ai_addr, rp->ai_addrlen) != -1) {
+            LOG_DEBUG("Connected.");
+            break; /* Success */
+        }
+
+        close(fd);
+    }
+
+    freeaddrinfo(result);
+
+    if (!rp) { /* No address succeeded */
+        LOG_ERROR("CMSIS-DAP: unable to connect to device %s:%s",
+            cmsis_dap_tcp_host ? cmsis_dap_tcp_host : "localhost",
+            cmsis_dap_tcp_dap_port);
+        log_socket_error("Failed to connect");
+        free(dap->bdata);
+        dap->bdata = NULL;
+        return ERROR_FAIL;
+    }
+
+    /* Set NODELAY to minimize latency. */
+    int one = 1;
+    /* On Windows optval has to be a const char *. */
+    setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (const char *)&one, sizeof(one));
+
+    dap->bdata->sockfd = fd;
+
+    int retval = cmsis_dap_tcp_alloc(dap, CMSIS_DAP_PACKET_SIZE);
+    if (retval != ERROR_OK) {
+        cmsis_dap_tcp_close(dap);
+        return ERROR_FAIL;
+    }
+    return ERROR_OK;
+}
+
+static void cmsis_dap_tcp_close(struct cmsis_dap *dap)
+{
+    if (close_socket(dap->bdata->sockfd) != 0) {
+        log_socket_error("close_socket");
+    }
+
+    if(dap->bdata)
+        free(dap->bdata);
+    dap->bdata = NULL;
+    cmsis_dap_tcp_free(dap);
+}
+
+static int cmsis_dap_tcp_read(struct cmsis_dap *dap, int transfer_timeout_ms,
+                              enum cmsis_dap_blocking blocking)
+{
+    int wait_ms = (blocking == CMSIS_DAP_NON_BLOCKING) ? 0 :
+        transfer_timeout_ms;
+    socket_timeout(dap->bdata->sockfd, wait_ms);
+
+    if(blocking == CMSIS_DAP_NON_BLOCKING)
+        socket_nonblock(dap->bdata->sockfd);
+    else
+        socket_block(dap->bdata->sockfd);
+
+    // Read the header first to find the length, then read the rest.
+    struct cmsis_dap_tcp_packet_hdr *header = (void*)dap->packet_buffer;
+    int retval = read_socket(dap->bdata->sockfd, dap->packet_buffer,
+            sizeof(*header));
+    LOG_DEBUG_IO("Reading header returned %d", retval);
+
+    if (retval == 0) {
+        return ERROR_TIMEOUT_REACHED;
+    } else if (retval == -1) {
+        if (errno == EAGAIN || errno == EWOULDBLOCK) {
+            return ERROR_TIMEOUT_REACHED;
+        } else {
+            LOG_ERROR("CMSIS-DAP: error reading header");
+            log_socket_error("read_socket");
+            return ERROR_FAIL;
+        }
+    } else if (retval != sizeof(*header)) {
+        log_socket_error("read_socket header short read");
+        return ERROR_FAIL;
+    }
+
+    header->signature = le_to_h_u32((void*)&header->signature);
+    header->length = le_to_h_u16((void*)&header->length);
+
+    if(header->signature != DAP_PKT_HDR_SIGNATURE) {
+        LOG_ERROR("CMSIS-DAP: Unrecognized packet signature 0x%08x",
+                header->signature);
+        return ERROR_FAIL;
+    } else if(header->packet_type != DAP_PKT_TYPE_RESPONSE) {
+        LOG_ERROR("CMSIS-DAP: Unrecognized packet type 0x%02x",
+                header->packet_type);
+        return ERROR_FAIL;
+    } else if(header->length + sizeof(*header) > dap->packet_buffer_size) {
+        LOG_ERROR("CMSIS-DAP: Packet length %d too large to fit.",
+                header->length);
+        return ERROR_FAIL;
+    }
+
+    LOG_DEBUG_IO("Reading %d bytes...", header->length);
+    retval = read_socket(dap->bdata->sockfd, dap->packet_buffer +
+            sizeof(*header), header->length);
+
+    if (retval == 0) {
+        return ERROR_TIMEOUT_REACHED;
+    } else if (retval == -1) {
+        LOG_ERROR("CMSIS-DAP: error reading data");
+        log_socket_error("read_socket");
+        return ERROR_FAIL;
+    } else if (retval != header->length) {
+        log_socket_error("read_socket short read");
+        return ERROR_FAIL;
+    }
+    return retval;
+}
+
+static int cmsis_dap_tcp_write(struct cmsis_dap *dap, int txlen, int
+        timeout_ms)
+{
+    (void) timeout_ms;
+
+    struct cmsis_dap_tcp_packet_hdr *header = (void*)dap->packet_buffer;
+    const unsigned int len = txlen + sizeof(*header);
+    if(len > dap->packet_buffer_size) {
+        LOG_ERROR("CMSIS-DAP: Packet length %d exceeds TCP buffer size!", len);
+        return ERROR_FAIL;
+    }
+
+    /* Set the header values. */
+    h_u32_to_le((void*)&header->signature, DAP_PKT_HDR_SIGNATURE);
+    h_u16_to_le((void*)&header->length, txlen);
+    header->packet_type = DAP_PKT_TYPE_REQUEST;
+    header->reserved = 0;
+
+    /* write data to device */
+    LOG_DEBUG_IO("Writing %d bytes", len);
+    int retval = write_socket(dap->bdata->sockfd, dap->packet_buffer, len);
+    if (retval < 0) {
+        log_socket_error("write_socket");
+        return ERROR_FAIL;
+    } else if (retval != (int)len) {
+        LOG_ERROR("CMSIS-DAP: error writing data");
+        log_socket_error("write_socket short write");
+        return ERROR_FAIL;
+    }
+    return retval;
+}
+
+static int cmsis_dap_tcp_alloc(struct cmsis_dap *dap, unsigned int pkt_sz)
+{
+    // Reserve space for the packet header.
+    struct cmsis_dap_tcp_packet_hdr header;
+    unsigned int packet_buffer_size = pkt_sz + sizeof(header);
+    uint8_t *buf = malloc(packet_buffer_size);
+    if (!buf) {
+        LOG_ERROR("CMSIS-DAP: unable to allocate CMSIS-DAP packet buffer");
+        return ERROR_FAIL;
+    }
+
+    dap->packet_buffer = buf;
+    dap->packet_size = pkt_sz;
+    dap->packet_usable_size = pkt_sz;
+    dap->packet_buffer_size = packet_buffer_size;
+
+    dap->command = dap->packet_buffer + sizeof(header);
+    dap->response = dap->packet_buffer + sizeof(header);
+    return ERROR_OK;
+}
+
+static void cmsis_dap_tcp_free(struct cmsis_dap *dap)
+{
+    free(dap->packet_buffer);
+    dap->packet_buffer = NULL;
+}
+
+static void cmsis_dap_tcp_cancel_all(struct cmsis_dap *dap)
+{
+}
+
+COMMAND_HANDLER(cmsis_dap_handle_tcp_dap_port)
+{
+    if (CMD_ARGC == 1)
+        cmsis_dap_tcp_dap_port = strdup(CMD_ARGV[0]);
+    else
+        LOG_ERROR("CMSIS-DAP: expected exactly one argument to "
+                "cmsis-dap tcp dap_port <port_number>");
+
+    return ERROR_OK;
+}
+
+COMMAND_HANDLER(cmsis_dap_handle_tcp_host)
+{
+    if (CMD_ARGC == 1)
+        cmsis_dap_tcp_host = strdup(CMD_ARGV[0]);
+    else
+        LOG_ERROR("CMSIS-DAP: expected exactly one argument to "
+                "cmsis-dap tcp host <host_name>");
+
+    return ERROR_OK;
+}
+
+const struct command_registration cmsis_dap_tcp_subcommand_handlers[] = {
+    {
+        .name = "host",
+        .handler = &cmsis_dap_handle_tcp_host,
+        .mode = COMMAND_CONFIG,
+        .help = "set the host name to use (for TCP backend only)",
+        .usage = "<host_name>",
+    },
+    {
+        .name = "dap_port",
+        .handler = &cmsis_dap_handle_tcp_dap_port,
+        .mode = COMMAND_CONFIG,
+        .help = "set the port number to use for DAP (for TCP backend only)",
+        .usage = "<port_number>",
+    },
+    COMMAND_REGISTRATION_DONE
+};
+
+const struct cmsis_dap_backend cmsis_dap_tcp_backend = {
+    .name = "tcp",
+    .open = cmsis_dap_tcp_open,
+    .close = cmsis_dap_tcp_close,
+    .read = cmsis_dap_tcp_read,
+    .write = cmsis_dap_tcp_write,
+    .packet_buffer_alloc = cmsis_dap_tcp_alloc,
+    .packet_buffer_free = cmsis_dap_tcp_free,
+    .cancel_all = cmsis_dap_tcp_cancel_all,
+};
diff --git a/tcl/interface/cmsis-dap-tcp.cfg b/tcl/interface/cmsis-dap-tcp.cfg
new file mode 100644
index 0000000000..ddd9971c19
--- /dev/null
+++ b/tcl/interface/cmsis-dap-tcp.cfg
@@ -0,0 +1,12 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+#
+# cmsis-dap-tcp - CMSIS-DAP protocol over TCP/IP.
+#
+# Change the host and dap_port parameters to match your programmer.
+#
+
+adapter driver cmsis-dap
+cmsis-dap backend tcp
+cmsis-dap tcp host 192.168.1.4
+cmsis-dap tcp dap_port 4441

-- 

Reply via email to