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 =
nullptr;
81 struct termios currentTermios;
82 EnterCriticalSection(&pComm->ReadLock);
84 if (!CommIsHandled(hDevice))
87 if (lpOverlapped !=
nullptr)
89 SetLastError(ERROR_NOT_SUPPORTED);
93 if (lpNumberOfBytesRead ==
nullptr)
96 ERROR_INVALID_PARAMETER);
100 *lpNumberOfBytesRead = 0;
102 if (nNumberOfBytesToRead <= 0)
107 if (tcgetattr(pComm->fd, ¤tTermios) < 0)
109 SetLastError(ERROR_IO_DEVICE);
113 if (currentTermios.c_lflag & ICANON)
115 CommLog_Print(WLOG_WARN,
"Canonical mode not supported");
116 SetLastError(ERROR_NOT_SUPPORTED);
139 pTimeouts = &(pComm->timeouts);
141 if ((pTimeouts->ReadIntervalTimeout == MAXULONG) &&
142 (pTimeouts->ReadTotalTimeoutConstant == MAXULONG))
146 "ReadIntervalTimeout and ReadTotalTimeoutConstant cannot be both set to MAXULONG");
147 SetLastError(ERROR_INVALID_PARAMETER);
153 if ((pTimeouts->ReadIntervalTimeout == MAXULONG) &&
154 (pTimeouts->ReadTotalTimeoutMultiplier == 0) && (pTimeouts->ReadTotalTimeoutConstant == 0))
170 if ((pTimeouts->ReadIntervalTimeout > 0) && (pTimeouts->ReadIntervalTimeout < MAXULONG))
173 vtime = svtime(pTimeouts->ReadIntervalTimeout);
177 pTmaxTimeout = &tmaxTimeout;
179 if ((pTimeouts->ReadIntervalTimeout == MAXULONG) &&
180 (pTimeouts->ReadTotalTimeoutMultiplier == MAXULONG))
183 Tmax = pTimeouts->ReadTotalTimeoutConstant;
188 Tmax = 1ll * nNumberOfBytesToRead * pTimeouts->ReadTotalTimeoutMultiplier +
189 1ll * pTimeouts->ReadTotalTimeoutConstant;
192 if ((Tmax == 0) && (pTimeouts->ReadIntervalTimeout < MAXULONG) &&
193 (pTimeouts->ReadTotalTimeoutMultiplier == 0))
194 pTmaxTimeout =
nullptr;
197 if ((currentTermios.c_cc[VMIN] != vmin) || (currentTermios.c_cc[VTIME] != vtime))
199 currentTermios.c_cc[VMIN] = vmin;
200 currentTermios.c_cc[VTIME] = vtime;
202 if (tcsetattr(pComm->fd, TCSANOW, ¤tTermios) < 0)
204 CommLog_Print(WLOG_WARN,
205 "CommReadFile failure, could not apply new timeout values: VMIN=%" PRIu8
206 ", VTIME=%" PRIu8
"",
208 SetLastError(ERROR_IO_DEVICE);
215 if (pTmaxTimeout !=
nullptr)
217 ZeroMemory(pTmaxTimeout,
sizeof(
struct timeval));
221 pTmaxTimeout->tv_sec = Tmax / 1000;
222 pTmaxTimeout->tv_usec = (Tmax % 1000) * 1000;
229#if defined(WINPR_HAVE_SYS_EVENTFD_H)
232 (void)eventfd_read(pComm->fd_read_event, &val);
235 biggestFd = pComm->fd_read;
237 if (pComm->fd_read_event > biggestFd)
238 biggestFd = pComm->fd_read_event;
241 WINPR_ASSERT(pComm->fd_read_event < FD_SETSIZE);
242 WINPR_ASSERT(pComm->fd_read < FD_SETSIZE);
243 FD_SET(pComm->fd_read_event, &read_set);
244 FD_SET(pComm->fd_read, &read_set);
245 nbFds = select(biggestFd + 1, &read_set,
nullptr,
nullptr, pTmaxTimeout);
249 char ebuffer[256] = WINPR_C_ARRAY_INIT;
250 CommLog_Print(WLOG_WARN,
"select() failure, errno=[%d] %s\n", errno,
251 winpr_strerror(errno, ebuffer,
sizeof(ebuffer)));
252 SetLastError(ERROR_IO_DEVICE);
259 SetLastError(ERROR_TIMEOUT);
265 if (FD_ISSET(pComm->fd_read_event, &read_set))
267#if defined(WINPR_HAVE_SYS_EVENTFD_H)
270 if (eventfd_read(pComm->fd_read_event, &event) < 0)
279 char ebuffer[256] = WINPR_C_ARRAY_INIT;
280 CommLog_Print(WLOG_WARN,
281 "unexpected error on reading fd_read_event, errno=[%d] %s\n", errno,
282 winpr_strerror(errno, ebuffer,
sizeof(ebuffer)));
286 WINPR_ASSERT(errno == EAGAIN);
289 if (event == WINPR_PURGE_RXABORT)
291 SetLastError(ERROR_CANCELLED);
295 WINPR_ASSERT(event == WINPR_PURGE_RXABORT);
299 if (FD_ISSET(pComm->fd_read, &read_set))
301 ssize_t nbRead = read(pComm->fd_read, lpBuffer, nNumberOfBytesToRead);
303 if ((nbRead < 0) || (nbRead > nNumberOfBytesToRead))
305 char ebuffer[256] = WINPR_C_ARRAY_INIT;
306 CommLog_Print(WLOG_WARN,
307 "CommReadFile failed, ReadIntervalTimeout=%" PRIu32
308 ", ReadTotalTimeoutMultiplier=%" PRIu32
309 ", ReadTotalTimeoutConstant=%" PRIu32
" VMIN=%u, VTIME=%u",
310 pTimeouts->ReadIntervalTimeout, pTimeouts->ReadTotalTimeoutMultiplier,
311 pTimeouts->ReadTotalTimeoutConstant, currentTermios.c_cc[VMIN],
312 currentTermios.c_cc[VTIME]);
314 WLOG_WARN,
"CommReadFile failed, nNumberOfBytesToRead=%" PRIu32
", errno=[%d] %s",
315 nNumberOfBytesToRead, errno, winpr_strerror(errno, ebuffer,
sizeof(ebuffer)));
322 else if (errno == EBADF)
324 SetLastError(ERROR_BAD_DEVICE);
330 SetLastError(ERROR_IO_DEVICE);
338 SetLastError(ERROR_TIMEOUT);
342 *lpNumberOfBytesRead = WINPR_ASSERTING_INT_CAST(UINT32, nbRead);
344 EnterCriticalSection(&pComm->EventsLock);
345 if (pComm->PendingEvents & SERIAL_EV_WINPR_WAITING)
347 if (pComm->eventChar !=
'\0' &&
348 memchr(lpBuffer, pComm->eventChar, WINPR_ASSERTING_INT_CAST(
size_t, nbRead)))
349 pComm->PendingEvents |= SERIAL_EV_RXCHAR;
351 LeaveCriticalSection(&pComm->EventsLock);
356 *lpNumberOfBytesRead = 0;
358 LeaveCriticalSection(&pComm->ReadLock);
361 LeaveCriticalSection(&pComm->ReadLock);
372BOOL CommWriteFile(HANDLE hDevice, LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite,
373 LPDWORD lpNumberOfBytesWritten,
LPOVERLAPPED lpOverlapped)
375 WINPR_COMM* pComm = (WINPR_COMM*)hDevice;
376 struct timeval tmaxTimeout;
377 struct timeval* pTmaxTimeout =
nullptr;
378 EnterCriticalSection(&pComm->WriteLock);
380 if (!CommIsHandled(hDevice))
383 if (lpOverlapped !=
nullptr)
385 SetLastError(ERROR_NOT_SUPPORTED);
389 if (lpNumberOfBytesWritten ==
nullptr)
392 ERROR_INVALID_PARAMETER);
396 *lpNumberOfBytesWritten = 0;
398 if (nNumberOfBytesToWrite <= 0)
407#if defined(WINPR_HAVE_SYS_EVENTFD_H)
410 (void)eventfd_read(pComm->fd_write_event, &val);
416 const LONGLONG Tmax =
417 1ll * nNumberOfBytesToWrite * pComm->timeouts.WriteTotalTimeoutMultiplier +
418 1ll * pComm->timeouts.WriteTotalTimeoutConstant;
422 pTmaxTimeout = &tmaxTimeout;
423 ZeroMemory(pTmaxTimeout,
sizeof(
struct timeval));
427 pTmaxTimeout->tv_sec = Tmax / 1000;
428 pTmaxTimeout->tv_usec = (Tmax % 1000) * 1000;
430 else if ((pComm->timeouts.WriteTotalTimeoutMultiplier == 0) &&
431 (pComm->timeouts.WriteTotalTimeoutConstant == 0))
433 pTmaxTimeout =
nullptr;
439 while (*lpNumberOfBytesWritten < nNumberOfBytesToWrite)
445 biggestFd = pComm->fd_write;
447 if (pComm->fd_write_event > biggestFd)
448 biggestFd = pComm->fd_write_event;
452 WINPR_ASSERT(pComm->fd_write_event < FD_SETSIZE);
453 WINPR_ASSERT(pComm->fd_write < FD_SETSIZE);
454 FD_SET(pComm->fd_write_event, &event_set);
455 FD_SET(pComm->fd_write, &write_set);
456 nbFds = select(biggestFd + 1, &event_set, &write_set,
nullptr, pTmaxTimeout);
460 char ebuffer[256] = WINPR_C_ARRAY_INIT;
461 CommLog_Print(WLOG_WARN,
"select() failure, errno=[%d] %s\n", errno,
462 winpr_strerror(errno, ebuffer,
sizeof(ebuffer)));
463 SetLastError(ERROR_IO_DEVICE);
470 SetLastError(ERROR_TIMEOUT);
476 if (FD_ISSET(pComm->fd_write_event, &event_set))
478#if defined(WINPR_HAVE_SYS_EVENTFD_H)
481 if (eventfd_read(pComm->fd_write_event, &event) < 0)
490 char ebuffer[256] = WINPR_C_ARRAY_INIT;
491 CommLog_Print(WLOG_WARN,
492 "unexpected error on reading fd_write_event, errno=[%d] %s\n",
493 errno, winpr_strerror(errno, ebuffer,
sizeof(ebuffer)));
497 WINPR_ASSERT(errno == EAGAIN);
500 if (event == WINPR_PURGE_TXABORT)
502 SetLastError(ERROR_CANCELLED);
506 WINPR_ASSERT(event == WINPR_PURGE_TXABORT);
512 if (FD_ISSET(pComm->fd_write, &write_set))
514 ssize_t nbWritten = 0;
515 const BYTE* ptr = lpBuffer;
516 nbWritten = write(pComm->fd_write, &ptr[*lpNumberOfBytesWritten],
517 nNumberOfBytesToWrite - (*lpNumberOfBytesWritten));
521 char ebuffer[256] = WINPR_C_ARRAY_INIT;
522 CommLog_Print(WLOG_WARN,
523 "CommWriteFile failed after %" PRIu32
524 " bytes written, errno=[%d] %s\n",
525 *lpNumberOfBytesWritten, errno,
526 winpr_strerror(errno, ebuffer,
sizeof(ebuffer)));
533 else if (errno == EBADF)
535 SetLastError(ERROR_BAD_DEVICE);
541 SetLastError(ERROR_IO_DEVICE);
546 *lpNumberOfBytesWritten += nbWritten;
558 tcdrain(pComm->fd_write);
561 LeaveCriticalSection(&pComm->WriteLock);
565 LeaveCriticalSection(&pComm->WriteLock);