FreeRDP
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Modules Pages
comm_serial_sys.c
1
23#include <winpr/assert.h>
24#include <errno.h>
25#include <fcntl.h>
26#include <sys/ioctl.h>
27#include <termios.h>
28#include <unistd.h>
29
30#include "comm_serial_sys.h"
31#include "comm.h"
32
33#include <winpr/crt.h>
34#include <winpr/wlog.h>
35
36/* Undocumented flag, not supported everywhere.
37 * Provide a sensible fallback to avoid compilation problems. */
38#ifndef CMSPAR
39#define CMSPAR 010000000000
40#endif
41
42/* hard-coded in N_TTY */
43#define TTY_THRESHOLD_THROTTLE 128 /* now based on remaining room */
44#define TTY_THRESHOLD_UNTHROTTLE 128
45#define N_TTY_BUF_SIZE 4096
46
47#define BAUD_TABLE_END 0010020 /* __MAX_BAUD + 1 */
48
49/* 0: B* (Linux termios)
50 * 1: CBR_* or actual baud rate
51 * 2: BAUD_* (identical to SERIAL_BAUD_*)
52 */
53static const speed_t BAUD_TABLE[][3] = {
54#ifdef B0
55 { B0, 0, 0 }, /* hang up */
56#endif
57#ifdef B50
58 { B50, 50, 0 },
59#endif
60#ifdef B75
61 { B75, 75, BAUD_075 },
62#endif
63#ifdef B110
64 { B110, CBR_110, BAUD_110 },
65#endif
66#ifdef B134
67 { B134, 134, 0 /*BAUD_134_5*/ },
68#endif
69#ifdef B150
70 { B150, 150, BAUD_150 },
71#endif
72#ifdef B200
73 { B200, 200, 0 },
74#endif
75#ifdef B300
76 { B300, CBR_300, BAUD_300 },
77#endif
78#ifdef B600
79 { B600, CBR_600, BAUD_600 },
80#endif
81#ifdef B1200
82 { B1200, CBR_1200, BAUD_1200 },
83#endif
84#ifdef B1800
85 { B1800, 1800, BAUD_1800 },
86#endif
87#ifdef B2400
88 { B2400, CBR_2400, BAUD_2400 },
89#endif
90#ifdef B4800
91 { B4800, CBR_4800, BAUD_4800 },
92#endif
93/* {, ,BAUD_7200} */
94#ifdef B9600
95 { B9600, CBR_9600, BAUD_9600 },
96#endif
97/* {, CBR_14400, BAUD_14400}, /\* unsupported on Linux *\/ */
98#ifdef B19200
99 { B19200, CBR_19200, BAUD_19200 },
100#endif
101#ifdef B38400
102 { B38400, CBR_38400, BAUD_38400 },
103#endif
104/* {, CBR_56000, BAUD_56K}, /\* unsupported on Linux *\/ */
105#ifdef B57600
106 { B57600, CBR_57600, BAUD_57600 },
107#endif
108#ifdef B115200
109 { B115200, CBR_115200, BAUD_115200 },
110#endif
111/* {, CBR_128000, BAUD_128K}, /\* unsupported on Linux *\/ */
112/* {, CBR_256000, BAUD_USER}, /\* unsupported on Linux *\/ */
113#ifdef B230400
114 { B230400, 230400, BAUD_USER },
115#endif
116#ifdef B460800
117 { B460800, 460800, BAUD_USER },
118#endif
119#ifdef B500000
120 { B500000, 500000, BAUD_USER },
121#endif
122#ifdef B576000
123 { B576000, 576000, BAUD_USER },
124#endif
125#ifdef B921600
126 { B921600, 921600, BAUD_USER },
127#endif
128#ifdef B1000000
129 { B1000000, 1000000, BAUD_USER },
130#endif
131#ifdef B1152000
132 { B1152000, 1152000, BAUD_USER },
133#endif
134#ifdef B1500000
135 { B1500000, 1500000, BAUD_USER },
136#endif
137#ifdef B2000000
138 { B2000000, 2000000, BAUD_USER },
139#endif
140#ifdef B2500000
141 { B2500000, 2500000, BAUD_USER },
142#endif
143#ifdef B3000000
144 { B3000000, 3000000, BAUD_USER },
145#endif
146#ifdef B3500000
147 { B3500000, 3500000, BAUD_USER },
148#endif
149#ifdef B4000000
150 { B4000000, 4000000, BAUD_USER }, /* __MAX_BAUD */
151#endif
152 { BAUD_TABLE_END, 0, 0 }
153};
154
155static const char* get_modem_flag_str(int flag)
156{
157 if (flag & TIOCM_LE)
158 return "DSR";
159 if (flag & TIOCM_DTR)
160 return "DTR";
161 if (flag & TIOCM_RTS)
162 return "RTS";
163 if (flag & TIOCM_ST)
164 return "Secondary TXD";
165 if (flag & TIOCM_SR)
166 return "Secondary RXD";
167 if (flag & TIOCM_CTS)
168 return "CTS";
169 if (flag & TIOCM_CAR)
170 return "DCD";
171 if (flag & TIOCM_CD)
172 return "CD";
173 if (flag & TIOCM_RNG)
174 return "RNG";
175 if (flag & TIOCM_RI)
176 return "RI";
177 if (flag & TIOCM_DSR)
178 return "DSR";
179 return "UNKNOWN";
180}
181
182static const char* get_modem_status_str(int status, char* buffer, size_t size)
183{
184 const int flags[] = { TIOCM_LE, TIOCM_DTR, TIOCM_RTS, TIOCM_ST, TIOCM_SR, TIOCM_CTS,
185 TIOCM_CAR, TIOCM_CD, TIOCM_RNG, TIOCM_RI, TIOCM_DSR };
186 winpr_str_append("{", buffer, size, "");
187
188 const char* sep = "";
189 for (size_t x = 0; x < ARRAYSIZE(flags); x++)
190 {
191 const int flag = flags[x];
192 if (status & flag)
193 {
194 winpr_str_append(get_modem_flag_str(flag), buffer, size, sep);
195 sep = "|";
196 }
197 }
198
199 char number[32] = { 0 };
200 (void)_snprintf(number, sizeof(number), "}[0x%08x]", (unsigned)status);
201 winpr_str_append(number, buffer, size, "");
202 return buffer;
203}
204
205static BOOL get_properties(WINPR_ATTR_UNUSED WINPR_COMM* pComm, COMMPROP* pProperties)
206{
207 WINPR_ASSERT(pComm);
208 /* http://msdn.microsoft.com/en-us/library/windows/hardware/jj680684%28v=vs.85%29.aspx
209 * http://msdn.microsoft.com/en-us/library/windows/desktop/aa363189%28v=vs.85%29.aspx
210 */
211
212 /* FIXME: properties should be better probed. The current
213 * implementation just relies on the Linux' implementation.
214 */
215 WINPR_ASSERT(pProperties);
216 if (pProperties->dwProvSpec1 != COMMPROP_INITIALIZED)
217 {
218 ZeroMemory(pProperties, sizeof(COMMPROP));
219 pProperties->wPacketLength = sizeof(COMMPROP);
220 }
221
222 pProperties->wPacketVersion = 2;
223
224 pProperties->dwServiceMask = SERIAL_SP_SERIALCOMM;
225
226 /* pProperties->Reserved1; not used */
227
228 /* FIXME: could be implemented on top of N_TTY */
229 pProperties->dwMaxTxQueue = N_TTY_BUF_SIZE;
230 pProperties->dwMaxRxQueue = N_TTY_BUF_SIZE;
231
232 /* FIXME: to be probe on the device? */
233 pProperties->dwMaxBaud = BAUD_USER;
234
235 /* FIXME: what about PST_RS232? see also: serial_struct */
236 pProperties->dwProvSubType = PST_UNSPECIFIED;
237
238 /* TODO: to be finalized */
239 pProperties->dwProvCapabilities =
240 /*PCF_16BITMODE |*/ PCF_DTRDSR | PCF_INTTIMEOUTS | PCF_PARITY_CHECK | /*PCF_RLSD |*/
241 PCF_RTSCTS | PCF_SETXCHAR | /*PCF_SPECIALCHARS |*/ PCF_TOTALTIMEOUTS | PCF_XONXOFF;
242
243 /* TODO: double check SP_RLSD */
244 pProperties->dwSettableParams = SP_BAUD | SP_DATABITS | SP_HANDSHAKING | SP_PARITY |
245 SP_PARITY_CHECK | /*SP_RLSD |*/ SP_STOPBITS;
246
247 pProperties->dwSettableBaud = 0;
248 for (int i = 0; BAUD_TABLE[i][0] < BAUD_TABLE_END; i++)
249 {
250 pProperties->dwSettableBaud |= BAUD_TABLE[i][2];
251 }
252
253 pProperties->wSettableData =
254 DATABITS_5 | DATABITS_6 | DATABITS_7 | DATABITS_8 /*| DATABITS_16 | DATABITS_16X*/;
255
256 pProperties->wSettableStopParity = STOPBITS_10 | /*STOPBITS_15 |*/ STOPBITS_20 | PARITY_NONE |
257 PARITY_ODD | PARITY_EVEN | PARITY_MARK | PARITY_SPACE;
258
259 /* FIXME: additional input and output buffers could be implemented on top of N_TTY */
260 pProperties->dwCurrentTxQueue = N_TTY_BUF_SIZE;
261 pProperties->dwCurrentRxQueue = N_TTY_BUF_SIZE;
262
263 /* pProperties->ProvSpec1; see above */
264 /* pProperties->ProvSpec2; ignored */
265 /* pProperties->ProvChar[1]; ignored */
266
267 return TRUE;
268}
269
270static BOOL set_baud_rate(WINPR_COMM* pComm, const SERIAL_BAUD_RATE* pBaudRate)
271{
272 speed_t newSpeed = 0;
273 struct termios futureState = { 0 };
274
275 WINPR_ASSERT(pComm);
276 WINPR_ASSERT(pBaudRate);
277
278 if (tcgetattr(pComm->fd, &futureState) <
279 0) /* NB: preserves current settings not directly handled by the Communication Functions */
280 {
281 SetLastError(ERROR_IO_DEVICE);
282 return FALSE;
283 }
284
285 for (int i = 0; BAUD_TABLE[i][0] < BAUD_TABLE_END; i++)
286 {
287 if (BAUD_TABLE[i][1] == pBaudRate->BaudRate)
288 {
289 newSpeed = BAUD_TABLE[i][0];
290 if (cfsetspeed(&futureState, newSpeed) < 0)
291 {
292 CommLog_Print(WLOG_WARN, "failed to set speed 0x%x (%" PRIu32 ")", newSpeed,
293 pBaudRate->BaudRate);
294 return FALSE;
295 }
296
297 WINPR_ASSERT(cfgetispeed(&futureState) == newSpeed);
298
299 if (comm_ioctl_tcsetattr(pComm->fd, TCSANOW, &futureState) < 0)
300 {
301 CommLog_Print(WLOG_WARN, "comm_ioctl_tcsetattr failure: last-error: 0x%" PRIX32 "",
302 GetLastError());
303 return FALSE;
304 }
305
306 return TRUE;
307 }
308 }
309
310 CommLog_Print(WLOG_WARN, "could not find a matching speed for the baud rate %" PRIu32 "",
311 pBaudRate->BaudRate);
312 SetLastError(ERROR_INVALID_DATA);
313 return FALSE;
314}
315
316static BOOL get_baud_rate(WINPR_COMM* pComm, SERIAL_BAUD_RATE* pBaudRate)
317{
318 speed_t currentSpeed = 0;
319 struct termios currentState = { 0 };
320
321 WINPR_ASSERT(pComm);
322 WINPR_ASSERT(pBaudRate);
323
324 if (tcgetattr(pComm->fd, &currentState) < 0)
325 {
326 SetLastError(ERROR_IO_DEVICE);
327 return FALSE;
328 }
329
330 currentSpeed = cfgetispeed(&currentState);
331
332 for (int i = 0; BAUD_TABLE[i][0] < BAUD_TABLE_END; i++)
333 {
334 if (BAUD_TABLE[i][0] == currentSpeed)
335 {
336 pBaudRate->BaudRate = BAUD_TABLE[i][1];
337 return TRUE;
338 }
339 }
340
341 CommLog_Print(WLOG_WARN, "could not find a matching baud rate for the speed 0x%x",
342 currentSpeed);
343 SetLastError(ERROR_INVALID_DATA);
344 return FALSE;
345}
346
356static BOOL set_serial_chars(WINPR_COMM* pComm, const SERIAL_CHARS* pSerialChars)
357{
358 BOOL result = TRUE;
359 struct termios upcomingTermios = { 0 };
360
361 WINPR_ASSERT(pComm);
362 WINPR_ASSERT(pSerialChars);
363
364 if (tcgetattr(pComm->fd, &upcomingTermios) < 0)
365 {
366 SetLastError(ERROR_IO_DEVICE);
367 return FALSE;
368 }
369
370 /* termios(3): (..) above symbolic subscript values are all
371 * different, except that VTIME, VMIN may have the same value
372 * as VEOL, VEOF, respectively. In noncanonical mode the
373 * special character meaning is replaced by the timeout
374 * meaning.
375 *
376 * EofChar and c_cc[VEOF] are not quite the same, prefer to
377 * don't use c_cc[VEOF] at all.
378 *
379 * FIXME: might be implemented during read/write I/O
380 */
381 if (pSerialChars->EofChar != '\0')
382 {
383 CommLog_Print(WLOG_WARN, "EofChar %02" PRIX8 " cannot be set\n", pSerialChars->EofChar);
384 SetLastError(ERROR_NOT_SUPPORTED);
385 result = FALSE; /* but keep on */
386 }
387
388 /* According the Linux's n_tty discipline, characters with a
389 * parity error can only be let unchanged, replaced by \0 or
390 * get the prefix the prefix \377 \0
391 */
392
393 /* FIXME: see also: set_handflow() */
394 if (pSerialChars->ErrorChar != '\0')
395 {
396 CommLog_Print(WLOG_WARN, "ErrorChar 0x%02" PRIX8 " ('%c') cannot be set (unsupported).\n",
397 pSerialChars->ErrorChar, (char)pSerialChars->ErrorChar);
398 SetLastError(ERROR_NOT_SUPPORTED);
399 result = FALSE; /* but keep on */
400 }
401
402 /* FIXME: see also: set_handflow() */
403 if (pSerialChars->BreakChar != '\0')
404 {
405 CommLog_Print(WLOG_WARN, "BreakChar 0x%02" PRIX8 " ('%c') cannot be set (unsupported).\n",
406 pSerialChars->BreakChar, (char)pSerialChars->BreakChar);
407 SetLastError(ERROR_NOT_SUPPORTED);
408 result = FALSE; /* but keep on */
409 }
410
411 if (pSerialChars->EventChar != '\0')
412 {
413 pComm->eventChar = pSerialChars->EventChar;
414 }
415
416 upcomingTermios.c_cc[VSTART] = pSerialChars->XonChar;
417
418 upcomingTermios.c_cc[VSTOP] = pSerialChars->XoffChar;
419
420 if (comm_ioctl_tcsetattr(pComm->fd, TCSANOW, &upcomingTermios) < 0)
421 {
422 CommLog_Print(WLOG_WARN, "comm_ioctl_tcsetattr failure: last-error: 0x%08" PRIX32 "",
423 GetLastError());
424 return FALSE;
425 }
426
427 return result;
428}
429
430static BOOL get_serial_chars(WINPR_COMM* pComm, SERIAL_CHARS* pSerialChars)
431{
432 struct termios currentTermios = { 0 };
433
434 WINPR_ASSERT(pComm);
435 WINPR_ASSERT(pSerialChars);
436
437 if (tcgetattr(pComm->fd, &currentTermios) < 0)
438 {
439 SetLastError(ERROR_IO_DEVICE);
440 return FALSE;
441 }
442
443 ZeroMemory(pSerialChars, sizeof(SERIAL_CHARS));
444
445 /* EofChar unsupported */
446
447 /* ErrorChar unsupported */
448
449 /* BreakChar unsupported */
450
451 /* FIXME: see also: set_serial_chars() */
452 /* EventChar */
453
454 pSerialChars->XonChar = currentTermios.c_cc[VSTART];
455
456 pSerialChars->XoffChar = currentTermios.c_cc[VSTOP];
457
458 return TRUE;
459}
460
461static BOOL set_line_control(WINPR_COMM* pComm, const SERIAL_LINE_CONTROL* pLineControl)
462{
463 BOOL result = TRUE;
464 struct termios upcomingTermios = { 0 };
465
466 WINPR_ASSERT(pComm);
467 WINPR_ASSERT(pLineControl);
468
469 /* http://msdn.microsoft.com/en-us/library/windows/desktop/aa363214%28v=vs.85%29.aspx
470 *
471 * The use of 5 data bits with 2 stop bits is an invalid
472 * combination, as is 6, 7, or 8 data bits with 1.5 stop bits.
473 *
474 * FIXME: preferred to let the underlying driver to deal with
475 * this issue. At least produce a warning message?
476 */
477
478 if (tcgetattr(pComm->fd, &upcomingTermios) < 0)
479 {
480 SetLastError(ERROR_IO_DEVICE);
481 return FALSE;
482 }
483
484 /* FIXME: use of a COMMPROP to validate new settings? */
485
486 switch (pLineControl->StopBits)
487 {
488 case STOP_BIT_1:
489 upcomingTermios.c_cflag &= (uint32_t)~CSTOPB;
490 break;
491
492 case STOP_BITS_1_5:
493 CommLog_Print(WLOG_WARN, "Unsupported one and a half stop bits.");
494 break;
495
496 case STOP_BITS_2:
497 upcomingTermios.c_cflag |= CSTOPB;
498 break;
499
500 default:
501 CommLog_Print(WLOG_WARN, "unexpected number of stop bits: %" PRIu8 "\n",
502 pLineControl->StopBits);
503 result = FALSE; /* but keep on */
504 break;
505 }
506
507 switch (pLineControl->Parity)
508 {
509 case NO_PARITY:
510 upcomingTermios.c_cflag &= (uint32_t)~(PARENB | PARODD | CMSPAR);
511 break;
512
513 case ODD_PARITY:
514 upcomingTermios.c_cflag &= (uint32_t)~CMSPAR;
515 upcomingTermios.c_cflag |= PARENB | PARODD;
516 break;
517
518 case EVEN_PARITY:
519 upcomingTermios.c_cflag &= (uint32_t)~(PARODD | CMSPAR);
520 upcomingTermios.c_cflag |= PARENB;
521 break;
522
523 case MARK_PARITY:
524 upcomingTermios.c_cflag |= PARENB | PARODD | CMSPAR;
525 break;
526
527 case SPACE_PARITY:
528 upcomingTermios.c_cflag &= (uint32_t)~PARODD;
529 upcomingTermios.c_cflag |= PARENB | CMSPAR;
530 break;
531
532 default:
533 CommLog_Print(WLOG_WARN, "unexpected type of parity: %" PRIu8 "\n",
534 pLineControl->Parity);
535 result = FALSE; /* but keep on */
536 break;
537 }
538
539 switch (pLineControl->WordLength)
540 {
541 case 5:
542 upcomingTermios.c_cflag &= (uint32_t)~CSIZE;
543 upcomingTermios.c_cflag |= CS5;
544 break;
545
546 case 6:
547 upcomingTermios.c_cflag &= (uint32_t)~CSIZE;
548 upcomingTermios.c_cflag |= CS6;
549 break;
550
551 case 7:
552 upcomingTermios.c_cflag &= (uint32_t)~CSIZE;
553 upcomingTermios.c_cflag |= CS7;
554 break;
555
556 case 8:
557 upcomingTermios.c_cflag &= (uint32_t)~CSIZE;
558 upcomingTermios.c_cflag |= CS8;
559 break;
560
561 default:
562 CommLog_Print(WLOG_WARN, "unexpected number od data bits per character: %" PRIu8 "\n",
563 pLineControl->WordLength);
564 result = FALSE; /* but keep on */
565 break;
566 }
567
568 if (comm_ioctl_tcsetattr(pComm->fd, TCSANOW, &upcomingTermios) < 0)
569 {
570 CommLog_Print(WLOG_WARN, "comm_ioctl_tcsetattr failure: last-error: 0x%08" PRIX32 "",
571 GetLastError());
572 return FALSE;
573 }
574
575 return result;
576}
577
578static BOOL get_line_control(WINPR_COMM* pComm, SERIAL_LINE_CONTROL* pLineControl)
579{
580 struct termios currentTermios = { 0 };
581
582 WINPR_ASSERT(pComm);
583 WINPR_ASSERT(pLineControl);
584
585 if (tcgetattr(pComm->fd, &currentTermios) < 0)
586 {
587 SetLastError(ERROR_IO_DEVICE);
588 return FALSE;
589 }
590
591 pLineControl->StopBits = (currentTermios.c_cflag & CSTOPB) ? STOP_BITS_2 : STOP_BIT_1;
592
593 if (!(currentTermios.c_cflag & PARENB))
594 {
595 pLineControl->Parity = NO_PARITY;
596 }
597 else if (currentTermios.c_cflag & CMSPAR)
598 {
599 pLineControl->Parity = (currentTermios.c_cflag & PARODD) ? MARK_PARITY : SPACE_PARITY;
600 }
601 else
602 {
603 /* PARENB is set */
604 pLineControl->Parity = (currentTermios.c_cflag & PARODD) ? ODD_PARITY : EVEN_PARITY;
605 }
606
607 switch (currentTermios.c_cflag & CSIZE)
608 {
609 case CS5:
610 pLineControl->WordLength = 5;
611 break;
612 case CS6:
613 pLineControl->WordLength = 6;
614 break;
615 case CS7:
616 pLineControl->WordLength = 7;
617 break;
618 default:
619 pLineControl->WordLength = 8;
620 break;
621 }
622
623 return TRUE;
624}
625
626static BOOL set_handflow(WINPR_COMM* pComm, const SERIAL_HANDFLOW* pHandflow)
627{
628 BOOL result = TRUE;
629 struct termios upcomingTermios = { 0 };
630
631 WINPR_ASSERT(pComm);
632 WINPR_ASSERT(pHandflow);
633
634 if (tcgetattr(pComm->fd, &upcomingTermios) < 0)
635 {
636 SetLastError(ERROR_IO_DEVICE);
637 return FALSE;
638 }
639
640 /* HUPCL */
641
642 /* logical XOR */
643 if ((!(pHandflow->ControlHandShake & SERIAL_DTR_CONTROL) &&
644 (pHandflow->FlowReplace & SERIAL_RTS_CONTROL)) ||
645 ((pHandflow->ControlHandShake & SERIAL_DTR_CONTROL) &&
646 !(pHandflow->FlowReplace & SERIAL_RTS_CONTROL)))
647 {
648 CommLog_Print(WLOG_WARN,
649 "SERIAL_DTR_CONTROL:%s and SERIAL_RTS_CONTROL:%s cannot be different, HUPCL "
650 "will be set since it is claimed for one of the both lines.",
651 (pHandflow->ControlHandShake & SERIAL_DTR_CONTROL) ? "ON" : "OFF",
652 (pHandflow->FlowReplace & SERIAL_RTS_CONTROL) ? "ON" : "OFF");
653 }
654
655 if ((pHandflow->ControlHandShake & SERIAL_DTR_CONTROL) ||
656 (pHandflow->FlowReplace & SERIAL_RTS_CONTROL))
657 {
658 upcomingTermios.c_cflag |= HUPCL;
659 }
660 else
661 {
662 upcomingTermios.c_cflag &= (uint32_t)~HUPCL;
663
664 /* FIXME: is the DTR line also needs to be forced to a disable state according
665 * SERIAL_DTR_CONTROL? */
666 /* FIXME: is the RTS line also needs to be forced to a disable state according
667 * SERIAL_RTS_CONTROL? */
668 }
669
670 /* CRTSCTS */
671
672 /* logical XOR */
673 if ((!(pHandflow->ControlHandShake & SERIAL_CTS_HANDSHAKE) &&
674 (pHandflow->FlowReplace & SERIAL_RTS_HANDSHAKE)) ||
675 ((pHandflow->ControlHandShake & SERIAL_CTS_HANDSHAKE) &&
676 !(pHandflow->FlowReplace & SERIAL_RTS_HANDSHAKE)))
677 {
678 CommLog_Print(WLOG_WARN,
679 "SERIAL_CTS_HANDSHAKE:%s and SERIAL_RTS_HANDSHAKE:%s cannot be different, "
680 "CRTSCTS will be set since it is claimed for one of the both lines.",
681 (pHandflow->ControlHandShake & SERIAL_CTS_HANDSHAKE) ? "ON" : "OFF",
682 (pHandflow->FlowReplace & SERIAL_RTS_HANDSHAKE) ? "ON" : "OFF");
683 }
684
685 if ((pHandflow->ControlHandShake & SERIAL_CTS_HANDSHAKE) ||
686 (pHandflow->FlowReplace & SERIAL_RTS_HANDSHAKE))
687 {
688 upcomingTermios.c_cflag |= CRTSCTS;
689 }
690 else
691 {
692 upcomingTermios.c_cflag &= ~CRTSCTS;
693 }
694
695 /* ControlHandShake */
696
697 if (pHandflow->ControlHandShake & SERIAL_DTR_HANDSHAKE)
698 {
699 /* DTR/DSR flow control not supported on Linux */
700 CommLog_Print(WLOG_WARN, "Attempt to use the unsupported SERIAL_DTR_HANDSHAKE feature.");
701 SetLastError(ERROR_NOT_SUPPORTED);
702 result = FALSE; /* but keep on */
703 }
704
705 if (pHandflow->ControlHandShake & SERIAL_DSR_HANDSHAKE)
706 {
707 /* DTR/DSR flow control not supported on Linux */
708 CommLog_Print(WLOG_WARN, "Attempt to use the unsupported SERIAL_DSR_HANDSHAKE feature.");
709 SetLastError(ERROR_NOT_SUPPORTED);
710 result = FALSE; /* but keep on */
711 }
712
713 if (pHandflow->ControlHandShake & SERIAL_DCD_HANDSHAKE)
714 {
715 /* DCD flow control not supported on Linux */
716 CommLog_Print(WLOG_WARN, "Attempt to use the unsupported SERIAL_DCD_HANDSHAKE feature.");
717 SetLastError(ERROR_NOT_SUPPORTED);
718 result = FALSE; /* but keep on */
719 }
720
721 // FIXME: could be implemented during read/write I/O
722 if (pHandflow->ControlHandShake & SERIAL_DSR_SENSITIVITY)
723 {
724 /* DSR line control not supported on Linux */
725 CommLog_Print(WLOG_WARN, "Attempt to use the unsupported SERIAL_DSR_SENSITIVITY feature.");
726 SetLastError(ERROR_NOT_SUPPORTED);
727 result = FALSE; /* but keep on */
728 }
729
730 // FIXME: could be implemented during read/write I/O
731 if (pHandflow->ControlHandShake & SERIAL_ERROR_ABORT)
732 {
733 /* Aborting operations on error not supported on Linux */
734 CommLog_Print(WLOG_WARN, "Attempt to use the unsupported SERIAL_ERROR_ABORT feature.");
735 SetLastError(ERROR_NOT_SUPPORTED);
736 result = FALSE; /* but keep on */
737 }
738
739 /* FlowReplace */
740
741 if (pHandflow->FlowReplace & SERIAL_AUTO_TRANSMIT)
742 {
743 upcomingTermios.c_iflag |= IXON;
744 }
745 else
746 {
747 upcomingTermios.c_iflag &= (uint32_t)~IXON;
748 }
749
750 if (pHandflow->FlowReplace & SERIAL_AUTO_RECEIVE)
751 {
752 upcomingTermios.c_iflag |= IXOFF;
753 }
754 else
755 {
756 upcomingTermios.c_iflag &= (uint32_t)~IXOFF;
757 }
758
759 // FIXME: could be implemented during read/write I/O, as of today ErrorChar is necessary '\0'
760 if (pHandflow->FlowReplace & SERIAL_ERROR_CHAR)
761 {
762 /* errors will be replaced by the character '\0'. */
763 upcomingTermios.c_iflag &= (uint32_t)~IGNPAR;
764 }
765 else
766 {
767 upcomingTermios.c_iflag |= IGNPAR;
768 }
769
770 if (pHandflow->FlowReplace & SERIAL_NULL_STRIPPING)
771 {
772 upcomingTermios.c_iflag |= IGNBRK;
773 }
774 else
775 {
776 upcomingTermios.c_iflag &= (uint32_t)~IGNBRK;
777 }
778
779 // FIXME: could be implemented during read/write I/O
780 if (pHandflow->FlowReplace & SERIAL_BREAK_CHAR)
781 {
782 CommLog_Print(WLOG_WARN, "Attempt to use the unsupported SERIAL_BREAK_CHAR feature.");
783 SetLastError(ERROR_NOT_SUPPORTED);
784 result = FALSE; /* but keep on */
785 }
786
787 // FIXME: could be implemented during read/write I/O
788 if (pHandflow->FlowReplace & SERIAL_XOFF_CONTINUE)
789 {
790 /* not supported on Linux */
791 CommLog_Print(WLOG_WARN, "Attempt to use the unsupported SERIAL_XOFF_CONTINUE feature.");
792 SetLastError(ERROR_NOT_SUPPORTED);
793 result = FALSE; /* but keep on */
794 }
795
796 /* XonLimit */
797
798 pComm->XOffLimit = pHandflow->XoffLimit;
799 pComm->XOnLimit = pHandflow->XonLimit;
800
801 if (comm_ioctl_tcsetattr(pComm->fd, TCSANOW, &upcomingTermios) < 0)
802 {
803 CommLog_Print(WLOG_WARN, "comm_ioctl_tcsetattr failure: last-error: 0x%" PRIX32 "",
804 GetLastError());
805 return FALSE;
806 }
807
808 return result;
809}
810
811static BOOL get_handflow(WINPR_COMM* pComm, SERIAL_HANDFLOW* pHandflow)
812{
813 struct termios currentTermios = { 0 };
814
815 WINPR_ASSERT(pComm);
816 WINPR_ASSERT(pHandflow);
817
818 if (tcgetattr(pComm->fd, &currentTermios) < 0)
819 {
820 SetLastError(ERROR_IO_DEVICE);
821 return FALSE;
822 }
823
824 /* ControlHandShake */
825
826 pHandflow->ControlHandShake = 0;
827
828 if (currentTermios.c_cflag & HUPCL)
829 pHandflow->ControlHandShake |= SERIAL_DTR_CONTROL;
830
831 /* SERIAL_DTR_HANDSHAKE unsupported */
832
833 if (currentTermios.c_cflag & CRTSCTS)
834 pHandflow->ControlHandShake |= SERIAL_CTS_HANDSHAKE;
835
836 /* SERIAL_DSR_HANDSHAKE unsupported */
837
838 /* SERIAL_DCD_HANDSHAKE unsupported */
839
840 /* SERIAL_DSR_SENSITIVITY unsupported */
841
842 /* SERIAL_ERROR_ABORT unsupported */
843
844 /* FlowReplace */
845
846 pHandflow->FlowReplace = 0;
847
848 if (currentTermios.c_iflag & IXON)
849 pHandflow->FlowReplace |= SERIAL_AUTO_TRANSMIT;
850
851 if (currentTermios.c_iflag & IXOFF)
852 pHandflow->FlowReplace |= SERIAL_AUTO_RECEIVE;
853
854 if (!(currentTermios.c_iflag & IGNPAR))
855 pHandflow->FlowReplace |= SERIAL_ERROR_CHAR;
856
857 if (currentTermios.c_iflag & IGNBRK)
858 pHandflow->FlowReplace |= SERIAL_NULL_STRIPPING;
859
860 /* SERIAL_BREAK_CHAR unsupported */
861
862 if (currentTermios.c_cflag & HUPCL)
863 pHandflow->FlowReplace |= SERIAL_RTS_CONTROL;
864
865 if (currentTermios.c_cflag & CRTSCTS)
866 pHandflow->FlowReplace |= SERIAL_RTS_HANDSHAKE;
867
868 /* SERIAL_XOFF_CONTINUE unsupported */
869
870 pHandflow->XonLimit = pComm->XOnLimit;
871 pHandflow->XoffLimit = pComm->XOffLimit;
872
873 return TRUE;
874}
875
876static BOOL set_timeouts(WINPR_COMM* pComm, const SERIAL_TIMEOUTS* pTimeouts)
877{
878 WINPR_ASSERT(pComm);
879 WINPR_ASSERT(pTimeouts);
880
881 /* NB: timeouts are applied on system during read/write I/O */
882
883 /* http://msdn.microsoft.com/en-us/library/windows/hardware/hh439614%28v=vs.85%29.aspx */
884 if ((pTimeouts->ReadIntervalTimeout == MAXULONG) &&
885 (pTimeouts->ReadTotalTimeoutConstant == MAXULONG))
886 {
887 CommLog_Print(
888 WLOG_WARN,
889 "ReadIntervalTimeout and ReadTotalTimeoutConstant cannot be both set to MAXULONG");
890 SetLastError(ERROR_INVALID_PARAMETER);
891 return FALSE;
892 }
893
894 pComm->timeouts.ReadIntervalTimeout = pTimeouts->ReadIntervalTimeout;
895 pComm->timeouts.ReadTotalTimeoutMultiplier = pTimeouts->ReadTotalTimeoutMultiplier;
896 pComm->timeouts.ReadTotalTimeoutConstant = pTimeouts->ReadTotalTimeoutConstant;
897 pComm->timeouts.WriteTotalTimeoutMultiplier = pTimeouts->WriteTotalTimeoutMultiplier;
898 pComm->timeouts.WriteTotalTimeoutConstant = pTimeouts->WriteTotalTimeoutConstant;
899
900 CommLog_Print(WLOG_DEBUG, "ReadIntervalTimeout %" PRIu32 "",
901 pComm->timeouts.ReadIntervalTimeout);
902 CommLog_Print(WLOG_DEBUG, "ReadTotalTimeoutMultiplier %" PRIu32 "",
903 pComm->timeouts.ReadTotalTimeoutMultiplier);
904 CommLog_Print(WLOG_DEBUG, "ReadTotalTimeoutConstant %" PRIu32 "",
905 pComm->timeouts.ReadTotalTimeoutConstant);
906 CommLog_Print(WLOG_DEBUG, "WriteTotalTimeoutMultiplier %" PRIu32 "",
907 pComm->timeouts.WriteTotalTimeoutMultiplier);
908 CommLog_Print(WLOG_DEBUG, "WriteTotalTimeoutConstant %" PRIu32 "",
909 pComm->timeouts.WriteTotalTimeoutConstant);
910
911 return TRUE;
912}
913
914static BOOL get_timeouts(WINPR_COMM* pComm, SERIAL_TIMEOUTS* pTimeouts)
915{
916 WINPR_ASSERT(pComm);
917 WINPR_ASSERT(pTimeouts);
918
919 pTimeouts->ReadIntervalTimeout = pComm->timeouts.ReadIntervalTimeout;
920 pTimeouts->ReadTotalTimeoutMultiplier = pComm->timeouts.ReadTotalTimeoutMultiplier;
921 pTimeouts->ReadTotalTimeoutConstant = pComm->timeouts.ReadTotalTimeoutConstant;
922 pTimeouts->WriteTotalTimeoutMultiplier = pComm->timeouts.WriteTotalTimeoutMultiplier;
923 pTimeouts->WriteTotalTimeoutConstant = pComm->timeouts.WriteTotalTimeoutConstant;
924
925 return TRUE;
926}
927
928static BOOL set_lines(WINPR_COMM* pComm, UINT32 lines)
929{
930 WINPR_ASSERT(pComm);
931
932 return CommIoCtl(pComm, TIOCMBIS, &lines);
933}
934
935static BOOL clear_lines(WINPR_COMM* pComm, UINT32 lines)
936{
937 WINPR_ASSERT(pComm);
938
939 return CommIoCtl(pComm, TIOCMBIC, &lines);
940}
941
942static BOOL set_dtr(WINPR_COMM* pComm)
943{
944 SERIAL_HANDFLOW handflow = { 0 };
945 WINPR_ASSERT(pComm);
946
947 if (!get_handflow(pComm, &handflow))
948 return FALSE;
949
950 /* SERIAL_DTR_HANDSHAKE not supported as of today */
951 WINPR_ASSERT((handflow.ControlHandShake & SERIAL_DTR_HANDSHAKE) == 0);
952
953 if (handflow.ControlHandShake & SERIAL_DTR_HANDSHAKE)
954 {
955 SetLastError(ERROR_INVALID_PARAMETER);
956 return FALSE;
957 }
958
959 return set_lines(pComm, TIOCM_DTR);
960}
961
962static BOOL clear_dtr(WINPR_COMM* pComm)
963{
964 SERIAL_HANDFLOW handflow = { 0 };
965 WINPR_ASSERT(pComm);
966
967 if (!get_handflow(pComm, &handflow))
968 return FALSE;
969
970 /* SERIAL_DTR_HANDSHAKE not supported as of today */
971 WINPR_ASSERT((handflow.ControlHandShake & SERIAL_DTR_HANDSHAKE) == 0);
972
973 if (handflow.ControlHandShake & SERIAL_DTR_HANDSHAKE)
974 {
975 SetLastError(ERROR_INVALID_PARAMETER);
976 return FALSE;
977 }
978
979 return clear_lines(pComm, TIOCM_DTR);
980}
981
982static BOOL set_rts(WINPR_COMM* pComm)
983{
984 SERIAL_HANDFLOW handflow = { 0 };
985 WINPR_ASSERT(pComm);
986
987 if (!get_handflow(pComm, &handflow))
988 return FALSE;
989
990 if (handflow.FlowReplace & SERIAL_RTS_HANDSHAKE)
991 {
992 SetLastError(ERROR_INVALID_PARAMETER);
993 return FALSE;
994 }
995
996 return set_lines(pComm, TIOCM_RTS);
997}
998
999static BOOL clear_rts(WINPR_COMM* pComm)
1000{
1001 SERIAL_HANDFLOW handflow = { 0 };
1002 WINPR_ASSERT(pComm);
1003 if (!get_handflow(pComm, &handflow))
1004 return FALSE;
1005
1006 if (handflow.FlowReplace & SERIAL_RTS_HANDSHAKE)
1007 {
1008 SetLastError(ERROR_INVALID_PARAMETER);
1009 return FALSE;
1010 }
1011
1012 return clear_lines(pComm, TIOCM_RTS);
1013}
1014
1015static BOOL get_raw_modemstatus(WINPR_COMM* pComm, int* pRegister)
1016{
1017 WINPR_ASSERT(pComm);
1018 WINPR_ASSERT(pRegister);
1019
1020 const BOOL rc = CommIoCtl(pComm, TIOCMGET, pRegister);
1021
1022 char buffer[128] = { 0 };
1023 CommLog_Print(WLOG_DEBUG, "status %s",
1024 get_modem_status_str(*pRegister, buffer, sizeof(buffer)));
1025 return rc;
1026}
1027
1028static BOOL get_modemstatus(WINPR_COMM* pComm, ULONG* pRegister)
1029{
1030 int lines = 0;
1031
1032 if (!get_raw_modemstatus(pComm, &lines))
1033 return FALSE;
1034
1035 *pRegister = 0;
1036 if (lines & TIOCM_CTS)
1037 *pRegister |= SERIAL_MSR_CTS | SERIAL_MSR_DCTS;
1038 if (lines & TIOCM_DSR)
1039 *pRegister |= SERIAL_MSR_DSR | SERIAL_MSR_DDSR;
1040 if (lines & TIOCM_RI)
1041 *pRegister |= SERIAL_MSR_RI | SERIAL_MSR_TERI;
1042 if (lines & TIOCM_CD)
1043 *pRegister |= SERIAL_MSR_DCD | SERIAL_MSR_DDCD;
1044
1045 return TRUE;
1046}
1047
1048/* http://msdn.microsoft.com/en-us/library/windows/hardware/hh439605%28v=vs.85%29.aspx */
1049static const ULONG SERIAL_SYS_SUPPORTED_EV_MASK =
1050 SERIAL_EV_RXCHAR | SERIAL_EV_RXFLAG | SERIAL_EV_TXEMPTY | SERIAL_EV_CTS | SERIAL_EV_DSR |
1051 SERIAL_EV_RLSD | SERIAL_EV_BREAK | SERIAL_EV_ERR | SERIAL_EV_RING |
1052 /* SERIAL_EV_PERR | */
1053 SERIAL_EV_RX80FULL /*|
1054 SERIAL_EV_EVENT1 |
1055 SERIAL_EV_EVENT2*/
1056 ;
1057
1058static BOOL is_wait_set(WINPR_COMM* pComm)
1059{
1060 WINPR_ASSERT(pComm);
1061
1062 EnterCriticalSection(&pComm->EventsLock);
1063 const BOOL isWaiting = (pComm->PendingEvents & SERIAL_EV_WINPR_WAITING) != 0;
1064 LeaveCriticalSection(&pComm->EventsLock);
1065 return isWaiting;
1066}
1067
1068static BOOL set_wait_mask(WINPR_COMM* pComm, const ULONG* pWaitMask)
1069{
1070 ULONG possibleMask = 0;
1071
1072 WINPR_ASSERT(pComm);
1073 WINPR_ASSERT(pWaitMask);
1074
1075 /* Stops pending IOCTL_SERIAL_WAIT_ON_MASK
1076 * http://msdn.microsoft.com/en-us/library/ff546805%28v=vs.85%29.aspx
1077 */
1078 if (is_wait_set(pComm))
1079 {
1080 /* FIXME: any doubt on reading PendingEvents out of a critical section? */
1081
1082 EnterCriticalSection(&pComm->EventsLock);
1083 pComm->PendingEvents |= SERIAL_EV_WINPR_STOP;
1084 LeaveCriticalSection(&pComm->EventsLock);
1085
1086 /* waiting the end of the pending wait_on_mask() */
1087 while (is_wait_set(pComm))
1088 Sleep(10); /* 10ms */
1089
1090 EnterCriticalSection(&pComm->EventsLock);
1091 pComm->PendingEvents &= (uint32_t)~SERIAL_EV_WINPR_STOP;
1092 LeaveCriticalSection(&pComm->EventsLock);
1093 }
1094
1095 /* NB: ensure to leave the critical section before to return */
1096 EnterCriticalSection(&pComm->EventsLock);
1097
1098 if (*pWaitMask == 0)
1099 {
1100 /* clearing pending events */
1101 if (!CommUpdateIOCount(pComm, FALSE))
1102 {
1103 LeaveCriticalSection(&pComm->EventsLock);
1104 return FALSE;
1105 }
1106
1107 pComm->PendingEvents = 0;
1108 }
1109
1110 possibleMask = *pWaitMask & SERIAL_SYS_SUPPORTED_EV_MASK;
1111
1112 if (possibleMask != *pWaitMask)
1113 {
1114 CommLog_Print(WLOG_WARN,
1115 "Not all wait events supported (Serial.sys), requested events= 0x%08" PRIX32
1116 ", possible events= 0x%08" PRIX32 "",
1117 *pWaitMask, possibleMask);
1118
1119 /* FIXME: shall we really set the possibleMask and return FALSE? */
1120 pComm->WaitEventMask = possibleMask;
1121
1122 LeaveCriticalSection(&pComm->EventsLock);
1123 return FALSE;
1124 }
1125
1126 pComm->WaitEventMask = possibleMask;
1127
1128 LeaveCriticalSection(&pComm->EventsLock);
1129 return TRUE;
1130}
1131
1132static BOOL get_wait_mask(WINPR_COMM* pComm, ULONG* pWaitMask)
1133{
1134 WINPR_ASSERT(pComm);
1135 WINPR_ASSERT(pWaitMask);
1136
1137 *pWaitMask = pComm->WaitEventMask;
1138 return TRUE;
1139}
1140
1141static BOOL set_queue_size(WINPR_ATTR_UNUSED WINPR_COMM* pComm, const SERIAL_QUEUE_SIZE* pQueueSize)
1142{
1143 WINPR_ASSERT(pComm);
1144 WINPR_ASSERT(pQueueSize);
1145
1146 if ((pQueueSize->InSize <= N_TTY_BUF_SIZE) && (pQueueSize->OutSize <= N_TTY_BUF_SIZE))
1147 return TRUE; /* nothing to do */
1148
1149 /* FIXME: could be implemented on top of N_TTY */
1150
1151 if (pQueueSize->InSize > N_TTY_BUF_SIZE)
1152 CommLog_Print(WLOG_WARN,
1153 "Requested an incompatible input buffer size: %" PRIu32
1154 ", keeping on with a %" PRIu32 " bytes buffer.",
1155 pQueueSize->InSize, N_TTY_BUF_SIZE);
1156
1157 if (pQueueSize->OutSize > N_TTY_BUF_SIZE)
1158 CommLog_Print(WLOG_WARN,
1159 "Requested an incompatible output buffer size: %" PRIu32
1160 ", keeping on with a %" PRIu32 " bytes buffer.",
1161 pQueueSize->OutSize, N_TTY_BUF_SIZE);
1162
1163 SetLastError(ERROR_CANCELLED);
1164 return FALSE;
1165}
1166
1167static BOOL purge(WINPR_COMM* pComm, const ULONG* pPurgeMask)
1168{
1169 WINPR_ASSERT(pComm);
1170 WINPR_ASSERT(pPurgeMask);
1171
1172 if ((*pPurgeMask & (uint32_t)~(SERIAL_PURGE_TXABORT | SERIAL_PURGE_RXABORT |
1173 SERIAL_PURGE_TXCLEAR | SERIAL_PURGE_RXCLEAR)) > 0)
1174 {
1175 CommLog_Print(WLOG_WARN, "Invalid purge mask: 0x%" PRIX32 "\n", *pPurgeMask);
1176 SetLastError(ERROR_INVALID_PARAMETER);
1177 return FALSE;
1178 }
1179
1180 /* FIXME: currently relying too much on the fact the server
1181 * sends a single IRP_MJ_WRITE or IRP_MJ_READ at a time
1182 * (taking care though that one IRP_MJ_WRITE and one
1183 * IRP_MJ_READ can be sent simultaneously) */
1184
1185 if (*pPurgeMask & SERIAL_PURGE_TXABORT)
1186 {
1187 /* Purges all write (IRP_MJ_WRITE) requests. */
1188#if defined(WINPR_HAVE_SYS_EVENTFD_H)
1189 if (eventfd_write(pComm->fd_write_event, WINPR_PURGE_TXABORT) < 0)
1190 {
1191 if (errno != EAGAIN)
1192 {
1193 char ebuffer[256] = { 0 };
1194 CommLog_Print(WLOG_WARN, "eventfd_write failed, errno=[%d] %s", errno,
1195 winpr_strerror(errno, ebuffer, sizeof(ebuffer)));
1196 }
1197
1198 WINPR_ASSERT(errno == EAGAIN); /* no reader <=> no pending IRP_MJ_WRITE */
1199 }
1200#endif
1201 }
1202
1203 if (*pPurgeMask & SERIAL_PURGE_RXABORT)
1204 {
1205 /* Purges all read (IRP_MJ_READ) requests. */
1206#if defined(WINPR_HAVE_SYS_EVENTFD_H)
1207 if (eventfd_write(pComm->fd_read_event, WINPR_PURGE_RXABORT) < 0)
1208 {
1209 if (errno != EAGAIN)
1210 {
1211 char ebuffer[256] = { 0 };
1212 CommLog_Print(WLOG_WARN, "eventfd_write failed, errno=[%d] %s", errno,
1213 winpr_strerror(errno, ebuffer, sizeof(ebuffer)));
1214 }
1215
1216 WINPR_ASSERT(errno == EAGAIN); /* no reader <=> no pending IRP_MJ_READ */
1217 }
1218#endif
1219 }
1220
1221 if (*pPurgeMask & SERIAL_PURGE_TXCLEAR)
1222 {
1223 /* Purges the transmit buffer, if one exists. */
1224
1225 if (tcflush(pComm->fd, TCOFLUSH) < 0)
1226 {
1227 char ebuffer[256] = { 0 };
1228 CommLog_Print(WLOG_WARN, "tcflush(TCOFLUSH) failure, errno=[%d] %s", errno,
1229 winpr_strerror(errno, ebuffer, sizeof(ebuffer)));
1230 SetLastError(ERROR_CANCELLED);
1231 return FALSE;
1232 }
1233 }
1234
1235 if (*pPurgeMask & SERIAL_PURGE_RXCLEAR)
1236 {
1237 /* Purges the receive buffer, if one exists. */
1238
1239 if (tcflush(pComm->fd, TCIFLUSH) < 0)
1240 {
1241 char ebuffer[256] = { 0 };
1242 CommLog_Print(WLOG_WARN, "tcflush(TCIFLUSH) failure, errno=[%d] %s", errno,
1243 winpr_strerror(errno, ebuffer, sizeof(ebuffer)));
1244 SetLastError(ERROR_CANCELLED);
1245 return FALSE;
1246 }
1247 }
1248
1249 return TRUE;
1250}
1251
1252/* NB: get_commstatus also produces most of the events consumed by wait_on_mask(). Exceptions:
1253 * - SERIAL_EV_RXFLAG: FIXME: once EventChar supported
1254 *
1255 */
1256static BOOL get_commstatus(WINPR_COMM* pComm, SERIAL_STATUS* pCommstatus)
1257{
1258 BOOL rc = FALSE;
1259 /* http://msdn.microsoft.com/en-us/library/jj673022%28v=vs.85%29.aspx */
1260#if defined(WINPR_HAVE_COMM_COUNTERS)
1261 struct serial_icounter_struct currentCounters = { 0 };
1262#endif
1263 WINPR_ASSERT(pComm);
1264 WINPR_ASSERT(pCommstatus);
1265
1266 /* NB: ensure to leave the critical section before to return */
1267 EnterCriticalSection(&pComm->EventsLock);
1268
1269 ZeroMemory(pCommstatus, sizeof(SERIAL_STATUS));
1270
1271 int status = 0;
1272 if (!get_raw_modemstatus(pComm, &status))
1273 goto fail;
1274
1275#if defined(WINPR_HAVE_COMM_COUNTERS)
1276 if (!CommUpdateIOCount(pComm, FALSE))
1277 goto fail;
1278 currentCounters = pComm->counters;
1279
1280 /* NB: preferred below (currentCounters.* != pComm->counters.*) over (currentCounters.* >
1281 * pComm->counters.*) thinking the counters can loop */
1282
1283 /* Errors */
1284
1285 if (currentCounters.buf_overrun != pComm->counters.buf_overrun)
1286 {
1287 pCommstatus->Errors |= SERIAL_ERROR_QUEUEOVERRUN;
1288 }
1289
1290 if (currentCounters.overrun != pComm->counters.overrun)
1291 {
1292 pCommstatus->Errors |= SERIAL_ERROR_OVERRUN;
1293 pComm->PendingEvents |= SERIAL_EV_ERR;
1294 }
1295
1296 if (currentCounters.brk != pComm->counters.brk)
1297 {
1298 pCommstatus->Errors |= SERIAL_ERROR_BREAK;
1299 pComm->PendingEvents |= SERIAL_EV_BREAK;
1300 }
1301
1302 if (currentCounters.parity != pComm->counters.parity)
1303 {
1304 pCommstatus->Errors |= SERIAL_ERROR_PARITY;
1305 pComm->PendingEvents |= SERIAL_EV_ERR;
1306 }
1307
1308 if (currentCounters.frame != pComm->counters.frame)
1309 {
1310 pCommstatus->Errors |= SERIAL_ERROR_FRAMING;
1311 pComm->PendingEvents |= SERIAL_EV_ERR;
1312 }
1313#endif
1314
1315 /* HoldReasons */
1316
1317 if (status & TIOCM_CTS)
1318 pComm->PendingEvents |= SERIAL_EV_CTS;
1319
1320 /* TODO: SERIAL_TX_WAITING_FOR_XON */
1321
1322 /* TODO: SERIAL_TX_WAITING_ON_BREAK, see LCR's bit 6 */
1323
1324 /* TODO: SERIAL_TX_WAITING_XOFF_SENT */
1325
1326 if (status & TIOCM_SR)
1327 pComm->PendingEvents |= SERIAL_EV_RXFLAG | SERIAL_EV_RXCHAR;
1328
1329 /* AmountInInQueue */
1330 int available = 0;
1331 if (!CommIoCtl(pComm, FIONREAD, &available))
1332 goto fail;
1333
1334#if defined(__linux__)
1335 if (!CommIoCtl(pComm, TIOCINQ, &pCommstatus->AmountInInQueue))
1336 goto fail;
1337#endif
1338
1339 /* AmountInOutQueue */
1340
1341 if (!CommIoCtl(pComm, TIOCOUTQ, &pCommstatus->AmountInOutQueue))
1342 goto fail;
1343
1344 /* BOOLEAN EofReceived; FIXME: once EofChar supported */
1345
1346 /* BOOLEAN WaitForImmediate; TODO: once IOCTL_SERIAL_IMMEDIATE_CHAR fully supported */
1347
1348 /* other events based on counters */
1349
1350 if (available > 0)
1351 pComm->PendingEvents |= SERIAL_EV_RXFLAG | SERIAL_EV_RXCHAR;
1352
1353 if (pCommstatus->AmountInOutQueue == 0) /* output buffer is now empty */
1354 {
1355 pComm->PendingEvents |= SERIAL_EV_TXEMPTY;
1356 }
1357 else
1358 {
1359 /* FIXME: "now empty" from the specs is ambiguous, need to track previous completed
1360 * transmission? */
1361 pComm->PendingEvents &= (uint32_t)~SERIAL_EV_TXEMPTY;
1362 }
1363
1364#if defined(WINPR_HAVE_COMM_COUNTERS)
1365 if (currentCounters.tx != pComm->counters.tx)
1366 {
1367 pComm->PendingEvents &= (uint32_t)~SERIAL_EV_TXEMPTY;
1368 }
1369 else
1370 {
1371 pComm->PendingEvents |= SERIAL_EV_TXEMPTY;
1372 }
1373
1374 if (currentCounters.rx != pComm->counters.rx)
1375 {
1376 pComm->PendingEvents |= SERIAL_EV_RXFLAG | SERIAL_EV_RXCHAR;
1377 }
1378
1379 if (currentCounters.cts != pComm->counters.cts)
1380 {
1381 pComm->PendingEvents |= SERIAL_EV_CTS;
1382 }
1383
1384 if (currentCounters.dsr != pComm->counters.dsr)
1385 {
1386 pComm->PendingEvents |= SERIAL_EV_DSR;
1387 }
1388
1389 if (currentCounters.dcd != pComm->counters.dcd)
1390 {
1391 pComm->PendingEvents |= SERIAL_EV_RLSD;
1392 }
1393
1394 if (currentCounters.rng != pComm->counters.rng)
1395 {
1396 pComm->PendingEvents |= SERIAL_EV_RING;
1397 }
1398 pComm->counters = currentCounters;
1399#endif
1400
1401 if (pCommstatus->AmountInInQueue > (0.8 * N_TTY_BUF_SIZE))
1402 {
1403 pComm->PendingEvents |= SERIAL_EV_RX80FULL;
1404 }
1405 else
1406 {
1407 /* FIXME: "is 80 percent full" from the specs is ambiguous, need to track when it previously
1408 * * occurred? */
1409 pComm->PendingEvents &= (uint32_t)~SERIAL_EV_RX80FULL;
1410 }
1411
1412 rc = TRUE;
1413fail:
1414 LeaveCriticalSection(&pComm->EventsLock);
1415 return rc;
1416}
1417
1418static BOOL refresh_PendingEvents(WINPR_COMM* pComm)
1419{
1420 SERIAL_STATUS serialStatus = { 0 };
1421
1422 WINPR_ASSERT(pComm);
1423
1424 /* NB: also ensures PendingEvents to be up to date */
1425 if (!get_commstatus(pComm, &serialStatus))
1426 {
1427 return FALSE;
1428 }
1429
1430 return TRUE;
1431}
1432
1433static void consume_event(WINPR_COMM* pComm, ULONG* pOutputMask, ULONG event)
1434{
1435 WINPR_ASSERT(pComm);
1436 WINPR_ASSERT(pOutputMask);
1437
1438 if ((pComm->WaitEventMask & event) && (pComm->PendingEvents & event))
1439 {
1440 pComm->PendingEvents &= ~event; /* consumed */
1441 *pOutputMask |= event;
1442 }
1443}
1444
1445static BOOL unlock_return(WINPR_COMM* pComm, BOOL res)
1446{
1447 EnterCriticalSection(&pComm->EventsLock);
1448 pComm->PendingEvents &= (uint32_t)~SERIAL_EV_WINPR_WAITING;
1449 LeaveCriticalSection(&pComm->EventsLock);
1450 return res;
1451}
1452
1453/*
1454 * NB: see also: set_wait_mask()
1455 */
1456static BOOL wait_on_mask(WINPR_COMM* pComm, ULONG* pOutputMask)
1457{
1458 WINPR_ASSERT(pComm);
1459 WINPR_ASSERT(*pOutputMask == 0);
1460
1461 EnterCriticalSection(&pComm->EventsLock);
1462 pComm->PendingEvents |= SERIAL_EV_WINPR_WAITING;
1463 LeaveCriticalSection(&pComm->EventsLock);
1464
1465 while (TRUE)
1466 {
1467 /* NB: EventsLock also used by refresh_PendingEvents() */
1468 if (!refresh_PendingEvents(pComm))
1469 return unlock_return(pComm, FALSE);
1470
1471 /* NB: ensure to leave the critical section before to return */
1472 EnterCriticalSection(&pComm->EventsLock);
1473
1474 if (pComm->PendingEvents & SERIAL_EV_WINPR_STOP)
1475 {
1476 /* pOutputMask must remain empty but should
1477 * not have been modified.
1478 *
1479 * http://msdn.microsoft.com/en-us/library/ff546805%28v=vs.85%29.aspx
1480 */
1481 WINPR_ASSERT(*pOutputMask == 0);
1482
1483 LeaveCriticalSection(&pComm->EventsLock);
1484 break;
1485 }
1486
1487 consume_event(pComm, pOutputMask, SERIAL_EV_RXCHAR);
1488 consume_event(pComm, pOutputMask, SERIAL_EV_RXFLAG);
1489 consume_event(pComm, pOutputMask, SERIAL_EV_TXEMPTY);
1490 consume_event(pComm, pOutputMask, SERIAL_EV_DSR);
1491 consume_event(pComm, pOutputMask, SERIAL_EV_RLSD);
1492 consume_event(pComm, pOutputMask, SERIAL_EV_BREAK);
1493 consume_event(pComm, pOutputMask, SERIAL_EV_ERR);
1494 consume_event(pComm, pOutputMask, SERIAL_EV_RING);
1495 consume_event(pComm, pOutputMask, SERIAL_EV_RX80FULL);
1496
1497 LeaveCriticalSection(&pComm->EventsLock);
1498
1499 /* NOTE: PendingEvents can be modified from now on but
1500 * not pOutputMask */
1501
1502 if (*pOutputMask != 0)
1503 break;
1504
1505 /* waiting for a modification of PendingEvents.
1506 *
1507 * NOTE: previously used a semaphore but used
1508 * sem_timedwait() anyway. Finally preferred a simpler
1509 * solution with Sleep() without the burden of the
1510 * semaphore initialization and destroying.
1511 */
1512
1513 Sleep(100); /* 100 ms */
1514 }
1515
1516 return unlock_return(pComm, TRUE);
1517}
1518
1519static BOOL set_break_on(WINPR_COMM* pComm)
1520{
1521 WINPR_ASSERT(pComm);
1522 return CommIoCtl(pComm, TIOCSBRK, NULL);
1523}
1524
1525static BOOL set_break_off(WINPR_COMM* pComm)
1526{
1527 WINPR_ASSERT(pComm);
1528
1529 return CommIoCtl(pComm, TIOCCBRK, NULL);
1530}
1531
1532static BOOL set_xoff(WINPR_COMM* pComm)
1533{
1534 WINPR_ASSERT(pComm);
1535 // NOLINTNEXTLINE(concurrency-mt-unsafe)
1536 if (tcflow(pComm->fd, TCIOFF) < 0)
1537 {
1538 char ebuffer[256] = { 0 };
1539 CommLog_Print(WLOG_WARN, "TCIOFF failure, errno=[%d] %s", errno,
1540 winpr_strerror(errno, ebuffer, sizeof(ebuffer)));
1541 SetLastError(ERROR_IO_DEVICE);
1542 return FALSE;
1543 }
1544
1545 return TRUE;
1546}
1547
1548static BOOL set_xon(WINPR_COMM* pComm)
1549{
1550 WINPR_ASSERT(pComm);
1551 // NOLINTNEXTLINE(concurrency-mt-unsafe)
1552 if (tcflow(pComm->fd, TCION) < 0)
1553 {
1554 char ebuffer[256] = { 0 };
1555 CommLog_Print(WLOG_WARN, "TCION failure, errno=[%d] %s", errno,
1556 winpr_strerror(errno, ebuffer, sizeof(ebuffer)));
1557 SetLastError(ERROR_IO_DEVICE);
1558 return FALSE;
1559 }
1560
1561 return TRUE;
1562}
1563
1564static BOOL get_dtrrts(WINPR_COMM* pComm, ULONG* pMask)
1565{
1566 int lines = 0;
1567
1568 WINPR_ASSERT(pComm);
1569 WINPR_ASSERT(pMask);
1570
1571 if (!get_raw_modemstatus(pComm, &lines))
1572 return FALSE;
1573
1574 *pMask = 0;
1575
1576 if (!(lines & TIOCM_DTR))
1577 *pMask |= SERIAL_DTR_STATE;
1578 if (!(lines & TIOCM_RTS))
1579 *pMask |= SERIAL_RTS_STATE;
1580
1581 return TRUE;
1582}
1583
1584static BOOL config_size(WINPR_ATTR_UNUSED WINPR_COMM* pComm, ULONG* pSize)
1585{
1586 WINPR_ASSERT(pComm);
1587 WINPR_ASSERT(pSize);
1588
1589 /* http://msdn.microsoft.com/en-us/library/ff546548%28v=vs.85%29.aspx */
1590 if (!pSize)
1591 return FALSE;
1592
1593 *pSize = 0;
1594 return TRUE;
1595}
1596
1597static BOOL immediate_char(WINPR_COMM* pComm, const UCHAR* pChar)
1598{
1599 BOOL result = 0;
1600 DWORD nbBytesWritten = 0;
1601
1602 WINPR_ASSERT(pComm);
1603 WINPR_ASSERT(pChar);
1604
1605 /* FIXME: CommWriteFile uses a critical section, shall it be
1606 * interrupted?
1607 *
1608 * FIXME: see also get_commstatus()'s WaitForImmediate boolean
1609 */
1610
1611 result = CommWriteFile(pComm, pChar, 1, &nbBytesWritten, NULL);
1612
1613 WINPR_ASSERT(nbBytesWritten == 1);
1614
1615 return result;
1616}
1617
1618static BOOL reset_device(WINPR_ATTR_UNUSED WINPR_COMM* pComm)
1619{
1620 /* http://msdn.microsoft.com/en-us/library/dn265347%28v=vs.85%29.aspx */
1621 WINPR_ASSERT(pComm);
1622
1623 pComm->XOnLimit = TTY_THRESHOLD_UNTHROTTLE;
1624 pComm->XOffLimit = TTY_THRESHOLD_THROTTLE;
1625
1626 (void)CommUpdateIOCount(pComm, TRUE);
1627
1628 return CommIoCtl(pComm, TIOCMSET, 0);
1629}
1630
1631static const SERIAL_DRIVER SerialSys = {
1632 .id = SerialDriverSerialSys,
1633 .name = _T("Serial.sys"),
1634 .set_baud_rate = set_baud_rate,
1635 .get_baud_rate = get_baud_rate,
1636 .get_properties = get_properties,
1637 .set_serial_chars = set_serial_chars,
1638 .get_serial_chars = get_serial_chars,
1639 .set_line_control = set_line_control,
1640 .get_line_control = get_line_control,
1641 .set_handflow = set_handflow,
1642 .get_handflow = get_handflow,
1643 .set_timeouts = set_timeouts,
1644 .get_timeouts = get_timeouts,
1645 .set_dtr = set_dtr,
1646 .clear_dtr = clear_dtr,
1647 .set_rts = set_rts,
1648 .clear_rts = clear_rts,
1649 .get_modemstatus = get_modemstatus,
1650 .set_wait_mask = set_wait_mask,
1651 .get_wait_mask = get_wait_mask,
1652 .wait_on_mask = wait_on_mask,
1653 .set_queue_size = set_queue_size,
1654 .purge = purge,
1655 .get_commstatus = get_commstatus,
1656 .set_break_on = set_break_on,
1657 .set_break_off = set_break_off,
1658 .set_xoff = set_xoff,
1659 .set_xon = set_xon,
1660 .get_dtrrts = get_dtrrts,
1661 .config_size = config_size,
1662 .immediate_char = immediate_char,
1663 .reset_device = reset_device,
1664};
1665
1666const SERIAL_DRIVER* SerialSys_s(void)
1667{
1668 return &SerialSys;
1669}