OpenOCD
cmsis_dap_tcp.c
Go to the documentation of this file.
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 
3 /***************************************************************************
4  * Provides CMSIS-DAP protocol over a TCP/IP socket. *
5  * UART and SWO are currently unsupported. *
6  * *
7  * Copyright (C) 2025 by Brian Kuschak <bkuschak@gmail.com> *
8  * *
9  * Adapted from cmsis_dap_usb_hid.c. Copyright (C) 2013-2018 by: *
10  * MickaĆ«l Thomas <mickael9@gmail.com> *
11  * Maksym Hilliaka <oter@frozen-team.com> *
12  * Phillip Pearson <pp@myelin.co.nz> *
13  * Paul Fertser <fercerpav@gmail.com> *
14  * mike brown <mike@theshedworks.org.uk> *
15  * Spencer Oliver <spen@spen-soft.co.uk> *
16  * *
17  ***************************************************************************/
18 
19 #ifdef HAVE_CONFIG_H
20 #include "config.h"
21 #endif
22 
23 #ifdef HAVE_NETDB_H
24 #include <netdb.h>
25 #endif
26 #ifdef HAVE_NETINET_TCP_H
27 #include <netinet/tcp.h>
28 #endif
29 #include <stdbool.h>
30 #include <string.h>
31 #ifdef HAVE_SYS_SOCKET_H
32 #include <sys/socket.h>
33 #endif
34 #include <sys/types.h>
35 
36 #include "helper/command.h"
37 #include "helper/log.h"
38 #include "helper/replacements.h"
39 #include "helper/system.h"
40 #include "cmsis_dap.h"
41 
42 #define STRINGIFY(x) #x
43 
44 // If the protocol changes in the future, the SIGNATURE should also be changed.
45 #define DAP_PKT_HDR_SIGNATURE 0x00504144 // "DAP"
46 #define DAP_PKT_TYPE_REQUEST 0x01
47 #define DAP_PKT_TYPE_RESPONSE 0x02
48 
49 #define CMSIS_DAP_TCP_PORT 4441 // Default. Can be overridden.
50 #define CMSIS_DAP_PACKET_SIZE 1024 // Max payload size not including
51  // header.
52 
53 /* When flushing after an error, the CMSIS-DAP driver assumes the pipeline is
54  * empty if it doesn't get a response after a short 10 msec timeout. While this
55  * works for USB, it may not work for TCP/IP due to higher network latency. TCP
56  * response packets may take longer to arrive. We set a lower bound on timeout
57  * for blocking reads, to give enough time for packets to arrive.
58  *
59  * The user may override this default value by setting the parameter
60  * 'cmsis-dap tcp min_timeout'
61  */
62 #define DEFAULT_MIN_TIMEOUT_MS 150
63 
64 /* CMSIS-DAP requests are variable length. With CMSIS-DAP over USB, the
65  * transfer sizes are preserved by the USB stack. However, TCP/IP is stream
66  * oriented so we perform our own packetization to preserve the boundaries
67  * between each request. This short header is prepended to each CMSIS-DAP
68  * request and response before being sent over the socket. Little endian format
69  * is used for multibyte values.
70  */
71 struct __attribute__((packed)) cmsis_dap_tcp_packet_hdr {
72  uint32_t signature; // "DAP"
73  uint16_t length; // Not including header length.
74  uint8_t packet_type;
75  uint8_t reserved; // Reserved for future use.
76 };
77 
78 /* Defines for struct cmsis_dap_tcp_packet_hdr requested by reviewer. */
79 #define HEADER_SIGNATURE_OFFSET 0
80 #define HEADER_LENGTH_OFFSET sizeof(uint32_t)
81 #define HEADER_PACKET_TYPE_OFFSET (sizeof(uint32_t) + sizeof(uint16_t))
82 #define HEADER_RESERVED_OFFSET (sizeof(uint32_t) + sizeof(uint16_t) + \
83  sizeof(uint8_t))
84 #define HEADER_SIZE (sizeof(uint32_t) + sizeof(uint16_t) + \
85  2 * sizeof(uint8_t))
86 
88  int sockfd;
89 };
90 
91 static char *cmsis_dap_tcp_host;
95 
96 static void cmsis_dap_tcp_close(struct cmsis_dap *dap);
97 static int cmsis_dap_tcp_alloc(struct cmsis_dap *dap, unsigned int pkt_sz);
98 static void cmsis_dap_tcp_free(struct cmsis_dap *dap);
99 
100 static int cmsis_dap_tcp_open(struct cmsis_dap *dap,
101  uint16_t vids[] __attribute__((unused)),
102  uint16_t pids[] __attribute__((unused)),
103  const char *serial __attribute__((unused)))
104 {
105  // Skip the open if the user has not provided a hostname.
106  if (!cmsis_dap_tcp_host) {
107  LOG_DEBUG("No TCP hostname, skipping open.");
108  return ERROR_FAIL;
109  }
110 
111  // Ignore vids, pids, serial. We use host and port subcommands instead.
112 
113  dap->bdata = malloc(sizeof(struct cmsis_dap_backend_data));
114  if (!dap->bdata) {
115  LOG_ERROR("CMSIS-DAP: unable to allocate memory");
116  return ERROR_FAIL;
117  }
118 
119  struct addrinfo hints = {
120  .ai_family = AF_UNSPEC,
121  .ai_socktype = SOCK_STREAM
122  };
123  struct addrinfo *result, *rp;
124  int fd = 0;
125 
126  LOG_INFO("CMSIS-DAP: Connecting to %s:%s using TCP backend",
127  cmsis_dap_tcp_host ? cmsis_dap_tcp_host : "localhost",
129 
130  /* Some of the following code was taken from remote_bitbang.c */
131  /* Obtain address(es) matching host/port */
132  int s = getaddrinfo(cmsis_dap_tcp_host, cmsis_dap_tcp_port, &hints,
133  &result);
134  if (s != 0) {
135  LOG_ERROR("CMSIS-DAP: getaddrinfo: %s\n", gai_strerror(s));
136  free(dap->bdata);
137  return ERROR_FAIL;
138  }
139 
140  /* getaddrinfo() returns a list of address structures.
141  Try each address until we successfully connect(2).
142  If socket(2) (or connect(2)) fails, we (close the socket
143  and) try the next address. */
144 
145  for (rp = result; rp ; rp = rp->ai_next) {
146  fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
147  if (fd == -1)
148  continue;
149 
150  if (connect(fd, rp->ai_addr, rp->ai_addrlen) != -1) {
151  LOG_DEBUG("Connected.");
152  break; /* Success */
153  }
154 
155  close(fd);
156  }
157 
158  freeaddrinfo(result);
159 
160  if (!rp) { /* No address succeeded */
161  LOG_ERROR("CMSIS-DAP: unable to connect to device %s:%s",
162  cmsis_dap_tcp_host ? cmsis_dap_tcp_host : "localhost",
164  log_socket_error("Failed to connect");
165  free(dap->bdata);
166  dap->bdata = NULL;
167  return ERROR_FAIL;
168  }
169 
170  /* Set NODELAY to minimize latency. */
171  int one = 1;
172  /* On Windows optval has to be a const char *. */
173  setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (const char *)&one, sizeof(one));
174 
175  dap->bdata->sockfd = fd;
176 
177  int retval = cmsis_dap_tcp_alloc(dap, CMSIS_DAP_PACKET_SIZE);
178  if (retval != ERROR_OK) {
179  cmsis_dap_tcp_close(dap);
180  return retval;
181  }
182  return ERROR_OK;
183 }
184 
185 static void cmsis_dap_tcp_close(struct cmsis_dap *dap)
186 {
187  if (close_socket(dap->bdata->sockfd) != 0)
188  log_socket_error("close_socket");
189 
190  if (dap->bdata)
191  free(dap->bdata);
192  dap->bdata = NULL;
193  cmsis_dap_tcp_free(dap);
194 }
195 
196 static inline int readall_socket(int handle, void *buffer, unsigned int count)
197 {
198  // Return after all count bytes available, or timeout, or error.
199  return recv(handle, buffer, count, MSG_WAITALL);
200 }
201 
202 static inline int peekall_socket(int handle, void *buffer, unsigned int count)
203 {
204  /* Data remains unread on the socket until recv() is called later without
205  * the MSG_PEEK flag. Return after all count bytes available, or timeout,
206  * or error.
207  */
208  return recv(handle, buffer, count, MSG_PEEK | MSG_WAITALL);
209 }
210 
211 static int cmsis_dap_tcp_read(struct cmsis_dap *dap, int transfer_timeout_ms,
212  enum cmsis_dap_blocking blocking)
213 {
214  int wait_ms = (blocking == CMSIS_DAP_NON_BLOCKING) ? 0 :
215  transfer_timeout_ms;
216  if (wait_ms) {
217  LOG_DEBUG_IO("CMSIS-DAP: using tcp timeout %d msec", wait_ms);
218 
219  // Don't use very short timeouts with TCP/IP as it may not be as fast
220  // to respond as USB. User configurable minimum value.
221  if (wait_ms < cmsis_dap_tcp_min_timeout_ms) {
223  LOG_DEBUG_IO("CMSIS-DAP: extending timeout to %d msec", wait_ms);
224  }
225  }
226  socket_recv_timeout(dap->bdata->sockfd, wait_ms);
227 
228  if (blocking == CMSIS_DAP_NON_BLOCKING)
230  else
231  socket_block(dap->bdata->sockfd);
232 
233  // Peek at the header first to find the length.
234  int retval = peekall_socket(dap->bdata->sockfd, dap->packet_buffer,
235  HEADER_SIZE);
236  LOG_DEBUG_IO("Reading header returned %d", retval);
237  if (retval == 0) {
238  LOG_DEBUG_IO("CMSIS-DAP: tcp timeout reached 1");
239  return ERROR_TIMEOUT_REACHED;
240  } else if (retval == -1) {
241  if (errno == EAGAIN || errno == EWOULDBLOCK) {
242  if (blocking == CMSIS_DAP_NON_BLOCKING)
243  return ERROR_TIMEOUT_REACHED;
244 
245  LOG_DEBUG_IO("CMSIS-DAP: tcp timeout reached 2. timeout = %d msec",
246  wait_ms);
247  return ERROR_TIMEOUT_REACHED;
248  }
249 
250  LOG_ERROR("CMSIS-DAP: error reading header");
251  log_socket_error("peek_socket");
252  return ERROR_FAIL;
253  } else if (retval != HEADER_SIZE) {
254  LOG_ERROR("CMSIS-DAP: short header read");
255  log_socket_error("peek_socket header short read");
256  return ERROR_FAIL;
257  }
258 
259  struct cmsis_dap_tcp_packet_hdr header;
260  header.signature = le_to_h_u32(dap->packet_buffer +
262  header.length = le_to_h_u16(dap->packet_buffer + HEADER_LENGTH_OFFSET);
263  header.packet_type = dap->packet_buffer[HEADER_PACKET_TYPE_OFFSET];
264  header.reserved = dap->packet_buffer[HEADER_RESERVED_OFFSET];
265 
266  if (header.signature != DAP_PKT_HDR_SIGNATURE) {
267  LOG_ERROR("CMSIS-DAP: Unrecognized packet signature 0x%08x",
268  header.signature);
269  return ERROR_FAIL;
270  } else if (header.packet_type != DAP_PKT_TYPE_RESPONSE) {
271  LOG_ERROR("CMSIS-DAP: Unrecognized packet type 0x%02x",
272  header.packet_type);
273  return ERROR_FAIL;
274  } else if (header.length + HEADER_SIZE > dap->packet_buffer_size) {
275  LOG_ERROR("CMSIS-DAP: Packet length %d too large to fit.",
276  header.length);
277  return ERROR_FAIL;
278  }
279 
280  // Read the complete packet.
281  int read_len = HEADER_SIZE + header.length;
282  LOG_DEBUG_IO("Reading %d bytes (%d payload)...", read_len, header.length);
283  retval = readall_socket(dap->bdata->sockfd, dap->packet_buffer, read_len);
284 
285  if (retval == 0) {
286  LOG_DEBUG_IO("CMSIS-DAP: tcp timeout reached 3");
287  return ERROR_TIMEOUT_REACHED;
288  } else if (retval == -1) {
289  LOG_ERROR("CMSIS-DAP: error reading data");
290  log_socket_error("read_socket");
291  return ERROR_FAIL;
292  } else if (retval != read_len) {
293  LOG_ERROR("CMSIS-DAP: short read. retval = %d. read_len = %d. "
294  "blocking = %s. wait_ms = %d", retval, read_len,
295  (blocking == CMSIS_DAP_NON_BLOCKING) ? "yes" : "no", wait_ms);
296  log_socket_error("read_socket short read");
297  return ERROR_FAIL;
298  }
299  return retval;
300 }
301 
302 static int cmsis_dap_tcp_write(struct cmsis_dap *dap, int txlen,
303  int timeout_ms __attribute__((unused)))
304 {
305  const unsigned int len = txlen + HEADER_SIZE;
306  if (len > dap->packet_buffer_size) {
307  LOG_ERROR("CMSIS-DAP: Packet length %d exceeds TCP buffer size!", len);
308  return ERROR_FAIL;
309  }
310 
311  /* Set the header values. */
317 
318  /* write data to device */
319  LOG_DEBUG_IO("Writing %d bytes (%d payload)", len, txlen);
320  int retval = write_socket(dap->bdata->sockfd, dap->packet_buffer, len);
321  if (retval < 0) {
322  log_socket_error("write_socket");
323  return ERROR_FAIL;
324  } else if (retval != (int)len) {
325  LOG_ERROR("CMSIS-DAP: error writing data");
326  log_socket_error("write_socket short write");
327  return ERROR_FAIL;
328  }
329  return retval;
330 }
331 
332 static int cmsis_dap_tcp_alloc(struct cmsis_dap *dap, unsigned int pkt_sz)
333 {
334  // Reserve space for the packet header.
335  unsigned int packet_buffer_size = pkt_sz + HEADER_SIZE;
336  uint8_t *buf = malloc(packet_buffer_size);
337  if (!buf) {
338  LOG_ERROR("CMSIS-DAP: unable to allocate CMSIS-DAP packet buffer");
339  return ERROR_FAIL;
340  }
341 
342  dap->packet_buffer = buf;
343  dap->packet_size = pkt_sz;
344  dap->packet_usable_size = pkt_sz;
345  dap->packet_buffer_size = packet_buffer_size;
346 
347  dap->command = dap->packet_buffer + HEADER_SIZE;
348  dap->response = dap->packet_buffer + HEADER_SIZE;
349  return ERROR_OK;
350 }
351 
352 static void cmsis_dap_tcp_free(struct cmsis_dap *dap)
353 {
354  free(dap->packet_buffer);
355  dap->packet_buffer = NULL;
356 }
357 
358 static void cmsis_dap_tcp_cancel_all(struct cmsis_dap *dap)
359 {
360 }
361 
362 COMMAND_HANDLER(cmsis_dap_handle_tcp_port)
363 {
364  if (CMD_ARGC != 1)
366 
368  free(cmsis_dap_tcp_port);
369 
370  cmsis_dap_tcp_port = strdup(CMD_ARGV[0]);
371  if (!cmsis_dap_tcp_port) {
372  LOG_ERROR("CMSIS-DAP: out of memory");
373  return ERROR_FAIL;
374  }
375  return ERROR_OK;
376 }
377 
378 COMMAND_HANDLER(cmsis_dap_handle_tcp_host)
379 {
380  if (CMD_ARGC != 1)
382 
383  free(cmsis_dap_tcp_host);
384  cmsis_dap_tcp_host = strdup(CMD_ARGV[0]);
385  if (!cmsis_dap_tcp_host) {
386  LOG_ERROR("CMSIS-DAP: out of memory");
387  return ERROR_FAIL;
388  }
389  return ERROR_OK;
390 }
391 
392 COMMAND_HANDLER(cmsis_dap_handle_tcp_min_timeout)
393 {
394  if (CMD_ARGC != 1)
396 
398  LOG_INFO("CMSIS-DAP: using minimum timeout of %d ms for TCP packets.",
400  return ERROR_OK;
401 }
402 
404  {
405  .name = "host",
406  .handler = &cmsis_dap_handle_tcp_host,
407  .mode = COMMAND_CONFIG,
408  .help = "set the host name to use (for TCP backend only)",
409  .usage = "<host_name>",
410  },
411  {
412  .name = "port",
413  .handler = &cmsis_dap_handle_tcp_port,
414  .mode = COMMAND_CONFIG,
415  .help = "set the port number to use for DAP (for TCP backend only)",
416  .usage = "<port_number>",
417  },
418  {
419  .name = "min_timeout",
420  .handler = &cmsis_dap_handle_tcp_min_timeout,
421  .mode = COMMAND_CONFIG,
422  .help = "set the minimum timeout in milliseconds to wait for response "
423  "packets (for TCP backend only)",
424  .usage = "<milliseconds>",
425  },
427 };
428 
430  .name = "tcp",
431  .open = cmsis_dap_tcp_open,
432  .close = cmsis_dap_tcp_close,
433  .read = cmsis_dap_tcp_read,
434  .write = cmsis_dap_tcp_write,
435  .packet_buffer_alloc = cmsis_dap_tcp_alloc,
436  .packet_buffer_free = cmsis_dap_tcp_free,
437  .cancel_all = cmsis_dap_tcp_cancel_all,
438 };
char * serial
Definition: adapter.c:43
cmsis_dap_blocking
Definition: cmsis_dap.h:61
@ CMSIS_DAP_NON_BLOCKING
Definition: cmsis_dap.h:62
#define HEADER_PACKET_TYPE_OFFSET
Definition: cmsis_dap_tcp.c:81
static int cmsis_dap_tcp_write(struct cmsis_dap *dap, int txlen, int timeout_ms __attribute__((unused)))
static void cmsis_dap_tcp_free(struct cmsis_dap *dap)
static char * cmsis_dap_tcp_host
Definition: cmsis_dap_tcp.c:91
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)))
#define HEADER_RESERVED_OFFSET
Definition: cmsis_dap_tcp.c:82
static int cmsis_dap_tcp_read(struct cmsis_dap *dap, int transfer_timeout_ms, enum cmsis_dap_blocking blocking)
#define DAP_PKT_HDR_SIGNATURE
Definition: cmsis_dap_tcp.c:45
#define DEFAULT_MIN_TIMEOUT_MS
Definition: cmsis_dap_tcp.c:62
static int readall_socket(int handle, void *buffer, unsigned int count)
static int cmsis_dap_tcp_alloc(struct cmsis_dap *dap, unsigned int pkt_sz)
static char * cmsis_dap_tcp_port
Definition: cmsis_dap_tcp.c:93
static int peekall_socket(int handle, void *buffer, unsigned int count)
#define HEADER_SIZE
Definition: cmsis_dap_tcp.c:84
COMMAND_HANDLER(cmsis_dap_handle_tcp_port)
static char cmsis_dap_tcp_port_default[]
Definition: cmsis_dap_tcp.c:92
static void cmsis_dap_tcp_close(struct cmsis_dap *dap)
#define STRINGIFY(x)
Definition: cmsis_dap_tcp.c:42
static void cmsis_dap_tcp_cancel_all(struct cmsis_dap *dap)
#define HEADER_LENGTH_OFFSET
Definition: cmsis_dap_tcp.c:80
#define DAP_PKT_TYPE_REQUEST
Definition: cmsis_dap_tcp.c:46
const struct command_registration cmsis_dap_tcp_subcommand_handlers[]
struct __attribute__((packed))
Definition: cmsis_dap_tcp.c:71
#define DAP_PKT_TYPE_RESPONSE
Definition: cmsis_dap_tcp.c:47
const struct cmsis_dap_backend cmsis_dap_tcp_backend
#define CMSIS_DAP_TCP_PORT
Definition: cmsis_dap_tcp.c:49
static int cmsis_dap_tcp_min_timeout_ms
Definition: cmsis_dap_tcp.c:94
#define CMSIS_DAP_PACKET_SIZE
Definition: cmsis_dap_tcp.c:50
#define HEADER_SIGNATURE_OFFSET
Definition: cmsis_dap_tcp.c:79
#define CMD_ARGV
Use this macro to access the arguments for the command being handled, rather than accessing the varia...
Definition: command.h:156
#define ERROR_COMMAND_SYNTAX_ERROR
Definition: command.h:400
#define CMD_ARGC
Use this macro to access the number of arguments for the command being handled, rather than accessing...
Definition: command.h:151
#define COMMAND_PARSE_NUMBER(type, in, out)
parses the string in into out as a type, or prints a command error and passes the error code to the c...
Definition: command.h:440
#define COMMAND_REGISTRATION_DONE
Use this as the last entry in an array of command_registration records.
Definition: command.h:251
@ COMMAND_CONFIG
Definition: command.h:41
uint64_t buffer
Pointer to data buffer to send over SPI.
Definition: dw-spi-helper.h:0
uint8_t length
Definition: esp_usb_jtag.c:1
void log_socket_error(const char *socket_desc)
Definition: log.c:496
#define LOG_DEBUG_IO(expr ...)
Definition: log.h:102
#define ERROR_FAIL
Definition: log.h:174
#define LOG_ERROR(expr ...)
Definition: log.h:133
#define ERROR_TIMEOUT_REACHED
Definition: log.h:177
#define LOG_INFO(expr ...)
Definition: log.h:127
#define LOG_DEBUG(expr ...)
Definition: log.h:110
#define ERROR_OK
Definition: log.h:168
static void socket_block(int fd)
Definition: replacements.h:193
static int socket_recv_timeout(int fd, unsigned long timeout_msec)
Definition: replacements.h:228
static int close_socket(int sock)
Definition: replacements.h:184
static int write_socket(int handle, const void *buffer, unsigned int count)
Definition: replacements.h:166
static void socket_nonblock(int fd)
Definition: replacements.h:204
const char * name
Definition: cmsis_dap.h:67
unsigned int packet_buffer_size
Definition: cmsis_dap.h:32
unsigned int packet_usable_size
Definition: cmsis_dap.h:31
unsigned int packet_size
Definition: cmsis_dap.h:30
struct cmsis_dap_backend_data * bdata
Definition: cmsis_dap.h:28
uint8_t * response
Definition: cmsis_dap.h:35
uint8_t * command
Definition: cmsis_dap.h:34
uint8_t * packet_buffer
Definition: cmsis_dap.h:33
const char * name
Definition: command.h:234
static uint16_t le_to_h_u16(const uint8_t *buf)
Definition: types.h:122
static void h_u32_to_le(uint8_t *buf, uint32_t val)
Definition: types.h:178
static void h_u16_to_le(uint8_t *buf, uint16_t val)
Definition: types.h:208
static uint32_t le_to_h_u32(const uint8_t *buf)
Definition: types.h:112
#define NULL
Definition: usb.h:16
uint8_t count[4]
Definition: vdebug.c:22