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