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