Skip to content

Commit 8967382

Browse files
Brian Kuschakborneoa
authored andcommitted
jtag/drivers/cmsis_dap_tcp: fix socket handling for Windows
Windows does not support socket recv() with a combination of MSG_PEEK and MSG_WAITALL flags. Work around this limitation in a way that works for both Windows and other platforms. Change-Id: Ib77e2cc872e5fe3d1fc41034010b86390131fff3 Fixes: https://sourceforge.net/p/openocd/tickets/457/ Signed-off-by: Brian Kuschak <[email protected]> Reviewed-on: https://review.openocd.org/c/openocd/+/9136 Reviewed-by: IRON ALEKS <[email protected]> Reviewed-by: Tomas Vanek <[email protected]> Tested-by: jenkins
1 parent 5bf6333 commit 8967382

File tree

1 file changed

+107
-4
lines changed

1 file changed

+107
-4
lines changed

src/jtag/drivers/cmsis_dap_tcp.c

Lines changed: 107 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@
2020
#include "config.h"
2121
#endif
2222

23+
#ifdef HAVE_ARPA_INET_H
24+
#include <arpa/inet.h>
25+
#endif
26+
#include <errno.h>
2327
#ifdef HAVE_NETDB_H
2428
#include <netdb.h>
2529
#endif
@@ -28,10 +32,21 @@
2832
#endif
2933
#include <stdbool.h>
3034
#include <string.h>
35+
#ifdef HAVE_SYS_IOCTL_H
36+
#include <sys/ioctl.h>
37+
#endif
3138
#ifdef HAVE_SYS_SOCKET_H
3239
#include <sys/socket.h>
3340
#endif
3441
#include <sys/types.h>
42+
#ifdef HAVE_UNISTD_H
43+
#include <unistd.h>
44+
#endif
45+
46+
#ifdef _WIN32
47+
#include <winsock2.h>
48+
#include <ws2tcpip.h>
49+
#endif
3550

3651
#include "helper/command.h"
3752
#include "helper/log.h"
@@ -193,19 +208,107 @@ static void cmsis_dap_tcp_close(struct cmsis_dap *dap)
193208
cmsis_dap_tcp_free(dap);
194209
}
195210

211+
static int socket_bytes_available(int sock, unsigned int *out_avail)
212+
{
213+
#ifdef _WIN32
214+
u_long avail = 0;
215+
if (ioctlsocket((SOCKET)sock, FIONREAD, &avail) == SOCKET_ERROR)
216+
return -1;
217+
#else
218+
int avail = 0;
219+
if (ioctl(sock, FIONREAD, &avail) < 0)
220+
return -1;
221+
#endif
222+
*out_avail = avail;
223+
return 0;
224+
}
225+
196226
static inline int readall_socket(int handle, void *buffer, unsigned int count)
197227
{
198228
// Return after all count bytes available, or timeout, or error.
199229
return recv(handle, buffer, count, MSG_WAITALL);
200230
}
201231

202-
static inline int peekall_socket(int handle, void *buffer, unsigned int count)
232+
static int peekall_socket(int handle, void *buffer, unsigned int count,
233+
enum cmsis_dap_blocking blocking, unsigned int timeout_ms)
203234
{
204-
/* Data remains unread on the socket until recv() is called later without
235+
/* Windows doesn't support MSG_PEEK in combination with MSG_WAITALL:
236+
* return recv(handle, buffer, count, MSG_PEEK | MSG_WAITALL);
237+
*
238+
* So, use this method instead which should work for Windows and others.
239+
*
240+
* Data remains unread on the socket until recv() is called later without
205241
* the MSG_PEEK flag. Return after all count bytes available, or timeout,
206242
* or error.
207243
*/
208-
return recv(handle, buffer, count, MSG_PEEK | MSG_WAITALL);
244+
245+
if (count == 0)
246+
return 0;
247+
248+
while (true) {
249+
int ret;
250+
unsigned int avail;
251+
if (socket_bytes_available(handle, &avail) < 0)
252+
return -1;
253+
254+
if (avail >= count) {
255+
ret = recv(handle, (char *)buffer, (int)count, MSG_PEEK);
256+
if (ret < 0) {
257+
#ifdef _WIN32
258+
int err = WSAGetLastError();
259+
if (err == WSAEINTR)
260+
continue;
261+
if (err == WSAEWOULDBLOCK)
262+
return -1;
263+
#else
264+
if (errno == EINTR)
265+
continue;
266+
if (errno == EAGAIN || errno == EWOULDBLOCK)
267+
return -1; // Timeout or nonblocking.
268+
#endif
269+
}
270+
return ret; // 0: Closed, <0: Other error, >0 Success.
271+
}
272+
273+
// Not enough data available.
274+
if (blocking == CMSIS_DAP_NON_BLOCKING) {
275+
#ifdef _WIN32
276+
WSASetLastError(WSAEWOULDBLOCK);
277+
#else
278+
errno = EAGAIN;
279+
#endif
280+
return -1;
281+
}
282+
283+
// Blocking wait.
284+
fd_set rfds;
285+
FD_ZERO(&rfds);
286+
FD_SET(handle, &rfds);
287+
288+
struct timeval tv;
289+
tv.tv_sec = timeout_ms / 1000;
290+
tv.tv_usec = (timeout_ms % 1000) * 1000;
291+
292+
ret = select(handle + 1, &rfds, NULL, NULL, &tv);
293+
if (ret > 0)
294+
continue; // Readable
295+
296+
if (ret == 0) { // Timeout
297+
#ifdef _WIN32
298+
WSASetLastError(WSAEWOULDBLOCK);
299+
#else
300+
errno = EAGAIN;
301+
#endif
302+
return -1;
303+
}
304+
305+
// Error
306+
#ifndef _WIN32
307+
if (errno == EINTR)
308+
continue;
309+
#endif
310+
return ret;
311+
}
209312
}
210313

211314
static int cmsis_dap_tcp_read(struct cmsis_dap *dap, int transfer_timeout_ms,
@@ -232,7 +335,7 @@ static int cmsis_dap_tcp_read(struct cmsis_dap *dap, int transfer_timeout_ms,
232335

233336
// Peek at the header first to find the length.
234337
int retval = peekall_socket(dap->bdata->sockfd, dap->packet_buffer,
235-
HEADER_SIZE);
338+
HEADER_SIZE, blocking, wait_ms);
236339
LOG_DEBUG_IO("Reading header returned %d", retval);
237340
if (retval == 0) {
238341
LOG_DEBUG_IO("CMSIS-DAP: tcp timeout reached 1");

0 commit comments

Comments
 (0)