FreeRDP
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Modules Pages
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
57static wLog* sLog = NULL;
58
59struct comm_device
60{
61 LPTSTR name;
62 LPTSTR path;
63};
64
65typedef 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 maximum of COMM_DEVICE_MAX COMM_DEVICE */
69#define COMM_DEVICE_MAX 128
70static COMM_DEVICE** sCommDevices = NULL;
71static CRITICAL_SECTION sCommDevicesLock = { 0 };
72
73static pthread_once_t sCommInitialized = PTHREAD_ONCE_INIT;
74
75static 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
130const char* _comm_serial_ioctl_name(ULONG number)
131{
132 for (size_t x = 0; x < ARRAYSIZE(S_SERIAL_IOCTL_NAMES); x++)
133 {
134 const _SERIAL_IOCTL_NAME* cur = &S_SERIAL_IOCTL_NAMES[x];
135 if (cur->number == number)
136 return cur->name;
137 }
138
139 return "(unknown ioctl name)";
140}
141
142static int CommGetFd(HANDLE handle)
143{
144 WINPR_COMM* comm = (WINPR_COMM*)handle;
145
146 if (!CommIsHandled(handle))
147 return -1;
148
149 return comm->fd;
150}
151
152const HANDLE_CREATOR* GetCommHandleCreator(void)
153{
154#if defined(WINPR_HAVE_SERIAL_SUPPORT)
155 static const HANDLE_CREATOR sCommHandleCreator = { .IsHandled = IsCommDevice,
156 .CreateFileA = CommCreateFileA };
157 return &sCommHandleCreator;
158#else
159 return NULL;
160#endif
161}
162
163static void CommInit(void)
164{
165 /* NB: error management to be done outside of this function */
166 WINPR_ASSERT(sLog == NULL);
167 WINPR_ASSERT(sCommDevices == NULL);
168 sCommDevices = (COMM_DEVICE**)calloc(COMM_DEVICE_MAX + 1, sizeof(COMM_DEVICE*));
169
170 if (!sCommDevices)
171 return;
172
173 if (!InitializeCriticalSectionEx(&sCommDevicesLock, 0, 0))
174 {
175 free((void*)sCommDevices);
176 sCommDevices = NULL;
177 return;
178 }
179
180 sLog = WLog_Get(TAG);
181 WINPR_ASSERT(sLog != NULL);
182}
183
188static BOOL CommInitialized(void)
189{
190 if (pthread_once(&sCommInitialized, CommInit) != 0)
191 {
192 SetLastError(ERROR_DLL_INIT_FAILED);
193 return FALSE;
194 }
195
196 return TRUE;
197}
198
199void CommLog_PrintEx(DWORD level, const char* file, size_t line, const char* fkt, ...)
200{
201 if (!CommInitialized())
202 return;
203
204 if (!WLog_IsLevelActive(sLog, level))
205 return;
206 va_list ap = { 0 };
207 va_start(ap, fkt);
208 WLog_PrintMessageVA(sLog, WLOG_MESSAGE_TEXT, level, line, file, fkt, ap);
209 va_end(ap);
210}
211
212BOOL BuildCommDCBA(WINPR_ATTR_UNUSED LPCSTR lpDef, WINPR_ATTR_UNUSED LPDCB lpDCB)
213{
214 if (!CommInitialized())
215 return FALSE;
216
217 /* TODO: not implemented */
218 CommLog_Print(WLOG_ERROR, "Not implemented");
219 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
220 return FALSE;
221}
222
223BOOL BuildCommDCBW(WINPR_ATTR_UNUSED LPCWSTR lpDef, WINPR_ATTR_UNUSED LPDCB lpDCB)
224{
225 if (!CommInitialized())
226 return FALSE;
227
228 /* TODO: not implemented */
229 CommLog_Print(WLOG_ERROR, "Not implemented");
230 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
231 return FALSE;
232}
233
234BOOL BuildCommDCBAndTimeoutsA(WINPR_ATTR_UNUSED LPCSTR lpDef, WINPR_ATTR_UNUSED LPDCB lpDCB,
235 WINPR_ATTR_UNUSED LPCOMMTIMEOUTS lpCommTimeouts)
236{
237 if (!CommInitialized())
238 return FALSE;
239
240 /* TODO: not implemented */
241 CommLog_Print(WLOG_ERROR, "Not implemented");
242 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
243 return FALSE;
244}
245
246BOOL BuildCommDCBAndTimeoutsW(WINPR_ATTR_UNUSED LPCWSTR lpDef, WINPR_ATTR_UNUSED LPDCB lpDCB,
247 WINPR_ATTR_UNUSED LPCOMMTIMEOUTS lpCommTimeouts)
248{
249 if (!CommInitialized())
250 return FALSE;
251
252 /* TODO: not implemented */
253 CommLog_Print(WLOG_ERROR, "Not implemented");
254 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
255 return FALSE;
256}
257
258BOOL CommConfigDialogA(WINPR_ATTR_UNUSED LPCSTR lpszName, WINPR_ATTR_UNUSED HWND hWnd,
259 WINPR_ATTR_UNUSED LPCOMMCONFIG lpCC)
260{
261 if (!CommInitialized())
262 return FALSE;
263
264 /* TODO: not implemented */
265 CommLog_Print(WLOG_ERROR, "Not implemented");
266 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
267 return FALSE;
268}
269
270BOOL CommConfigDialogW(WINPR_ATTR_UNUSED LPCWSTR lpszName, WINPR_ATTR_UNUSED HWND hWnd,
271 WINPR_ATTR_UNUSED LPCOMMCONFIG lpCC)
272{
273 if (!CommInitialized())
274 return FALSE;
275
276 /* TODO: not implemented */
277 CommLog_Print(WLOG_ERROR, "Not implemented");
278 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
279 return FALSE;
280}
281
282BOOL GetCommConfig(HANDLE hCommDev, WINPR_ATTR_UNUSED LPCOMMCONFIG lpCC,
283 WINPR_ATTR_UNUSED LPDWORD lpdwSize)
284{
285 WINPR_COMM* pComm = (WINPR_COMM*)hCommDev;
286
287 if (!CommInitialized())
288 return FALSE;
289
290 /* TODO: not implemented */
291
292 if (!pComm)
293 return FALSE;
294
295 CommLog_Print(WLOG_ERROR, "Not implemented");
296 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
297 return FALSE;
298}
299
300BOOL SetCommConfig(HANDLE hCommDev, WINPR_ATTR_UNUSED LPCOMMCONFIG lpCC,
301 WINPR_ATTR_UNUSED DWORD dwSize)
302{
303 WINPR_COMM* pComm = (WINPR_COMM*)hCommDev;
304
305 if (!CommInitialized())
306 return FALSE;
307
308 /* TODO: not implemented */
309
310 if (!pComm)
311 return FALSE;
312
313 CommLog_Print(WLOG_ERROR, "Not implemented");
314 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
315 return FALSE;
316}
317
318BOOL GetCommMask(HANDLE hFile, WINPR_ATTR_UNUSED PDWORD lpEvtMask)
319{
320 WINPR_COMM* pComm = (WINPR_COMM*)hFile;
321
322 if (!CommInitialized())
323 return FALSE;
324
325 /* TODO: not implemented */
326
327 if (!pComm)
328 return FALSE;
329
330 CommLog_Print(WLOG_ERROR, "Not implemented");
331 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
332 return FALSE;
333}
334
335BOOL SetCommMask(HANDLE hFile, WINPR_ATTR_UNUSED DWORD dwEvtMask)
336{
337 WINPR_COMM* pComm = (WINPR_COMM*)hFile;
338
339 if (!CommInitialized())
340 return FALSE;
341
342 /* TODO: not implemented */
343
344 if (!pComm)
345 return FALSE;
346
347 CommLog_Print(WLOG_ERROR, "Not implemented");
348 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
349 return FALSE;
350}
351
352BOOL GetCommModemStatus(HANDLE hFile, WINPR_ATTR_UNUSED PDWORD lpModemStat)
353{
354 WINPR_COMM* pComm = (WINPR_COMM*)hFile;
355
356 if (!CommInitialized())
357 return FALSE;
358
359 /* TODO: not implemented */
360
361 if (!pComm)
362 return FALSE;
363
364 CommLog_Print(WLOG_ERROR, "Not implemented");
365 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
366 return FALSE;
367}
368
374BOOL GetCommProperties(HANDLE hFile, LPCOMMPROP lpCommProp)
375{
376 WINPR_COMM* pComm = (WINPR_COMM*)hFile;
377 DWORD bytesReturned = 0;
378
379 if (!CommIsHandleValid(hFile))
380 return FALSE;
381
382 if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_GET_PROPERTIES, NULL, 0, lpCommProp,
383 sizeof(COMMPROP), &bytesReturned, NULL))
384 {
385 CommLog_Print(WLOG_WARN, "GetCommProperties failure.");
386 return FALSE;
387 }
388
389 return TRUE;
390}
391
401BOOL GetCommState(HANDLE hFile, LPDCB lpDCB)
402{
403 DCB* lpLocalDcb = NULL;
404 struct termios currentState;
405 WINPR_COMM* pComm = (WINPR_COMM*)hFile;
406 DWORD bytesReturned = 0;
407
408 if (!CommIsHandleValid(hFile))
409 return FALSE;
410
411 if (!lpDCB)
412 {
413 SetLastError(ERROR_INVALID_DATA);
414 return FALSE;
415 }
416
417 if (lpDCB->DCBlength < sizeof(DCB))
418 {
419 SetLastError(ERROR_INVALID_DATA);
420 return FALSE;
421 }
422
423 if (tcgetattr(pComm->fd, &currentState) < 0)
424 {
425 SetLastError(ERROR_IO_DEVICE);
426 return FALSE;
427 }
428
429 lpLocalDcb = (DCB*)calloc(1, lpDCB->DCBlength);
430
431 if (lpLocalDcb == NULL)
432 {
433 SetLastError(ERROR_OUTOFMEMORY);
434 return FALSE;
435 }
436
437 /* error_handle */
438 lpLocalDcb->DCBlength = lpDCB->DCBlength;
439 SERIAL_BAUD_RATE baudRate;
440
441 if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_GET_BAUD_RATE, NULL, 0, &baudRate,
442 sizeof(SERIAL_BAUD_RATE), &bytesReturned, NULL))
443 {
444 CommLog_Print(WLOG_WARN, "GetCommState failure: could not get the baud rate.");
445 goto error_handle;
446 }
447
448 lpLocalDcb->BaudRate = baudRate.BaudRate;
449 lpLocalDcb->fBinary = (currentState.c_cflag & ICANON) == 0;
450
451 if (!lpLocalDcb->fBinary)
452 {
453 CommLog_Print(WLOG_WARN, "Unexpected nonbinary mode, consider to unset the ICANON flag.");
454 }
455
456 lpLocalDcb->fParity = (currentState.c_iflag & INPCK) != 0;
457 SERIAL_HANDFLOW handflow;
458
459 if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_GET_HANDFLOW, NULL, 0, &handflow,
460 sizeof(SERIAL_HANDFLOW), &bytesReturned, NULL))
461 {
462 CommLog_Print(WLOG_WARN, "GetCommState failure: could not get the handflow settings.");
463 goto error_handle;
464 }
465
466 lpLocalDcb->fOutxCtsFlow = (handflow.ControlHandShake & SERIAL_CTS_HANDSHAKE) != 0;
467 lpLocalDcb->fOutxDsrFlow = (handflow.ControlHandShake & SERIAL_DSR_HANDSHAKE) != 0;
468
469 if (handflow.ControlHandShake & SERIAL_DTR_HANDSHAKE)
470 {
471 lpLocalDcb->fDtrControl = DTR_CONTROL_HANDSHAKE;
472 }
473 else if (handflow.ControlHandShake & SERIAL_DTR_CONTROL)
474 {
475 lpLocalDcb->fDtrControl = DTR_CONTROL_ENABLE;
476 }
477 else
478 {
479 lpLocalDcb->fDtrControl = DTR_CONTROL_DISABLE;
480 }
481
482 lpLocalDcb->fDsrSensitivity = (handflow.ControlHandShake & SERIAL_DSR_SENSITIVITY) != 0;
483 lpLocalDcb->fTXContinueOnXoff = (handflow.FlowReplace & SERIAL_XOFF_CONTINUE) != 0;
484 lpLocalDcb->fOutX = (handflow.FlowReplace & SERIAL_AUTO_TRANSMIT) != 0;
485 lpLocalDcb->fInX = (handflow.FlowReplace & SERIAL_AUTO_RECEIVE) != 0;
486 lpLocalDcb->fErrorChar = (handflow.FlowReplace & SERIAL_ERROR_CHAR) != 0;
487 lpLocalDcb->fNull = (handflow.FlowReplace & SERIAL_NULL_STRIPPING) != 0;
488
489 if (handflow.FlowReplace & SERIAL_RTS_HANDSHAKE)
490 {
491 lpLocalDcb->fRtsControl = RTS_CONTROL_HANDSHAKE;
492 }
493 else if (handflow.FlowReplace & SERIAL_RTS_CONTROL)
494 {
495 lpLocalDcb->fRtsControl = RTS_CONTROL_ENABLE;
496 }
497 else
498 {
499 lpLocalDcb->fRtsControl = RTS_CONTROL_DISABLE;
500 }
501
502 // FIXME: how to get the RTS_CONTROL_TOGGLE state? Does it match the UART 16750's Autoflow
503 // Control Enabled bit in its Modem Control Register (MCR)
504 lpLocalDcb->fAbortOnError = (handflow.ControlHandShake & SERIAL_ERROR_ABORT) != 0;
505 /* lpLocalDcb->fDummy2 not used */
506 lpLocalDcb->wReserved = 0; /* must be zero */
507 lpLocalDcb->XonLim = WINPR_ASSERTING_INT_CAST(WORD, handflow.XonLimit);
508 lpLocalDcb->XoffLim = WINPR_ASSERTING_INT_CAST(WORD, handflow.XoffLimit);
509 SERIAL_LINE_CONTROL lineControl = { 0 };
510
511 if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_GET_LINE_CONTROL, NULL, 0, &lineControl,
512 sizeof(SERIAL_LINE_CONTROL), &bytesReturned, NULL))
513 {
514 CommLog_Print(WLOG_WARN, "GetCommState failure: could not get the control settings.");
515 goto error_handle;
516 }
517
518 lpLocalDcb->ByteSize = lineControl.WordLength;
519 lpLocalDcb->Parity = lineControl.Parity;
520 lpLocalDcb->StopBits = lineControl.StopBits;
521 SERIAL_CHARS serialChars;
522
523 if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_GET_CHARS, NULL, 0, &serialChars,
524 sizeof(SERIAL_CHARS), &bytesReturned, NULL))
525 {
526 CommLog_Print(WLOG_WARN, "GetCommState failure: could not get the serial chars.");
527 goto error_handle;
528 }
529
530 lpLocalDcb->XonChar = serialChars.XonChar;
531 lpLocalDcb->XoffChar = serialChars.XoffChar;
532 lpLocalDcb->ErrorChar = serialChars.ErrorChar;
533 lpLocalDcb->EofChar = serialChars.EofChar;
534 lpLocalDcb->EvtChar = serialChars.EventChar;
535 memcpy(lpDCB, lpLocalDcb, lpDCB->DCBlength);
536 free(lpLocalDcb);
537 return TRUE;
538error_handle:
539 free(lpLocalDcb);
540 return FALSE;
541}
542
554BOOL SetCommState(HANDLE hFile, LPDCB lpDCB)
555{
556 struct termios upcomingTermios = { 0 };
557 WINPR_COMM* pComm = (WINPR_COMM*)hFile;
558 DWORD bytesReturned = 0;
559
560 /* FIXME: validate changes according GetCommProperties? */
561
562 if (!CommIsHandleValid(hFile))
563 return FALSE;
564
565 if (!lpDCB)
566 {
567 SetLastError(ERROR_INVALID_DATA);
568 return FALSE;
569 }
570
571 /* NB: did the choice to call ioctls first when available and
572 then to setup upcomingTermios. Don't mix both stages. */
574 SERIAL_BAUD_RATE baudRate;
575 baudRate.BaudRate = lpDCB->BaudRate;
576
577 if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_SET_BAUD_RATE, &baudRate, sizeof(SERIAL_BAUD_RATE),
578 NULL, 0, &bytesReturned, NULL))
579 {
580 CommLog_Print(WLOG_WARN, "SetCommState failure: could not set the baud rate.");
581 return FALSE;
582 }
583
584 SERIAL_CHARS serialChars;
585
586 if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_GET_CHARS, NULL, 0, &serialChars,
587 sizeof(SERIAL_CHARS), &bytesReturned,
588 NULL)) /* as of today, required for BreakChar */
589 {
590 CommLog_Print(WLOG_WARN, "SetCommState failure: could not get the initial serial chars.");
591 return FALSE;
592 }
593
594 serialChars.XonChar = lpDCB->XonChar;
595 serialChars.XoffChar = lpDCB->XoffChar;
596 serialChars.ErrorChar = lpDCB->ErrorChar;
597 serialChars.EofChar = lpDCB->EofChar;
598 serialChars.EventChar = lpDCB->EvtChar;
599
600 if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_SET_CHARS, &serialChars, sizeof(SERIAL_CHARS),
601 NULL, 0, &bytesReturned, NULL))
602 {
603 CommLog_Print(WLOG_WARN, "SetCommState failure: could not set the serial chars.");
604 return FALSE;
605 }
606
607 SERIAL_LINE_CONTROL lineControl;
608 lineControl.StopBits = lpDCB->StopBits;
609 lineControl.Parity = lpDCB->Parity;
610 lineControl.WordLength = lpDCB->ByteSize;
611
612 if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_SET_LINE_CONTROL, &lineControl,
613 sizeof(SERIAL_LINE_CONTROL), NULL, 0, &bytesReturned, NULL))
614 {
615 CommLog_Print(WLOG_WARN, "SetCommState failure: could not set the control settings.");
616 return FALSE;
617 }
618
619 SERIAL_HANDFLOW handflow = { 0 };
620
621 if (lpDCB->fOutxCtsFlow)
622 {
623 handflow.ControlHandShake |= SERIAL_CTS_HANDSHAKE;
624 }
625
626 if (lpDCB->fOutxDsrFlow)
627 {
628 handflow.ControlHandShake |= SERIAL_DSR_HANDSHAKE;
629 }
630
631 switch (lpDCB->fDtrControl)
632 {
633 case SERIAL_DTR_HANDSHAKE:
634 handflow.ControlHandShake |= DTR_CONTROL_HANDSHAKE;
635 break;
636
637 case SERIAL_DTR_CONTROL:
638 handflow.ControlHandShake |= DTR_CONTROL_ENABLE;
639 break;
640
641 case DTR_CONTROL_DISABLE:
642 /* do nothing since handflow is init-zeroed */
643 break;
644
645 default:
646 CommLog_Print(WLOG_WARN, "Unexpected fDtrControl value: %" PRIu32 "\n",
647 lpDCB->fDtrControl);
648 return FALSE;
649 }
650
651 if (lpDCB->fDsrSensitivity)
652 {
653 handflow.ControlHandShake |= SERIAL_DSR_SENSITIVITY;
654 }
655
656 if (lpDCB->fTXContinueOnXoff)
657 {
658 handflow.FlowReplace |= SERIAL_XOFF_CONTINUE;
659 }
660
661 if (lpDCB->fOutX)
662 {
663 handflow.FlowReplace |= SERIAL_AUTO_TRANSMIT;
664 }
665
666 if (lpDCB->fInX)
667 {
668 handflow.FlowReplace |= SERIAL_AUTO_RECEIVE;
669 }
670
671 if (lpDCB->fErrorChar)
672 {
673 handflow.FlowReplace |= SERIAL_ERROR_CHAR;
674 }
675
676 if (lpDCB->fNull)
677 {
678 handflow.FlowReplace |= SERIAL_NULL_STRIPPING;
679 }
680
681 switch (lpDCB->fRtsControl)
682 {
683 case RTS_CONTROL_TOGGLE:
684 CommLog_Print(WLOG_WARN, "Unsupported RTS_CONTROL_TOGGLE feature");
685 // FIXME: see also GetCommState()
686 return FALSE;
687
688 case RTS_CONTROL_HANDSHAKE:
689 handflow.FlowReplace |= SERIAL_RTS_HANDSHAKE;
690 break;
691
692 case RTS_CONTROL_ENABLE:
693 handflow.FlowReplace |= SERIAL_RTS_CONTROL;
694 break;
695
696 case RTS_CONTROL_DISABLE:
697 /* do nothing since handflow is init-zeroed */
698 break;
699
700 default:
701 CommLog_Print(WLOG_WARN, "Unexpected fRtsControl value: %" PRIu32 "\n",
702 lpDCB->fRtsControl);
703 return FALSE;
704 }
705
706 if (lpDCB->fAbortOnError)
707 {
708 handflow.ControlHandShake |= SERIAL_ERROR_ABORT;
709 }
710
711 /* lpDCB->fDummy2 not used */
712 /* lpLocalDcb->wReserved ignored */
713 handflow.XonLimit = lpDCB->XonLim;
714 handflow.XoffLimit = lpDCB->XoffLim;
715
716 if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_SET_HANDFLOW, &handflow, sizeof(SERIAL_HANDFLOW),
717 NULL, 0, &bytesReturned, NULL))
718 {
719 CommLog_Print(WLOG_WARN, "SetCommState failure: could not set the handflow settings.");
720 return FALSE;
721 }
722
725 if (tcgetattr(pComm->fd, &upcomingTermios) <
726 0) /* NB: preserves current settings not directly handled by the Communication Functions */
727 {
728 SetLastError(ERROR_IO_DEVICE);
729 return FALSE;
730 }
731
732 if (lpDCB->fBinary)
733 {
734 upcomingTermios.c_lflag &= (tcflag_t)~ICANON;
735 }
736 else
737 {
738 upcomingTermios.c_lflag |= ICANON;
739 CommLog_Print(WLOG_WARN, "Unexpected nonbinary mode, consider to unset the ICANON flag.");
740 }
741
742 if (lpDCB->fParity)
743 {
744 upcomingTermios.c_iflag |= INPCK;
745 }
746 else
747 {
748 upcomingTermios.c_iflag &= (tcflag_t)~INPCK;
749 }
750
751 /* http://msdn.microsoft.com/en-us/library/windows/desktop/aa363423%28v=vs.85%29.aspx
752 *
753 * The SetCommState function reconfigures the communications
754 * resource, but it does not affect the internal output and
755 * input buffers of the specified driver. The buffers are not
756 * flushed, and pending read and write operations are not
757 * terminated prematurely.
758 *
759 * TCSANOW matches the best this definition
760 */
761
762 if (comm_ioctl_tcsetattr(pComm->fd, TCSANOW, &upcomingTermios) < 0)
763 {
764 SetLastError(ERROR_IO_DEVICE);
765 return FALSE;
766 }
767
768 return TRUE;
769}
770
775BOOL GetCommTimeouts(HANDLE hFile, LPCOMMTIMEOUTS lpCommTimeouts)
776{
777 WINPR_COMM* pComm = (WINPR_COMM*)hFile;
778 DWORD bytesReturned = 0;
779
780 if (!CommIsHandleValid(hFile))
781 return FALSE;
782
783 /* as of today, SERIAL_TIMEOUTS and COMMTIMEOUTS structures are identical */
784
785 if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_GET_TIMEOUTS, NULL, 0, lpCommTimeouts,
786 sizeof(COMMTIMEOUTS), &bytesReturned, NULL))
787 {
788 CommLog_Print(WLOG_WARN, "GetCommTimeouts failure.");
789 return FALSE;
790 }
791
792 return TRUE;
793}
794
799BOOL SetCommTimeouts(HANDLE hFile, LPCOMMTIMEOUTS lpCommTimeouts)
800{
801 WINPR_COMM* pComm = (WINPR_COMM*)hFile;
802 DWORD bytesReturned = 0;
803
804 if (!CommIsHandleValid(hFile))
805 return FALSE;
806
807 /* as of today, SERIAL_TIMEOUTS and COMMTIMEOUTS structures are identical */
808
809 if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_SET_TIMEOUTS, lpCommTimeouts, sizeof(COMMTIMEOUTS),
810 NULL, 0, &bytesReturned, NULL))
811 {
812 CommLog_Print(WLOG_WARN, "SetCommTimeouts failure.");
813 return FALSE;
814 }
815
816 return TRUE;
817}
818
819BOOL GetDefaultCommConfigA(WINPR_ATTR_UNUSED LPCSTR lpszName, WINPR_ATTR_UNUSED LPCOMMCONFIG lpCC,
820 WINPR_ATTR_UNUSED LPDWORD lpdwSize)
821{
822 if (!CommInitialized())
823 return FALSE;
824
825 /* TODO: not implemented */
826 CommLog_Print(WLOG_ERROR, "Not implemented");
827 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
828 return FALSE;
829}
830
831BOOL GetDefaultCommConfigW(WINPR_ATTR_UNUSED LPCWSTR lpszName, WINPR_ATTR_UNUSED LPCOMMCONFIG lpCC,
832 WINPR_ATTR_UNUSED LPDWORD lpdwSize)
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
843BOOL SetDefaultCommConfigA(WINPR_ATTR_UNUSED LPCSTR lpszName, WINPR_ATTR_UNUSED LPCOMMCONFIG lpCC,
844 WINPR_ATTR_UNUSED DWORD dwSize)
845{
846 if (!CommInitialized())
847 return FALSE;
848
849 /* TODO: not implemented */
850 CommLog_Print(WLOG_ERROR, "Not implemented");
851 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
852 return FALSE;
853}
854
855BOOL SetDefaultCommConfigW(WINPR_ATTR_UNUSED LPCWSTR lpszName, WINPR_ATTR_UNUSED LPCOMMCONFIG lpCC,
856 WINPR_ATTR_UNUSED DWORD dwSize)
857{
858 if (!CommInitialized())
859 return FALSE;
860
861 /* TODO: not implemented */
862 CommLog_Print(WLOG_ERROR, "Not implemented");
863 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
864 return FALSE;
865}
866
867BOOL SetCommBreak(HANDLE hFile)
868{
869 WINPR_COMM* pComm = (WINPR_COMM*)hFile;
870
871 if (!CommInitialized())
872 return FALSE;
873
874 /* TODO: not implemented */
875
876 if (!pComm)
877 return FALSE;
878
879 CommLog_Print(WLOG_ERROR, "Not implemented");
880 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
881 return FALSE;
882}
883
884BOOL ClearCommBreak(HANDLE hFile)
885{
886 WINPR_COMM* pComm = (WINPR_COMM*)hFile;
887
888 if (!CommInitialized())
889 return FALSE;
890
891 /* TODO: not implemented */
892
893 if (!pComm)
894 return FALSE;
895
896 CommLog_Print(WLOG_ERROR, "Not implemented");
897 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
898 return FALSE;
899}
900
901BOOL ClearCommError(HANDLE hFile, WINPR_ATTR_UNUSED PDWORD lpErrors,
902 WINPR_ATTR_UNUSED LPCOMSTAT lpStat)
903{
904 WINPR_COMM* pComm = (WINPR_COMM*)hFile;
905
906 if (!CommInitialized())
907 return FALSE;
908
909 /* TODO: not implemented */
910
911 if (!pComm)
912 return FALSE;
913
914 CommLog_Print(WLOG_ERROR, "Not implemented");
915 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
916 return FALSE;
917}
918
919BOOL PurgeComm(HANDLE hFile, DWORD dwFlags)
920{
921 WINPR_COMM* pComm = (WINPR_COMM*)hFile;
922 DWORD bytesReturned = 0;
923
924 if (!CommIsHandleValid(hFile))
925 return FALSE;
926
927 if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_PURGE, &dwFlags, sizeof(DWORD), NULL, 0,
928 &bytesReturned, NULL))
929 {
930 CommLog_Print(WLOG_WARN, "PurgeComm failure.");
931 return FALSE;
932 }
933
934 return TRUE;
935}
936
937BOOL SetupComm(HANDLE hFile, DWORD dwInQueue, DWORD dwOutQueue)
938{
939 WINPR_COMM* pComm = (WINPR_COMM*)hFile;
940 SERIAL_QUEUE_SIZE queueSize;
941 DWORD bytesReturned = 0;
942
943 if (!CommIsHandleValid(hFile))
944 return FALSE;
945
946 queueSize.InSize = dwInQueue;
947 queueSize.OutSize = dwOutQueue;
948
949 if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_SET_QUEUE_SIZE, &queueSize,
950 sizeof(SERIAL_QUEUE_SIZE), NULL, 0, &bytesReturned, NULL))
951 {
952 CommLog_Print(WLOG_WARN, "SetCommTimeouts failure.");
953 return FALSE;
954 }
955
956 return TRUE;
957}
958
959BOOL EscapeCommFunction(HANDLE hFile, WINPR_ATTR_UNUSED DWORD dwFunc)
960{
961 WINPR_COMM* pComm = (WINPR_COMM*)hFile;
962
963 if (!CommInitialized())
964 return FALSE;
965
966 /* TODO: not implemented */
967
968 if (!pComm)
969 return FALSE;
970
971 CommLog_Print(WLOG_ERROR, "Not implemented");
972 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
973 return FALSE;
974}
975
976BOOL TransmitCommChar(HANDLE hFile, WINPR_ATTR_UNUSED char cChar)
977{
978 WINPR_COMM* pComm = (WINPR_COMM*)hFile;
979
980 if (!CommInitialized())
981 return FALSE;
982
983 /* TODO: not implemented */
984
985 if (!pComm)
986 return FALSE;
987
988 CommLog_Print(WLOG_ERROR, "Not implemented");
989 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
990 return FALSE;
991}
992
993BOOL WaitCommEvent(HANDLE hFile, WINPR_ATTR_UNUSED PDWORD lpEvtMask,
994 WINPR_ATTR_UNUSED LPOVERLAPPED lpOverlapped)
995{
996 WINPR_COMM* pComm = (WINPR_COMM*)hFile;
997
998 if (!CommInitialized())
999 return FALSE;
1000
1001 /* TODO: not implemented */
1002
1003 if (!pComm)
1004 return FALSE;
1005
1006 CommLog_Print(WLOG_ERROR, "Not implemented");
1007 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1008 return FALSE;
1009}
1010
1020BOOL DefineCommDevice(/* DWORD dwFlags,*/ LPCTSTR lpDeviceName, LPCTSTR lpTargetPath)
1021{
1022 LPTSTR storedDeviceName = NULL;
1023 LPTSTR storedTargetPath = NULL;
1024
1025 if (!CommInitialized())
1026 return FALSE;
1027
1028 EnterCriticalSection(&sCommDevicesLock);
1029
1030 if (sCommDevices == NULL)
1031 {
1032 SetLastError(ERROR_DLL_INIT_FAILED);
1033 goto error_handle;
1034 }
1035
1036 storedDeviceName = _tcsdup(lpDeviceName);
1037
1038 if (storedDeviceName == NULL)
1039 {
1040 SetLastError(ERROR_OUTOFMEMORY);
1041 goto error_handle;
1042 }
1043
1044 storedTargetPath = _tcsdup(lpTargetPath);
1045
1046 if (storedTargetPath == NULL)
1047 {
1048 SetLastError(ERROR_OUTOFMEMORY);
1049 goto error_handle;
1050 }
1051
1052 int i = 0;
1053 for (; i < COMM_DEVICE_MAX; i++)
1054 {
1055 if (sCommDevices[i] != NULL)
1056 {
1057 if (_tcscmp(sCommDevices[i]->name, storedDeviceName) == 0)
1058 {
1059 /* take over the emplacement */
1060 free(sCommDevices[i]->name);
1061 free(sCommDevices[i]->path);
1062 sCommDevices[i]->name = storedDeviceName;
1063 sCommDevices[i]->path = storedTargetPath;
1064 break;
1065 }
1066 }
1067 else
1068 {
1069 /* new emplacement */
1070 sCommDevices[i] = (COMM_DEVICE*)calloc(1, sizeof(COMM_DEVICE));
1071
1072 if (sCommDevices[i] == NULL)
1073 {
1074 SetLastError(ERROR_OUTOFMEMORY);
1075 goto error_handle;
1076 }
1077
1078 sCommDevices[i]->name = storedDeviceName;
1079 sCommDevices[i]->path = storedTargetPath;
1080 break;
1081 }
1082 }
1083
1084 if (i == COMM_DEVICE_MAX)
1085 {
1086 SetLastError(ERROR_OUTOFMEMORY);
1087 goto error_handle;
1088 }
1089
1090 LeaveCriticalSection(&sCommDevicesLock);
1091 return TRUE;
1092error_handle:
1093 free(storedDeviceName);
1094 free(storedTargetPath);
1095 LeaveCriticalSection(&sCommDevicesLock);
1096 return FALSE;
1097}
1098
1115DWORD QueryCommDevice(LPCTSTR lpDeviceName, LPTSTR lpTargetPath, DWORD ucchMax)
1116{
1117 LPTSTR storedTargetPath = NULL;
1118 SetLastError(ERROR_SUCCESS);
1119
1120 if (!CommInitialized())
1121 return 0;
1122
1123 if (sCommDevices == NULL)
1124 {
1125 SetLastError(ERROR_DLL_INIT_FAILED);
1126 return 0;
1127 }
1128
1129 if (lpDeviceName == NULL || lpTargetPath == NULL)
1130 {
1131 SetLastError(ERROR_NOT_SUPPORTED);
1132 return 0;
1133 }
1134
1135 EnterCriticalSection(&sCommDevicesLock);
1136 storedTargetPath = NULL;
1137
1138 for (int i = 0; i < COMM_DEVICE_MAX; i++)
1139 {
1140 if (sCommDevices[i] != NULL)
1141 {
1142 if (_tcscmp(sCommDevices[i]->name, lpDeviceName) == 0)
1143 {
1144 storedTargetPath = sCommDevices[i]->path;
1145 break;
1146 }
1147
1148 continue;
1149 }
1150
1151 break;
1152 }
1153
1154 LeaveCriticalSection(&sCommDevicesLock);
1155
1156 if (storedTargetPath == NULL)
1157 {
1158 SetLastError(ERROR_INVALID_DATA);
1159 return 0;
1160 }
1161
1162 const size_t size = _tcsnlen(storedTargetPath, ucchMax);
1163 if (size + 2 > ucchMax)
1164 {
1165 SetLastError(ERROR_INSUFFICIENT_BUFFER);
1166 return 0;
1167 }
1168
1169 _tcsncpy(lpTargetPath, storedTargetPath, size + 1);
1170 lpTargetPath[size + 2] = '\0'; /* 2nd final '\0' */
1171 return (DWORD)size + 2UL;
1172}
1173
1177BOOL IsCommDevice(LPCTSTR lpDeviceName)
1178{
1179 TCHAR lpTargetPath[MAX_PATH];
1180
1181 if (!CommInitialized())
1182 return FALSE;
1183
1184 if (QueryCommDevice(lpDeviceName, lpTargetPath, MAX_PATH) > 0)
1185 {
1186 return TRUE;
1187 }
1188
1189 return FALSE;
1190}
1191
1195void _comm_setServerSerialDriver(HANDLE hComm, SERIAL_DRIVER_ID driverId)
1196{
1197 ULONG Type = 0;
1198 WINPR_HANDLE* Object = NULL;
1199 WINPR_COMM* pComm = NULL;
1200
1201 if (!CommInitialized())
1202 return;
1203
1204 if (!winpr_Handle_GetInfo(hComm, &Type, &Object))
1205 {
1206 CommLog_Print(WLOG_WARN, "_comm_setServerSerialDriver failure");
1207 return;
1208 }
1209
1210 pComm = (WINPR_COMM*)Object;
1211 pComm->serverSerialDriverId = driverId;
1212}
1213
1214static HANDLE_OPS ops = { CommIsHandled, CommCloseHandle,
1215 CommGetFd, NULL, /* CleanupHandle */
1216 NULL, NULL,
1217 NULL, NULL,
1218 NULL, NULL,
1219 NULL, NULL,
1220 NULL, NULL,
1221 NULL, NULL,
1222 NULL, NULL,
1223 NULL, NULL,
1224 NULL };
1225
1251HANDLE CommCreateFileA(LPCSTR lpDeviceName, DWORD dwDesiredAccess, DWORD dwShareMode,
1252 LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition,
1253 DWORD dwFlagsAndAttributes, HANDLE hTemplateFile)
1254{
1255 CHAR devicePath[MAX_PATH] = { 0 };
1256 struct stat deviceStat = { 0 };
1257 WINPR_COMM* pComm = NULL;
1258 struct termios upcomingTermios = { 0 };
1259
1260 if (!CommInitialized())
1261 return INVALID_HANDLE_VALUE;
1262
1263 if (dwDesiredAccess != (GENERIC_READ | GENERIC_WRITE))
1264 {
1265 CommLog_Print(WLOG_WARN, "unexpected access to the device: 0x%08" PRIX32 "",
1266 dwDesiredAccess);
1267 }
1268
1269 if (dwShareMode != 0)
1270 {
1271 SetLastError(ERROR_SHARING_VIOLATION);
1272 return INVALID_HANDLE_VALUE;
1273 }
1274
1275 /* TODO: Prevents other processes from opening a file or
1276 * device if they request delete, read, or write access. */
1277
1278 if (lpSecurityAttributes != NULL)
1279 {
1280 CommLog_Print(WLOG_WARN, "unexpected security attributes, nLength=%" PRIu32 "",
1281 lpSecurityAttributes->nLength);
1282 }
1283
1284 if (dwCreationDisposition != OPEN_EXISTING)
1285 {
1286 SetLastError(ERROR_FILE_NOT_FOUND); /* FIXME: ERROR_NOT_SUPPORTED better? */
1287 return INVALID_HANDLE_VALUE;
1288 }
1289
1290 if (QueryCommDevice(lpDeviceName, devicePath, MAX_PATH) <= 0)
1291 {
1292 /* SetLastError(GetLastError()); */
1293 return INVALID_HANDLE_VALUE;
1294 }
1295
1296 if (stat(devicePath, &deviceStat) < 0)
1297 {
1298 CommLog_Print(WLOG_WARN, "device not found %s", devicePath);
1299 SetLastError(ERROR_FILE_NOT_FOUND);
1300 return INVALID_HANDLE_VALUE;
1301 }
1302
1303 if (!S_ISCHR(deviceStat.st_mode))
1304 {
1305 CommLog_Print(WLOG_WARN, "bad device %s", devicePath);
1306 SetLastError(ERROR_BAD_DEVICE);
1307 return INVALID_HANDLE_VALUE;
1308 }
1309
1310 if (dwFlagsAndAttributes != 0)
1311 {
1312 CommLog_Print(WLOG_WARN, "unexpected flags and attributes: 0x%08" PRIX32 "",
1313 dwFlagsAndAttributes);
1314 }
1315
1316 if (hTemplateFile != NULL)
1317 {
1318 SetLastError(ERROR_NOT_SUPPORTED); /* FIXME: other proper error? */
1319 return INVALID_HANDLE_VALUE;
1320 }
1321
1322 pComm = (WINPR_COMM*)calloc(1, sizeof(WINPR_COMM));
1323
1324 if (pComm == NULL)
1325 {
1326 SetLastError(ERROR_OUTOFMEMORY);
1327 return INVALID_HANDLE_VALUE;
1328 }
1329
1330 WINPR_HANDLE_SET_TYPE_AND_MODE(pComm, HANDLE_TYPE_COMM, WINPR_FD_READ);
1331 pComm->common.ops = &ops;
1332 /* error_handle */
1333 pComm->fd = open(devicePath, O_RDWR | O_NOCTTY | O_NONBLOCK);
1334
1335 if (pComm->fd < 0)
1336 {
1337 CommLog_Print(WLOG_WARN, "failed to open device %s", devicePath);
1338 SetLastError(ERROR_BAD_DEVICE);
1339 goto error_handle;
1340 }
1341
1342 pComm->fd_read = open(devicePath, O_RDONLY | O_NOCTTY | O_NONBLOCK);
1343
1344 if (pComm->fd_read < 0)
1345 {
1346 CommLog_Print(WLOG_WARN, "failed to open fd_read, device: %s", devicePath);
1347 SetLastError(ERROR_BAD_DEVICE);
1348 goto error_handle;
1349 }
1350
1351#if defined(WINPR_HAVE_SYS_EVENTFD_H)
1352 pComm->fd_read_event = eventfd(
1353 0, EFD_NONBLOCK); /* EFD_NONBLOCK required because a read() is not always expected */
1354#endif
1355
1356 if (pComm->fd_read_event < 0)
1357 {
1358 CommLog_Print(WLOG_WARN, "failed to open fd_read_event, device: %s", devicePath);
1359 SetLastError(ERROR_BAD_DEVICE);
1360 goto error_handle;
1361 }
1362
1363 InitializeCriticalSection(&pComm->ReadLock);
1364 pComm->fd_write = open(devicePath, O_WRONLY | O_NOCTTY | O_NONBLOCK);
1365
1366 if (pComm->fd_write < 0)
1367 {
1368 CommLog_Print(WLOG_WARN, "failed to open fd_write, device: %s", devicePath);
1369 SetLastError(ERROR_BAD_DEVICE);
1370 goto error_handle;
1371 }
1372
1373#if defined(WINPR_HAVE_SYS_EVENTFD_H)
1374 pComm->fd_write_event = eventfd(
1375 0, EFD_NONBLOCK); /* EFD_NONBLOCK required because a read() is not always expected */
1376#endif
1377
1378 if (pComm->fd_write_event < 0)
1379 {
1380 CommLog_Print(WLOG_WARN, "failed to open fd_write_event, device: %s", devicePath);
1381 SetLastError(ERROR_BAD_DEVICE);
1382 goto error_handle;
1383 }
1384
1385 InitializeCriticalSection(&pComm->WriteLock);
1386 /* can also be setup later on with _comm_setServerSerialDriver() */
1387 pComm->serverSerialDriverId = SerialDriverUnknown;
1388 InitializeCriticalSection(&pComm->EventsLock);
1389
1390 (void)CommUpdateIOCount(pComm, TRUE);
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 (tcflag_t) ~(/*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;
1424error_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
1431BOOL CommIsHandled(HANDLE handle)
1432{
1433 if (!CommInitialized())
1434 return FALSE;
1435
1436 return WINPR_HANDLE_IS_HANDLED(handle, HANDLE_TYPE_COMM, TRUE);
1437}
1438
1439BOOL 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
1452BOOL 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
1484int eventfd_read(int fd, eventfd_t* value)
1485{
1486 return (read(fd, value, sizeof(*value)) == sizeof(*value)) ? 0 : -1;
1487}
1488
1489int eventfd_write(int fd, eventfd_t value)
1490{
1491 return (write(fd, &value, sizeof(value)) == sizeof(value)) ? 0 : -1;
1492}
1493#endif
1494#endif
1495
1496static const char* CommIoCtlToStr(unsigned long int io)
1497{
1498 switch (io)
1499 {
1500#if defined(WINPR_HAVE_SERIAL_SUPPORT)
1501#if defined(TCGETS)
1502 case TCGETS:
1503 return "TCGETS";
1504#endif
1505#if defined(TCSETS)
1506 case TCSETS:
1507 return "TCSETS";
1508#endif
1509#if defined(TCSETSW)
1510 case TCSETSW:
1511 return "TCSETSW";
1512#endif
1513#if defined(TCSETSF)
1514 case TCSETSF:
1515 return "TCSETSF";
1516#endif
1517#if defined(TCGETA)
1518 case TCGETA:
1519 return "TCGETA";
1520#endif
1521#if defined(TCSETA)
1522 case TCSETA:
1523 return "TCSETA";
1524#endif
1525#if defined(TCSETAW)
1526 case TCSETAW:
1527 return "TCSETAW";
1528#endif
1529#if defined(TCSETAF)
1530 case TCSETAF:
1531 return "TCSETAF";
1532#endif
1533#if defined(TCSBRK)
1534 case TCSBRK:
1535 return "TCSBRK";
1536#endif
1537#if defined(TCXONC)
1538 case TCXONC:
1539 return "TCXONC";
1540#endif
1541#if defined(TCFLSH)
1542 case TCFLSH:
1543 return "TCFLSH";
1544#endif
1545#if defined(TIOCEXCL)
1546 case TIOCEXCL:
1547 return "TIOCEXCL";
1548#endif
1549#if defined(TIOCNXCL)
1550 case TIOCNXCL:
1551 return "TIOCNXCL";
1552#endif
1553#if defined(TIOCSCTTY)
1554 case TIOCSCTTY:
1555 return "TIOCSCTTY";
1556#endif
1557#if defined(TIOCGPGRP)
1558 case TIOCGPGRP:
1559 return "TIOCGPGRP";
1560#endif
1561#if defined(TIOCSPGRP)
1562 case TIOCSPGRP:
1563 return "TIOCSPGRP";
1564#endif
1565#if defined(TIOCOUTQ)
1566 case TIOCOUTQ:
1567 return "TIOCOUTQ";
1568#endif
1569#if defined(TIOCSTI)
1570 case TIOCSTI:
1571 return "TIOCSTI";
1572#endif
1573#if defined(TIOCGWINSZ)
1574 case TIOCGWINSZ:
1575 return "TIOCGWINSZ";
1576#endif
1577#if defined(TIOCSWINSZ)
1578 case TIOCSWINSZ:
1579 return "TIOCSWINSZ";
1580#endif
1581#if defined(TIOCMGET)
1582 case TIOCMGET:
1583 return "TIOCMGET";
1584#endif
1585#if defined(TIOCMBIS)
1586 case TIOCMBIS:
1587 return "TIOCMBIS";
1588#endif
1589#if defined(TIOCMBIC)
1590 case TIOCMBIC:
1591 return "TIOCMBIC";
1592#endif
1593#if defined(TIOCMSET)
1594 case TIOCMSET:
1595 return "TIOCMSET";
1596#endif
1597#if defined(TIOCGSOFTCAR)
1598 case TIOCGSOFTCAR:
1599 return "TIOCGSOFTCAR";
1600#endif
1601#if defined(TIOCSSOFTCAR)
1602 case TIOCSSOFTCAR:
1603 return "TIOCSSOFTCAR";
1604#endif
1605#if defined(FIONREAD)
1606 case FIONREAD:
1607 return "FIONREAD/TIOCINQ";
1608#endif
1609#if defined(TIOCLINUX)
1610 case TIOCLINUX:
1611 return "TIOCLINUX";
1612#endif
1613#if defined(TIOCCONS)
1614 case TIOCCONS:
1615 return "TIOCCONS";
1616#endif
1617#if defined(TIOCGSERIAL)
1618 case TIOCGSERIAL:
1619 return "TIOCGSERIAL";
1620#endif
1621#if defined(TIOCSSERIAL)
1622 case TIOCSSERIAL:
1623 return "TIOCSSERIAL";
1624#endif
1625#if defined(TIOCPKT)
1626 case TIOCPKT:
1627 return "TIOCPKT";
1628#endif
1629#if defined(FIONBIO)
1630 case FIONBIO:
1631 return "FIONBIO";
1632#endif
1633#if defined(TIOCNOTTY)
1634 case TIOCNOTTY:
1635 return "TIOCNOTTY";
1636#endif
1637#if defined(TIOCSETD)
1638 case TIOCSETD:
1639 return "TIOCSETD";
1640#endif
1641#if defined(TIOCGETD)
1642 case TIOCGETD:
1643 return "TIOCGETD";
1644#endif
1645#if defined(TCSBRKP)
1646 case TCSBRKP:
1647 return "TCSBRKP";
1648#endif
1649#if defined(TIOCSBRK)
1650 case TIOCSBRK:
1651 return "TIOCSBRK";
1652#endif
1653#if defined(TIOCCBRK)
1654 case TIOCCBRK:
1655 return "TIOCCBRK";
1656#endif
1657#if defined(TIOCGSID)
1658 case TIOCGSID:
1659 return "TIOCGSID";
1660#endif
1661#if defined(TIOCGRS485)
1662 case TIOCGRS485:
1663 return "TIOCGRS485";
1664#endif
1665#if defined(TIOCSRS485)
1666 case TIOCSRS485:
1667 return "TIOCSRS485";
1668#endif
1669#if defined(TIOCSPTLCK)
1670 case TIOCSPTLCK:
1671 return "TIOCSPTLCK";
1672#endif
1673#if defined(TCGETX)
1674 case TCGETX:
1675 return "TCGETX";
1676#endif
1677#if defined(TCSETX)
1678 case TCSETX:
1679 return "TCSETX";
1680#endif
1681#if defined(TCSETXF)
1682 case TCSETXF:
1683 return "TCSETXF";
1684#endif
1685#if defined(TCSETXW)
1686 case TCSETXW:
1687 return "TCSETXW";
1688#endif
1689#if defined(TIOCSIG)
1690 case TIOCSIG:
1691 return "TIOCSIG";
1692#endif
1693#if defined(TIOCVHANGUP)
1694 case TIOCVHANGUP:
1695 return "TIOCVHANGUP";
1696#endif
1697#if defined(TIOCGPTPEER)
1698 case TIOCGPTPEER:
1699 return "TIOCGPTPEER";
1700#endif
1701#if defined(FIONCLEX)
1702 case FIONCLEX:
1703 return "FIONCLEX";
1704#endif
1705#if defined(FIOCLEX)
1706 case FIOCLEX:
1707 return "FIOCLEX";
1708#endif
1709#if defined(FIOASYNC)
1710 case FIOASYNC:
1711 return "FIOASYNC";
1712#endif
1713#if defined(TIOCSERCONFIG)
1714 case TIOCSERCONFIG:
1715 return "TIOCSERCONFIG";
1716#endif
1717#if defined(TIOCSERGWILD)
1718 case TIOCSERGWILD:
1719 return "TIOCSERGWILD";
1720#endif
1721#if defined(TIOCSERSWILD)
1722 case TIOCSERSWILD:
1723 return "TIOCSERSWILD";
1724#endif
1725#if defined(TIOCGLCKTRMIOS)
1726 case TIOCGLCKTRMIOS:
1727 return "TIOCGLCKTRMIOS";
1728#endif
1729#if defined(TIOCSLCKTRMIOS)
1730 case TIOCSLCKTRMIOS:
1731 return "TIOCSLCKTRMIOS";
1732#endif
1733#if defined(TIOCSERGSTRUCT)
1734 case TIOCSERGSTRUCT:
1735 return "TIOCSERGSTRUCT";
1736#endif
1737#if defined(TIOCSERGETLSR)
1738 case TIOCSERGETLSR:
1739 return "TIOCSERGETLSR";
1740#endif
1741#if defined(TIOCSERGETMULTI)
1742 case TIOCSERGETMULTI:
1743 return "TIOCSERGETMULTI";
1744#endif
1745#if defined(TIOCSERSETMULTI)
1746 case TIOCSERSETMULTI:
1747 return "TIOCSERSETMULTI";
1748#endif
1749#if defined(TIOCMIWAIT)
1750 case TIOCMIWAIT:
1751 return "TIOCMIWAIT";
1752#endif
1753#if defined(TIOCGICOUNT)
1754 case TIOCGICOUNT:
1755 return "TIOCGICOUNT";
1756#endif
1757#if defined(FIOQSIZE)
1758 case FIOQSIZE:
1759 return "FIOQSIZE";
1760#endif
1761#if defined(TIOCPKT_DATA)
1762 case TIOCPKT_DATA:
1763 return "TIOCPKT_DATA";
1764#endif
1765#if defined(TIOCPKT_FLUSHWRITE)
1766 case TIOCPKT_FLUSHWRITE:
1767 return "TIOCPKT_FLUSHWRITE";
1768#endif
1769#if defined(TIOCPKT_STOP)
1770 case TIOCPKT_STOP:
1771 return "TIOCPKT_STOP";
1772#endif
1773#if defined(TIOCPKT_START)
1774 case TIOCPKT_START:
1775 return "TIOCPKT_START";
1776#endif
1777#if defined(TIOCPKT_NOSTOP)
1778 case TIOCPKT_NOSTOP:
1779 return "TIOCPKT_NOSTOP";
1780#endif
1781#if defined(TIOCPKT_DOSTOP)
1782 case TIOCPKT_DOSTOP:
1783 return "TIOCPKT_DOSTOP";
1784#endif
1785#if defined(TIOCPKT_IOCTL)
1786 case TIOCPKT_IOCTL:
1787 return "TIOCPKT_IOCTL";
1788#endif
1789#endif
1790 default:
1791 return "UNKNOWN";
1792 }
1793}
1794
1795static BOOL CommStatusErrorEx(WINPR_COMM* pComm, unsigned long int ctl, const char* file,
1796 const char* fkt, size_t line)
1797{
1798 WINPR_ASSERT(pComm);
1799 BOOL rc = pComm->permissive ? TRUE : FALSE;
1800 const DWORD level = rc ? WLOG_DEBUG : WLOG_WARN;
1801 char ebuffer[256] = { 0 };
1802 const char* str = CommIoCtlToStr(ctl);
1803
1804 if (CommInitialized())
1805 {
1806 if (WLog_IsLevelActive(sLog, level))
1807 {
1808 WLog_PrintMessage(sLog, WLOG_MESSAGE_TEXT, level, line, file, fkt,
1809 "%s [0x%08" PRIx32 "] ioctl failed, errno=[%d] %s.", str, ctl, errno,
1810 winpr_strerror(errno, ebuffer, sizeof(ebuffer)));
1811 }
1812 }
1813
1814 if (!rc)
1815 SetLastError(ERROR_IO_DEVICE);
1816
1817 return rc;
1818}
1819
1820BOOL CommIoCtl_int(WINPR_COMM* pComm, unsigned long int ctl, void* data, const char* file,
1821 const char* fkt, size_t line)
1822{
1823 if (ioctl(pComm->fd, ctl, data) < 0)
1824 {
1825 if (!CommStatusErrorEx(pComm, ctl, file, fkt, line))
1826 return FALSE;
1827 }
1828 return TRUE;
1829}
1830
1831BOOL CommUpdateIOCount(WINPR_ATTR_UNUSED HANDLE handle, WINPR_ATTR_UNUSED BOOL checkSupportStatus)
1832{
1833 WINPR_COMM* pComm = (WINPR_COMM*)handle;
1834 WINPR_ASSERT(pComm);
1835
1836#if defined(WINPR_HAVE_COMM_COUNTERS)
1837 ZeroMemory(&(pComm->counters), sizeof(struct serial_icounter_struct));
1838 if (pComm->TIOCGICOUNTSupported || checkSupportStatus)
1839 {
1840 const int rc = ioctl(pComm->fd, TIOCGICOUNT, &(pComm->counters));
1841 if (checkSupportStatus)
1842 pComm->TIOCGICOUNTSupported = rc >= 0;
1843 else if (rc < 0)
1844 {
1845 if (!CommStatusErrorEx(pComm, TIOCGICOUNT, __FILE__, __func__, __LINE__))
1846 return FALSE;
1847 }
1848 }
1849#endif
1850 return TRUE;
1851}
1852
1853static const char* CommSerialEvFlagString(ULONG flag)
1854{
1855 switch (flag)
1856 {
1857 case SERIAL_EV_RXCHAR:
1858 return "SERIAL_EV_RXCHAR";
1859 case SERIAL_EV_RXFLAG:
1860 return "SERIAL_EV_RXFLAG";
1861 case SERIAL_EV_TXEMPTY:
1862 return "SERIAL_EV_TXEMPTY";
1863 case SERIAL_EV_CTS:
1864 return "SERIAL_EV_CTS ";
1865 case SERIAL_EV_DSR:
1866 return "SERIAL_EV_DSR ";
1867 case SERIAL_EV_RLSD:
1868 return "SERIAL_EV_RLSD";
1869 case SERIAL_EV_BREAK:
1870 return "SERIAL_EV_BREAK";
1871 case SERIAL_EV_ERR:
1872 return "SERIAL_EV_ERR ";
1873 case SERIAL_EV_RING:
1874 return "SERIAL_EV_RING";
1875 case SERIAL_EV_PERR:
1876 return "SERIAL_EV_PERR";
1877 case SERIAL_EV_RX80FULL:
1878 return "SERIAL_EV_RX80FULL";
1879 case SERIAL_EV_EVENT1:
1880 return "SERIAL_EV_EVENT1";
1881 case SERIAL_EV_EVENT2:
1882 return "SERIAL_EV_EVENT2";
1883 case SERIAL_EV_WINPR_WAITING:
1884 return "SERIAL_EV_WINPR_WAITING";
1885 case SERIAL_EV_WINPR_STOP:
1886 return "SERIAL_EV_WINPR_STOP";
1887 default:
1888 return "SERIAL_EV_UNKNOWN";
1889 }
1890}
1891
1892const char* CommSerialEvString(ULONG status, char* buffer, size_t size)
1893{
1894 const ULONG flags[] = { SERIAL_EV_RXCHAR, SERIAL_EV_RXFLAG, SERIAL_EV_TXEMPTY,
1895 SERIAL_EV_CTS, SERIAL_EV_DSR, SERIAL_EV_RLSD,
1896 SERIAL_EV_BREAK, SERIAL_EV_ERR, SERIAL_EV_RING,
1897 SERIAL_EV_PERR, SERIAL_EV_RX80FULL, SERIAL_EV_EVENT1,
1898 SERIAL_EV_EVENT2, SERIAL_EV_WINPR_WAITING, SERIAL_EV_WINPR_STOP };
1899
1900 winpr_str_append("{", buffer, size, "");
1901
1902 const char* sep = "";
1903 for (size_t x = 0; x < ARRAYSIZE(flags); x++)
1904 {
1905 const ULONG flag = flags[x];
1906 if (status & flag)
1907 {
1908 winpr_str_append(CommSerialEvFlagString(flag), buffer, size, sep);
1909 sep = "|";
1910 }
1911 }
1912
1913 char number[32] = { 0 };
1914 (void)_snprintf(number, sizeof(number), "}[0x%08" PRIx32 "]", status);
1915 winpr_str_append(number, buffer, size, "");
1916 return buffer;
1917}