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 --