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