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