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 example 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 on an ESP32-C6
programmer and STM32F401RE target shows the following performance:
- loading 96KB image to RAM: 80 KB/sec
- dumping 96KB image from RAM: 72 KB/sec
- flashing 512KB image completes in about 13.5 seconds (including erase,
  program, and verify).

Change-Id: I6e3e45016bd16ef2259561b1046788f5536b0687
Signed-off-by: Brian Kuschak <bkuschak@gmail.com>
Reviewed-on: https://review.openocd.org/c/openocd/+/8973
Reviewed-by: Tomas Vanek <vanekt@fbl.cz>
Reviewed-by: Antonio Borneo <borneo.antonio@gmail.com>
Tested-by: jenkins
This commit is contained in:
Brian Kuschak
2025-06-11 12:41:04 +08:00
committed by Tomas Vanek
parent 64ed1c74d5
commit fcff4b712c
8 changed files with 543 additions and 11 deletions

View File

@@ -204,6 +204,9 @@ m4_define([RSHIM_ADAPTER],
m4_define([AMTJTAGACCEL_ADAPTER],
[[[amtjtagaccel], [Amontec JTAG-Accelerator driver], [AMTJTAGACCEL]]])
m4_define([CMSIS_DAP_TCP_ADAPTER],
[[[cmsis_dap_tcp], [CMSIS-DAP v2 compliant dongle (TCP)], [CMSIS_DAP_TCP]]])
m4_define([HOST_ARM_BITBANG_ADAPTERS],
[[[ep93xx], [Bitbanging on EP93xx-based SBCs], [EP93XX]],
[[at91rm9200], [Bitbanging on AT91RM9200-based SBCs], [AT91RM9200]]])
@@ -327,7 +330,8 @@ AC_ARG_ADAPTERS([
JTAG_VPI_ADAPTER,
RSHIM_ADAPTER,
XVC_ADAPTERS,
LIBJAYLINK_ADAPTERS
LIBJAYLINK_ADAPTERS,
CMSIS_DAP_TCP_ADAPTER
],[auto])
AC_ARG_ADAPTERS([
@@ -623,6 +627,7 @@ PROCESS_ADAPTERS([LIBGPIOD_ADAPTERS], ["x$use_libgpiod" = "xyes"], [Linux libgpi
PROCESS_ADAPTERS([DMEM_ADAPTER], ["x$is_linux" = "xyes"], [Linux /dev/mem])
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([XVC_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"],
@@ -682,6 +687,8 @@ AS_IF([test "x$enable_stlink" != "xno" -o "x$enable_ti_icdi" != "xno" -o "x$enab
AM_CONDITIONAL([HLADAPTER_STLINK], [test "x$enable_stlink" != "xno"])
AM_CONDITIONAL([HLADAPTER_ICDI], [test "x$enable_ti_icdi" != "xno"])
AM_CONDITIONAL([HLADAPTER_NULINK], [test "x$enable_nulink" != "xno"])
AM_CONDITIONAL([CMSIS_DAP_CORE],
[test "x$enable_cmsis_dap" != "xno" -o "x$enable_cmsis_dap_v2" != "xno" -o "x$enable_cmsis_dap_tcp" != "xno"])
AS_IF([test "x$enable_jlink" != "xno"], [
AS_IF([test "x$use_internal_libjaylink" = "xyes"], [
@@ -832,6 +839,7 @@ m4_foreach([adapterTuple], [USB1_ADAPTERS,
AMTJTAGACCEL_ADAPTER,
HOST_ARM_BITBANG_ADAPTERS,
HOST_ARM_OR_AARCH64_BITBANG_ADAPTERS,
CMSIS_DAP_TCP_ADAPTER,
DUMMY_ADAPTER,
OPTIONAL_LIBRARIES,
COVERAGE],

View File

@@ -2621,7 +2621,7 @@ ch347 activity_led n4
@deffn {Interface Driver} {cmsis-dap}
ARM CMSIS-DAP compliant based adapter v1 (USB HID based)
or v2 (USB bulk).
or v2 (USB bulk or TCP/IP).
@deffn {Config Command} {cmsis-dap vid_pid} [vid pid]+
The vendor ID and product ID of the CMSIS-DAP device. If not specified
@@ -2632,14 +2632,17 @@ cmsis-dap vid_pid 0xc251 0xf001 0x0d28 0x0204
@end example
@end deffn
@deffn {Config Command} {cmsis-dap backend} [@option{auto}|@option{usb_bulk}|@option{hid}]
@deffn {Config Command} {cmsis-dap backend} [@option{auto}|@option{usb_bulk}|@option{hid}|@option{tcp}]
Specifies how to communicate with the adapter:
@itemize @minus
@item @option{hid} Use HID generic reports - CMSIS-DAP v1
@item @option{hid} Use USB HID generic reports - CMSIS-DAP v1
@item @option{usb_bulk} Use USB bulk - CMSIS-DAP v2
@item @option{auto} First try USB bulk CMSIS-DAP v2, if not found try HID CMSIS-DAP v1.
This is the default if @command{cmsis-dap backend} is not specified.
@item @option{tcp} Use TCP/IP instead of USB
@item @option{auto} First try USB bulk CMSIS-DAP v2, if not found try USB HID
CMSIS-DAP v1, if not found try TCP (if @command{cmsis-dap tcp host} is
configured). This is the default if @command{cmsis-dap backend} is not
specified.
@end itemize
@end deffn
@@ -2649,6 +2652,36 @@ In most cases need not to be specified and interfaces are searched by
interface string or for user class interface.
@end deffn
@deffn {Config Command} {cmsis-dap tcp host} hostname
Specifies the @var{hostname} or IP address of the remote programmer. For use
with 'tcp' backend only.
@example
cmsis-dap backend tcp
cmsis-dap tcp host 192.168.1.4
@end example
@end deffn
@deffn {Config Command} {cmsis-dap tcp port} port
Specifies the TCP @var{port} number used by the remote programmer for DAP
commands. The default port is 4441. For use with 'tcp' backend only.
@example
cmsis-dap backend tcp
cmsis-dap tcp host 192.168.1.4
cmsis-dap tcp port 4441
@end example
@end deffn
@deffn {Config Command} {cmsis-dap tcp min_timeout} milliseconds
Sets a lower bound on the requested timeout in @var{milliseconds} to wait for
response packets from the remote programmer when using the 'tcp' backend. The
user may want to use a larger value on slower networks, to avoid getting
command mismatch errors. Default value is 150 milliseconds. For use with 'tcp'
backend only.
@example
cmsis-dap tcp min_timeout 200
@end example
@end deffn
@deffn {Command} {cmsis-dap quirk} [@option{enable}|@option{disable}]
Enables or disables the following workarounds of known CMSIS-DAP adapter
quirks:

View File

@@ -225,6 +225,20 @@ static inline int socket_select(int max_fd,
#endif
}
static inline int socket_recv_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;

View File

@@ -186,15 +186,17 @@ endif
if OPENJTAG
DRIVERFILES += %D%/openjtag.c
endif
if CMSIS_DAP_CORE
DRIVERFILES += %D%/cmsis_dap.c
endif
if CMSIS_DAP_HID
DRIVERFILES += %D%/cmsis_dap_usb_hid.c
DRIVERFILES += %D%/cmsis_dap.c
endif
if CMSIS_DAP_USB
DRIVERFILES += %D%/cmsis_dap_usb_bulk.c
if !CMSIS_DAP_HID
DRIVERFILES += %D%/cmsis_dap.c
endif
if CMSIS_DAP_TCP
DRIVERFILES += %D%/cmsis_dap_tcp.c
endif
if IMX_GPIO
DRIVERFILES += %D%/imx_gpio.c

View File

@@ -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 */
@@ -2275,8 +2282,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",
@@ -2293,6 +2300,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
};

View File

@@ -77,7 +77,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

View File

@@ -0,0 +1,439 @@
// 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 <bkuschak@gmail.com> *
* *
* Adapted from cmsis_dap_usb_hid.c. Copyright (C) 2013-2018 by: *
* Mickaël Thomas <mickael9@gmail.com> *
* Maksym Hilliaka <oter@frozen-team.com> *
* Phillip Pearson <pp@myelin.co.nz> *
* Paul Fertser <fercerpav@gmail.com> *
* mike brown <mike@theshedworks.org.uk> *
* Spencer Oliver <spen@spen-soft.co.uk> *
* *
***************************************************************************/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <hidapi.h>
#ifdef HAVE_NETDB_H
#include <netdb.h>
#endif
#ifdef HAVE_NETINET_TCP_H
#include <netinet/tcp.h>
#endif
#include <stdbool.h>
#include <string.h>
#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#endif
#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.
/* When flushing after an error, the CMSIS-DAP driver assumes the pipeline is
* empty if it doesn't get a response after a short 10 msec timeout. While this
* works for USB, it may not work for TCP/IP due to higher network latency. TCP
* response packets may take longer to arrive. We set a lower bound on timeout
* for blocking reads, to give enough time for packets to arrive.
*
* The user may override this default value by setting the parameter
* 'cmsis-dap tcp min_timeout'
*/
#define DEFAULT_MIN_TIMEOUT_MS 150
/* 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 __attribute__((packed)) 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.
};
/* Defines for struct cmsis_dap_tcp_packet_hdr requested by reviewer. */
#define HEADER_SIGNATURE_OFFSET 0
#define HEADER_LENGTH_OFFSET sizeof(uint32_t)
#define HEADER_PACKET_TYPE_OFFSET (sizeof(uint32_t) + sizeof(uint16_t))
#define HEADER_RESERVED_OFFSET (sizeof(uint32_t) + sizeof(uint16_t) + \
sizeof(uint8_t))
#define HEADER_SIZE (sizeof(uint32_t) + sizeof(uint16_t) + \
2 * sizeof(uint8_t))
struct cmsis_dap_backend_data {
int sockfd;
};
static char *cmsis_dap_tcp_host;
static char *const cmsis_dap_tcp_port_default = STRINGIFY(CMSIS_DAP_TCP_PORT);
static char *cmsis_dap_tcp_port = cmsis_dap_tcp_port_default;
static int cmsis_dap_tcp_min_timeout_ms = DEFAULT_MIN_TIMEOUT_MS;
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[] __attribute__((unused)),
uint16_t pids[] __attribute__((unused)),
const char *serial __attribute__((unused)))
{
// Skip the open if the user has not provided a hostname.
if (!cmsis_dap_tcp_host) {
LOG_DEBUG("No TCP hostname, skipping open.");
return ERROR_FAIL;
}
// Ignore vids, pids, serial. We use host and port subcommands instead.
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_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_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_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 retval;
}
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 inline int readall_socket(int handle, void *buffer, unsigned int count)
{
// Return after all count bytes available, or timeout, or error.
return recv(handle, buffer, count, MSG_WAITALL);
}
static inline int peekall_socket(int handle, void *buffer, unsigned int count)
{
/* Data remains unread on the socket until recv() is called later without
* the MSG_PEEK flag. Return after all count bytes available, or timeout,
* or error.
*/
return recv(handle, buffer, count, MSG_PEEK | MSG_WAITALL);
}
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;
if (wait_ms) {
LOG_DEBUG_IO("CMSIS-DAP: using tcp timeout %d msec", wait_ms);
// Don't use very short timeouts with TCP/IP as it may not be as fast
// to respond as USB. User configurable minimum value.
if (wait_ms < cmsis_dap_tcp_min_timeout_ms) {
wait_ms = cmsis_dap_tcp_min_timeout_ms;
LOG_DEBUG_IO("CMSIS-DAP: extending timeout to %d msec", wait_ms);
}
}
socket_recv_timeout(dap->bdata->sockfd, wait_ms);
if (blocking == CMSIS_DAP_NON_BLOCKING)
socket_nonblock(dap->bdata->sockfd);
else
socket_block(dap->bdata->sockfd);
// Peek at the header first to find the length.
int retval = peekall_socket(dap->bdata->sockfd, dap->packet_buffer,
HEADER_SIZE);
LOG_DEBUG_IO("Reading header returned %d", retval);
if (retval == 0) {
LOG_DEBUG_IO("CMSIS-DAP: tcp timeout reached 1");
return ERROR_TIMEOUT_REACHED;
} else if (retval == -1) {
if (errno == EAGAIN || errno == EWOULDBLOCK) {
if (blocking == CMSIS_DAP_NON_BLOCKING)
return ERROR_TIMEOUT_REACHED;
LOG_DEBUG_IO("CMSIS-DAP: tcp timeout reached 2. timeout = %d msec",
wait_ms);
return ERROR_TIMEOUT_REACHED;
}
LOG_ERROR("CMSIS-DAP: error reading header");
log_socket_error("peek_socket");
return ERROR_FAIL;
} else if (retval != HEADER_SIZE) {
LOG_ERROR("CMSIS-DAP: short header read");
log_socket_error("peek_socket header short read");
return ERROR_FAIL;
}
struct cmsis_dap_tcp_packet_hdr header;
header.signature = le_to_h_u32(dap->packet_buffer +
HEADER_SIGNATURE_OFFSET);
header.length = le_to_h_u16(dap->packet_buffer + HEADER_LENGTH_OFFSET);
header.packet_type = dap->packet_buffer[HEADER_PACKET_TYPE_OFFSET];
header.reserved = dap->packet_buffer[HEADER_RESERVED_OFFSET];
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 + HEADER_SIZE > dap->packet_buffer_size) {
LOG_ERROR("CMSIS-DAP: Packet length %d too large to fit.",
header.length);
return ERROR_FAIL;
}
// Read the complete packet.
int read_len = HEADER_SIZE + header.length;
LOG_DEBUG_IO("Reading %d bytes (%d payload)...", read_len, header.length);
retval = readall_socket(dap->bdata->sockfd, dap->packet_buffer, read_len);
if (retval == 0) {
LOG_DEBUG_IO("CMSIS-DAP: tcp timeout reached 3");
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 != read_len) {
LOG_ERROR("CMSIS-DAP: short read. retval = %d. read_len = %d. "
"blocking = %s. wait_ms = %d", retval, read_len,
(blocking == CMSIS_DAP_NON_BLOCKING) ? "yes" : "no", wait_ms);
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 __attribute__((unused)))
{
const unsigned int len = txlen + HEADER_SIZE;
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(dap->packet_buffer + HEADER_SIGNATURE_OFFSET,
DAP_PKT_HDR_SIGNATURE);
h_u16_to_le(dap->packet_buffer + HEADER_LENGTH_OFFSET, txlen);
dap->packet_buffer[HEADER_PACKET_TYPE_OFFSET] = DAP_PKT_TYPE_REQUEST;
dap->packet_buffer[HEADER_RESERVED_OFFSET] = 0;
/* write data to device */
LOG_DEBUG_IO("Writing %d bytes (%d payload)", len, txlen);
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.
unsigned int packet_buffer_size = pkt_sz + HEADER_SIZE;
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 + HEADER_SIZE;
dap->response = dap->packet_buffer + HEADER_SIZE;
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_port)
{
if (CMD_ARGC != 1)
return ERROR_COMMAND_SYNTAX_ERROR;
if (cmsis_dap_tcp_port != cmsis_dap_tcp_port_default)
free(cmsis_dap_tcp_port);
cmsis_dap_tcp_port = strdup(CMD_ARGV[0]);
if (!cmsis_dap_tcp_port) {
LOG_ERROR("CMSIS-DAP: out of memory");
return ERROR_FAIL;
}
return ERROR_OK;
}
COMMAND_HANDLER(cmsis_dap_handle_tcp_host)
{
if (CMD_ARGC != 1)
return ERROR_COMMAND_SYNTAX_ERROR;
free(cmsis_dap_tcp_host);
cmsis_dap_tcp_host = strdup(CMD_ARGV[0]);
if (!cmsis_dap_tcp_host) {
LOG_ERROR("CMSIS-DAP: out of memory");
return ERROR_FAIL;
}
return ERROR_OK;
}
COMMAND_HANDLER(cmsis_dap_handle_tcp_min_timeout)
{
if (CMD_ARGC != 1)
return ERROR_COMMAND_SYNTAX_ERROR;
COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], cmsis_dap_tcp_min_timeout_ms);
LOG_INFO("CMSIS-DAP: using minimum timeout of %d ms for TCP packets.",
cmsis_dap_tcp_min_timeout_ms);
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 = "port",
.handler = &cmsis_dap_handle_tcp_port,
.mode = COMMAND_CONFIG,
.help = "set the port number to use for DAP (for TCP backend only)",
.usage = "<port_number>",
},
{
.name = "min_timeout",
.handler = &cmsis_dap_handle_tcp_min_timeout,
.mode = COMMAND_CONFIG,
.help = "set the minimum timeout in milliseconds to wait for response "
"packets (for TCP backend only)",
.usage = "<milliseconds>",
},
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,
};

View File

@@ -0,0 +1,18 @@
# SPDX-License-Identifier: GPL-2.0-or-later
#
# cmsis-dap-tcp - CMSIS-DAP protocol over TCP/IP.
#
adapter driver cmsis-dap
cmsis-dap backend tcp
# Specify the hostname or IP address of your programmer.
# cmsis-dap tcp host 192.168.1.4
# Optionally specify a port number. Default port is 4441.
# cmsis-dap tcp port 4441
# Optionally set a lower bound on packet timeouts (milliseconds), if using a
# slow network.
# cmsis-dap tcp min_timeout 300