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/+/8941

-- gerrit

commit b8b60f4f55aaaf182c3ed21ffe996bb558f05adf
Author: Brian Kuschak <bkusc...@gmail.com>
Date:   Sun Jun 8 15:25:09 2025 +0800

    jtag/drivers: add new driver remote_swd
    
    Create a new driver 'remote_swd' that supports an SWD interface to a
    remote programmer over a simple TCP/IP protocol. It has a lightweight
    remote-side implementation. Supports SWD only, not JTAG. When used for
    flash programming an MCU, the performance is much faster than using the
    remote_bitbang driver. (Flashing a 64 KB image to an STM32 completes in
    about 7 seconds, as compared to 20 minutes for remote_bitbang).
    
    An implementation of the firmware for an SWD programmer that uses this
    remote_swd protocol can be found at the link below.
    https://github.com/bkuschak/openocd_remote_swd_esp32
    
    Change-Id: If0580d1c831a8dd8ef2ab6d8afa6d6852f30ebe5
    Signed-off-by: Brian Kuschak <bkusc...@gmail.com>

diff --git a/configure.ac b/configure.ac
index 3e1d9a2baa..4d5c48ac3a 100644
--- a/configure.ac
+++ b/configure.ac
@@ -172,6 +172,9 @@ m4_define([SYSFSGPIO_ADAPTER],
 m4_define([REMOTE_BITBANG_ADAPTER],
        [[[remote_bitbang], [Remote Bitbang driver], [REMOTE_BITBANG]]])
 
+m4_define([REMOTE_SWD_ADAPTER],
+       [[[remote_swd], [Remote SWD programmer over TCP/IP], [REMOTE_SWD]]])
+
 m4_define([LIBJAYLINK_ADAPTERS],
        [[[jlink], [SEGGER J-Link Programmer], [JLINK]]])
 
@@ -322,6 +325,7 @@ AC_ARG_ADAPTERS([
   LIBGPIOD_ADAPTERS,
   SYSFSGPIO_ADAPTER,
   REMOTE_BITBANG_ADAPTER,
+  REMOTE_SWD_ADAPTER,
   LINUXSPIDEV_ADAPTER,
   SERIAL_PORT_ADAPTERS,
   DUMMY_ADAPTER,
@@ -698,6 +702,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([REMOTE_SWD_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"],
@@ -867,6 +872,7 @@ m4_foreach([adapter], [USB1_ADAPTERS,
        LIBGPIOD_ADAPTERS,
        SYSFSGPIO_ADAPTER,
        REMOTE_BITBANG_ADAPTER,
+       REMOTE_SWD_ADAPTER,
        LIBJAYLINK_ADAPTERS, PCIE_ADAPTERS, SERIAL_PORT_ADAPTERS,
        LINUXSPIDEV_ADAPTER,
        VDEBUG_ADAPTER,
diff --git a/doc/openocd.texi b/doc/openocd.texi
index bd6b3704a8..cb7276ee17 100644
--- a/doc/openocd.texi
+++ b/doc/openocd.texi
@@ -627,6 +627,9 @@ This is deprecated from Linux v5.3; prefer using 
@b{linuxgpiod}.
 @item @b{esp_usb_jtag}
 @* A JTAG driver to communicate with builtin debug modules of Espressif 
ESP32-C3 and ESP32-S3 chips using OpenOCD.
 
+@item @b{remote_swd}
+@* An SWD driver for use with remote programmers over TCP/IP.
+
 @end itemize
 
 @node About Jim Tcl
@@ -2881,6 +2884,34 @@ remote_bitbang host mysocket
 @end example
 @end deffn
 
+@deffn {Interface Driver} {remote_swd}
+Controls a remote SWD programmer over TCP/IP using a simple protocol. For flash
+programming, this is much faster than using the remote_bitbang driver.  It has
+a lightweight remote-side implementation. Supports SWD only, not JTAG.
+
+An implementation of the firmware for an ESP32-based SWD programmer that uses
+this remote_swd protocol can be found at:
+https://github.com/bkuschak/openocd_remote_swd_esp32
+
+@deffn {Config Command} {remote_swd host} hostname
+Specifies the hostname or IP address of the remote SWD programmer to connect to
+using TCP.
+@end deffn
+
+@deffn {Config Command} {remote_swd port} number
+Specifies the TCP port number of the remote SWD programmer.
+@end deffn
+
+For example, to connect remotely via TCP to the host foobar you might have
+something like:
+
+@example
+adapter driver remote_swd
+remote_swd host foobar
+remote_swd port 5253
+@end example
+@end deffn
+
 @deffn {Interface Driver} {usb_blaster}
 USB JTAG/USB-Blaster compatibles over one of the userspace libraries
 for FTDI chips. These interfaces have several commands, used to
diff --git a/src/jtag/drivers/Makefile.am b/src/jtag/drivers/Makefile.am
index b0dd8e3ad1..a8921de9df 100644
--- a/src/jtag/drivers/Makefile.am
+++ b/src/jtag/drivers/Makefile.am
@@ -210,6 +210,9 @@ endif
 if AM335XGPIO
 DRIVERFILES += %D%/am335xgpio.c
 endif
+if REMOTE_SWD
+DRIVERFILES += %D%/remote_swd.c
+endif
 
 DRIVERHEADERS = \
        %D%/bitbang.h \
diff --git a/src/jtag/drivers/remote_swd.c b/src/jtag/drivers/remote_swd.c
new file mode 100644
index 0000000000..e0182cc22f
--- /dev/null
+++ b/src/jtag/drivers/remote_swd.c
@@ -0,0 +1,509 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+/***************************************************************************
+ *      Copyright (C) 2025 by Brian Kuschak <bkusc...@gmail.com>               
           *
+ *      Adapted from remote_bitbang.c                                          
                                   *
+ *                                                                             
                                                                   *
+ *      Controls a remote SWD programmer over TCP/IP. Much faster than using  *
+ *      the remote_bitbang driver. Lightweight remote-side implementation.    *
+ *      Supports SWD only. JTAG not supported.                                 
                           *
+ *                                                                             
                                                                   *
+ *      Tested using a XIAO ESP32C6 as the programmer and STM32 Blue Pill as  *
+ *      the target. Flash programming performance: 9.5 KB/sec.                 
           *
+ *                                                                             
                                                                   *
+ *              openocd \                                                      
                                                           *
+ *                      --search tcl \                                         
                                                   *
+ *                      -c "debug_level 2" \                                   
                                           *
+ *                      -c "reset_config none" \                               
                                           *
+ *                      -c "adapter driver remote_swd" \                       
                                   *
+ *                      -c "remote_swd host 192.168.100.40" \                  
                           *
+ *                      -c "remote_swd port 5253" \                            
                                   *
+ *                      -f tcl/target/stm32f1x.cfg \                           
                                   *
+ *                      -c "program firmware.elf verify reset exit"            
                   *
+ *                                                                             
                                                                   *
+ *      Refer to the implementation of the remote-side firmware for ESP32 at  *
+ *      https://github.com/bkuschak/openocd_remote_swd_esp32                   
           *
+ *                                                                             
                                                                   *
+ ***************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifndef _WIN32
+#include <sys/un.h>
+#include <netdb.h>
+#include <netinet/tcp.h>
+#endif
+#include <arpa/inet.h>
+#include "bitbang.h"
+#include "helper/system.h"
+#include "helper/replacements.h"
+#include <jtag/interface.h>
+
+// Define our version of the protocol used.  Remote side must match.
+#define PROTOCOL_VERSION               0x01
+
+// Arbitrary limit on host name length.
+#define REMOTE_SWD_HOST_MAX            255
+
+// First 4 bits of the flags hold the operation code.
+#define FLAGS_OP_PROTOCOL              0x1                     // Get remote 
protocol version.
+#define FLAGS_OP_VERSION               0x2                     // Get remote 
HW/SW version.
+#define FLAGS_OP_SERIAL_NUM            0x3                     // Get remote 
serial number.
+#define FLAGS_OP_SPEED                 0x4                     // Set speed in 
'data' field.
+#define FLAGS_OP_RESET                 0x5                     // Set NRST 
state in 'data' field.
+#define FLAGS_OP_SWITCH_SEQ            0x6                     // Send 
sequence in 'data' field.
+#define FLAGS_OP_READ_REG              0x7                     // SWD read 
register.
+#define FLAGS_OP_WRITE_REG             0x8                     // SWD write 
register.
+#define FLAGS_OP(flags)                        ((flags) & 0xF)
+
+// Remaining bits are reserved for flags.
+#define FLAGS_EXPECT_ACK               BIT(4)
+#define FLAGS_EXPECT_DATA              BIT(5)
+#define FLAGS_SRST_OPEN_DRAIN  BIT(4)          // Used for OP_RESET only.
+
+// The 3 ACK bits returned by the target.
+#define ACK_OK                                 BIT(0)
+#define ACK_WAIT                               BIT(1)
+#define ACK_FAULT                              BIT(2)
+
+// The maximum number of queued commands, before we write them to the target.
+#define MAX_QUEUE_LEN                  128
+
+// Very simple TCP stream protocol.
+// We send commands to the remote side, using the struct below.
+// The remote side returns the same struct back to us, with the ack field set,
+// and the data field set if the command was a read. The cmd, ack, and data
+// fields are what is actually transmitted on the wire, SWDIO.
+struct queued_command {
+       uint8_t flags;                  // Opcode and flags.
+       uint8_t ap_delay_clks;  // Additional clock cycles sent after the data.
+       uint8_t cmd;                    // SWD command.
+       uint8_t ack;                    // SWD ACK (3 bits) returned.
+       uint32_t data;                  // SWD data, sent or returned.
+};
+
+enum flush_bool {
+       NO_FLUSH,
+       FLUSH_SEND_BUF
+};
+
+static char *remote_swd_host;
+static char *remote_swd_port;
+static int sockfd;
+
+// The queued commands and a write index.
+static struct queued_command queued_commands[MAX_QUEUE_LEN];
+static unsigned int queued_commands_idx;
+
+// We need a place to store the return data pointers. These are the caller's
+// pointers for storing response data, and the ACK responses.
+static uint32_t *response_data[MAX_QUEUE_LEN];
+static uint8_t response_acks[MAX_QUEUE_LEN];
+
+static int remote_swd_run_queue(void);
+
+static int remote_swd_queue(struct queued_command *cmd, uint32_t *response,
+               enum flush_bool flush)
+{
+       // Use network byte order in the packets.
+       queued_commands[queued_commands_idx] = *cmd;
+       queued_commands[queued_commands_idx].data = htonl(cmd->data);
+
+       response_data[queued_commands_idx] = response;
+
+       if (!response)
+               LOG_DEBUG_IO("No response pointer provided.");
+       queued_commands_idx++;
+       if (flush == FLUSH_SEND_BUF || queued_commands_idx >=
+                       ARRAY_SIZE(queued_commands))
+               return remote_swd_run_queue();
+       return ERROR_OK;
+}
+
+static int remote_swd_quit(void)
+{
+       LOG_DEBUG("remote_swd interface quit");
+
+       if (close_socket(sockfd) != 0) {
+               log_socket_error("close_socket");
+               return ERROR_FAIL;
+       }
+
+       free(remote_swd_host);
+       free(remote_swd_port);
+       return ERROR_OK;
+}
+
+static int remote_swd_reset(int trst, int srst)
+{
+       /* SRST only. SWD doesn't have TRST. */
+       bool srst_open_drain = true;
+       enum reset_types cfg = jtag_get_reset_config();
+       if (cfg & RESET_SRST_PUSH_PULL)
+               srst_open_drain = false;
+
+       LOG_INFO("RESET: srst=%d, open_drain=%d", srst, srst_open_drain);
+
+       struct queued_command cmd;
+       memset(&cmd, 0, sizeof(cmd));
+       cmd.flags = FLAGS_OP_RESET;
+       cmd.flags |= srst_open_drain ? FLAGS_SRST_OPEN_DRAIN : 0;
+       cmd.data = srst ? 1 : 0;
+
+       /* Always flush the send buffer on reset */
+       return remote_swd_queue(&cmd, NULL, NO_FLUSH);
+}
+
+static int remote_swd_speed(int hz)
+{
+       LOG_DEBUG("SPEED: %d Hz", hz);
+
+       struct queued_command command;
+       memset(&command, 0, sizeof(command));
+       command.flags = FLAGS_OP_SPEED;
+       command.data = hz;
+       return remote_swd_queue(&command, NULL, NO_FLUSH);
+}
+
+static void remote_swd_write_reg(unsigned char cmd, unsigned int value,
+               unsigned int ap_delay_clk)
+{
+       LOG_DEBUG("WRITE_REG: cmd=0x%02x, value=0x%08x, ap_delay_clk=%d",
+                       cmd, value, ap_delay_clk);
+
+       struct queued_command command;
+       memset(&command, 0, sizeof(command));
+       command.flags = FLAGS_OP_WRITE_REG;
+       command.flags |= FLAGS_EXPECT_ACK;
+       command.cmd = cmd | SWD_CMD_START | SWD_CMD_PARK;
+       command.data = value;
+       command.ap_delay_clks = ap_delay_clk;
+
+       if (remote_swd_queue(&command, NULL, NO_FLUSH) == ERROR_FAIL)
+               LOG_ERROR("write_reg failed");
+}
+
+static void remote_swd_read_reg(unsigned char cmd, unsigned int *value,
+               unsigned int ap_delay_clk)
+{
+       LOG_DEBUG("READ_REG: cmd=0x%02x, ap_delay_clk=%d", cmd,
+                       ap_delay_clk);
+
+       struct queued_command command;
+       memset(&command, 0, sizeof(command));
+       command.flags = FLAGS_OP_READ_REG;
+       command.flags |= FLAGS_EXPECT_ACK;
+       command.flags |= FLAGS_EXPECT_DATA;
+       command.cmd = cmd | SWD_CMD_START | SWD_CMD_PARK;
+       command.ap_delay_clks = ap_delay_clk;
+
+       if (remote_swd_queue(&command, value, NO_FLUSH) == ERROR_FAIL)
+               LOG_ERROR("read_reg failed");
+}
+
+static int remote_swd_switch_seq(enum swd_special_seq seq)
+{
+       LOG_DEBUG("SWITCH_SEQ: %d", seq);
+
+       struct queued_command command;
+       memset(&command, 0, sizeof(command));
+       command.flags = FLAGS_OP_SWITCH_SEQ;
+       command.data = seq;
+
+       if (remote_swd_queue(&command, NULL, NO_FLUSH) == ERROR_FAIL)
+               return ERROR_FAIL;
+       return 0;
+}
+
+static int remote_swd_protocol(unsigned int *value)
+{
+       LOG_DEBUG("PROTOCOL");
+
+       struct queued_command command;
+       memset(&command, 0, sizeof(command));
+       command.flags = FLAGS_OP_PROTOCOL;
+       command.flags |= FLAGS_EXPECT_DATA;
+
+       if (remote_swd_queue(&command, value, FLUSH_SEND_BUF) == ERROR_FAIL)
+               return ERROR_FAIL;
+       return 0;
+}
+
+static int remote_swd_version(unsigned int *value)
+{
+       LOG_DEBUG("VERSION");
+
+       struct queued_command command;
+       memset(&command, 0, sizeof(command));
+       command.flags = FLAGS_OP_VERSION;
+       command.flags |= FLAGS_EXPECT_DATA;
+
+       if (remote_swd_queue(&command, value, FLUSH_SEND_BUF) == ERROR_FAIL)
+               return ERROR_FAIL;
+       return 0;
+}
+
+static int remote_swd_serial_number(unsigned int *value)
+{
+       LOG_DEBUG("SERIAL_NUM");
+
+       struct queued_command command;
+       memset(&command, 0, sizeof(command));
+       command.flags = FLAGS_OP_SERIAL_NUM;
+       command.flags |= FLAGS_EXPECT_DATA;
+
+       if (remote_swd_queue(&command, value, FLUSH_SEND_BUF) == ERROR_FAIL)
+               return ERROR_FAIL;
+       return 0;
+}
+
+static int remote_swd_init_tcp(void)
+{
+       struct addrinfo hints = {
+               .ai_family = AF_UNSPEC,
+               .ai_socktype = SOCK_STREAM
+       };
+       struct addrinfo *result, *rp;
+       int fd = 0;
+
+       LOG_INFO("Connecting to %s:%s",
+                       remote_swd_host ? remote_swd_host : "localhost",
+                       remote_swd_port);
+
+       /* Obtain address(es) matching host/port */
+       int s = getaddrinfo(remote_swd_host, remote_swd_port, &hints, &result);
+       if (s != 0) {
+               LOG_ERROR("getaddrinfo: %s\n", gai_strerror(s));
+               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)
+                       break; /* Success */
+
+               close(fd);
+       }
+
+       /* 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));
+
+       freeaddrinfo(result); /* No longer needed */
+
+       if (!rp) { /* No address succeeded */
+               log_socket_error("Failed to connect");
+               return ERROR_FAIL;
+       }
+
+       return fd;
+}
+
+static int remote_swd_swd_init(void)
+{
+       LOG_DEBUG("remote_swd_swd_init");
+       return ERROR_OK;
+}
+
+static int remote_swd_init(void)
+{
+       LOG_INFO("Initializing remote_swd driver");
+
+       memset(queued_commands, 0, sizeof(queued_commands));
+       memset(response_data, 0, sizeof(response_data));
+       memset(response_acks, 0, sizeof(response_acks));
+       queued_commands_idx = 0;
+
+       int ret = remote_swd_init_tcp();
+       if (ret < 0) {
+               log_socket_error("remote_swd socket init");
+               LOG_ERROR("Failed initializing socket. Error: %d", errno);
+               return ret;
+       }
+       sockfd = ret;
+
+       LOG_INFO("remote_swd driver initialized");
+
+       // Get device version and serial number.
+       unsigned int protocol = 0;
+       unsigned int version = 0;
+       unsigned int serial_num = 0;
+       ret = remote_swd_protocol(&protocol);
+       if (ret != ERROR_OK) {
+               LOG_ERROR("Failed getting remote protocol version!");
+               return ERROR_FAIL;
+       }
+       ret = remote_swd_version(&version);
+       if (ret != ERROR_OK) {
+               LOG_ERROR("Failed getting remote version!");
+               return ERROR_FAIL;
+       }
+       ret = remote_swd_serial_number(&serial_num);
+       if (ret != ERROR_OK) {
+               LOG_ERROR("Failed getting remote serial number!");
+               return ERROR_FAIL;
+       }
+
+       LOG_INFO("Remote protocol version: %u", protocol);
+       LOG_INFO("Remote HW/SW version: 0x%02hX / 0x%02hX",
+                       version & 0xFF, (version >> 8) & 0xFF);
+       LOG_INFO("Remote serial number: %08X", serial_num);
+
+       if (protocol != PROTOCOL_VERSION) {
+               LOG_ERROR("Remote protocol version (%u) does not match our own 
(%u)!",
+                               protocol, PROTOCOL_VERSION);
+               return ERROR_FAIL;
+       }
+       return ERROR_OK;
+}
+
+COMMAND_HANDLER(remote_swd_handle_remote_swd_port_command)
+{
+       if (CMD_ARGC == 1) {
+               uint16_t port;
+               COMMAND_PARSE_NUMBER(u16, CMD_ARGV[0], port);
+               free(remote_swd_port);
+               remote_swd_port = port == 0 ? NULL : strdup(CMD_ARGV[0]);
+               LOG_INFO("remote_swd got port %s", remote_swd_port);
+               return ERROR_OK;
+       }
+       return ERROR_COMMAND_SYNTAX_ERROR;
+}
+
+COMMAND_HANDLER(remote_swd_handle_remote_swd_host_command)
+{
+       if (CMD_ARGC == 1) {
+               free(remote_swd_host);
+               remote_swd_host = strdup(CMD_ARGV[0]);
+               LOG_INFO("remote_swd got host %s", remote_swd_host);
+               return ERROR_OK;
+       }
+       return ERROR_COMMAND_SYNTAX_ERROR;
+}
+
+static const struct command_registration remote_swd_subcommand_handlers[] = {
+       {
+               .name = "port",
+               .handler = remote_swd_handle_remote_swd_port_command,
+               .mode = COMMAND_CONFIG,
+               .help = "Set the TCP port to use to connect to the remote 
SWD.\n",
+               .usage = "port_number",
+       },
+       {
+               .name = "host",
+               .handler = remote_swd_handle_remote_swd_host_command,
+               .mode = COMMAND_CONFIG,
+               .help = "Set the host to use to connect to the remote SWD.\n",
+               .usage = "host_name",
+       },
+       // TODO add subcommands to set the pin numbers for SWDIO, SWCLK, SRST.
+       COMMAND_REGISTRATION_DONE
+};
+
+static const struct command_registration remote_swd_command_handlers[] = {
+       {
+               .name = "remote_swd",
+               .mode = COMMAND_ANY,
+               .help = "perform remote_swd management",
+               .chain = remote_swd_subcommand_handlers,
+               .usage = "",
+       },
+       COMMAND_REGISTRATION_DONE
+};
+
+
+static int remote_swd_run_queue(void)
+{
+       LOG_DEBUG("Executing %d queued transactions", queued_commands_idx);
+       if (queued_commands_idx <= 0)
+               return ERROR_OK;
+
+       // Send entire queue at once.
+       int nbytes = queued_commands_idx * sizeof(*queued_commands);
+       LOG_DEBUG_IO("Sending %d bytes... (fd=%d)", nbytes, sockfd);
+       ssize_t n = write_socket(sockfd, queued_commands, nbytes);
+       LOG_DEBUG_IO("write_socket returned %zd", n);
+       if (n < 0) {
+               log_socket_error("remote_swd write_socket error");
+               queued_commands_idx = 0;
+               return ERROR_FAIL;
+       }
+       if (n != nbytes) {
+               log_socket_error("remote_swd write_socket short write");
+               queued_commands_idx = 0;
+               return ERROR_FAIL;
+       }
+
+       // Wait for the entire response.
+       struct queued_command response[queued_commands_idx];
+       nbytes = sizeof(response);
+       LOG_DEBUG_IO("Reading %d bytes...", nbytes);
+       n = read_socket(sockfd, response, nbytes);
+       LOG_DEBUG_IO("read_socket returned %zd", n);
+       if (n < 0) {
+               log_socket_error("remote_swd read_socket error");
+               queued_commands_idx = 0;
+               return ERROR_FAIL;
+       }
+       if (n != nbytes) {
+               log_socket_error("remote_swd read_socket short read");
+               queued_commands_idx = 0;
+               return ERROR_FAIL;
+       }
+
+       // Iterate over the response data, storing the responses into the 
caller's
+       // pointers.
+       memset(response_acks, 0, sizeof(response_acks));
+       for (unsigned int i = 0; i < queued_commands_idx; i++) {
+               struct queued_command *cmd = &queued_commands[i];
+               struct queued_command *resp = &response[i];
+
+               if (response_data[i])
+                       *response_data[i] = 0;
+
+               if (cmd->flags & FLAGS_EXPECT_ACK) {
+                       LOG_DEBUG("Got response ack: %x", resp->data);
+                       response_acks[i] = resp->ack;
+               }
+               if (cmd->flags & FLAGS_EXPECT_DATA) {
+                       LOG_DEBUG("Got response data: %08x", resp->data);
+                       if (response_data[i])
+                               *response_data[i] = ntohl(resp->data);
+               }
+       }
+
+       LOG_DEBUG("SWD run_queue success");
+       queued_commands_idx = 0;
+       return ERROR_OK;
+}
+
+const struct swd_driver remote_swd_ops = {
+       .init = remote_swd_swd_init,
+       .switch_seq = remote_swd_switch_seq,
+       .read_reg = remote_swd_read_reg,
+       .write_reg = remote_swd_write_reg,
+       .run = remote_swd_run_queue,
+};
+
+struct adapter_driver remote_swd_adapter_driver = {
+       .name = "remote_swd",
+       .transport_ids = TRANSPORT_SWD,
+       .transport_preferred_id = TRANSPORT_SWD,
+       .commands = remote_swd_command_handlers,
+       .init = &remote_swd_init,
+       .quit = &remote_swd_quit,
+       .speed = &remote_swd_speed,
+       .reset = &remote_swd_reset,
+       .swd_ops = &remote_swd_ops,
+};
diff --git a/src/jtag/drivers/remote_swd.lua b/src/jtag/drivers/remote_swd.lua
new file mode 100644
index 0000000000..08225f5833
--- /dev/null
+++ b/src/jtag/drivers/remote_swd.lua
@@ -0,0 +1,99 @@
+-- Wireshark packet dissector for the remote_swd protocol.
+-- Adapted from ChatGPT.
+--
+-- Place this file into the Wireshark plugins directory.
+--   macOS/Linux: ~/.config/wireshark/plugins/
+--   Windows: AppData\Roaming\Wireshark\plugins\
+
+-- Define protocol
+local remote_swd_proto = Proto("remote_swd", "OpenOCD remote_swd protocol")
+
+-- Enumerated values for bit fields
+local field1_enum = {
+    [0] = "Unknown",
+    [1] = "Protocol Version",
+    [2] = "HW/SW Version",
+    [3] = "Serial Number",
+    [4] = "Speed",
+    [5] = "Reset",
+    [6] = "Switch Sequence",
+    [7] = "Read Register",
+    [8] = "Write Register",
+    [9] = "Unknown",
+    [10] = "Unknown",
+    [11] = "Unknown",
+    [12] = "Unknown",
+    [13] = "Unknown",
+    [14] = "Unknown",
+    [15] = "Unknown"
+}
+
+local field5_enum = {
+    [0] = "ACK none",
+    [1] = "ACK OK",
+    [2] = "ACK WAIT",
+    [3] = "ACK unknown",
+    [4] = "ACK FAULT",
+    [5] = "ACK unknown",
+    [6] = "ACK unknown",
+    [7] = "ACK unknown"
+}
+
+-- Define fields
+local f_field1 = ProtoField.uint8("remote_swd.opcode", "Opcode", base.DEC, 
field1_enum, 0x0F)
+local f_field2_ack = ProtoField.bool("remote_swd.flags.ack", "Expect ACK", 8, 
nil, 0x10)
+local f_field2_data = ProtoField.bool("remote_swd.flags.data", "Expect Data", 
8, nil, 0x20)
+local f_field3 = ProtoField.uint8("remote_swd.ap_delay_clks", "AP delay 
clocks", base.DEC)
+local f_field4 = ProtoField.uint8("remote_swd.cmd", "SWD command", base.HEX)
+local f_field5 = ProtoField.uint8("remote_swd.ack", "SWD ack", base.HEX, 
field5_enum, 0x07)
+local f_field6 = ProtoField.uint32("remote_swd.data", "SWD data", base.HEX)
+
+remote_swd_proto.fields = { f_field1, f_field2_ack, f_field2_data, f_field3, 
f_field4, f_field5, f_field6 }
+
+-- Struct size in bytes
+local STRUCT_SIZE = 8
+
+-- Dissector
+function remote_swd_proto.dissector(buffer, pinfo, tree)
+    pinfo.cols.protocol = remote_swd_proto.name
+
+    local total_len = buffer:len()
+
+    -- TCP reassembly: if not enough data for one struct, ask for more
+    if total_len < STRUCT_SIZE then
+        pinfo.desegment_len = STRUCT_SIZE - total_len
+        return
+    end
+
+    -- Reassemble if data is not a multiple of struct size
+    if (total_len % STRUCT_SIZE) ~= 0 then
+        pinfo.desegment_len = DESEGMENT_ONE_MORE_SEGMENT
+        return
+    end
+
+    -- Dissect
+    local subtree = tree:add(remote_swd_proto, buffer(), "Operations")
+
+    local offset = 0
+    local struct_num = 0
+
+    while offset + STRUCT_SIZE <= total_len do
+        local struct_buf = buffer(offset, STRUCT_SIZE)
+        local struct_tree = subtree:add(remote_swd_proto, struct_buf, 
"Operation " .. struct_num)
+
+        struct_tree:add(f_field1, struct_buf(0, 1))
+        struct_tree:add(f_field2_ack, struct_buf(0, 1))
+        struct_tree:add(f_field2_data, struct_buf(0, 1))
+        struct_tree:add(f_field3, struct_buf(1, 1))
+        struct_tree:add(f_field4, struct_buf(2, 1))
+        struct_tree:add(f_field5, struct_buf(3, 1))
+        struct_tree:add_le(f_field6, struct_buf(4, 4))
+
+        offset = offset + STRUCT_SIZE
+        struct_num = struct_num + 1
+    end
+end
+
+-- Register the protocol to a TCP port (e.g., 5253)
+local tcp_port = DissectorTable.get("tcp.port")
+tcp_port:add(5253, remote_swd_proto)
diff --git a/src/jtag/interface.h b/src/jtag/interface.h
index 475dbed36e..c5518454e1 100644
--- a/src/jtag/interface.h
+++ b/src/jtag/interface.h
@@ -400,6 +400,7 @@ extern struct adapter_driver osbdm_adapter_driver;
 extern struct adapter_driver parport_adapter_driver;
 extern struct adapter_driver presto_adapter_driver;
 extern struct adapter_driver remote_bitbang_adapter_driver;
+extern struct adapter_driver remote_swd_adapter_driver;
 extern struct adapter_driver rlink_adapter_driver;
 extern struct adapter_driver rshim_dap_adapter_driver;
 extern struct adapter_driver stlink_dap_adapter_driver;
diff --git a/src/jtag/interfaces.c b/src/jtag/interfaces.c
index e49bd9e0f3..07b2d3393e 100644
--- a/src/jtag/interfaces.c
+++ b/src/jtag/interfaces.c
@@ -108,6 +108,9 @@ struct adapter_driver *adapter_drivers[] = {
 #if BUILD_REMOTE_BITBANG == 1
                &remote_bitbang_adapter_driver,
 #endif
+#if BUILD_REMOTE_SWD == 1
+               &remote_swd_adapter_driver,
+#endif
 #if BUILD_HLADAPTER == 1
                &hl_adapter_driver,
 #endif
diff --git a/tcl/interface/remote_swd.cfg b/tcl/interface/remote_swd.cfg
new file mode 100644
index 0000000000..bcf11124bd
--- /dev/null
+++ b/tcl/interface/remote_swd.cfg
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: BSD-3-Clause
+#
+# configuration file for remote_swd adapter
+#
+
+adapter driver remote_swd
+remote_swd host 192.168.2.144
+remote_swd port 5253
+
+reset_config none

-- 

Reply via email to