20#include <winpr/config.h>
22#include <winpr/assert.h>
28#include <winpr/wlog.h>
29#include <winpr/wtypes.h>
33BOOL _comm_set_permissive(HANDLE hDevice, BOOL permissive)
35 WINPR_COMM* pComm = (WINPR_COMM*)hDevice;
37 if (!CommIsHandled(hDevice))
40 pComm->permissive = permissive;
45static UCHAR svtime(ULONG Ti)
56 return (UCHAR)(Ti / 100);
68BOOL CommReadFile(HANDLE hDevice, LPVOID lpBuffer, DWORD nNumberOfBytesToRead,
71 WINPR_COMM* pComm = (WINPR_COMM*)hDevice;
79 struct timeval tmaxTimeout;
80 struct timeval* pTmaxTimeout = NULL;
81 struct termios currentTermios;
82 EnterCriticalSection(&pComm->ReadLock);
84 if (!CommIsHandled(hDevice))
87 if (lpOverlapped != NULL)
89 SetLastError(ERROR_NOT_SUPPORTED);
93 if (lpNumberOfBytesRead == NULL)
95 SetLastError(ERROR_INVALID_PARAMETER);
99 *lpNumberOfBytesRead = 0;
101 if (nNumberOfBytesToRead <= 0)
106 if (tcgetattr(pComm->fd, ¤tTermios) < 0)
108 SetLastError(ERROR_IO_DEVICE);
112 if (currentTermios.c_lflag & ICANON)
114 CommLog_Print(WLOG_WARN,
"Canonical mode not supported");
115 SetLastError(ERROR_NOT_SUPPORTED);
138 pTimeouts = &(pComm->timeouts);
140 if ((pTimeouts->ReadIntervalTimeout == MAXULONG) &&
141 (pTimeouts->ReadTotalTimeoutConstant == MAXULONG))
145 "ReadIntervalTimeout and ReadTotalTimeoutConstant cannot be both set to MAXULONG");
146 SetLastError(ERROR_INVALID_PARAMETER);
152 if ((pTimeouts->ReadIntervalTimeout == MAXULONG) &&
153 (pTimeouts->ReadTotalTimeoutMultiplier == 0) && (pTimeouts->ReadTotalTimeoutConstant == 0))
169 if ((pTimeouts->ReadIntervalTimeout > 0) && (pTimeouts->ReadIntervalTimeout < MAXULONG))
172 vtime = svtime(pTimeouts->ReadIntervalTimeout);
176 pTmaxTimeout = &tmaxTimeout;
178 if ((pTimeouts->ReadIntervalTimeout == MAXULONG) &&
179 (pTimeouts->ReadTotalTimeoutMultiplier == MAXULONG))
182 Tmax = pTimeouts->ReadTotalTimeoutConstant;
187 Tmax = 1ll * nNumberOfBytesToRead * pTimeouts->ReadTotalTimeoutMultiplier +
188 1ll * pTimeouts->ReadTotalTimeoutConstant;
191 if ((Tmax == 0) && (pTimeouts->ReadIntervalTimeout < MAXULONG) &&
192 (pTimeouts->ReadTotalTimeoutMultiplier == 0))
196 if ((currentTermios.c_cc[VMIN] != vmin) || (currentTermios.c_cc[VTIME] != vtime))
198 currentTermios.c_cc[VMIN] = vmin;
199 currentTermios.c_cc[VTIME] = vtime;
201 if (tcsetattr(pComm->fd, TCSANOW, ¤tTermios) < 0)
203 CommLog_Print(WLOG_WARN,
204 "CommReadFile failure, could not apply new timeout values: VMIN=%" PRIu8
205 ", VTIME=%" PRIu8
"",
207 SetLastError(ERROR_IO_DEVICE);
214 if (pTmaxTimeout != NULL)
216 ZeroMemory(pTmaxTimeout,
sizeof(
struct timeval));
220 pTmaxTimeout->tv_sec = Tmax / 1000;
221 pTmaxTimeout->tv_usec = (Tmax % 1000) * 1000;
228#if defined(WINPR_HAVE_SYS_EVENTFD_H)
231 (void)eventfd_read(pComm->fd_read_event, &val);
234 biggestFd = pComm->fd_read;
236 if (pComm->fd_read_event > biggestFd)
237 biggestFd = pComm->fd_read_event;
240 WINPR_ASSERT(pComm->fd_read_event < FD_SETSIZE);
241 WINPR_ASSERT(pComm->fd_read < FD_SETSIZE);
242 FD_SET(pComm->fd_read_event, &read_set);
243 FD_SET(pComm->fd_read, &read_set);
244 nbFds = select(biggestFd + 1, &read_set, NULL, NULL, pTmaxTimeout);
248 char ebuffer[256] = { 0 };
249 CommLog_Print(WLOG_WARN,
"select() failure, errno=[%d] %s\n", errno,
250 winpr_strerror(errno, ebuffer,
sizeof(ebuffer)));
251 SetLastError(ERROR_IO_DEVICE);
258 SetLastError(ERROR_TIMEOUT);
264 if (FD_ISSET(pComm->fd_read_event, &read_set))
266#if defined(WINPR_HAVE_SYS_EVENTFD_H)
269 if (eventfd_read(pComm->fd_read_event, &event) < 0)
278 char ebuffer[256] = { 0 };
279 CommLog_Print(WLOG_WARN,
280 "unexpected error on reading fd_read_event, errno=[%d] %s\n", errno,
281 winpr_strerror(errno, ebuffer,
sizeof(ebuffer)));
285 WINPR_ASSERT(errno == EAGAIN);
288 if (event == WINPR_PURGE_RXABORT)
290 SetLastError(ERROR_CANCELLED);
294 WINPR_ASSERT(event == WINPR_PURGE_RXABORT);
298 if (FD_ISSET(pComm->fd_read, &read_set))
300 ssize_t nbRead = read(pComm->fd_read, lpBuffer, nNumberOfBytesToRead);
302 if ((nbRead < 0) || (nbRead > nNumberOfBytesToRead))
304 char ebuffer[256] = { 0 };
305 CommLog_Print(WLOG_WARN,
306 "CommReadFile failed, ReadIntervalTimeout=%" PRIu32
307 ", ReadTotalTimeoutMultiplier=%" PRIu32
308 ", ReadTotalTimeoutConstant=%" PRIu32
" VMIN=%u, VTIME=%u",
309 pTimeouts->ReadIntervalTimeout, pTimeouts->ReadTotalTimeoutMultiplier,
310 pTimeouts->ReadTotalTimeoutConstant, currentTermios.c_cc[VMIN],
311 currentTermios.c_cc[VTIME]);
313 WLOG_WARN,
"CommReadFile failed, nNumberOfBytesToRead=%" PRIu32
", errno=[%d] %s",
314 nNumberOfBytesToRead, errno, winpr_strerror(errno, ebuffer,
sizeof(ebuffer)));
321 else if (errno == EBADF)
323 SetLastError(ERROR_BAD_DEVICE);
329 SetLastError(ERROR_IO_DEVICE);
337 SetLastError(ERROR_TIMEOUT);
341 *lpNumberOfBytesRead = WINPR_ASSERTING_INT_CAST(UINT32, nbRead);
343 EnterCriticalSection(&pComm->EventsLock);
344 if (pComm->PendingEvents & SERIAL_EV_WINPR_WAITING)
346 if (pComm->eventChar !=
'\0' &&
347 memchr(lpBuffer, pComm->eventChar, WINPR_ASSERTING_INT_CAST(
size_t, nbRead)))
348 pComm->PendingEvents |= SERIAL_EV_RXCHAR;
350 LeaveCriticalSection(&pComm->EventsLock);
355 *lpNumberOfBytesRead = 0;
357 LeaveCriticalSection(&pComm->ReadLock);
360 LeaveCriticalSection(&pComm->ReadLock);
371BOOL CommWriteFile(HANDLE hDevice, LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite,
372 LPDWORD lpNumberOfBytesWritten,
LPOVERLAPPED lpOverlapped)
374 WINPR_COMM* pComm = (WINPR_COMM*)hDevice;
375 struct timeval tmaxTimeout;
376 struct timeval* pTmaxTimeout = NULL;
377 EnterCriticalSection(&pComm->WriteLock);
379 if (!CommIsHandled(hDevice))
382 if (lpOverlapped != NULL)
384 SetLastError(ERROR_NOT_SUPPORTED);
388 if (lpNumberOfBytesWritten == NULL)
390 SetLastError(ERROR_INVALID_PARAMETER);
394 *lpNumberOfBytesWritten = 0;
396 if (nNumberOfBytesToWrite <= 0)
405#if defined(WINPR_HAVE_SYS_EVENTFD_H)
408 (void)eventfd_read(pComm->fd_write_event, &val);
413 LONGLONG Tmax = 1ll * nNumberOfBytesToWrite * pComm->timeouts.WriteTotalTimeoutMultiplier +
414 1ll * pComm->timeouts.WriteTotalTimeoutConstant;
418 pTmaxTimeout = &tmaxTimeout;
419 ZeroMemory(pTmaxTimeout,
sizeof(
struct timeval));
423 pTmaxTimeout->tv_sec = Tmax / 1000;
424 pTmaxTimeout->tv_usec = (Tmax % 1000) * 1000;
426 else if ((pComm->timeouts.WriteTotalTimeoutMultiplier == 0) &&
427 (pComm->timeouts.WriteTotalTimeoutConstant == 0))
434 while (*lpNumberOfBytesWritten < nNumberOfBytesToWrite)
440 biggestFd = pComm->fd_write;
442 if (pComm->fd_write_event > biggestFd)
443 biggestFd = pComm->fd_write_event;
447 WINPR_ASSERT(pComm->fd_write_event < FD_SETSIZE);
448 WINPR_ASSERT(pComm->fd_write < FD_SETSIZE);
449 FD_SET(pComm->fd_write_event, &event_set);
450 FD_SET(pComm->fd_write, &write_set);
451 nbFds = select(biggestFd + 1, &event_set, &write_set, NULL, pTmaxTimeout);
455 char ebuffer[256] = { 0 };
456 CommLog_Print(WLOG_WARN,
"select() failure, errno=[%d] %s\n", errno,
457 winpr_strerror(errno, ebuffer,
sizeof(ebuffer)));
458 SetLastError(ERROR_IO_DEVICE);
465 SetLastError(ERROR_TIMEOUT);
471 if (FD_ISSET(pComm->fd_write_event, &event_set))
473#if defined(WINPR_HAVE_SYS_EVENTFD_H)
476 if (eventfd_read(pComm->fd_write_event, &event) < 0)
485 char ebuffer[256] = { 0 };
486 CommLog_Print(WLOG_WARN,
487 "unexpected error on reading fd_write_event, errno=[%d] %s\n",
488 errno, winpr_strerror(errno, ebuffer,
sizeof(ebuffer)));
492 WINPR_ASSERT(errno == EAGAIN);
495 if (event == WINPR_PURGE_TXABORT)
497 SetLastError(ERROR_CANCELLED);
501 WINPR_ASSERT(event == WINPR_PURGE_TXABORT);
507 if (FD_ISSET(pComm->fd_write, &write_set))
509 ssize_t nbWritten = 0;
510 const BYTE* ptr = lpBuffer;
511 nbWritten = write(pComm->fd_write, &ptr[*lpNumberOfBytesWritten],
512 nNumberOfBytesToWrite - (*lpNumberOfBytesWritten));
516 char ebuffer[256] = { 0 };
517 CommLog_Print(WLOG_WARN,
518 "CommWriteFile failed after %" PRIu32
519 " bytes written, errno=[%d] %s\n",
520 *lpNumberOfBytesWritten, errno,
521 winpr_strerror(errno, ebuffer,
sizeof(ebuffer)));
528 else if (errno == EBADF)
530 SetLastError(ERROR_BAD_DEVICE);
536 SetLastError(ERROR_IO_DEVICE);
541 *lpNumberOfBytesWritten += nbWritten;
553 tcdrain(pComm->fd_write);
556 LeaveCriticalSection(&pComm->WriteLock);
560 LeaveCriticalSection(&pComm->WriteLock);