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 = NULL;
58
59struct comm_device
60{
61 LPTSTR name;
62 LPTSTR path;
63};
64
65typedef struct comm_device COMM_DEVICE;
66
67/* FIXME: get a clever data structure, see also io.h functions */
68/* _CommDevices is a NULL-terminated array with a maximum of COMM_DEVICE_MAX COMM_DEVICE */
69#define COMM_DEVICE_MAX 128
70static COMM_DEVICE** sCommDevices = NULL;
71static CRITICAL_SECTION sCommDevicesLock = { 0 };
72
73static pthread_once_t sCommInitialized = PTHREAD_ONCE_INIT;
74
75static const _SERIAL_IOCTL_NAME S_SERIAL_IOCTL_NAMES[] = {
76 { IOCTL_SERIAL_SET_BAUD_RATE, "IOCTL_SERIAL_SET_BAUD_RATE" },
77 { IOCTL_SERIAL_GET_BAUD_RATE, "IOCTL_SERIAL_GET_BAUD_RATE" },
78 { IOCTL_SERIAL_SET_LINE_CONTROL, "IOCTL_SERIAL_SET_LINE_CONTROL" },
79 { IOCTL_SERIAL_GET_LINE_CONTROL, "IOCTL_SERIAL_GET_LINE_CONTROL" },
80 { IOCTL_SERIAL_SET_TIMEOUTS, "IOCTL_SERIAL_SET_TIMEOUTS" },
81 { IOCTL_SERIAL_GET_TIMEOUTS, "IOCTL_SERIAL_GET_TIMEOUTS" },
82 { IOCTL_SERIAL_GET_CHARS, "IOCTL_SERIAL_GET_CHARS" },
83 { IOCTL_SERIAL_SET_CHARS, "IOCTL_SERIAL_SET_CHARS" },
84 { IOCTL_SERIAL_SET_DTR, "IOCTL_SERIAL_SET_DTR" },
85 { IOCTL_SERIAL_CLR_DTR, "IOCTL_SERIAL_CLR_DTR" },
86 { IOCTL_SERIAL_RESET_DEVICE, "IOCTL_SERIAL_RESET_DEVICE" },
87 { IOCTL_SERIAL_SET_RTS, "IOCTL_SERIAL_SET_RTS" },
88 { IOCTL_SERIAL_CLR_RTS, "IOCTL_SERIAL_CLR_RTS" },
89 { IOCTL_SERIAL_SET_XOFF, "IOCTL_SERIAL_SET_XOFF" },
90 { IOCTL_SERIAL_SET_XON, "IOCTL_SERIAL_SET_XON" },
91 { IOCTL_SERIAL_SET_BREAK_ON, "IOCTL_SERIAL_SET_BREAK_ON" },
92 { IOCTL_SERIAL_SET_BREAK_OFF, "IOCTL_SERIAL_SET_BREAK_OFF" },
93 { IOCTL_SERIAL_SET_QUEUE_SIZE, "IOCTL_SERIAL_SET_QUEUE_SIZE" },
94 { IOCTL_SERIAL_GET_WAIT_MASK, "IOCTL_SERIAL_GET_WAIT_MASK" },
95 { IOCTL_SERIAL_SET_WAIT_MASK, "IOCTL_SERIAL_SET_WAIT_MASK" },
96 { IOCTL_SERIAL_WAIT_ON_MASK, "IOCTL_SERIAL_WAIT_ON_MASK" },
97 { IOCTL_SERIAL_IMMEDIATE_CHAR, "IOCTL_SERIAL_IMMEDIATE_CHAR" },
98 { IOCTL_SERIAL_PURGE, "IOCTL_SERIAL_PURGE" },
99 { IOCTL_SERIAL_GET_HANDFLOW, "IOCTL_SERIAL_GET_HANDFLOW" },
100 { IOCTL_SERIAL_SET_HANDFLOW, "IOCTL_SERIAL_SET_HANDFLOW" },
101 { IOCTL_SERIAL_GET_MODEMSTATUS, "IOCTL_SERIAL_GET_MODEMSTATUS" },
102 { IOCTL_SERIAL_GET_DTRRTS, "IOCTL_SERIAL_GET_DTRRTS" },
103 { IOCTL_SERIAL_GET_COMMSTATUS, "IOCTL_SERIAL_GET_COMMSTATUS" },
104 { IOCTL_SERIAL_GET_PROPERTIES, "IOCTL_SERIAL_GET_PROPERTIES" },
105 // {IOCTL_SERIAL_XOFF_COUNTER, "IOCTL_SERIAL_XOFF_COUNTER"},
106 // {IOCTL_SERIAL_LSRMST_INSERT, "IOCTL_SERIAL_LSRMST_INSERT"},
107 { IOCTL_SERIAL_CONFIG_SIZE, "IOCTL_SERIAL_CONFIG_SIZE" },
108 // {IOCTL_SERIAL_GET_STATS, "IOCTL_SERIAL_GET_STATS"},
109 // {IOCTL_SERIAL_CLEAR_STATS, "IOCTL_SERIAL_CLEAR_STATS"},
110 // {IOCTL_SERIAL_GET_MODEM_CONTROL,"IOCTL_SERIAL_GET_MODEM_CONTROL"},
111 // {IOCTL_SERIAL_SET_MODEM_CONTROL,"IOCTL_SERIAL_SET_MODEM_CONTROL"},
112 // {IOCTL_SERIAL_SET_FIFO_CONTROL, "IOCTL_SERIAL_SET_FIFO_CONTROL"},
113
114 // {IOCTL_PAR_QUERY_INFORMATION, "IOCTL_PAR_QUERY_INFORMATION"},
115 // {IOCTL_PAR_SET_INFORMATION, "IOCTL_PAR_SET_INFORMATION"},
116 // {IOCTL_PAR_QUERY_DEVICE_ID, "IOCTL_PAR_QUERY_DEVICE_ID"},
117 // {IOCTL_PAR_QUERY_DEVICE_ID_SIZE,"IOCTL_PAR_QUERY_DEVICE_ID_SIZE"},
118 // {IOCTL_IEEE1284_GET_MODE, "IOCTL_IEEE1284_GET_MODE"},
119 // {IOCTL_IEEE1284_NEGOTIATE, "IOCTL_IEEE1284_NEGOTIATE"},
120 // {IOCTL_PAR_SET_WRITE_ADDRESS, "IOCTL_PAR_SET_WRITE_ADDRESS"},
121 // {IOCTL_PAR_SET_READ_ADDRESS, "IOCTL_PAR_SET_READ_ADDRESS"},
122 // {IOCTL_PAR_GET_DEVICE_CAPS, "IOCTL_PAR_GET_DEVICE_CAPS"},
123 // {IOCTL_PAR_GET_DEFAULT_MODES, "IOCTL_PAR_GET_DEFAULT_MODES"},
124 // {IOCTL_PAR_QUERY_RAW_DEVICE_ID, "IOCTL_PAR_QUERY_RAW_DEVICE_ID"},
125 // {IOCTL_PAR_IS_PORT_FREE, "IOCTL_PAR_IS_PORT_FREE"},
126
127 { IOCTL_USBPRINT_GET_1284_ID, "IOCTL_USBPRINT_GET_1284_ID" }
128};
129
130const char* _comm_serial_ioctl_name(ULONG number)
131{
132 for (size_t x = 0; x < ARRAYSIZE(S_SERIAL_IOCTL_NAMES); x++)
133 {
134 const _SERIAL_IOCTL_NAME* cur = &S_SERIAL_IOCTL_NAMES[x];
135 if (cur->number == number)
136 return cur->name;
137 }
138
139 return "(unknown ioctl name)";
140}
141
142static int CommGetFd(HANDLE handle)
143{
144 WINPR_COMM* comm = (WINPR_COMM*)handle;
145
146 if (!CommIsHandled(handle))
147 return -1;
148
149 return comm->fd;
150}
151
152const HANDLE_CREATOR* GetCommHandleCreator(void)
153{
154#if defined(WINPR_HAVE_SERIAL_SUPPORT)
155 static const HANDLE_CREATOR sCommHandleCreator = { .IsHandled = IsCommDevice,
156 .CreateFileA = CommCreateFileA };
157 return &sCommHandleCreator;
158#else
159 return NULL;
160#endif
161}
162
163static void CommInit(void)
164{
165 /* NB: error management to be done outside of this function */
166 WINPR_ASSERT(sLog == NULL);
167 WINPR_ASSERT(sCommDevices == NULL);
168 sCommDevices = (COMM_DEVICE**)calloc(COMM_DEVICE_MAX + 1, sizeof(COMM_DEVICE*));
169
170 if (!sCommDevices)
171 return;
172
173 if (!InitializeCriticalSectionEx(&sCommDevicesLock, 0, 0))
174 {
175 free((void*)sCommDevices);
176 sCommDevices = NULL;
177 return;
178 }
179
180 sLog = WLog_Get(TAG);
181 WINPR_ASSERT(sLog != NULL);
182}
183
188static BOOL CommInitialized(void)
189{
190 if (pthread_once(&sCommInitialized, CommInit) != 0)
191 {
192 SetLastError(ERROR_DLL_INIT_FAILED);
193 return FALSE;
194 }
195
196 return TRUE;
197}
198
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 = { 0 };
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, NULL, 0, lpCommProp,
385 sizeof(COMMPROP), &bytesReturned, NULL))
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 = NULL;
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 == NULL)
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, NULL, 0, &baudRate,
444 sizeof(SERIAL_BAUD_RATE), &bytesReturned, NULL))
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, NULL, 0, &handflow,
462 sizeof(SERIAL_HANDFLOW), &bytesReturned, NULL))
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 SERIAL_LINE_CONTROL lineControl = { 0 };
512
513 if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_GET_LINE_CONTROL, NULL, 0, &lineControl,
514 sizeof(SERIAL_LINE_CONTROL), &bytesReturned, NULL))
515 {
516 CommLog_Print(WLOG_WARN, "GetCommState failure: could not get the control settings.");
517 goto error_handle;
518 }
519
520 lpLocalDcb->ByteSize = lineControl.WordLength;
521 lpLocalDcb->Parity = lineControl.Parity;
522 lpLocalDcb->StopBits = lineControl.StopBits;
523 SERIAL_CHARS serialChars;
524
525 if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_GET_CHARS, NULL, 0, &serialChars,
526 sizeof(SERIAL_CHARS), &bytesReturned, NULL))
527 {
528 CommLog_Print(WLOG_WARN, "GetCommState failure: could not get the serial chars.");
529 goto error_handle;
530 }
531
532 lpLocalDcb->XonChar = serialChars.XonChar;
533 lpLocalDcb->XoffChar = serialChars.XoffChar;
534 lpLocalDcb->ErrorChar = serialChars.ErrorChar;
535 lpLocalDcb->EofChar = serialChars.EofChar;
536 lpLocalDcb->EvtChar = serialChars.EventChar;
537 memcpy(lpDCB, lpLocalDcb, lpDCB->DCBlength);
538 free(lpLocalDcb);
539 return TRUE;
540error_handle:
541 free(lpLocalDcb);
542 return FALSE;
543}
544
556BOOL SetCommState(HANDLE hFile, LPDCB lpDCB)
557{
558 struct termios upcomingTermios = { 0 };
559 WINPR_COMM* pComm = (WINPR_COMM*)hFile;
560 DWORD bytesReturned = 0;
561
562 /* FIXME: validate changes according GetCommProperties? */
563
564 if (!CommIsHandleValid(hFile))
565 return FALSE;
566
567 if (!lpDCB)
568 {
569 SetLastError(ERROR_INVALID_DATA);
570 return FALSE;
571 }
572
573 /* NB: did the choice to call ioctls first when available and
574 then to setup upcomingTermios. Don't mix both stages. */
576 SERIAL_BAUD_RATE baudRate;
577 baudRate.BaudRate = lpDCB->BaudRate;
578
579 if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_SET_BAUD_RATE, &baudRate, sizeof(SERIAL_BAUD_RATE),
580 NULL, 0, &bytesReturned, NULL))
581 {
582 CommLog_Print(WLOG_WARN, "SetCommState failure: could not set the baud rate.");
583 return FALSE;
584 }
585
586 SERIAL_CHARS serialChars;
587
588 if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_GET_CHARS, NULL, 0, &serialChars,
589 sizeof(SERIAL_CHARS), &bytesReturned,
590 NULL)) /* as of today, required for BreakChar */
591 {
592 CommLog_Print(WLOG_WARN, "SetCommState failure: could not get the initial serial chars.");
593 return FALSE;
594 }
595
596 serialChars.XonChar = lpDCB->XonChar;
597 serialChars.XoffChar = lpDCB->XoffChar;
598 serialChars.ErrorChar = lpDCB->ErrorChar;
599 serialChars.EofChar = lpDCB->EofChar;
600 serialChars.EventChar = lpDCB->EvtChar;
601
602 if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_SET_CHARS, &serialChars, sizeof(SERIAL_CHARS),
603 NULL, 0, &bytesReturned, NULL))
604 {
605 CommLog_Print(WLOG_WARN, "SetCommState failure: could not set the serial chars.");
606 return FALSE;
607 }
608
609 SERIAL_LINE_CONTROL lineControl;
610 lineControl.StopBits = lpDCB->StopBits;
611 lineControl.Parity = lpDCB->Parity;
612 lineControl.WordLength = lpDCB->ByteSize;
613
614 if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_SET_LINE_CONTROL, &lineControl,
615 sizeof(SERIAL_LINE_CONTROL), NULL, 0, &bytesReturned, NULL))
616 {
617 CommLog_Print(WLOG_WARN, "SetCommState failure: could not set the control settings.");
618 return FALSE;
619 }
620
621 SERIAL_HANDFLOW handflow = { 0 };
622
623 if (lpDCB->fOutxCtsFlow)
624 {
625 handflow.ControlHandShake |= SERIAL_CTS_HANDSHAKE;
626 }
627
628 if (lpDCB->fOutxDsrFlow)
629 {
630 handflow.ControlHandShake |= SERIAL_DSR_HANDSHAKE;
631 }
632
633 switch (lpDCB->fDtrControl)
634 {
635 case SERIAL_DTR_HANDSHAKE:
636 handflow.ControlHandShake |= DTR_CONTROL_HANDSHAKE;
637 break;
638
639 case SERIAL_DTR_CONTROL:
640 handflow.ControlHandShake |= DTR_CONTROL_ENABLE;
641 break;
642
643 case DTR_CONTROL_DISABLE:
644 /* do nothing since handflow is init-zeroed */
645 break;
646
647 default:
648 CommLog_Print(WLOG_WARN, "Unexpected fDtrControl value: %" PRId32 "\n",
649 lpDCB->fDtrControl);
650 return FALSE;
651 }
652
653 if (lpDCB->fDsrSensitivity)
654 {
655 handflow.ControlHandShake |= SERIAL_DSR_SENSITIVITY;
656 }
657
658 if (lpDCB->fTXContinueOnXoff)
659 {
660 handflow.FlowReplace |= SERIAL_XOFF_CONTINUE;
661 }
662
663 if (lpDCB->fOutX)
664 {
665 handflow.FlowReplace |= SERIAL_AUTO_TRANSMIT;
666 }
667
668 if (lpDCB->fInX)
669 {
670 handflow.FlowReplace |= SERIAL_AUTO_RECEIVE;
671 }
672
673 if (lpDCB->fErrorChar)
674 {
675 handflow.FlowReplace |= SERIAL_ERROR_CHAR;
676 }
677
678 if (lpDCB->fNull)
679 {
680 handflow.FlowReplace |= SERIAL_NULL_STRIPPING;
681 }
682
683 switch (lpDCB->fRtsControl)
684 {
685 case RTS_CONTROL_TOGGLE:
686 CommLog_Print(WLOG_WARN, "Unsupported RTS_CONTROL_TOGGLE feature");
687 // FIXME: see also GetCommState()
688 return FALSE;
689
690 case RTS_CONTROL_HANDSHAKE:
691 handflow.FlowReplace |= SERIAL_RTS_HANDSHAKE;
692 break;
693
694 case RTS_CONTROL_ENABLE:
695 handflow.FlowReplace |= SERIAL_RTS_CONTROL;
696 break;
697
698 case RTS_CONTROL_DISABLE:
699 /* do nothing since handflow is init-zeroed */
700 break;
701
702 default:
703 CommLog_Print(WLOG_WARN, "Unexpected fRtsControl value: %" PRId32 "\n",
704 lpDCB->fRtsControl);
705 return FALSE;
706 }
707
708 if (lpDCB->fAbortOnError)
709 {
710 handflow.ControlHandShake |= SERIAL_ERROR_ABORT;
711 }
712
713 /* lpDCB->fDummy2 not used */
714 /* lpLocalDcb->wReserved ignored */
715 handflow.XonLimit = lpDCB->XonLim;
716 handflow.XoffLimit = lpDCB->XoffLim;
717
718 if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_SET_HANDFLOW, &handflow, sizeof(SERIAL_HANDFLOW),
719 NULL, 0, &bytesReturned, NULL))
720 {
721 CommLog_Print(WLOG_WARN, "SetCommState failure: could not set the handflow settings.");
722 return FALSE;
723 }
724
727 if (tcgetattr(pComm->fd, &upcomingTermios) <
728 0) /* NB: preserves current settings not directly handled by the Communication Functions */
729 {
730 SetLastError(ERROR_IO_DEVICE);
731 return FALSE;
732 }
733
734 if (lpDCB->fBinary)
735 {
736 upcomingTermios.c_lflag &= (tcflag_t)~ICANON;
737 }
738 else
739 {
740 upcomingTermios.c_lflag |= ICANON;
741 CommLog_Print(WLOG_WARN, "Unexpected nonbinary mode, consider to unset the ICANON flag.");
742 }
743
744 if (lpDCB->fParity)
745 {
746 upcomingTermios.c_iflag |= INPCK;
747 }
748 else
749 {
750 upcomingTermios.c_iflag &= (tcflag_t)~INPCK;
751 }
752
753 /* http://msdn.microsoft.com/en-us/library/windows/desktop/aa363423%28v=vs.85%29.aspx
754 *
755 * The SetCommState function reconfigures the communications
756 * resource, but it does not affect the internal output and
757 * input buffers of the specified driver. The buffers are not
758 * flushed, and pending read and write operations are not
759 * terminated prematurely.
760 *
761 * TCSANOW matches the best this definition
762 */
763
764 if (comm_ioctl_tcsetattr(pComm->fd, TCSANOW, &upcomingTermios) < 0)
765 {
766 SetLastError(ERROR_IO_DEVICE);
767 return FALSE;
768 }
769
770 return TRUE;
771}
772
777BOOL GetCommTimeouts(HANDLE hFile, LPCOMMTIMEOUTS lpCommTimeouts)
778{
779 WINPR_COMM* pComm = (WINPR_COMM*)hFile;
780 DWORD bytesReturned = 0;
781
782 if (!CommIsHandleValid(hFile))
783 return FALSE;
784
785 /* as of today, SERIAL_TIMEOUTS and COMMTIMEOUTS structures are identical */
786
787 if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_GET_TIMEOUTS, NULL, 0, lpCommTimeouts,
788 sizeof(COMMTIMEOUTS), &bytesReturned, NULL))
789 {
790 CommLog_Print(WLOG_WARN, "GetCommTimeouts failure.");
791 return FALSE;
792 }
793
794 return TRUE;
795}
796
801BOOL SetCommTimeouts(HANDLE hFile, LPCOMMTIMEOUTS lpCommTimeouts)
802{
803 WINPR_COMM* pComm = (WINPR_COMM*)hFile;
804 DWORD bytesReturned = 0;
805
806 if (!CommIsHandleValid(hFile))
807 return FALSE;
808
809 /* as of today, SERIAL_TIMEOUTS and COMMTIMEOUTS structures are identical */
810
811 if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_SET_TIMEOUTS, lpCommTimeouts, sizeof(COMMTIMEOUTS),
812 NULL, 0, &bytesReturned, NULL))
813 {
814 CommLog_Print(WLOG_WARN, "SetCommTimeouts failure.");
815 return FALSE;
816 }
817
818 return TRUE;
819}
820
821BOOL GetDefaultCommConfigA(WINPR_ATTR_UNUSED LPCSTR lpszName, WINPR_ATTR_UNUSED LPCOMMCONFIG lpCC,
822 WINPR_ATTR_UNUSED LPDWORD lpdwSize)
823{
824 if (!CommInitialized())
825 return FALSE;
826
827 /* TODO: not implemented */
828 CommLog_Print(WLOG_ERROR, "Not implemented");
829 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
830 return FALSE;
831}
832
833BOOL GetDefaultCommConfigW(WINPR_ATTR_UNUSED LPCWSTR lpszName, WINPR_ATTR_UNUSED LPCOMMCONFIG lpCC,
834 WINPR_ATTR_UNUSED LPDWORD lpdwSize)
835{
836 if (!CommInitialized())
837 return FALSE;
838
839 /* TODO: not implemented */
840 CommLog_Print(WLOG_ERROR, "Not implemented");
841 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
842 return FALSE;
843}
844
845BOOL SetDefaultCommConfigA(WINPR_ATTR_UNUSED LPCSTR lpszName, WINPR_ATTR_UNUSED LPCOMMCONFIG lpCC,
846 WINPR_ATTR_UNUSED DWORD dwSize)
847{
848 if (!CommInitialized())
849 return FALSE;
850
851 /* TODO: not implemented */
852 CommLog_Print(WLOG_ERROR, "Not implemented");
853 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
854 return FALSE;
855}
856
857BOOL SetDefaultCommConfigW(WINPR_ATTR_UNUSED LPCWSTR lpszName, WINPR_ATTR_UNUSED LPCOMMCONFIG lpCC,
858 WINPR_ATTR_UNUSED DWORD dwSize)
859{
860 if (!CommInitialized())
861 return FALSE;
862
863 /* TODO: not implemented */
864 CommLog_Print(WLOG_ERROR, "Not implemented");
865 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
866 return FALSE;
867}
868
869BOOL SetCommBreak(HANDLE hFile)
870{
871 WINPR_COMM* pComm = (WINPR_COMM*)hFile;
872
873 if (!CommInitialized())
874 return FALSE;
875
876 /* TODO: not implemented */
877
878 if (!pComm)
879 return FALSE;
880
881 CommLog_Print(WLOG_ERROR, "Not implemented");
882 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
883 return FALSE;
884}
885
886BOOL ClearCommBreak(HANDLE hFile)
887{
888 WINPR_COMM* pComm = (WINPR_COMM*)hFile;
889
890 if (!CommInitialized())
891 return FALSE;
892
893 /* TODO: not implemented */
894
895 if (!pComm)
896 return FALSE;
897
898 CommLog_Print(WLOG_ERROR, "Not implemented");
899 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
900 return FALSE;
901}
902
903BOOL ClearCommError(HANDLE hFile, WINPR_ATTR_UNUSED PDWORD lpErrors,
904 WINPR_ATTR_UNUSED LPCOMSTAT lpStat)
905{
906 WINPR_COMM* pComm = (WINPR_COMM*)hFile;
907
908 if (!CommInitialized())
909 return FALSE;
910
911 /* TODO: not implemented */
912
913 if (!pComm)
914 return FALSE;
915
916 CommLog_Print(WLOG_ERROR, "Not implemented");
917 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
918 return FALSE;
919}
920
921BOOL PurgeComm(HANDLE hFile, DWORD dwFlags)
922{
923 WINPR_COMM* pComm = (WINPR_COMM*)hFile;
924 DWORD bytesReturned = 0;
925
926 if (!CommIsHandleValid(hFile))
927 return FALSE;
928
929 if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_PURGE, &dwFlags, sizeof(DWORD), NULL, 0,
930 &bytesReturned, NULL))
931 {
932 CommLog_Print(WLOG_WARN, "PurgeComm failure.");
933 return FALSE;
934 }
935
936 return TRUE;
937}
938
939BOOL SetupComm(HANDLE hFile, DWORD dwInQueue, DWORD dwOutQueue)
940{
941 WINPR_COMM* pComm = (WINPR_COMM*)hFile;
942 SERIAL_QUEUE_SIZE queueSize;
943 DWORD bytesReturned = 0;
944
945 if (!CommIsHandleValid(hFile))
946 return FALSE;
947
948 queueSize.InSize = dwInQueue;
949 queueSize.OutSize = dwOutQueue;
950
951 if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_SET_QUEUE_SIZE, &queueSize,
952 sizeof(SERIAL_QUEUE_SIZE), NULL, 0, &bytesReturned, NULL))
953 {
954 CommLog_Print(WLOG_WARN, "SetCommTimeouts failure.");
955 return FALSE;
956 }
957
958 return TRUE;
959}
960
961BOOL EscapeCommFunction(HANDLE hFile, WINPR_ATTR_UNUSED DWORD dwFunc)
962{
963 WINPR_COMM* pComm = (WINPR_COMM*)hFile;
964
965 if (!CommInitialized())
966 return FALSE;
967
968 /* TODO: not implemented */
969
970 if (!pComm)
971 return FALSE;
972
973 CommLog_Print(WLOG_ERROR, "Not implemented");
974 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
975 return FALSE;
976}
977
978BOOL TransmitCommChar(HANDLE hFile, WINPR_ATTR_UNUSED char cChar)
979{
980 WINPR_COMM* pComm = (WINPR_COMM*)hFile;
981
982 if (!CommInitialized())
983 return FALSE;
984
985 /* TODO: not implemented */
986
987 if (!pComm)
988 return FALSE;
989
990 CommLog_Print(WLOG_ERROR, "Not implemented");
991 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
992 return FALSE;
993}
994
995BOOL WaitCommEvent(HANDLE hFile, WINPR_ATTR_UNUSED PDWORD lpEvtMask,
996 WINPR_ATTR_UNUSED LPOVERLAPPED lpOverlapped)
997{
998 WINPR_COMM* pComm = (WINPR_COMM*)hFile;
999
1000 if (!CommInitialized())
1001 return FALSE;
1002
1003 /* TODO: not implemented */
1004
1005 if (!pComm)
1006 return FALSE;
1007
1008 CommLog_Print(WLOG_ERROR, "Not implemented");
1009 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1010 return FALSE;
1011}
1012
1022BOOL DefineCommDevice(/* DWORD dwFlags,*/ LPCTSTR lpDeviceName, LPCTSTR lpTargetPath)
1023{
1024 LPTSTR storedDeviceName = NULL;
1025 LPTSTR storedTargetPath = NULL;
1026
1027 if (!CommInitialized())
1028 return FALSE;
1029
1030 EnterCriticalSection(&sCommDevicesLock);
1031
1032 if (sCommDevices == NULL)
1033 {
1034 SetLastError(ERROR_DLL_INIT_FAILED);
1035 goto error_handle;
1036 }
1037
1038 storedDeviceName = _tcsdup(lpDeviceName);
1039
1040 if (storedDeviceName == NULL)
1041 {
1042 SetLastError(ERROR_OUTOFMEMORY);
1043 goto error_handle;
1044 }
1045
1046 storedTargetPath = _tcsdup(lpTargetPath);
1047
1048 if (storedTargetPath == NULL)
1049 {
1050 SetLastError(ERROR_OUTOFMEMORY);
1051 goto error_handle;
1052 }
1053
1054 int i = 0;
1055 for (; i < COMM_DEVICE_MAX; i++)
1056 {
1057 if (sCommDevices[i] != NULL)
1058 {
1059 if (_tcscmp(sCommDevices[i]->name, storedDeviceName) == 0)
1060 {
1061 /* take over the emplacement */
1062 free(sCommDevices[i]->name);
1063 free(sCommDevices[i]->path);
1064 sCommDevices[i]->name = storedDeviceName;
1065 sCommDevices[i]->path = storedTargetPath;
1066 break;
1067 }
1068 }
1069 else
1070 {
1071 /* new emplacement */
1072 sCommDevices[i] = (COMM_DEVICE*)calloc(1, sizeof(COMM_DEVICE));
1073
1074 if (sCommDevices[i] == NULL)
1075 {
1076 SetLastError(ERROR_OUTOFMEMORY);
1077 goto error_handle;
1078 }
1079
1080 sCommDevices[i]->name = storedDeviceName;
1081 sCommDevices[i]->path = storedTargetPath;
1082 break;
1083 }
1084 }
1085
1086 if (i == COMM_DEVICE_MAX)
1087 {
1088 SetLastError(ERROR_OUTOFMEMORY);
1089 goto error_handle;
1090 }
1091
1092 LeaveCriticalSection(&sCommDevicesLock);
1093 return TRUE;
1094error_handle:
1095 free(storedDeviceName);
1096 free(storedTargetPath);
1097 LeaveCriticalSection(&sCommDevicesLock);
1098 return FALSE;
1099}
1100
1117DWORD QueryCommDevice(LPCTSTR lpDeviceName, LPTSTR lpTargetPath, DWORD ucchMax)
1118{
1119 LPTSTR storedTargetPath = NULL;
1120 SetLastError(ERROR_SUCCESS);
1121
1122 if (!CommInitialized())
1123 return 0;
1124
1125 if (sCommDevices == NULL)
1126 {
1127 SetLastError(ERROR_DLL_INIT_FAILED);
1128 return 0;
1129 }
1130
1131 if (lpDeviceName == NULL || lpTargetPath == NULL)
1132 {
1133 SetLastError(ERROR_NOT_SUPPORTED);
1134 return 0;
1135 }
1136
1137 EnterCriticalSection(&sCommDevicesLock);
1138 storedTargetPath = NULL;
1139
1140 for (int i = 0; i < COMM_DEVICE_MAX; i++)
1141 {
1142 if (sCommDevices[i] != NULL)
1143 {
1144 if (_tcscmp(sCommDevices[i]->name, lpDeviceName) == 0)
1145 {
1146 storedTargetPath = sCommDevices[i]->path;
1147 break;
1148 }
1149
1150 continue;
1151 }
1152
1153 break;
1154 }
1155
1156 LeaveCriticalSection(&sCommDevicesLock);
1157
1158 if (storedTargetPath == NULL)
1159 {
1160 SetLastError(ERROR_INVALID_DATA);
1161 return 0;
1162 }
1163
1164 const size_t size = _tcsnlen(storedTargetPath, ucchMax);
1165 if (size + 2 > ucchMax)
1166 {
1167 SetLastError(ERROR_INSUFFICIENT_BUFFER);
1168 return 0;
1169 }
1170
1171 _tcsncpy(lpTargetPath, storedTargetPath, size + 1);
1172 lpTargetPath[size + 2] = '\0'; /* 2nd final '\0' */
1173 return (DWORD)size + 2UL;
1174}
1175
1179BOOL IsCommDevice(LPCTSTR lpDeviceName)
1180{
1181 TCHAR lpTargetPath[MAX_PATH];
1182
1183 if (!CommInitialized())
1184 return FALSE;
1185
1186 if (QueryCommDevice(lpDeviceName, lpTargetPath, MAX_PATH) > 0)
1187 {
1188 return TRUE;
1189 }
1190
1191 return FALSE;
1192}
1193
1197void _comm_setServerSerialDriver(HANDLE hComm, SERIAL_DRIVER_ID driverId)
1198{
1199 ULONG Type = 0;
1200 WINPR_HANDLE* Object = NULL;
1201 WINPR_COMM* pComm = NULL;
1202
1203 if (!CommInitialized())
1204 return;
1205
1206 if (!winpr_Handle_GetInfo(hComm, &Type, &Object))
1207 {
1208 CommLog_Print(WLOG_WARN, "_comm_setServerSerialDriver failure");
1209 return;
1210 }
1211
1212 pComm = (WINPR_COMM*)Object;
1213 pComm->serverSerialDriverId = driverId;
1214}
1215
1216static HANDLE_OPS ops = { CommIsHandled, CommCloseHandle,
1217 CommGetFd, NULL, /* CleanupHandle */
1218 NULL, NULL,
1219 NULL, NULL,
1220 NULL, NULL,
1221 NULL, NULL,
1222 NULL, NULL,
1223 NULL, NULL,
1224 NULL, NULL,
1225 NULL, NULL,
1226 NULL };
1227
1253HANDLE CommCreateFileA(LPCSTR lpDeviceName, DWORD dwDesiredAccess, DWORD dwShareMode,
1254 LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition,
1255 DWORD dwFlagsAndAttributes, HANDLE hTemplateFile)
1256{
1257 CHAR devicePath[MAX_PATH] = { 0 };
1258 struct stat deviceStat = { 0 };
1259 WINPR_COMM* pComm = NULL;
1260 struct termios upcomingTermios = { 0 };
1261
1262 if (!CommInitialized())
1263 return INVALID_HANDLE_VALUE;
1264
1265 if (dwDesiredAccess != (GENERIC_READ | GENERIC_WRITE))
1266 {
1267 CommLog_Print(WLOG_WARN, "unexpected access to the device: 0x%08" PRIX32 "",
1268 dwDesiredAccess);
1269 }
1270
1271 if (dwShareMode != 0)
1272 {
1273 SetLastError(ERROR_SHARING_VIOLATION);
1274 return INVALID_HANDLE_VALUE;
1275 }
1276
1277 /* TODO: Prevents other processes from opening a file or
1278 * device if they request delete, read, or write access. */
1279
1280 if (lpSecurityAttributes != NULL)
1281 {
1282 CommLog_Print(WLOG_WARN, "unexpected security attributes, nLength=%" PRIu32 "",
1283 lpSecurityAttributes->nLength);
1284 }
1285
1286 if (dwCreationDisposition != OPEN_EXISTING)
1287 {
1288 SetLastError(ERROR_FILE_NOT_FOUND); /* FIXME: ERROR_NOT_SUPPORTED better? */
1289 return INVALID_HANDLE_VALUE;
1290 }
1291
1292 if (QueryCommDevice(lpDeviceName, devicePath, MAX_PATH) <= 0)
1293 {
1294 /* SetLastError(GetLastError()); */
1295 return INVALID_HANDLE_VALUE;
1296 }
1297
1298 if (stat(devicePath, &deviceStat) < 0)
1299 {
1300 CommLog_Print(WLOG_WARN, "device not found %s", devicePath);
1301 SetLastError(ERROR_FILE_NOT_FOUND);
1302 return INVALID_HANDLE_VALUE;
1303 }
1304
1305 if (!S_ISCHR(deviceStat.st_mode))
1306 {
1307 CommLog_Print(WLOG_WARN, "bad device %s", devicePath);
1308 SetLastError(ERROR_BAD_DEVICE);
1309 return INVALID_HANDLE_VALUE;
1310 }
1311
1312 if (dwFlagsAndAttributes != 0)
1313 {
1314 CommLog_Print(WLOG_WARN, "unexpected flags and attributes: 0x%08" PRIX32 "",
1315 dwFlagsAndAttributes);
1316 }
1317
1318 if (hTemplateFile != NULL)
1319 {
1320 SetLastError(ERROR_NOT_SUPPORTED); /* FIXME: other proper error? */
1321 return INVALID_HANDLE_VALUE;
1322 }
1323
1324 pComm = (WINPR_COMM*)calloc(1, sizeof(WINPR_COMM));
1325
1326 if (pComm == NULL)
1327 {
1328 SetLastError(ERROR_OUTOFMEMORY);
1329 return INVALID_HANDLE_VALUE;
1330 }
1331
1332 WINPR_HANDLE_SET_TYPE_AND_MODE(pComm, HANDLE_TYPE_COMM, WINPR_FD_READ);
1333 pComm->common.ops = &ops;
1334 /* error_handle */
1335 pComm->fd = open(devicePath, O_RDWR | O_NOCTTY | O_NONBLOCK);
1336
1337 if (pComm->fd < 0)
1338 {
1339 CommLog_Print(WLOG_WARN, "failed to open device %s", devicePath);
1340 SetLastError(ERROR_BAD_DEVICE);
1341 goto error_handle;
1342 }
1343
1344 pComm->fd_read = open(devicePath, O_RDONLY | O_NOCTTY | O_NONBLOCK);
1345
1346 if (pComm->fd_read < 0)
1347 {
1348 CommLog_Print(WLOG_WARN, "failed to open fd_read, device: %s", devicePath);
1349 SetLastError(ERROR_BAD_DEVICE);
1350 goto error_handle;
1351 }
1352
1353#if defined(WINPR_HAVE_SYS_EVENTFD_H)
1354 pComm->fd_read_event = eventfd(
1355 0, EFD_NONBLOCK); /* EFD_NONBLOCK required because a read() is not always expected */
1356#endif
1357
1358 if (pComm->fd_read_event < 0)
1359 {
1360 CommLog_Print(WLOG_WARN, "failed to open fd_read_event, device: %s", devicePath);
1361 SetLastError(ERROR_BAD_DEVICE);
1362 goto error_handle;
1363 }
1364
1365 InitializeCriticalSection(&pComm->ReadLock);
1366 pComm->fd_write = open(devicePath, O_WRONLY | O_NOCTTY | O_NONBLOCK);
1367
1368 if (pComm->fd_write < 0)
1369 {
1370 CommLog_Print(WLOG_WARN, "failed to open fd_write, device: %s", devicePath);
1371 SetLastError(ERROR_BAD_DEVICE);
1372 goto error_handle;
1373 }
1374
1375#if defined(WINPR_HAVE_SYS_EVENTFD_H)
1376 pComm->fd_write_event = eventfd(
1377 0, EFD_NONBLOCK); /* EFD_NONBLOCK required because a read() is not always expected */
1378#endif
1379
1380 if (pComm->fd_write_event < 0)
1381 {
1382 CommLog_Print(WLOG_WARN, "failed to open fd_write_event, device: %s", devicePath);
1383 SetLastError(ERROR_BAD_DEVICE);
1384 goto error_handle;
1385 }
1386
1387 InitializeCriticalSection(&pComm->WriteLock);
1388 /* can also be setup later on with _comm_setServerSerialDriver() */
1389 pComm->serverSerialDriverId = SerialDriverUnknown;
1390 InitializeCriticalSection(&pComm->EventsLock);
1391
1392 (void)CommUpdateIOCount(pComm, TRUE);
1393
1394 /* The binary/raw mode is required for the redirection but
1395 * only flags that are not handle somewhere-else, except
1396 * ICANON, are forced here. */
1397 ZeroMemory(&upcomingTermios, sizeof(struct termios));
1398
1399 if (tcgetattr(pComm->fd, &upcomingTermios) < 0)
1400 {
1401 SetLastError(ERROR_IO_DEVICE);
1402 goto error_handle;
1403 }
1404
1405 upcomingTermios.c_iflag &=
1406 (tcflag_t) ~(/*IGNBRK |*/ BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL /*| IXON*/);
1407 upcomingTermios.c_oflag = 0; /* <=> &= ~OPOST */
1408 upcomingTermios.c_lflag = 0; /* <=> &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN); */
1409 /* upcomingTermios.c_cflag &= ~(CSIZE | PARENB); */
1410 /* upcomingTermios.c_cflag |= CS8; */
1411 /* About missing flags recommended by termios(3):
1412 *
1413 * IGNBRK and IXON, see: IOCTL_SERIAL_SET_HANDFLOW
1414 * CSIZE, PARENB and CS8, see: IOCTL_SERIAL_SET_LINE_CONTROL
1415 */
1416 /* a few more settings required for the redirection */
1417 upcomingTermios.c_cflag |= CLOCAL | CREAD;
1418
1419 if (comm_ioctl_tcsetattr(pComm->fd, TCSANOW, &upcomingTermios) < 0)
1420 {
1421 SetLastError(ERROR_IO_DEVICE);
1422 goto error_handle;
1423 }
1424
1425 return (HANDLE)pComm;
1426error_handle:
1427 WINPR_PRAGMA_DIAG_PUSH
1428 WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC(void) CloseHandle(pComm);
1429 WINPR_PRAGMA_DIAG_POP
1430 return INVALID_HANDLE_VALUE;
1431}
1432
1433BOOL CommIsHandled(HANDLE handle)
1434{
1435 if (!CommInitialized())
1436 return FALSE;
1437
1438 return WINPR_HANDLE_IS_HANDLED(handle, HANDLE_TYPE_COMM, TRUE);
1439}
1440
1441BOOL CommIsHandleValid(HANDLE handle)
1442{
1443 WINPR_COMM* pComm = (WINPR_COMM*)handle;
1444 if (!CommIsHandled(handle))
1445 return FALSE;
1446 if (pComm->fd <= 0)
1447 {
1448 SetLastError(ERROR_INVALID_HANDLE);
1449 return FALSE;
1450 }
1451 return TRUE;
1452}
1453
1454BOOL CommCloseHandle(HANDLE handle)
1455{
1456 WINPR_COMM* pComm = (WINPR_COMM*)handle;
1457
1458 if (!CommIsHandled(handle))
1459 return FALSE;
1460
1461 DeleteCriticalSection(&pComm->ReadLock);
1462 DeleteCriticalSection(&pComm->WriteLock);
1463 DeleteCriticalSection(&pComm->EventsLock);
1464
1465 if (pComm->fd > 0)
1466 close(pComm->fd);
1467
1468 if (pComm->fd_write > 0)
1469 close(pComm->fd_write);
1470
1471 if (pComm->fd_write_event > 0)
1472 close(pComm->fd_write_event);
1473
1474 if (pComm->fd_read > 0)
1475 close(pComm->fd_read);
1476
1477 if (pComm->fd_read_event > 0)
1478 close(pComm->fd_read_event);
1479
1480 free(pComm);
1481 return TRUE;
1482}
1483
1484#if defined(WINPR_HAVE_SYS_EVENTFD_H)
1485#ifndef WITH_EVENTFD_READ_WRITE
1486int eventfd_read(int fd, eventfd_t* value)
1487{
1488 return (read(fd, value, sizeof(*value)) == sizeof(*value)) ? 0 : -1;
1489}
1490
1491int eventfd_write(int fd, eventfd_t value)
1492{
1493 return (write(fd, &value, sizeof(value)) == sizeof(value)) ? 0 : -1;
1494}
1495#endif
1496#endif
1497
1498static const char* CommIoCtlToStr(unsigned long int io)
1499{
1500 switch (io)
1501 {
1502#if defined(WINPR_HAVE_SERIAL_SUPPORT)
1503#if defined(TCGETS)
1504 case TCGETS:
1505 return "TCGETS";
1506#endif
1507#if defined(TCSETS)
1508 case TCSETS:
1509 return "TCSETS";
1510#endif
1511#if defined(TCSETSW)
1512 case TCSETSW:
1513 return "TCSETSW";
1514#endif
1515#if defined(TCSETSF)
1516 case TCSETSF:
1517 return "TCSETSF";
1518#endif
1519#if defined(TCGETA)
1520 case TCGETA:
1521 return "TCGETA";
1522#endif
1523#if defined(TCSETA)
1524 case TCSETA:
1525 return "TCSETA";
1526#endif
1527#if defined(TCSETAW)
1528 case TCSETAW:
1529 return "TCSETAW";
1530#endif
1531#if defined(TCSETAF)
1532 case TCSETAF:
1533 return "TCSETAF";
1534#endif
1535#if defined(TCSBRK)
1536 case TCSBRK:
1537 return "TCSBRK";
1538#endif
1539#if defined(TCXONC)
1540 case TCXONC:
1541 return "TCXONC";
1542#endif
1543#if defined(TCFLSH)
1544 case TCFLSH:
1545 return "TCFLSH";
1546#endif
1547#if defined(TIOCEXCL)
1548 case TIOCEXCL:
1549 return "TIOCEXCL";
1550#endif
1551#if defined(TIOCNXCL)
1552 case TIOCNXCL:
1553 return "TIOCNXCL";
1554#endif
1555#if defined(TIOCSCTTY)
1556 case TIOCSCTTY:
1557 return "TIOCSCTTY";
1558#endif
1559#if defined(TIOCGPGRP)
1560 case TIOCGPGRP:
1561 return "TIOCGPGRP";
1562#endif
1563#if defined(TIOCSPGRP)
1564 case TIOCSPGRP:
1565 return "TIOCSPGRP";
1566#endif
1567#if defined(TIOCOUTQ)
1568 case TIOCOUTQ:
1569 return "TIOCOUTQ";
1570#endif
1571#if defined(TIOCSTI)
1572 case TIOCSTI:
1573 return "TIOCSTI";
1574#endif
1575#if defined(TIOCGWINSZ)
1576 case TIOCGWINSZ:
1577 return "TIOCGWINSZ";
1578#endif
1579#if defined(TIOCSWINSZ)
1580 case TIOCSWINSZ:
1581 return "TIOCSWINSZ";
1582#endif
1583#if defined(TIOCMGET)
1584 case TIOCMGET:
1585 return "TIOCMGET";
1586#endif
1587#if defined(TIOCMBIS)
1588 case TIOCMBIS:
1589 return "TIOCMBIS";
1590#endif
1591#if defined(TIOCMBIC)
1592 case TIOCMBIC:
1593 return "TIOCMBIC";
1594#endif
1595#if defined(TIOCMSET)
1596 case TIOCMSET:
1597 return "TIOCMSET";
1598#endif
1599#if defined(TIOCGSOFTCAR)
1600 case TIOCGSOFTCAR:
1601 return "TIOCGSOFTCAR";
1602#endif
1603#if defined(TIOCSSOFTCAR)
1604 case TIOCSSOFTCAR:
1605 return "TIOCSSOFTCAR";
1606#endif
1607#if defined(FIONREAD)
1608 case FIONREAD:
1609 return "FIONREAD/TIOCINQ";
1610#endif
1611#if defined(TIOCLINUX)
1612 case TIOCLINUX:
1613 return "TIOCLINUX";
1614#endif
1615#if defined(TIOCCONS)
1616 case TIOCCONS:
1617 return "TIOCCONS";
1618#endif
1619#if defined(TIOCGSERIAL)
1620 case TIOCGSERIAL:
1621 return "TIOCGSERIAL";
1622#endif
1623#if defined(TIOCSSERIAL)
1624 case TIOCSSERIAL:
1625 return "TIOCSSERIAL";
1626#endif
1627#if defined(TIOCPKT)
1628 case TIOCPKT:
1629 return "TIOCPKT";
1630#endif
1631#if defined(FIONBIO)
1632 case FIONBIO:
1633 return "FIONBIO";
1634#endif
1635#if defined(TIOCNOTTY)
1636 case TIOCNOTTY:
1637 return "TIOCNOTTY";
1638#endif
1639#if defined(TIOCSETD)
1640 case TIOCSETD:
1641 return "TIOCSETD";
1642#endif
1643#if defined(TIOCGETD)
1644 case TIOCGETD:
1645 return "TIOCGETD";
1646#endif
1647#if defined(TCSBRKP)
1648 case TCSBRKP:
1649 return "TCSBRKP";
1650#endif
1651#if defined(TIOCSBRK)
1652 case TIOCSBRK:
1653 return "TIOCSBRK";
1654#endif
1655#if defined(TIOCCBRK)
1656 case TIOCCBRK:
1657 return "TIOCCBRK";
1658#endif
1659#if defined(TIOCGSID)
1660 case TIOCGSID:
1661 return "TIOCGSID";
1662#endif
1663#if defined(TIOCGRS485)
1664 case TIOCGRS485:
1665 return "TIOCGRS485";
1666#endif
1667#if defined(TIOCSRS485)
1668 case TIOCSRS485:
1669 return "TIOCSRS485";
1670#endif
1671#if defined(TIOCSPTLCK)
1672 case TIOCSPTLCK:
1673 return "TIOCSPTLCK";
1674#endif
1675#if defined(TCGETX)
1676 case TCGETX:
1677 return "TCGETX";
1678#endif
1679#if defined(TCSETX)
1680 case TCSETX:
1681 return "TCSETX";
1682#endif
1683#if defined(TCSETXF)
1684 case TCSETXF:
1685 return "TCSETXF";
1686#endif
1687#if defined(TCSETXW)
1688 case TCSETXW:
1689 return "TCSETXW";
1690#endif
1691#if defined(TIOCSIG)
1692 case TIOCSIG:
1693 return "TIOCSIG";
1694#endif
1695#if defined(TIOCVHANGUP)
1696 case TIOCVHANGUP:
1697 return "TIOCVHANGUP";
1698#endif
1699#if defined(TIOCGPTPEER)
1700 case TIOCGPTPEER:
1701 return "TIOCGPTPEER";
1702#endif
1703#if defined(FIONCLEX)
1704 case FIONCLEX:
1705 return "FIONCLEX";
1706#endif
1707#if defined(FIOCLEX)
1708 case FIOCLEX:
1709 return "FIOCLEX";
1710#endif
1711#if defined(FIOASYNC)
1712 case FIOASYNC:
1713 return "FIOASYNC";
1714#endif
1715#if defined(TIOCSERCONFIG)
1716 case TIOCSERCONFIG:
1717 return "TIOCSERCONFIG";
1718#endif
1719#if defined(TIOCSERGWILD)
1720 case TIOCSERGWILD:
1721 return "TIOCSERGWILD";
1722#endif
1723#if defined(TIOCSERSWILD)
1724 case TIOCSERSWILD:
1725 return "TIOCSERSWILD";
1726#endif
1727#if defined(TIOCGLCKTRMIOS)
1728 case TIOCGLCKTRMIOS:
1729 return "TIOCGLCKTRMIOS";
1730#endif
1731#if defined(TIOCSLCKTRMIOS)
1732 case TIOCSLCKTRMIOS:
1733 return "TIOCSLCKTRMIOS";
1734#endif
1735#if defined(TIOCSERGSTRUCT)
1736 case TIOCSERGSTRUCT:
1737 return "TIOCSERGSTRUCT";
1738#endif
1739#if defined(TIOCSERGETLSR)
1740 case TIOCSERGETLSR:
1741 return "TIOCSERGETLSR";
1742#endif
1743#if defined(TIOCSERGETMULTI)
1744 case TIOCSERGETMULTI:
1745 return "TIOCSERGETMULTI";
1746#endif
1747#if defined(TIOCSERSETMULTI)
1748 case TIOCSERSETMULTI:
1749 return "TIOCSERSETMULTI";
1750#endif
1751#if defined(TIOCMIWAIT)
1752 case TIOCMIWAIT:
1753 return "TIOCMIWAIT";
1754#endif
1755#if defined(TIOCGICOUNT)
1756 case TIOCGICOUNT:
1757 return "TIOCGICOUNT";
1758#endif
1759#if defined(FIOQSIZE)
1760 case FIOQSIZE:
1761 return "FIOQSIZE";
1762#endif
1763#if defined(TIOCPKT_DATA)
1764 case TIOCPKT_DATA:
1765 return "TIOCPKT_DATA";
1766#endif
1767#if defined(TIOCPKT_FLUSHWRITE)
1768 case TIOCPKT_FLUSHWRITE:
1769 return "TIOCPKT_FLUSHWRITE";
1770#endif
1771#if defined(TIOCPKT_STOP)
1772 case TIOCPKT_STOP:
1773 return "TIOCPKT_STOP";
1774#endif
1775#if defined(TIOCPKT_START)
1776 case TIOCPKT_START:
1777 return "TIOCPKT_START";
1778#endif
1779#if defined(TIOCPKT_NOSTOP)
1780 case TIOCPKT_NOSTOP:
1781 return "TIOCPKT_NOSTOP";
1782#endif
1783#if defined(TIOCPKT_DOSTOP)
1784 case TIOCPKT_DOSTOP:
1785 return "TIOCPKT_DOSTOP";
1786#endif
1787#if defined(TIOCPKT_IOCTL)
1788 case TIOCPKT_IOCTL:
1789 return "TIOCPKT_IOCTL";
1790#endif
1791#endif
1792 default:
1793 return "UNKNOWN";
1794 }
1795}
1796
1797static BOOL CommStatusErrorEx(WINPR_COMM* pComm, unsigned long int ctl, const char* file,
1798 const char* fkt, size_t line)
1799{
1800 WINPR_ASSERT(pComm);
1801 BOOL rc = pComm->permissive ? TRUE : FALSE;
1802 const DWORD level = rc ? WLOG_DEBUG : WLOG_WARN;
1803 char ebuffer[256] = { 0 };
1804 const char* str = CommIoCtlToStr(ctl);
1805
1806 if (CommInitialized())
1807 {
1808 if (WLog_IsLevelActive(sLog, level))
1809 {
1810 WLog_PrintTextMessage(sLog, level, line, file, fkt,
1811 "%s [0x%08lx] ioctl failed, errno=[%d] %s.", str, ctl, errno,
1812 winpr_strerror(errno, ebuffer, sizeof(ebuffer)));
1813 }
1814 }
1815
1816 if (!rc)
1817 SetLastError(ERROR_IO_DEVICE);
1818
1819 return rc;
1820}
1821
1822BOOL CommIoCtl_int(WINPR_COMM* pComm, unsigned long int ctl, void* data, const char* file,
1823 const char* fkt, size_t line)
1824{
1825 if (ioctl(pComm->fd, ctl, data) < 0)
1826 {
1827 if (!CommStatusErrorEx(pComm, ctl, file, fkt, line))
1828 return FALSE;
1829 }
1830 return TRUE;
1831}
1832
1833BOOL CommUpdateIOCount(WINPR_ATTR_UNUSED HANDLE handle, WINPR_ATTR_UNUSED BOOL checkSupportStatus)
1834{
1835 WINPR_COMM* pComm = (WINPR_COMM*)handle;
1836 WINPR_ASSERT(pComm);
1837
1838#if defined(WINPR_HAVE_COMM_COUNTERS)
1839 ZeroMemory(&(pComm->counters), sizeof(struct serial_icounter_struct));
1840 if (pComm->TIOCGICOUNTSupported || checkSupportStatus)
1841 {
1842 const int rc = ioctl(pComm->fd, TIOCGICOUNT, &(pComm->counters));
1843 if (checkSupportStatus)
1844 pComm->TIOCGICOUNTSupported = rc >= 0;
1845 else if (rc < 0)
1846 {
1847 if (!CommStatusErrorEx(pComm, TIOCGICOUNT, __FILE__, __func__, __LINE__))
1848 return FALSE;
1849 }
1850 }
1851#endif
1852 return TRUE;
1853}
1854
1855static const char* CommSerialEvFlagString(ULONG flag)
1856{
1857 switch (flag)
1858 {
1859 case SERIAL_EV_RXCHAR:
1860 return "SERIAL_EV_RXCHAR";
1861 case SERIAL_EV_RXFLAG:
1862 return "SERIAL_EV_RXFLAG";
1863 case SERIAL_EV_TXEMPTY:
1864 return "SERIAL_EV_TXEMPTY";
1865 case SERIAL_EV_CTS:
1866 return "SERIAL_EV_CTS ";
1867 case SERIAL_EV_DSR:
1868 return "SERIAL_EV_DSR ";
1869 case SERIAL_EV_RLSD:
1870 return "SERIAL_EV_RLSD";
1871 case SERIAL_EV_BREAK:
1872 return "SERIAL_EV_BREAK";
1873 case SERIAL_EV_ERR:
1874 return "SERIAL_EV_ERR ";
1875 case SERIAL_EV_RING:
1876 return "SERIAL_EV_RING";
1877 case SERIAL_EV_PERR:
1878 return "SERIAL_EV_PERR";
1879 case SERIAL_EV_RX80FULL:
1880 return "SERIAL_EV_RX80FULL";
1881 case SERIAL_EV_EVENT1:
1882 return "SERIAL_EV_EVENT1";
1883 case SERIAL_EV_EVENT2:
1884 return "SERIAL_EV_EVENT2";
1885 case SERIAL_EV_WINPR_WAITING:
1886 return "SERIAL_EV_WINPR_WAITING";
1887 case SERIAL_EV_WINPR_STOP:
1888 return "SERIAL_EV_WINPR_STOP";
1889 default:
1890 return "SERIAL_EV_UNKNOWN";
1891 }
1892}
1893
1894const char* CommSerialEvString(ULONG status, char* buffer, size_t size)
1895{
1896 const ULONG flags[] = { SERIAL_EV_RXCHAR, SERIAL_EV_RXFLAG, SERIAL_EV_TXEMPTY,
1897 SERIAL_EV_CTS, SERIAL_EV_DSR, SERIAL_EV_RLSD,
1898 SERIAL_EV_BREAK, SERIAL_EV_ERR, SERIAL_EV_RING,
1899 SERIAL_EV_PERR, SERIAL_EV_RX80FULL, SERIAL_EV_EVENT1,
1900 SERIAL_EV_EVENT2, SERIAL_EV_WINPR_WAITING, SERIAL_EV_WINPR_STOP };
1901
1902 winpr_str_append("{", buffer, size, "");
1903
1904 const char* sep = "";
1905 for (size_t x = 0; x < ARRAYSIZE(flags); x++)
1906 {
1907 const ULONG flag = flags[x];
1908 if (status & flag)
1909 {
1910 winpr_str_append(CommSerialEvFlagString(flag), buffer, size, sep);
1911 sep = "|";
1912 }
1913 }
1914
1915 char number[32] = { 0 };
1916 (void)_snprintf(number, sizeof(number), "}[0x%08" PRIx32 "]", status);
1917 winpr_str_append(number, buffer, size, "");
1918 return buffer;
1919}