20 #include <winpr/config.h>
22 #include <winpr/assert.h>
28 #include <winpr/wlog.h>
29 #include <winpr/wtypes.h>
33 BOOL _comm_set_permissive(HANDLE hDevice, BOOL permissive)
35 WINPR_COMM* pComm = (WINPR_COMM*)hDevice;
37 if (!CommIsHandled(hDevice))
40 pComm->permissive = permissive;
45 static UCHAR svtime(ULONG Ti)
56 return (UCHAR)(Ti / 100);
68 BOOL 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)
229 eventfd_read(pComm->fd_read_event, NULL);
231 biggestFd = pComm->fd_read;
233 if (pComm->fd_read_event > biggestFd)
234 biggestFd = pComm->fd_read_event;
237 WINPR_ASSERT(pComm->fd_read_event < FD_SETSIZE);
238 WINPR_ASSERT(pComm->fd_read < FD_SETSIZE);
239 FD_SET(pComm->fd_read_event, &read_set);
240 FD_SET(pComm->fd_read, &read_set);
241 nbFds = select(biggestFd + 1, &read_set, NULL, NULL, pTmaxTimeout);
245 char ebuffer[256] = { 0 };
246 CommLog_Print(WLOG_WARN,
"select() failure, errno=[%d] %s\n", errno,
247 winpr_strerror(errno, ebuffer,
sizeof(ebuffer)));
248 SetLastError(ERROR_IO_DEVICE);
255 SetLastError(ERROR_TIMEOUT);
261 if (FD_ISSET(pComm->fd_read_event, &read_set))
263 #if defined(WINPR_HAVE_SYS_EVENTFD_H)
266 if (eventfd_read(pComm->fd_read_event, &event) < 0)
275 char ebuffer[256] = { 0 };
276 CommLog_Print(WLOG_WARN,
277 "unexpected error on reading fd_read_event, errno=[%d] %s\n", errno,
278 winpr_strerror(errno, ebuffer,
sizeof(ebuffer)));
282 WINPR_ASSERT(errno == EAGAIN);
285 if (event == WINPR_PURGE_RXABORT)
287 SetLastError(ERROR_CANCELLED);
291 WINPR_ASSERT(event == WINPR_PURGE_RXABORT);
295 if (FD_ISSET(pComm->fd_read, &read_set))
297 ssize_t nbRead = read(pComm->fd_read, lpBuffer, nNumberOfBytesToRead);
299 if ((nbRead < 0) || (nbRead > nNumberOfBytesToRead))
301 char ebuffer[256] = { 0 };
302 CommLog_Print(WLOG_WARN,
303 "CommReadFile failed, ReadIntervalTimeout=%" PRIu32
304 ", ReadTotalTimeoutMultiplier=%" PRIu32
305 ", ReadTotalTimeoutConstant=%" PRIu32
" VMIN=%u, VTIME=%u",
306 pTimeouts->ReadIntervalTimeout, pTimeouts->ReadTotalTimeoutMultiplier,
307 pTimeouts->ReadTotalTimeoutConstant, currentTermios.c_cc[VMIN],
308 currentTermios.c_cc[VTIME]);
310 WLOG_WARN,
"CommReadFile failed, nNumberOfBytesToRead=%" PRIu32
", errno=[%d] %s",
311 nNumberOfBytesToRead, errno, winpr_strerror(errno, ebuffer,
sizeof(ebuffer)));
318 else if (errno == EBADF)
320 SetLastError(ERROR_BAD_DEVICE);
326 SetLastError(ERROR_IO_DEVICE);
334 SetLastError(ERROR_TIMEOUT);
338 *lpNumberOfBytesRead = WINPR_ASSERTING_INT_CAST(UINT32, nbRead);
340 EnterCriticalSection(&pComm->EventsLock);
341 if (pComm->PendingEvents & SERIAL_EV_WINPR_WAITING)
343 if (pComm->eventChar !=
'\0' &&
344 memchr(lpBuffer, pComm->eventChar, WINPR_ASSERTING_INT_CAST(
size_t, nbRead)))
345 pComm->PendingEvents |= SERIAL_EV_RXCHAR;
347 LeaveCriticalSection(&pComm->EventsLock);
352 *lpNumberOfBytesRead = 0;
354 LeaveCriticalSection(&pComm->ReadLock);
357 LeaveCriticalSection(&pComm->ReadLock);
368 BOOL CommWriteFile(HANDLE hDevice, LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite,
369 LPDWORD lpNumberOfBytesWritten,
LPOVERLAPPED lpOverlapped)
371 WINPR_COMM* pComm = (WINPR_COMM*)hDevice;
372 struct timeval tmaxTimeout;
373 struct timeval* pTmaxTimeout = NULL;
374 EnterCriticalSection(&pComm->WriteLock);
376 if (!CommIsHandled(hDevice))
379 if (lpOverlapped != NULL)
381 SetLastError(ERROR_NOT_SUPPORTED);
385 if (lpNumberOfBytesWritten == NULL)
387 SetLastError(ERROR_INVALID_PARAMETER);
391 *lpNumberOfBytesWritten = 0;
393 if (nNumberOfBytesToWrite <= 0)
402 #if defined(WINPR_HAVE_SYS_EVENTFD_H)
403 eventfd_read(pComm->fd_write_event, NULL);
407 LONGLONG Tmax = 1ll * nNumberOfBytesToWrite * pComm->timeouts.WriteTotalTimeoutMultiplier +
408 1ll * pComm->timeouts.WriteTotalTimeoutConstant;
412 pTmaxTimeout = &tmaxTimeout;
413 ZeroMemory(pTmaxTimeout,
sizeof(
struct timeval));
417 pTmaxTimeout->tv_sec = Tmax / 1000;
418 pTmaxTimeout->tv_usec = (Tmax % 1000) * 1000;
420 else if ((pComm->timeouts.WriteTotalTimeoutMultiplier == 0) &&
421 (pComm->timeouts.WriteTotalTimeoutConstant == 0))
428 while (*lpNumberOfBytesWritten < nNumberOfBytesToWrite)
434 biggestFd = pComm->fd_write;
436 if (pComm->fd_write_event > biggestFd)
437 biggestFd = pComm->fd_write_event;
441 WINPR_ASSERT(pComm->fd_write_event < FD_SETSIZE);
442 WINPR_ASSERT(pComm->fd_write < FD_SETSIZE);
443 FD_SET(pComm->fd_write_event, &event_set);
444 FD_SET(pComm->fd_write, &write_set);
445 nbFds = select(biggestFd + 1, &event_set, &write_set, NULL, pTmaxTimeout);
449 char ebuffer[256] = { 0 };
450 CommLog_Print(WLOG_WARN,
"select() failure, errno=[%d] %s\n", errno,
451 winpr_strerror(errno, ebuffer,
sizeof(ebuffer)));
452 SetLastError(ERROR_IO_DEVICE);
459 SetLastError(ERROR_TIMEOUT);
465 if (FD_ISSET(pComm->fd_write_event, &event_set))
467 #if defined(WINPR_HAVE_SYS_EVENTFD_H)
470 if (eventfd_read(pComm->fd_write_event, &event) < 0)
479 char ebuffer[256] = { 0 };
480 CommLog_Print(WLOG_WARN,
481 "unexpected error on reading fd_write_event, errno=[%d] %s\n",
482 errno, winpr_strerror(errno, ebuffer,
sizeof(ebuffer)));
486 WINPR_ASSERT(errno == EAGAIN);
489 if (event == WINPR_PURGE_TXABORT)
491 SetLastError(ERROR_CANCELLED);
495 WINPR_ASSERT(event == WINPR_PURGE_TXABORT);
501 if (FD_ISSET(pComm->fd_write, &write_set))
503 ssize_t nbWritten = 0;
504 nbWritten = write(pComm->fd_write, ((
const BYTE*)lpBuffer) + (*lpNumberOfBytesWritten),
505 nNumberOfBytesToWrite - (*lpNumberOfBytesWritten));
509 char ebuffer[256] = { 0 };
510 CommLog_Print(WLOG_WARN,
511 "CommWriteFile failed after %" PRIu32
512 " bytes written, errno=[%d] %s\n",
513 *lpNumberOfBytesWritten, errno,
514 winpr_strerror(errno, ebuffer,
sizeof(ebuffer)));
521 else if (errno == EBADF)
523 SetLastError(ERROR_BAD_DEVICE);
529 SetLastError(ERROR_IO_DEVICE);
534 *lpNumberOfBytesWritten += nbWritten;
546 tcdrain(pComm->fd_write);
549 LeaveCriticalSection(&pComm->WriteLock);
553 LeaveCriticalSection(&pComm->WriteLock);