FreeRDP
Loading...
Searching...
No Matches
comm_io.c
1
20#include <winpr/config.h>
21
22#include <winpr/assert.h>
23#include <errno.h>
24#include <termios.h>
25#include <unistd.h>
26
27#include <winpr/io.h>
28#include <winpr/wlog.h>
29#include <winpr/wtypes.h>
30
31#include "comm.h"
32
33BOOL _comm_set_permissive(HANDLE hDevice, BOOL permissive)
34{
35 WINPR_COMM* pComm = (WINPR_COMM*)hDevice;
36
37 if (!CommIsHandled(hDevice))
38 return FALSE;
39
40 pComm->permissive = permissive;
41 return TRUE;
42}
43
44/* Computes VTIME in deciseconds from Ti in milliseconds */
45static UCHAR svtime(ULONG Ti)
46{
47 /* FIXME: look for an equivalent math function otherwise let
48 * do the compiler do the optimization */
49 if (Ti == 0)
50 return 0;
51 else if (Ti < 100)
52 return 1;
53 else if (Ti > 25500)
54 return 255; /* 0xFF */
55 else
56 return (UCHAR)(Ti / 100);
57}
58
68BOOL CommReadFile(HANDLE hDevice, LPVOID lpBuffer, DWORD nNumberOfBytesToRead,
69 LPDWORD lpNumberOfBytesRead, LPOVERLAPPED lpOverlapped)
70{
71 WINPR_COMM* pComm = (WINPR_COMM*)hDevice;
72 int biggestFd = -1;
73 fd_set read_set;
74 int nbFds = 0;
75 COMMTIMEOUTS* pTimeouts = nullptr;
76 UCHAR vmin = 0;
77 UCHAR vtime = 0;
78 LONGLONG Tmax = 0;
79 struct timeval tmaxTimeout;
80 struct timeval* pTmaxTimeout = nullptr;
81 struct termios currentTermios;
82 EnterCriticalSection(&pComm->ReadLock); /* KISSer by the function's beginning */
83
84 if (!CommIsHandled(hDevice))
85 goto return_false;
86
87 if (lpOverlapped != nullptr)
88 {
89 SetLastError(ERROR_NOT_SUPPORTED);
90 goto return_false;
91 }
92
93 if (lpNumberOfBytesRead == nullptr)
94 {
95 SetLastError(
96 ERROR_INVALID_PARAMETER); /* since we doesn't support lpOverlapped != nullptr */
97 goto return_false;
98 }
99
100 *lpNumberOfBytesRead = 0; /* will be adjusted if required ... */
101
102 if (nNumberOfBytesToRead <= 0) /* N */
103 {
104 goto return_true; /* FIXME: or FALSE? */
105 }
106
107 if (tcgetattr(pComm->fd, &currentTermios) < 0)
108 {
109 SetLastError(ERROR_IO_DEVICE);
110 goto return_false;
111 }
112
113 if (currentTermios.c_lflag & ICANON)
114 {
115 CommLog_Print(WLOG_WARN, "Canonical mode not supported"); /* the timeout could not be set */
116 SetLastError(ERROR_NOT_SUPPORTED);
117 goto return_false;
118 }
119
120 /* http://msdn.microsoft.com/en-us/library/hh439614%28v=vs.85%29.aspx
121 * http://msdn.microsoft.com/en-us/library/windows/hardware/hh439614%28v=vs.85%29.aspx
122 *
123 * ReadIntervalTimeout | ReadTotalTimeoutMultiplier | ReadTotalTimeoutConstant | VMIN | VTIME |
124 * TMAX | 0 | 0 | 0 | N | 0 |
125 * INDEF | Blocks for N bytes available. 0< Ti <MAXULONG | 0 | 0 |
126 * N | Ti | INDEF | Blocks on first byte, then use Ti between bytes. MAXULONG | 0 | 0
127 * | 0 | 0 | 0 | Returns immediately with bytes available (don't block) MAXULONG |
128 * MAXULONG | 0< Tc <MAXULONG | N | 0 | Tc | Blocks on first byte
129 * during Tc or returns immediately with bytes available MAXULONG | m |
130 * MAXULONG | | Invalid 0 | m | 0< Tc
131 * <MAXULONG | N | 0 | Tmax | Blocks on first byte during Tmax or returns
132 * immediately with bytes available 0< Ti <MAXULONG | m | 0<
133 * Tc <MAXULONG | N | Ti | Tmax | Blocks on first byte, then use Ti between bytes.
134 * Tmax is used for the whole system call.
135 */
136 /* NB: timeouts are in milliseconds, VTIME are in deciseconds and is an unsigned char */
137 /* FIXME: double check whether open(pComm->fd_read_event, O_NONBLOCK) doesn't conflict with
138 * above use cases */
139 pTimeouts = &(pComm->timeouts);
140
141 if ((pTimeouts->ReadIntervalTimeout == MAXULONG) &&
142 (pTimeouts->ReadTotalTimeoutConstant == MAXULONG))
143 {
144 CommLog_Print(
145 WLOG_WARN,
146 "ReadIntervalTimeout and ReadTotalTimeoutConstant cannot be both set to MAXULONG");
147 SetLastError(ERROR_INVALID_PARAMETER);
148 goto return_false;
149 }
150
151 /* VMIN */
152
153 if ((pTimeouts->ReadIntervalTimeout == MAXULONG) &&
154 (pTimeouts->ReadTotalTimeoutMultiplier == 0) && (pTimeouts->ReadTotalTimeoutConstant == 0))
155 {
156 vmin = 0;
157 }
158 else
159 {
160 /* N */
161 /* vmin = nNumberOfBytesToRead < 256 ? nNumberOfBytesToRead : 255;*/ /* 0xFF */
162 /* NB: we might wait endlessly with vmin=N, prefer to
163 * force vmin=1 and return with bytes
164 * available. FIXME: is a feature disarded here? */
165 vmin = 1;
166 }
167
168 /* VTIME */
169
170 if ((pTimeouts->ReadIntervalTimeout > 0) && (pTimeouts->ReadIntervalTimeout < MAXULONG))
171 {
172 /* Ti */
173 vtime = svtime(pTimeouts->ReadIntervalTimeout);
174 }
175
176 /* TMAX */
177 pTmaxTimeout = &tmaxTimeout;
178
179 if ((pTimeouts->ReadIntervalTimeout == MAXULONG) &&
180 (pTimeouts->ReadTotalTimeoutMultiplier == MAXULONG))
181 {
182 /* Tc */
183 Tmax = pTimeouts->ReadTotalTimeoutConstant;
184 }
185 else
186 {
187 /* Tmax */
188 Tmax = 1ll * nNumberOfBytesToRead * pTimeouts->ReadTotalTimeoutMultiplier +
189 1ll * pTimeouts->ReadTotalTimeoutConstant;
190
191 /* INDEFinitely */
192 if ((Tmax == 0) && (pTimeouts->ReadIntervalTimeout < MAXULONG) &&
193 (pTimeouts->ReadTotalTimeoutMultiplier == 0))
194 pTmaxTimeout = nullptr;
195 }
196
197 if ((currentTermios.c_cc[VMIN] != vmin) || (currentTermios.c_cc[VTIME] != vtime))
198 {
199 currentTermios.c_cc[VMIN] = vmin;
200 currentTermios.c_cc[VTIME] = vtime;
201
202 if (tcsetattr(pComm->fd, TCSANOW, &currentTermios) < 0)
203 {
204 CommLog_Print(WLOG_WARN,
205 "CommReadFile failure, could not apply new timeout values: VMIN=%" PRIu8
206 ", VTIME=%" PRIu8 "",
207 vmin, vtime);
208 SetLastError(ERROR_IO_DEVICE);
209 goto return_false;
210 }
211 }
212
213 /* wait indefinitely if pTmaxTimeout is nullptr */
214
215 if (pTmaxTimeout != nullptr)
216 {
217 ZeroMemory(pTmaxTimeout, sizeof(struct timeval));
218
219 if (Tmax > 0) /* return immdiately if Tmax == 0 */
220 {
221 pTmaxTimeout->tv_sec = Tmax / 1000; /* s */
222 pTmaxTimeout->tv_usec = (Tmax % 1000) * 1000; /* us */
223 }
224 }
225
226 /* FIXME: had expected eventfd_write() to return EAGAIN when
227 * there is no eventfd_read() but this not the case. */
228 /* discard a possible and no more relevant event */
229#if defined(WINPR_HAVE_SYS_EVENTFD_H)
230 {
231 eventfd_t val = 0;
232 (void)eventfd_read(pComm->fd_read_event, &val);
233 }
234#endif
235 biggestFd = pComm->fd_read;
236
237 if (pComm->fd_read_event > biggestFd)
238 biggestFd = pComm->fd_read_event;
239
240 FD_ZERO(&read_set);
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);
246
247 if (nbFds < 0)
248 {
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);
253 goto return_false;
254 }
255
256 if (nbFds == 0)
257 {
258 /* timeout */
259 SetLastError(ERROR_TIMEOUT);
260 goto return_false;
261 }
262
263 /* read_set */
264
265 if (FD_ISSET(pComm->fd_read_event, &read_set))
266 {
267#if defined(WINPR_HAVE_SYS_EVENTFD_H)
268 eventfd_t event = 0;
269
270 if (eventfd_read(pComm->fd_read_event, &event) < 0)
271 {
272 if (errno == EAGAIN)
273 {
274 WINPR_ASSERT(FALSE); /* not quite sure this should ever happen */
275 /* keep on */
276 }
277 else
278 {
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)));
283 /* FIXME: goto return_false ? */
284 }
285
286 WINPR_ASSERT(errno == EAGAIN);
287 }
288
289 if (event == WINPR_PURGE_RXABORT)
290 {
291 SetLastError(ERROR_CANCELLED);
292 goto return_false;
293 }
294
295 WINPR_ASSERT(event == WINPR_PURGE_RXABORT); /* no other expected event so far */
296#endif
297 }
298
299 if (FD_ISSET(pComm->fd_read, &read_set))
300 {
301 ssize_t nbRead = read(pComm->fd_read, lpBuffer, nNumberOfBytesToRead);
302
303 if ((nbRead < 0) || (nbRead > nNumberOfBytesToRead))
304 {
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]);
313 CommLog_Print(
314 WLOG_WARN, "CommReadFile failed, nNumberOfBytesToRead=%" PRIu32 ", errno=[%d] %s",
315 nNumberOfBytesToRead, errno, winpr_strerror(errno, ebuffer, sizeof(ebuffer)));
316
317 if (errno == EAGAIN)
318 {
319 /* keep on */
320 goto return_true; /* expect a read-loop to be implemented on the server side */
321 }
322 else if (errno == EBADF)
323 {
324 SetLastError(ERROR_BAD_DEVICE); /* STATUS_INVALID_DEVICE_REQUEST */
325 goto return_false;
326 }
327 else
328 {
329 WINPR_ASSERT(FALSE);
330 SetLastError(ERROR_IO_DEVICE);
331 goto return_false;
332 }
333 }
334
335 if (nbRead == 0)
336 {
337 /* termios timeout */
338 SetLastError(ERROR_TIMEOUT);
339 goto return_false;
340 }
341
342 *lpNumberOfBytesRead = WINPR_ASSERTING_INT_CAST(UINT32, nbRead);
343
344 EnterCriticalSection(&pComm->EventsLock);
345 if (pComm->PendingEvents & SERIAL_EV_WINPR_WAITING)
346 {
347 if (pComm->eventChar != '\0' &&
348 memchr(lpBuffer, pComm->eventChar, WINPR_ASSERTING_INT_CAST(size_t, nbRead)))
349 pComm->PendingEvents |= SERIAL_EV_RXCHAR;
350 }
351 LeaveCriticalSection(&pComm->EventsLock);
352 goto return_true;
353 }
354
355 WINPR_ASSERT(FALSE);
356 *lpNumberOfBytesRead = 0;
357return_false:
358 LeaveCriticalSection(&pComm->ReadLock);
359 return FALSE;
360return_true:
361 LeaveCriticalSection(&pComm->ReadLock);
362 return TRUE;
363}
364
372BOOL CommWriteFile(HANDLE hDevice, LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite,
373 LPDWORD lpNumberOfBytesWritten, LPOVERLAPPED lpOverlapped)
374{
375 WINPR_COMM* pComm = (WINPR_COMM*)hDevice;
376 struct timeval tmaxTimeout;
377 struct timeval* pTmaxTimeout = nullptr;
378 EnterCriticalSection(&pComm->WriteLock); /* KISSer by the function's beginning */
379
380 if (!CommIsHandled(hDevice))
381 goto return_false;
382
383 if (lpOverlapped != nullptr)
384 {
385 SetLastError(ERROR_NOT_SUPPORTED);
386 goto return_false;
387 }
388
389 if (lpNumberOfBytesWritten == nullptr)
390 {
391 SetLastError(
392 ERROR_INVALID_PARAMETER); /* since we doesn't support lpOverlapped != nullptr */
393 goto return_false;
394 }
395
396 *lpNumberOfBytesWritten = 0; /* will be adjusted if required ... */
397
398 if (nNumberOfBytesToWrite <= 0)
399 {
400 goto return_true; /* FIXME: or FALSE? */
401 }
402
403 /* FIXME: had expected eventfd_write() to return EAGAIN when
404 * there is no eventfd_read() but this not the case. */
405 /* discard a possible and no more relevant event */
406
407#if defined(WINPR_HAVE_SYS_EVENTFD_H)
408 {
409 eventfd_t val = 0;
410 (void)eventfd_read(pComm->fd_write_event, &val);
411 }
412#endif
413
414 {
415 /* ms */
416 const LONGLONG Tmax =
417 1ll * nNumberOfBytesToWrite * pComm->timeouts.WriteTotalTimeoutMultiplier +
418 1ll * pComm->timeouts.WriteTotalTimeoutConstant;
419 /* NB: select() may update the timeout argument to indicate
420 * how much time was left. Keep the timeout variable out of
421 * the while() */
422 pTmaxTimeout = &tmaxTimeout;
423 ZeroMemory(pTmaxTimeout, sizeof(struct timeval));
424
425 if (Tmax > 0)
426 {
427 pTmaxTimeout->tv_sec = Tmax / 1000; /* s */
428 pTmaxTimeout->tv_usec = (Tmax % 1000) * 1000; /* us */
429 }
430 else if ((pComm->timeouts.WriteTotalTimeoutMultiplier == 0) &&
431 (pComm->timeouts.WriteTotalTimeoutConstant == 0))
432 {
433 pTmaxTimeout = nullptr;
434 }
435 }
436
437 /* else return immdiately */
438
439 while (*lpNumberOfBytesWritten < nNumberOfBytesToWrite)
440 {
441 int biggestFd = -1;
442 fd_set event_set;
443 fd_set write_set;
444 int nbFds = 0;
445 biggestFd = pComm->fd_write;
446
447 if (pComm->fd_write_event > biggestFd)
448 biggestFd = pComm->fd_write_event;
449
450 FD_ZERO(&event_set);
451 FD_ZERO(&write_set);
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);
457
458 if (nbFds < 0)
459 {
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);
464 goto return_false;
465 }
466
467 if (nbFds == 0)
468 {
469 /* timeout */
470 SetLastError(ERROR_TIMEOUT);
471 goto return_false;
472 }
473
474 /* event_set */
475
476 if (FD_ISSET(pComm->fd_write_event, &event_set))
477 {
478#if defined(WINPR_HAVE_SYS_EVENTFD_H)
479 eventfd_t event = 0;
480
481 if (eventfd_read(pComm->fd_write_event, &event) < 0)
482 {
483 if (errno == EAGAIN)
484 {
485 WINPR_ASSERT(FALSE); /* not quite sure this should ever happen */
486 /* keep on */
487 }
488 else
489 {
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)));
494 /* FIXME: goto return_false ? */
495 }
496
497 WINPR_ASSERT(errno == EAGAIN);
498 }
499
500 if (event == WINPR_PURGE_TXABORT)
501 {
502 SetLastError(ERROR_CANCELLED);
503 goto return_false;
504 }
505
506 WINPR_ASSERT(event == WINPR_PURGE_TXABORT); /* no other expected event so far */
507#endif
508 }
509
510 /* write_set */
511
512 if (FD_ISSET(pComm->fd_write, &write_set))
513 {
514 ssize_t nbWritten = 0;
515 const BYTE* ptr = lpBuffer;
516 nbWritten = write(pComm->fd_write, &ptr[*lpNumberOfBytesWritten],
517 nNumberOfBytesToWrite - (*lpNumberOfBytesWritten));
518
519 if (nbWritten < 0)
520 {
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)));
527
528 if (errno == EAGAIN)
529 {
530 /* keep on */
531 continue;
532 }
533 else if (errno == EBADF)
534 {
535 SetLastError(ERROR_BAD_DEVICE); /* STATUS_INVALID_DEVICE_REQUEST */
536 goto return_false;
537 }
538 else
539 {
540 WINPR_ASSERT(FALSE);
541 SetLastError(ERROR_IO_DEVICE);
542 goto return_false;
543 }
544 }
545
546 *lpNumberOfBytesWritten += nbWritten;
547 }
548 } /* while */
549
550 /* FIXME: this call to tcdrain() doesn't look correct and
551 * might hide a bug but was required while testing a serial
552 * printer. Its driver was expecting the modem line status
553 * SERIAL_MSR_DSR true after the sending which was never
554 * happening otherwise. A purge was also done before each
555 * Write operation. The serial port was opened with:
556 * DesiredAccess=0x0012019F. The printer worked fine with
557 * mstsc. */
558 tcdrain(pComm->fd_write);
559
560return_true:
561 LeaveCriticalSection(&pComm->WriteLock);
562 return TRUE;
563
564return_false:
565 LeaveCriticalSection(&pComm->WriteLock);
566 return FALSE;
567}