FreeRDP
comm.c
1 
23 #include <winpr/config.h>
24 
25 #include <winpr/assert.h>
26 #include <errno.h>
27 #include <fcntl.h>
28 #include <pthread.h>
29 #include <stdarg.h>
30 #if defined(WINPR_HAVE_SYS_EVENTFD_H)
31 #include <sys/eventfd.h>
32 #endif
33 #include <sys/ioctl.h>
34 #include <sys/stat.h>
35 #include <sys/types.h>
36 #include <termios.h>
37 #include <unistd.h>
38 
39 #include <winpr/crt.h>
40 #include <winpr/comm.h>
41 #include <winpr/tchar.h>
42 #include <winpr/wlog.h>
43 #include <winpr/handle.h>
44 
45 #include "comm_ioctl.h"
46 
47 #include "../log.h"
48 #define TAG WINPR_TAG("comm")
49 
55 #include "comm.h"
56 
57 static wLog* sLog = NULL;
58 
59 struct comm_device
60 {
61  LPTSTR name;
62  LPTSTR path;
63 };
64 
65 typedef struct comm_device COMM_DEVICE;
66 
67 /* FIXME: get a clever data structure, see also io.h functions */
68 /* _CommDevices is a NULL-terminated array with a maximun of COMM_DEVICE_MAX COMM_DEVICE */
69 #define COMM_DEVICE_MAX 128
70 static COMM_DEVICE** sCommDevices = NULL;
71 static CRITICAL_SECTION sCommDevicesLock = { 0 };
72 
73 static pthread_once_t sCommInitialized = PTHREAD_ONCE_INIT;
74 
75 static const _SERIAL_IOCTL_NAME S_SERIAL_IOCTL_NAMES[] = {
76  { IOCTL_SERIAL_SET_BAUD_RATE, "IOCTL_SERIAL_SET_BAUD_RATE" },
77  { IOCTL_SERIAL_GET_BAUD_RATE, "IOCTL_SERIAL_GET_BAUD_RATE" },
78  { IOCTL_SERIAL_SET_LINE_CONTROL, "IOCTL_SERIAL_SET_LINE_CONTROL" },
79  { IOCTL_SERIAL_GET_LINE_CONTROL, "IOCTL_SERIAL_GET_LINE_CONTROL" },
80  { IOCTL_SERIAL_SET_TIMEOUTS, "IOCTL_SERIAL_SET_TIMEOUTS" },
81  { IOCTL_SERIAL_GET_TIMEOUTS, "IOCTL_SERIAL_GET_TIMEOUTS" },
82  { IOCTL_SERIAL_GET_CHARS, "IOCTL_SERIAL_GET_CHARS" },
83  { IOCTL_SERIAL_SET_CHARS, "IOCTL_SERIAL_SET_CHARS" },
84  { IOCTL_SERIAL_SET_DTR, "IOCTL_SERIAL_SET_DTR" },
85  { IOCTL_SERIAL_CLR_DTR, "IOCTL_SERIAL_CLR_DTR" },
86  { IOCTL_SERIAL_RESET_DEVICE, "IOCTL_SERIAL_RESET_DEVICE" },
87  { IOCTL_SERIAL_SET_RTS, "IOCTL_SERIAL_SET_RTS" },
88  { IOCTL_SERIAL_CLR_RTS, "IOCTL_SERIAL_CLR_RTS" },
89  { IOCTL_SERIAL_SET_XOFF, "IOCTL_SERIAL_SET_XOFF" },
90  { IOCTL_SERIAL_SET_XON, "IOCTL_SERIAL_SET_XON" },
91  { IOCTL_SERIAL_SET_BREAK_ON, "IOCTL_SERIAL_SET_BREAK_ON" },
92  { IOCTL_SERIAL_SET_BREAK_OFF, "IOCTL_SERIAL_SET_BREAK_OFF" },
93  { IOCTL_SERIAL_SET_QUEUE_SIZE, "IOCTL_SERIAL_SET_QUEUE_SIZE" },
94  { IOCTL_SERIAL_GET_WAIT_MASK, "IOCTL_SERIAL_GET_WAIT_MASK" },
95  { IOCTL_SERIAL_SET_WAIT_MASK, "IOCTL_SERIAL_SET_WAIT_MASK" },
96  { IOCTL_SERIAL_WAIT_ON_MASK, "IOCTL_SERIAL_WAIT_ON_MASK" },
97  { IOCTL_SERIAL_IMMEDIATE_CHAR, "IOCTL_SERIAL_IMMEDIATE_CHAR" },
98  { IOCTL_SERIAL_PURGE, "IOCTL_SERIAL_PURGE" },
99  { IOCTL_SERIAL_GET_HANDFLOW, "IOCTL_SERIAL_GET_HANDFLOW" },
100  { IOCTL_SERIAL_SET_HANDFLOW, "IOCTL_SERIAL_SET_HANDFLOW" },
101  { IOCTL_SERIAL_GET_MODEMSTATUS, "IOCTL_SERIAL_GET_MODEMSTATUS" },
102  { IOCTL_SERIAL_GET_DTRRTS, "IOCTL_SERIAL_GET_DTRRTS" },
103  { IOCTL_SERIAL_GET_COMMSTATUS, "IOCTL_SERIAL_GET_COMMSTATUS" },
104  { IOCTL_SERIAL_GET_PROPERTIES, "IOCTL_SERIAL_GET_PROPERTIES" },
105  // {IOCTL_SERIAL_XOFF_COUNTER, "IOCTL_SERIAL_XOFF_COUNTER"},
106  // {IOCTL_SERIAL_LSRMST_INSERT, "IOCTL_SERIAL_LSRMST_INSERT"},
107  { IOCTL_SERIAL_CONFIG_SIZE, "IOCTL_SERIAL_CONFIG_SIZE" },
108  // {IOCTL_SERIAL_GET_STATS, "IOCTL_SERIAL_GET_STATS"},
109  // {IOCTL_SERIAL_CLEAR_STATS, "IOCTL_SERIAL_CLEAR_STATS"},
110  // {IOCTL_SERIAL_GET_MODEM_CONTROL,"IOCTL_SERIAL_GET_MODEM_CONTROL"},
111  // {IOCTL_SERIAL_SET_MODEM_CONTROL,"IOCTL_SERIAL_SET_MODEM_CONTROL"},
112  // {IOCTL_SERIAL_SET_FIFO_CONTROL, "IOCTL_SERIAL_SET_FIFO_CONTROL"},
113 
114  // {IOCTL_PAR_QUERY_INFORMATION, "IOCTL_PAR_QUERY_INFORMATION"},
115  // {IOCTL_PAR_SET_INFORMATION, "IOCTL_PAR_SET_INFORMATION"},
116  // {IOCTL_PAR_QUERY_DEVICE_ID, "IOCTL_PAR_QUERY_DEVICE_ID"},
117  // {IOCTL_PAR_QUERY_DEVICE_ID_SIZE,"IOCTL_PAR_QUERY_DEVICE_ID_SIZE"},
118  // {IOCTL_IEEE1284_GET_MODE, "IOCTL_IEEE1284_GET_MODE"},
119  // {IOCTL_IEEE1284_NEGOTIATE, "IOCTL_IEEE1284_NEGOTIATE"},
120  // {IOCTL_PAR_SET_WRITE_ADDRESS, "IOCTL_PAR_SET_WRITE_ADDRESS"},
121  // {IOCTL_PAR_SET_READ_ADDRESS, "IOCTL_PAR_SET_READ_ADDRESS"},
122  // {IOCTL_PAR_GET_DEVICE_CAPS, "IOCTL_PAR_GET_DEVICE_CAPS"},
123  // {IOCTL_PAR_GET_DEFAULT_MODES, "IOCTL_PAR_GET_DEFAULT_MODES"},
124  // {IOCTL_PAR_QUERY_RAW_DEVICE_ID, "IOCTL_PAR_QUERY_RAW_DEVICE_ID"},
125  // {IOCTL_PAR_IS_PORT_FREE, "IOCTL_PAR_IS_PORT_FREE"},
126 
127  { IOCTL_USBPRINT_GET_1284_ID, "IOCTL_USBPRINT_GET_1284_ID" }
128 };
129 const char* _comm_serial_ioctl_name(ULONG number)
130 {
131  for (size_t x = 0; x < ARRAYSIZE(S_SERIAL_IOCTL_NAMES); x++)
132  {
133  const _SERIAL_IOCTL_NAME* cur = &S_SERIAL_IOCTL_NAMES[x];
134  if (cur->number == number)
135  return cur->name;
136  }
137 
138  return "(unknown ioctl name)";
139 }
140 
141 static int CommGetFd(HANDLE handle)
142 {
143  WINPR_COMM* comm = (WINPR_COMM*)handle;
144 
145  if (!CommIsHandled(handle))
146  return -1;
147 
148  return comm->fd;
149 }
150 
151 const HANDLE_CREATOR* GetCommHandleCreator(void)
152 {
153 #if defined(WINPR_HAVE_SERIAL_SUPPORT)
154  static const HANDLE_CREATOR sCommHandleCreator = { .IsHandled = IsCommDevice,
155  .CreateFileA = CommCreateFileA };
156  return &sCommHandleCreator;
157 #else
158  return NULL;
159 #endif
160 }
161 
162 static void CommInit(void)
163 {
164  /* NB: error management to be done outside of this function */
165  WINPR_ASSERT(sLog == NULL);
166  WINPR_ASSERT(sCommDevices == NULL);
167  sCommDevices = (COMM_DEVICE**)calloc(COMM_DEVICE_MAX + 1, sizeof(COMM_DEVICE*));
168 
169  if (!sCommDevices)
170  return;
171 
172  if (!InitializeCriticalSectionEx(&sCommDevicesLock, 0, 0))
173  {
174  free(sCommDevices);
175  sCommDevices = NULL;
176  return;
177  }
178 
179  sLog = WLog_Get(TAG);
180  WINPR_ASSERT(sLog != NULL);
181 }
182 
187 static BOOL CommInitialized(void)
188 {
189  if (pthread_once(&sCommInitialized, CommInit) != 0)
190  {
191  SetLastError(ERROR_DLL_INIT_FAILED);
192  return FALSE;
193  }
194 
195  return TRUE;
196 }
197 
198 void CommLog_Print(DWORD level, ...)
199 {
200  if (!CommInitialized())
201  return;
202 
203  va_list ap = { 0 };
204  va_start(ap, level);
205  WLog_PrintVA(sLog, level, ap);
206  va_end(ap);
207 }
208 
209 BOOL BuildCommDCBA(LPCSTR lpDef, LPDCB lpDCB)
210 {
211  if (!CommInitialized())
212  return FALSE;
213 
214  /* TODO: not implemented */
215  CommLog_Print(WLOG_ERROR, "Not implemented");
216  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
217  return FALSE;
218 }
219 
220 BOOL BuildCommDCBW(LPCWSTR lpDef, LPDCB lpDCB)
221 {
222  if (!CommInitialized())
223  return FALSE;
224 
225  /* TODO: not implemented */
226  CommLog_Print(WLOG_ERROR, "Not implemented");
227  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
228  return FALSE;
229 }
230 
231 BOOL BuildCommDCBAndTimeoutsA(LPCSTR lpDef, LPDCB lpDCB, LPCOMMTIMEOUTS lpCommTimeouts)
232 {
233  if (!CommInitialized())
234  return FALSE;
235 
236  /* TODO: not implemented */
237  CommLog_Print(WLOG_ERROR, "Not implemented");
238  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
239  return FALSE;
240 }
241 
242 BOOL BuildCommDCBAndTimeoutsW(LPCWSTR lpDef, LPDCB lpDCB, LPCOMMTIMEOUTS lpCommTimeouts)
243 {
244  if (!CommInitialized())
245  return FALSE;
246 
247  /* TODO: not implemented */
248  CommLog_Print(WLOG_ERROR, "Not implemented");
249  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
250  return FALSE;
251 }
252 
253 BOOL CommConfigDialogA(LPCSTR lpszName, HWND hWnd, LPCOMMCONFIG lpCC)
254 {
255  if (!CommInitialized())
256  return FALSE;
257 
258  /* TODO: not implemented */
259  CommLog_Print(WLOG_ERROR, "Not implemented");
260  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
261  return FALSE;
262 }
263 
264 BOOL CommConfigDialogW(LPCWSTR lpszName, HWND hWnd, LPCOMMCONFIG lpCC)
265 {
266  if (!CommInitialized())
267  return FALSE;
268 
269  /* TODO: not implemented */
270  CommLog_Print(WLOG_ERROR, "Not implemented");
271  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
272  return FALSE;
273 }
274 
275 BOOL GetCommConfig(HANDLE hCommDev, LPCOMMCONFIG lpCC, LPDWORD lpdwSize)
276 {
277  WINPR_COMM* pComm = (WINPR_COMM*)hCommDev;
278 
279  if (!CommInitialized())
280  return FALSE;
281 
282  /* TODO: not implemented */
283 
284  if (!pComm)
285  return FALSE;
286 
287  CommLog_Print(WLOG_ERROR, "Not implemented");
288  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
289  return FALSE;
290 }
291 
292 BOOL SetCommConfig(HANDLE hCommDev, LPCOMMCONFIG lpCC, DWORD dwSize)
293 {
294  WINPR_COMM* pComm = (WINPR_COMM*)hCommDev;
295 
296  if (!CommInitialized())
297  return FALSE;
298 
299  /* TODO: not implemented */
300 
301  if (!pComm)
302  return FALSE;
303 
304  CommLog_Print(WLOG_ERROR, "Not implemented");
305  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
306  return FALSE;
307 }
308 
309 BOOL GetCommMask(HANDLE hFile, PDWORD lpEvtMask)
310 {
311  WINPR_COMM* pComm = (WINPR_COMM*)hFile;
312 
313  if (!CommInitialized())
314  return FALSE;
315 
316  /* TODO: not implemented */
317 
318  if (!pComm)
319  return FALSE;
320 
321  CommLog_Print(WLOG_ERROR, "Not implemented");
322  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
323  return FALSE;
324 }
325 
326 BOOL SetCommMask(HANDLE hFile, DWORD dwEvtMask)
327 {
328  WINPR_COMM* pComm = (WINPR_COMM*)hFile;
329 
330  if (!CommInitialized())
331  return FALSE;
332 
333  /* TODO: not implemented */
334 
335  if (!pComm)
336  return FALSE;
337 
338  CommLog_Print(WLOG_ERROR, "Not implemented");
339  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
340  return FALSE;
341 }
342 
343 BOOL GetCommModemStatus(HANDLE hFile, PDWORD lpModemStat)
344 {
345  WINPR_COMM* pComm = (WINPR_COMM*)hFile;
346 
347  if (!CommInitialized())
348  return FALSE;
349 
350  /* TODO: not implemented */
351 
352  if (!pComm)
353  return FALSE;
354 
355  CommLog_Print(WLOG_ERROR, "Not implemented");
356  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
357  return FALSE;
358 }
359 
365 BOOL GetCommProperties(HANDLE hFile, LPCOMMPROP lpCommProp)
366 {
367  WINPR_COMM* pComm = (WINPR_COMM*)hFile;
368  DWORD bytesReturned = 0;
369 
370  if (!CommIsHandleValid(hFile))
371  return FALSE;
372 
373  if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_GET_PROPERTIES, NULL, 0, lpCommProp,
374  sizeof(COMMPROP), &bytesReturned, NULL))
375  {
376  CommLog_Print(WLOG_WARN, "GetCommProperties failure.");
377  return FALSE;
378  }
379 
380  return TRUE;
381 }
382 
392 BOOL GetCommState(HANDLE hFile, LPDCB lpDCB)
393 {
394  DCB* lpLocalDcb = NULL;
395  struct termios currentState;
396  WINPR_COMM* pComm = (WINPR_COMM*)hFile;
397  DWORD bytesReturned = 0;
398 
399  if (!CommIsHandleValid(hFile))
400  return FALSE;
401 
402  if (!lpDCB)
403  {
404  SetLastError(ERROR_INVALID_DATA);
405  return FALSE;
406  }
407 
408  if (lpDCB->DCBlength < sizeof(DCB))
409  {
410  SetLastError(ERROR_INVALID_DATA);
411  return FALSE;
412  }
413 
414  if (tcgetattr(pComm->fd, &currentState) < 0)
415  {
416  SetLastError(ERROR_IO_DEVICE);
417  return FALSE;
418  }
419 
420  lpLocalDcb = (DCB*)calloc(1, lpDCB->DCBlength);
421 
422  if (lpLocalDcb == NULL)
423  {
424  SetLastError(ERROR_OUTOFMEMORY);
425  return FALSE;
426  }
427 
428  /* error_handle */
429  lpLocalDcb->DCBlength = lpDCB->DCBlength;
430  SERIAL_BAUD_RATE baudRate;
431 
432  if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_GET_BAUD_RATE, NULL, 0, &baudRate,
433  sizeof(SERIAL_BAUD_RATE), &bytesReturned, NULL))
434  {
435  CommLog_Print(WLOG_WARN, "GetCommState failure: could not get the baud rate.");
436  goto error_handle;
437  }
438 
439  lpLocalDcb->BaudRate = baudRate.BaudRate;
440  lpLocalDcb->fBinary = (currentState.c_cflag & ICANON) == 0;
441 
442  if (!lpLocalDcb->fBinary)
443  {
444  CommLog_Print(WLOG_WARN, "Unexpected nonbinary mode, consider to unset the ICANON flag.");
445  }
446 
447  lpLocalDcb->fParity = (currentState.c_iflag & INPCK) != 0;
448  SERIAL_HANDFLOW handflow;
449 
450  if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_GET_HANDFLOW, NULL, 0, &handflow,
451  sizeof(SERIAL_HANDFLOW), &bytesReturned, NULL))
452  {
453  CommLog_Print(WLOG_WARN, "GetCommState failure: could not get the handflow settings.");
454  goto error_handle;
455  }
456 
457  lpLocalDcb->fOutxCtsFlow = (handflow.ControlHandShake & SERIAL_CTS_HANDSHAKE) != 0;
458  lpLocalDcb->fOutxDsrFlow = (handflow.ControlHandShake & SERIAL_DSR_HANDSHAKE) != 0;
459 
460  if (handflow.ControlHandShake & SERIAL_DTR_HANDSHAKE)
461  {
462  lpLocalDcb->fDtrControl = DTR_CONTROL_HANDSHAKE;
463  }
464  else if (handflow.ControlHandShake & SERIAL_DTR_CONTROL)
465  {
466  lpLocalDcb->fDtrControl = DTR_CONTROL_ENABLE;
467  }
468  else
469  {
470  lpLocalDcb->fDtrControl = DTR_CONTROL_DISABLE;
471  }
472 
473  lpLocalDcb->fDsrSensitivity = (handflow.ControlHandShake & SERIAL_DSR_SENSITIVITY) != 0;
474  lpLocalDcb->fTXContinueOnXoff = (handflow.FlowReplace & SERIAL_XOFF_CONTINUE) != 0;
475  lpLocalDcb->fOutX = (handflow.FlowReplace & SERIAL_AUTO_TRANSMIT) != 0;
476  lpLocalDcb->fInX = (handflow.FlowReplace & SERIAL_AUTO_RECEIVE) != 0;
477  lpLocalDcb->fErrorChar = (handflow.FlowReplace & SERIAL_ERROR_CHAR) != 0;
478  lpLocalDcb->fNull = (handflow.FlowReplace & SERIAL_NULL_STRIPPING) != 0;
479 
480  if (handflow.FlowReplace & SERIAL_RTS_HANDSHAKE)
481  {
482  lpLocalDcb->fRtsControl = RTS_CONTROL_HANDSHAKE;
483  }
484  else if (handflow.FlowReplace & SERIAL_RTS_CONTROL)
485  {
486  lpLocalDcb->fRtsControl = RTS_CONTROL_ENABLE;
487  }
488  else
489  {
490  lpLocalDcb->fRtsControl = RTS_CONTROL_DISABLE;
491  }
492 
493  // FIXME: how to get the RTS_CONTROL_TOGGLE state? Does it match the UART 16750's Autoflow
494  // Control Enabled bit in its Modem Control Register (MCR)
495  lpLocalDcb->fAbortOnError = (handflow.ControlHandShake & SERIAL_ERROR_ABORT) != 0;
496  /* lpLocalDcb->fDummy2 not used */
497  lpLocalDcb->wReserved = 0; /* must be zero */
498  lpLocalDcb->XonLim = handflow.XonLimit;
499  lpLocalDcb->XoffLim = handflow.XoffLimit;
500  SERIAL_LINE_CONTROL lineControl = { 0 };
501 
502  if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_GET_LINE_CONTROL, NULL, 0, &lineControl,
503  sizeof(SERIAL_LINE_CONTROL), &bytesReturned, NULL))
504  {
505  CommLog_Print(WLOG_WARN, "GetCommState failure: could not get the control settings.");
506  goto error_handle;
507  }
508 
509  lpLocalDcb->ByteSize = lineControl.WordLength;
510  lpLocalDcb->Parity = lineControl.Parity;
511  lpLocalDcb->StopBits = lineControl.StopBits;
512  SERIAL_CHARS serialChars;
513 
514  if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_GET_CHARS, NULL, 0, &serialChars,
515  sizeof(SERIAL_CHARS), &bytesReturned, NULL))
516  {
517  CommLog_Print(WLOG_WARN, "GetCommState failure: could not get the serial chars.");
518  goto error_handle;
519  }
520 
521  lpLocalDcb->XonChar = serialChars.XonChar;
522  lpLocalDcb->XoffChar = serialChars.XoffChar;
523  lpLocalDcb->ErrorChar = serialChars.ErrorChar;
524  lpLocalDcb->EofChar = serialChars.EofChar;
525  lpLocalDcb->EvtChar = serialChars.EventChar;
526  memcpy(lpDCB, lpLocalDcb, lpDCB->DCBlength);
527  free(lpLocalDcb);
528  return TRUE;
529 error_handle:
530  free(lpLocalDcb);
531  return FALSE;
532 }
533 
545 BOOL SetCommState(HANDLE hFile, LPDCB lpDCB)
546 {
547  struct termios upcomingTermios = { 0 };
548  WINPR_COMM* pComm = (WINPR_COMM*)hFile;
549  DWORD bytesReturned = 0;
550 
551  /* FIXME: validate changes according GetCommProperties? */
552 
553  if (!CommIsHandleValid(hFile))
554  return FALSE;
555 
556  if (!lpDCB)
557  {
558  SetLastError(ERROR_INVALID_DATA);
559  return FALSE;
560  }
561 
562  /* NB: did the choice to call ioctls first when available and
563  then to setup upcomingTermios. Don't mix both stages. */
565  SERIAL_BAUD_RATE baudRate;
566  baudRate.BaudRate = lpDCB->BaudRate;
567 
568  if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_SET_BAUD_RATE, &baudRate, sizeof(SERIAL_BAUD_RATE),
569  NULL, 0, &bytesReturned, NULL))
570  {
571  CommLog_Print(WLOG_WARN, "SetCommState failure: could not set the baud rate.");
572  return FALSE;
573  }
574 
575  SERIAL_CHARS serialChars;
576 
577  if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_GET_CHARS, NULL, 0, &serialChars,
578  sizeof(SERIAL_CHARS), &bytesReturned,
579  NULL)) /* as of today, required for BreakChar */
580  {
581  CommLog_Print(WLOG_WARN, "SetCommState failure: could not get the initial serial chars.");
582  return FALSE;
583  }
584 
585  serialChars.XonChar = lpDCB->XonChar;
586  serialChars.XoffChar = lpDCB->XoffChar;
587  serialChars.ErrorChar = lpDCB->ErrorChar;
588  serialChars.EofChar = lpDCB->EofChar;
589  serialChars.EventChar = lpDCB->EvtChar;
590 
591  if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_SET_CHARS, &serialChars, sizeof(SERIAL_CHARS),
592  NULL, 0, &bytesReturned, NULL))
593  {
594  CommLog_Print(WLOG_WARN, "SetCommState failure: could not set the serial chars.");
595  return FALSE;
596  }
597 
598  SERIAL_LINE_CONTROL lineControl;
599  lineControl.StopBits = lpDCB->StopBits;
600  lineControl.Parity = lpDCB->Parity;
601  lineControl.WordLength = lpDCB->ByteSize;
602 
603  if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_SET_LINE_CONTROL, &lineControl,
604  sizeof(SERIAL_LINE_CONTROL), NULL, 0, &bytesReturned, NULL))
605  {
606  CommLog_Print(WLOG_WARN, "SetCommState failure: could not set the control settings.");
607  return FALSE;
608  }
609 
610  SERIAL_HANDFLOW handflow = { 0 };
611 
612  if (lpDCB->fOutxCtsFlow)
613  {
614  handflow.ControlHandShake |= SERIAL_CTS_HANDSHAKE;
615  }
616 
617  if (lpDCB->fOutxDsrFlow)
618  {
619  handflow.ControlHandShake |= SERIAL_DSR_HANDSHAKE;
620  }
621 
622  switch (lpDCB->fDtrControl)
623  {
624  case SERIAL_DTR_HANDSHAKE:
625  handflow.ControlHandShake |= DTR_CONTROL_HANDSHAKE;
626  break;
627 
628  case SERIAL_DTR_CONTROL:
629  handflow.ControlHandShake |= DTR_CONTROL_ENABLE;
630  break;
631 
632  case DTR_CONTROL_DISABLE:
633  /* do nothing since handflow is init-zeroed */
634  break;
635 
636  default:
637  CommLog_Print(WLOG_WARN, "Unexpected fDtrControl value: %" PRIu32 "\n",
638  lpDCB->fDtrControl);
639  return FALSE;
640  }
641 
642  if (lpDCB->fDsrSensitivity)
643  {
644  handflow.ControlHandShake |= SERIAL_DSR_SENSITIVITY;
645  }
646 
647  if (lpDCB->fTXContinueOnXoff)
648  {
649  handflow.FlowReplace |= SERIAL_XOFF_CONTINUE;
650  }
651 
652  if (lpDCB->fOutX)
653  {
654  handflow.FlowReplace |= SERIAL_AUTO_TRANSMIT;
655  }
656 
657  if (lpDCB->fInX)
658  {
659  handflow.FlowReplace |= SERIAL_AUTO_RECEIVE;
660  }
661 
662  if (lpDCB->fErrorChar)
663  {
664  handflow.FlowReplace |= SERIAL_ERROR_CHAR;
665  }
666 
667  if (lpDCB->fNull)
668  {
669  handflow.FlowReplace |= SERIAL_NULL_STRIPPING;
670  }
671 
672  switch (lpDCB->fRtsControl)
673  {
674  case RTS_CONTROL_TOGGLE:
675  CommLog_Print(WLOG_WARN, "Unsupported RTS_CONTROL_TOGGLE feature");
676  // FIXME: see also GetCommState()
677  return FALSE;
678 
679  case RTS_CONTROL_HANDSHAKE:
680  handflow.FlowReplace |= SERIAL_RTS_HANDSHAKE;
681  break;
682 
683  case RTS_CONTROL_ENABLE:
684  handflow.FlowReplace |= SERIAL_RTS_CONTROL;
685  break;
686 
687  case RTS_CONTROL_DISABLE:
688  /* do nothing since handflow is init-zeroed */
689  break;
690 
691  default:
692  CommLog_Print(WLOG_WARN, "Unexpected fRtsControl value: %" PRIu32 "\n",
693  lpDCB->fRtsControl);
694  return FALSE;
695  }
696 
697  if (lpDCB->fAbortOnError)
698  {
699  handflow.ControlHandShake |= SERIAL_ERROR_ABORT;
700  }
701 
702  /* lpDCB->fDummy2 not used */
703  /* lpLocalDcb->wReserved ignored */
704  handflow.XonLimit = lpDCB->XonLim;
705  handflow.XoffLimit = lpDCB->XoffLim;
706 
707  if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_SET_HANDFLOW, &handflow, sizeof(SERIAL_HANDFLOW),
708  NULL, 0, &bytesReturned, NULL))
709  {
710  CommLog_Print(WLOG_WARN, "SetCommState failure: could not set the handflow settings.");
711  return FALSE;
712  }
713 
716  if (tcgetattr(pComm->fd, &upcomingTermios) <
717  0) /* NB: preserves current settings not directly handled by the Communication Functions */
718  {
719  SetLastError(ERROR_IO_DEVICE);
720  return FALSE;
721  }
722 
723  if (lpDCB->fBinary)
724  {
725  upcomingTermios.c_lflag &= ~ICANON;
726  }
727  else
728  {
729  upcomingTermios.c_lflag |= ICANON;
730  CommLog_Print(WLOG_WARN, "Unexpected nonbinary mode, consider to unset the ICANON flag.");
731  }
732 
733  if (lpDCB->fParity)
734  {
735  upcomingTermios.c_iflag |= INPCK;
736  }
737  else
738  {
739  upcomingTermios.c_iflag &= ~INPCK;
740  }
741 
742  /* http://msdn.microsoft.com/en-us/library/windows/desktop/aa363423%28v=vs.85%29.aspx
743  *
744  * The SetCommState function reconfigures the communications
745  * resource, but it does not affect the internal output and
746  * input buffers of the specified driver. The buffers are not
747  * flushed, and pending read and write operations are not
748  * terminated prematurely.
749  *
750  * TCSANOW matches the best this definition
751  */
752 
753  if (_comm_ioctl_tcsetattr(pComm->fd, TCSANOW, &upcomingTermios) < 0)
754  {
755  SetLastError(ERROR_IO_DEVICE);
756  return FALSE;
757  }
758 
759  return TRUE;
760 }
761 
766 BOOL GetCommTimeouts(HANDLE hFile, LPCOMMTIMEOUTS lpCommTimeouts)
767 {
768  WINPR_COMM* pComm = (WINPR_COMM*)hFile;
769  DWORD bytesReturned = 0;
770 
771  if (!CommIsHandleValid(hFile))
772  return FALSE;
773 
774  /* as of today, SERIAL_TIMEOUTS and COMMTIMEOUTS structures are identical */
775 
776  if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_GET_TIMEOUTS, NULL, 0, lpCommTimeouts,
777  sizeof(COMMTIMEOUTS), &bytesReturned, NULL))
778  {
779  CommLog_Print(WLOG_WARN, "GetCommTimeouts failure.");
780  return FALSE;
781  }
782 
783  return TRUE;
784 }
785 
790 BOOL SetCommTimeouts(HANDLE hFile, LPCOMMTIMEOUTS lpCommTimeouts)
791 {
792  WINPR_COMM* pComm = (WINPR_COMM*)hFile;
793  DWORD bytesReturned = 0;
794 
795  if (!CommIsHandleValid(hFile))
796  return FALSE;
797 
798  /* as of today, SERIAL_TIMEOUTS and COMMTIMEOUTS structures are identical */
799 
800  if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_SET_TIMEOUTS, lpCommTimeouts, sizeof(COMMTIMEOUTS),
801  NULL, 0, &bytesReturned, NULL))
802  {
803  CommLog_Print(WLOG_WARN, "SetCommTimeouts failure.");
804  return FALSE;
805  }
806 
807  return TRUE;
808 }
809 
810 BOOL GetDefaultCommConfigA(LPCSTR lpszName, LPCOMMCONFIG lpCC, LPDWORD lpdwSize)
811 {
812  if (!CommInitialized())
813  return FALSE;
814 
815  /* TODO: not implemented */
816  CommLog_Print(WLOG_ERROR, "Not implemented");
817  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
818  return FALSE;
819 }
820 
821 BOOL GetDefaultCommConfigW(LPCWSTR lpszName, LPCOMMCONFIG lpCC, LPDWORD lpdwSize)
822 {
823  if (!CommInitialized())
824  return FALSE;
825 
826  /* TODO: not implemented */
827  CommLog_Print(WLOG_ERROR, "Not implemented");
828  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
829  return FALSE;
830 }
831 
832 BOOL SetDefaultCommConfigA(LPCSTR lpszName, LPCOMMCONFIG lpCC, DWORD dwSize)
833 {
834  if (!CommInitialized())
835  return FALSE;
836 
837  /* TODO: not implemented */
838  CommLog_Print(WLOG_ERROR, "Not implemented");
839  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
840  return FALSE;
841 }
842 
843 BOOL SetDefaultCommConfigW(LPCWSTR lpszName, LPCOMMCONFIG lpCC, DWORD dwSize)
844 {
845  if (!CommInitialized())
846  return FALSE;
847 
848  /* TODO: not implemented */
849  CommLog_Print(WLOG_ERROR, "Not implemented");
850  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
851  return FALSE;
852 }
853 
854 BOOL SetCommBreak(HANDLE hFile)
855 {
856  WINPR_COMM* pComm = (WINPR_COMM*)hFile;
857 
858  if (!CommInitialized())
859  return FALSE;
860 
861  /* TODO: not implemented */
862 
863  if (!pComm)
864  return FALSE;
865 
866  CommLog_Print(WLOG_ERROR, "Not implemented");
867  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
868  return FALSE;
869 }
870 
871 BOOL ClearCommBreak(HANDLE hFile)
872 {
873  WINPR_COMM* pComm = (WINPR_COMM*)hFile;
874 
875  if (!CommInitialized())
876  return FALSE;
877 
878  /* TODO: not implemented */
879 
880  if (!pComm)
881  return FALSE;
882 
883  CommLog_Print(WLOG_ERROR, "Not implemented");
884  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
885  return FALSE;
886 }
887 
888 BOOL ClearCommError(HANDLE hFile, PDWORD lpErrors, LPCOMSTAT lpStat)
889 {
890  WINPR_COMM* pComm = (WINPR_COMM*)hFile;
891 
892  if (!CommInitialized())
893  return FALSE;
894 
895  /* TODO: not implemented */
896 
897  if (!pComm)
898  return FALSE;
899 
900  CommLog_Print(WLOG_ERROR, "Not implemented");
901  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
902  return FALSE;
903 }
904 
905 BOOL PurgeComm(HANDLE hFile, DWORD dwFlags)
906 {
907  WINPR_COMM* pComm = (WINPR_COMM*)hFile;
908  DWORD bytesReturned = 0;
909 
910  if (!CommIsHandleValid(hFile))
911  return FALSE;
912 
913  if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_PURGE, &dwFlags, sizeof(DWORD), NULL, 0,
914  &bytesReturned, NULL))
915  {
916  CommLog_Print(WLOG_WARN, "PurgeComm failure.");
917  return FALSE;
918  }
919 
920  return TRUE;
921 }
922 
923 BOOL SetupComm(HANDLE hFile, DWORD dwInQueue, DWORD dwOutQueue)
924 {
925  WINPR_COMM* pComm = (WINPR_COMM*)hFile;
926  SERIAL_QUEUE_SIZE queueSize;
927  DWORD bytesReturned = 0;
928 
929  if (!CommIsHandleValid(hFile))
930  return FALSE;
931 
932  queueSize.InSize = dwInQueue;
933  queueSize.OutSize = dwOutQueue;
934 
935  if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_SET_QUEUE_SIZE, &queueSize,
936  sizeof(SERIAL_QUEUE_SIZE), NULL, 0, &bytesReturned, NULL))
937  {
938  CommLog_Print(WLOG_WARN, "SetCommTimeouts failure.");
939  return FALSE;
940  }
941 
942  return TRUE;
943 }
944 
945 BOOL EscapeCommFunction(HANDLE hFile, DWORD dwFunc)
946 {
947  WINPR_COMM* pComm = (WINPR_COMM*)hFile;
948 
949  if (!CommInitialized())
950  return FALSE;
951 
952  /* TODO: not implemented */
953 
954  if (!pComm)
955  return FALSE;
956 
957  CommLog_Print(WLOG_ERROR, "Not implemented");
958  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
959  return FALSE;
960 }
961 
962 BOOL TransmitCommChar(HANDLE hFile, char cChar)
963 {
964  WINPR_COMM* pComm = (WINPR_COMM*)hFile;
965 
966  if (!CommInitialized())
967  return FALSE;
968 
969  /* TODO: not implemented */
970 
971  if (!pComm)
972  return FALSE;
973 
974  CommLog_Print(WLOG_ERROR, "Not implemented");
975  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
976  return FALSE;
977 }
978 
979 BOOL WaitCommEvent(HANDLE hFile, PDWORD lpEvtMask, LPOVERLAPPED lpOverlapped)
980 {
981  WINPR_COMM* pComm = (WINPR_COMM*)hFile;
982 
983  if (!CommInitialized())
984  return FALSE;
985 
986  /* TODO: not implemented */
987 
988  if (!pComm)
989  return FALSE;
990 
991  CommLog_Print(WLOG_ERROR, "Not implemented");
992  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
993  return FALSE;
994 }
995 
1005 BOOL DefineCommDevice(/* DWORD dwFlags,*/ LPCTSTR lpDeviceName, LPCTSTR lpTargetPath)
1006 {
1007  LPTSTR storedDeviceName = NULL;
1008  LPTSTR storedTargetPath = NULL;
1009 
1010  if (!CommInitialized())
1011  return FALSE;
1012 
1013  EnterCriticalSection(&sCommDevicesLock);
1014 
1015  if (sCommDevices == NULL)
1016  {
1017  SetLastError(ERROR_DLL_INIT_FAILED);
1018  goto error_handle;
1019  }
1020 
1021  storedDeviceName = _tcsdup(lpDeviceName);
1022 
1023  if (storedDeviceName == NULL)
1024  {
1025  SetLastError(ERROR_OUTOFMEMORY);
1026  goto error_handle;
1027  }
1028 
1029  storedTargetPath = _tcsdup(lpTargetPath);
1030 
1031  if (storedTargetPath == NULL)
1032  {
1033  SetLastError(ERROR_OUTOFMEMORY);
1034  goto error_handle;
1035  }
1036 
1037  int i = 0;
1038  for (; i < COMM_DEVICE_MAX; i++)
1039  {
1040  if (sCommDevices[i] != NULL)
1041  {
1042  if (_tcscmp(sCommDevices[i]->name, storedDeviceName) == 0)
1043  {
1044  /* take over the emplacement */
1045  free(sCommDevices[i]->name);
1046  free(sCommDevices[i]->path);
1047  sCommDevices[i]->name = storedDeviceName;
1048  sCommDevices[i]->path = storedTargetPath;
1049  break;
1050  }
1051  }
1052  else
1053  {
1054  /* new emplacement */
1055  sCommDevices[i] = (COMM_DEVICE*)calloc(1, sizeof(COMM_DEVICE));
1056 
1057  if (sCommDevices[i] == NULL)
1058  {
1059  SetLastError(ERROR_OUTOFMEMORY);
1060  goto error_handle;
1061  }
1062 
1063  sCommDevices[i]->name = storedDeviceName;
1064  sCommDevices[i]->path = storedTargetPath;
1065  break;
1066  }
1067  }
1068 
1069  if (i == COMM_DEVICE_MAX)
1070  {
1071  SetLastError(ERROR_OUTOFMEMORY);
1072  goto error_handle;
1073  }
1074 
1075  LeaveCriticalSection(&sCommDevicesLock);
1076  return TRUE;
1077 error_handle:
1078  free(storedDeviceName);
1079  free(storedTargetPath);
1080  LeaveCriticalSection(&sCommDevicesLock);
1081  return FALSE;
1082 }
1083 
1100 DWORD QueryCommDevice(LPCTSTR lpDeviceName, LPTSTR lpTargetPath, DWORD ucchMax)
1101 {
1102  LPTSTR storedTargetPath = NULL;
1103  SetLastError(ERROR_SUCCESS);
1104 
1105  if (!CommInitialized())
1106  return 0;
1107 
1108  if (sCommDevices == NULL)
1109  {
1110  SetLastError(ERROR_DLL_INIT_FAILED);
1111  return 0;
1112  }
1113 
1114  if (lpDeviceName == NULL || lpTargetPath == NULL)
1115  {
1116  SetLastError(ERROR_NOT_SUPPORTED);
1117  return 0;
1118  }
1119 
1120  EnterCriticalSection(&sCommDevicesLock);
1121  storedTargetPath = NULL;
1122 
1123  for (int i = 0; i < COMM_DEVICE_MAX; i++)
1124  {
1125  if (sCommDevices[i] != NULL)
1126  {
1127  if (_tcscmp(sCommDevices[i]->name, lpDeviceName) == 0)
1128  {
1129  storedTargetPath = sCommDevices[i]->path;
1130  break;
1131  }
1132 
1133  continue;
1134  }
1135 
1136  break;
1137  }
1138 
1139  LeaveCriticalSection(&sCommDevicesLock);
1140 
1141  if (storedTargetPath == NULL)
1142  {
1143  SetLastError(ERROR_INVALID_DATA);
1144  return 0;
1145  }
1146 
1147  const size_t size = _tcsnlen(storedTargetPath, ucchMax);
1148  if (size + 2 > ucchMax)
1149  {
1150  SetLastError(ERROR_INSUFFICIENT_BUFFER);
1151  return 0;
1152  }
1153 
1154  _tcsncpy(lpTargetPath, storedTargetPath, size + 1);
1155  lpTargetPath[size + 2] = '\0'; /* 2nd final '\0' */
1156  return (DWORD)size + 2UL;
1157 }
1158 
1162 BOOL IsCommDevice(LPCTSTR lpDeviceName)
1163 {
1164  TCHAR lpTargetPath[MAX_PATH];
1165 
1166  if (!CommInitialized())
1167  return FALSE;
1168 
1169  if (QueryCommDevice(lpDeviceName, lpTargetPath, MAX_PATH) > 0)
1170  {
1171  return TRUE;
1172  }
1173 
1174  return FALSE;
1175 }
1176 
1180 void _comm_setServerSerialDriver(HANDLE hComm, SERIAL_DRIVER_ID driverId)
1181 {
1182  ULONG Type = 0;
1183  WINPR_HANDLE* Object = NULL;
1184  WINPR_COMM* pComm = NULL;
1185 
1186  if (!CommInitialized())
1187  return;
1188 
1189  if (!winpr_Handle_GetInfo(hComm, &Type, &Object))
1190  {
1191  CommLog_Print(WLOG_WARN, "_comm_setServerSerialDriver failure");
1192  return;
1193  }
1194 
1195  pComm = (WINPR_COMM*)Object;
1196  pComm->serverSerialDriverId = driverId;
1197 }
1198 
1199 static HANDLE_OPS ops = { CommIsHandled, CommCloseHandle,
1200  CommGetFd, NULL, /* CleanupHandle */
1201  NULL, NULL,
1202  NULL, NULL,
1203  NULL, NULL,
1204  NULL, NULL,
1205  NULL, NULL,
1206  NULL, NULL,
1207  NULL, NULL,
1208  NULL, NULL,
1209  NULL };
1210 
1236 HANDLE CommCreateFileA(LPCSTR lpDeviceName, DWORD dwDesiredAccess, DWORD dwShareMode,
1237  LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition,
1238  DWORD dwFlagsAndAttributes, HANDLE hTemplateFile)
1239 {
1240  CHAR devicePath[MAX_PATH] = { 0 };
1241  struct stat deviceStat = { 0 };
1242  WINPR_COMM* pComm = NULL;
1243  struct termios upcomingTermios = { 0 };
1244 
1245  if (!CommInitialized())
1246  return INVALID_HANDLE_VALUE;
1247 
1248  if (dwDesiredAccess != (GENERIC_READ | GENERIC_WRITE))
1249  {
1250  CommLog_Print(WLOG_WARN, "unexpected access to the device: 0x%08" PRIX32 "",
1251  dwDesiredAccess);
1252  }
1253 
1254  if (dwShareMode != 0)
1255  {
1256  SetLastError(ERROR_SHARING_VIOLATION);
1257  return INVALID_HANDLE_VALUE;
1258  }
1259 
1260  /* TODO: Prevents other processes from opening a file or
1261  * device if they request delete, read, or write access. */
1262 
1263  if (lpSecurityAttributes != NULL)
1264  {
1265  CommLog_Print(WLOG_WARN, "unexpected security attributes, nLength=%" PRIu32 "",
1266  lpSecurityAttributes->nLength);
1267  }
1268 
1269  if (dwCreationDisposition != OPEN_EXISTING)
1270  {
1271  SetLastError(ERROR_FILE_NOT_FOUND); /* FIXME: ERROR_NOT_SUPPORTED better? */
1272  return INVALID_HANDLE_VALUE;
1273  }
1274 
1275  if (QueryCommDevice(lpDeviceName, devicePath, MAX_PATH) <= 0)
1276  {
1277  /* SetLastError(GetLastError()); */
1278  return INVALID_HANDLE_VALUE;
1279  }
1280 
1281  if (stat(devicePath, &deviceStat) < 0)
1282  {
1283  CommLog_Print(WLOG_WARN, "device not found %s", devicePath);
1284  SetLastError(ERROR_FILE_NOT_FOUND);
1285  return INVALID_HANDLE_VALUE;
1286  }
1287 
1288  if (!S_ISCHR(deviceStat.st_mode))
1289  {
1290  CommLog_Print(WLOG_WARN, "bad device %s", devicePath);
1291  SetLastError(ERROR_BAD_DEVICE);
1292  return INVALID_HANDLE_VALUE;
1293  }
1294 
1295  if (dwFlagsAndAttributes != 0)
1296  {
1297  CommLog_Print(WLOG_WARN, "unexpected flags and attributes: 0x%08" PRIX32 "",
1298  dwFlagsAndAttributes);
1299  }
1300 
1301  if (hTemplateFile != NULL)
1302  {
1303  SetLastError(ERROR_NOT_SUPPORTED); /* FIXME: other proper error? */
1304  return INVALID_HANDLE_VALUE;
1305  }
1306 
1307  pComm = (WINPR_COMM*)calloc(1, sizeof(WINPR_COMM));
1308 
1309  if (pComm == NULL)
1310  {
1311  SetLastError(ERROR_OUTOFMEMORY);
1312  return INVALID_HANDLE_VALUE;
1313  }
1314 
1315  WINPR_HANDLE_SET_TYPE_AND_MODE(pComm, HANDLE_TYPE_COMM, WINPR_FD_READ);
1316  pComm->common.ops = &ops;
1317  /* error_handle */
1318  pComm->fd = open(devicePath, O_RDWR | O_NOCTTY | O_NONBLOCK);
1319 
1320  if (pComm->fd < 0)
1321  {
1322  CommLog_Print(WLOG_WARN, "failed to open device %s", devicePath);
1323  SetLastError(ERROR_BAD_DEVICE);
1324  goto error_handle;
1325  }
1326 
1327  pComm->fd_read = open(devicePath, O_RDONLY | O_NOCTTY | O_NONBLOCK);
1328 
1329  if (pComm->fd_read < 0)
1330  {
1331  CommLog_Print(WLOG_WARN, "failed to open fd_read, device: %s", devicePath);
1332  SetLastError(ERROR_BAD_DEVICE);
1333  goto error_handle;
1334  }
1335 
1336 #if defined(WINPR_HAVE_SYS_EVENTFD_H)
1337  pComm->fd_read_event = eventfd(
1338  0, EFD_NONBLOCK); /* EFD_NONBLOCK required because a read() is not always expected */
1339 #endif
1340 
1341  if (pComm->fd_read_event < 0)
1342  {
1343  CommLog_Print(WLOG_WARN, "failed to open fd_read_event, device: %s", devicePath);
1344  SetLastError(ERROR_BAD_DEVICE);
1345  goto error_handle;
1346  }
1347 
1348  InitializeCriticalSection(&pComm->ReadLock);
1349  pComm->fd_write = open(devicePath, O_WRONLY | O_NOCTTY | O_NONBLOCK);
1350 
1351  if (pComm->fd_write < 0)
1352  {
1353  CommLog_Print(WLOG_WARN, "failed to open fd_write, device: %s", devicePath);
1354  SetLastError(ERROR_BAD_DEVICE);
1355  goto error_handle;
1356  }
1357 
1358 #if defined(WINPR_HAVE_SYS_EVENTFD_H)
1359  pComm->fd_write_event = eventfd(
1360  0, EFD_NONBLOCK); /* EFD_NONBLOCK required because a read() is not always expected */
1361 #endif
1362 
1363  if (pComm->fd_write_event < 0)
1364  {
1365  CommLog_Print(WLOG_WARN, "failed to open fd_write_event, device: %s", devicePath);
1366  SetLastError(ERROR_BAD_DEVICE);
1367  goto error_handle;
1368  }
1369 
1370  InitializeCriticalSection(&pComm->WriteLock);
1371  /* can also be setup later on with _comm_setServerSerialDriver() */
1372  pComm->serverSerialDriverId = SerialDriverUnknown;
1373  InitializeCriticalSection(&pComm->EventsLock);
1374 
1375 #if defined(WINPR_HAVE_COMM_COUNTERS)
1376  if (ioctl(pComm->fd, TIOCGICOUNT, &(pComm->counters)) < 0)
1377  {
1378  char ebuffer[256] = { 0 };
1379  CommLog_Print(WLOG_WARN, "TIOCGICOUNT ioctl failed, errno=[%d] %s.", errno,
1380  winpr_strerror(errno, ebuffer, sizeof(ebuffer)));
1381  CommLog_Print(WLOG_WARN, "could not read counters.");
1382  /* could not initialize counters but keep on.
1383  *
1384  * Not all drivers, especially for USB to serial
1385  * adapters (e.g. those based on pl2303), does support
1386  * this call.
1387  */
1388  ZeroMemory(&(pComm->counters), sizeof(struct serial_icounter_struct));
1389  }
1390 #endif
1391 
1392  /* The binary/raw mode is required for the redirection but
1393  * only flags that are not handle somewhere-else, except
1394  * ICANON, are forced here. */
1395  ZeroMemory(&upcomingTermios, sizeof(struct termios));
1396 
1397  if (tcgetattr(pComm->fd, &upcomingTermios) < 0)
1398  {
1399  SetLastError(ERROR_IO_DEVICE);
1400  goto error_handle;
1401  }
1402 
1403  upcomingTermios.c_iflag &=
1404  ~(/*IGNBRK |*/ BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL /*| IXON*/);
1405  upcomingTermios.c_oflag = 0; /* <=> &= ~OPOST */
1406  upcomingTermios.c_lflag = 0; /* <=> &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN); */
1407  /* upcomingTermios.c_cflag &= ~(CSIZE | PARENB); */
1408  /* upcomingTermios.c_cflag |= CS8; */
1409  /* About missing flags recommended by termios(3):
1410  *
1411  * IGNBRK and IXON, see: IOCTL_SERIAL_SET_HANDFLOW
1412  * CSIZE, PARENB and CS8, see: IOCTL_SERIAL_SET_LINE_CONTROL
1413  */
1414  /* a few more settings required for the redirection */
1415  upcomingTermios.c_cflag |= CLOCAL | CREAD;
1416 
1417  if (_comm_ioctl_tcsetattr(pComm->fd, TCSANOW, &upcomingTermios) < 0)
1418  {
1419  SetLastError(ERROR_IO_DEVICE);
1420  goto error_handle;
1421  }
1422 
1423  return (HANDLE)pComm;
1424 error_handle:
1425  WINPR_PRAGMA_DIAG_PUSH
1426  WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC(void) CloseHandle(pComm);
1427  WINPR_PRAGMA_DIAG_POP
1428  return INVALID_HANDLE_VALUE;
1429 }
1430 
1431 BOOL CommIsHandled(HANDLE handle)
1432 {
1433  if (!CommInitialized())
1434  return FALSE;
1435 
1436  return WINPR_HANDLE_IS_HANDLED(handle, HANDLE_TYPE_COMM, TRUE);
1437 }
1438 
1439 BOOL CommIsHandleValid(HANDLE handle)
1440 {
1441  WINPR_COMM* pComm = (WINPR_COMM*)handle;
1442  if (!CommIsHandled(handle))
1443  return FALSE;
1444  if (pComm->fd <= 0)
1445  {
1446  SetLastError(ERROR_INVALID_HANDLE);
1447  return FALSE;
1448  }
1449  return TRUE;
1450 }
1451 
1452 BOOL CommCloseHandle(HANDLE handle)
1453 {
1454  WINPR_COMM* pComm = (WINPR_COMM*)handle;
1455 
1456  if (!CommIsHandled(handle))
1457  return FALSE;
1458 
1459  DeleteCriticalSection(&pComm->ReadLock);
1460  DeleteCriticalSection(&pComm->WriteLock);
1461  DeleteCriticalSection(&pComm->EventsLock);
1462 
1463  if (pComm->fd > 0)
1464  close(pComm->fd);
1465 
1466  if (pComm->fd_write > 0)
1467  close(pComm->fd_write);
1468 
1469  if (pComm->fd_write_event > 0)
1470  close(pComm->fd_write_event);
1471 
1472  if (pComm->fd_read > 0)
1473  close(pComm->fd_read);
1474 
1475  if (pComm->fd_read_event > 0)
1476  close(pComm->fd_read_event);
1477 
1478  free(pComm);
1479  return TRUE;
1480 }
1481 
1482 #if defined(WINPR_HAVE_SYS_EVENTFD_H)
1483 #ifndef WITH_EVENTFD_READ_WRITE
1484 int eventfd_read(int fd, eventfd_t* value)
1485 {
1486  return (read(fd, value, sizeof(*value)) == sizeof(*value)) ? 0 : -1;
1487 }
1488 
1489 int eventfd_write(int fd, eventfd_t value)
1490 {
1491  return (write(fd, &value, sizeof(value)) == sizeof(value)) ? 0 : -1;
1492 }
1493 #endif
1494 #endif