23#include <winpr/config.h>
25#include <winpr/assert.h>
30#if defined(WINPR_HAVE_SYS_EVENTFD_H)
31#include <sys/eventfd.h>
40#include <winpr/comm.h>
41#include <winpr/tchar.h>
42#include <winpr/wlog.h>
43#include <winpr/handle.h>
45#include "comm_ioctl.h"
47#include "../handle/handle.h"
49#define TAG WINPR_TAG("comm")
58static wLog* sLog =
nullptr;
66typedef struct comm_device COMM_DEVICE;
70#define COMM_DEVICE_MAX 128
71static COMM_DEVICE** sCommDevices =
nullptr;
74static pthread_once_t sCommInitialized = PTHREAD_ONCE_INIT;
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" },
108 { IOCTL_SERIAL_CONFIG_SIZE,
"IOCTL_SERIAL_CONFIG_SIZE" },
128 { IOCTL_USBPRINT_GET_1284_ID,
"IOCTL_USBPRINT_GET_1284_ID" }
131const char* _comm_serial_ioctl_name(ULONG number)
133 for (
size_t x = 0; x < ARRAYSIZE(S_SERIAL_IOCTL_NAMES); x++)
136 if (cur->number == number)
140 return "(unknown ioctl name)";
143static int CommGetFd(HANDLE handle)
145 WINPR_COMM* comm = (WINPR_COMM*)handle;
147 if (!CommIsHandled(handle))
155#if defined(WINPR_HAVE_SERIAL_SUPPORT)
156 static const HANDLE_CREATOR sCommHandleCreator = { .IsHandled = IsCommDevice,
157 .CreateFileA = CommCreateFileA };
158 return &sCommHandleCreator;
164static void CommInit(
void)
167 WINPR_ASSERT(sLog ==
nullptr);
168 WINPR_ASSERT(sCommDevices ==
nullptr);
169 sCommDevices = (COMM_DEVICE**)calloc(COMM_DEVICE_MAX + 1,
sizeof(COMM_DEVICE*));
174 if (!InitializeCriticalSectionEx(&sCommDevicesLock, 0, 0))
176 free((
void*)sCommDevices);
177 sCommDevices =
nullptr;
181 sLog = WLog_Get(TAG);
182 WINPR_ASSERT(sLog !=
nullptr);
189static BOOL CommInitialized(
void)
191 if (pthread_once(&sCommInitialized, CommInit) != 0)
193 SetLastError(ERROR_DLL_INIT_FAILED);
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, ...)
204 if (!CommInitialized())
207 if (!WLog_IsLevelActive(sLog, level))
209 va_list ap = WINPR_C_ARRAY_INIT;
211 WLog_PrintTextMessageVA(sLog, level, line, file, fkt, fmt, ap);
215BOOL BuildCommDCBA(WINPR_ATTR_UNUSED LPCSTR lpDef, WINPR_ATTR_UNUSED
LPDCB lpDCB)
217 if (!CommInitialized())
221 CommLog_Print(WLOG_ERROR,
"Not implemented");
222 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
226BOOL BuildCommDCBW(WINPR_ATTR_UNUSED LPCWSTR lpDef, WINPR_ATTR_UNUSED
LPDCB lpDCB)
228 if (!CommInitialized())
232 CommLog_Print(WLOG_ERROR,
"Not implemented");
233 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
237BOOL BuildCommDCBAndTimeoutsA(WINPR_ATTR_UNUSED LPCSTR lpDef, WINPR_ATTR_UNUSED
LPDCB lpDCB,
240 if (!CommInitialized())
244 CommLog_Print(WLOG_ERROR,
"Not implemented");
245 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
249BOOL BuildCommDCBAndTimeoutsW(WINPR_ATTR_UNUSED LPCWSTR lpDef, WINPR_ATTR_UNUSED
LPDCB lpDCB,
252 if (!CommInitialized())
256 CommLog_Print(WLOG_ERROR,
"Not implemented");
257 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
261BOOL CommConfigDialogA(WINPR_ATTR_UNUSED LPCSTR lpszName, WINPR_ATTR_UNUSED HWND hWnd,
264 if (!CommInitialized())
268 CommLog_Print(WLOG_ERROR,
"Not implemented");
269 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
273BOOL CommConfigDialogW(WINPR_ATTR_UNUSED LPCWSTR lpszName, WINPR_ATTR_UNUSED HWND hWnd,
276 if (!CommInitialized())
280 CommLog_Print(WLOG_ERROR,
"Not implemented");
281 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
285BOOL GetCommConfig(HANDLE hCommDev, WINPR_ATTR_UNUSED
LPCOMMCONFIG lpCC,
286 WINPR_ATTR_UNUSED LPDWORD lpdwSize)
288 WINPR_COMM* pComm = (WINPR_COMM*)hCommDev;
290 if (!CommInitialized())
298 CommLog_Print(WLOG_ERROR,
"Not implemented");
299 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
303BOOL SetCommConfig(HANDLE hCommDev, WINPR_ATTR_UNUSED
LPCOMMCONFIG lpCC,
304 WINPR_ATTR_UNUSED DWORD dwSize)
306 WINPR_COMM* pComm = (WINPR_COMM*)hCommDev;
308 if (!CommInitialized())
316 CommLog_Print(WLOG_ERROR,
"Not implemented");
317 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
321BOOL GetCommMask(HANDLE hFile, WINPR_ATTR_UNUSED PDWORD lpEvtMask)
323 WINPR_COMM* pComm = (WINPR_COMM*)hFile;
325 if (!CommInitialized())
333 CommLog_Print(WLOG_ERROR,
"Not implemented");
334 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
338BOOL SetCommMask(HANDLE hFile, WINPR_ATTR_UNUSED DWORD dwEvtMask)
340 WINPR_COMM* pComm = (WINPR_COMM*)hFile;
342 if (!CommInitialized())
350 CommLog_Print(WLOG_ERROR,
"Not implemented");
351 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
355BOOL GetCommModemStatus(HANDLE hFile, WINPR_ATTR_UNUSED PDWORD lpModemStat)
357 WINPR_COMM* pComm = (WINPR_COMM*)hFile;
359 if (!CommInitialized())
367 CommLog_Print(WLOG_ERROR,
"Not implemented");
368 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
377BOOL GetCommProperties(HANDLE hFile,
LPCOMMPROP lpCommProp)
379 WINPR_COMM* pComm = (WINPR_COMM*)hFile;
380 DWORD bytesReturned = 0;
382 if (!CommIsHandleValid(hFile))
385 if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_GET_PROPERTIES,
nullptr, 0, lpCommProp,
386 sizeof(
COMMPROP), &bytesReturned,
nullptr))
388 CommLog_Print(WLOG_WARN,
"GetCommProperties failure.");
404BOOL GetCommState(HANDLE hFile,
LPDCB lpDCB)
406 DCB* lpLocalDcb =
nullptr;
407 struct termios currentState;
408 WINPR_COMM* pComm = (WINPR_COMM*)hFile;
409 DWORD bytesReturned = 0;
411 if (!CommIsHandleValid(hFile))
416 SetLastError(ERROR_INVALID_DATA);
420 if (lpDCB->DCBlength <
sizeof(
DCB))
422 SetLastError(ERROR_INVALID_DATA);
426 if (tcgetattr(pComm->fd, ¤tState) < 0)
428 SetLastError(ERROR_IO_DEVICE);
432 lpLocalDcb = (
DCB*)calloc(1, lpDCB->DCBlength);
434 if (lpLocalDcb ==
nullptr)
436 SetLastError(ERROR_OUTOFMEMORY);
441 lpLocalDcb->DCBlength = lpDCB->DCBlength;
444 if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_GET_BAUD_RATE,
nullptr, 0, &baudRate,
447 CommLog_Print(WLOG_WARN,
"GetCommState failure: could not get the baud rate.");
451 lpLocalDcb->BaudRate = baudRate.BaudRate;
452 lpLocalDcb->fBinary = (currentState.c_cflag & ICANON) == 0;
454 if (!lpLocalDcb->fBinary)
456 CommLog_Print(WLOG_WARN,
"Unexpected nonbinary mode, consider to unset the ICANON flag.");
459 lpLocalDcb->fParity = (currentState.c_iflag & INPCK) != 0;
462 if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_GET_HANDFLOW,
nullptr, 0, &handflow,
465 CommLog_Print(WLOG_WARN,
"GetCommState failure: could not get the handflow settings.");
469 lpLocalDcb->fOutxCtsFlow = (handflow.ControlHandShake & SERIAL_CTS_HANDSHAKE) != 0;
470 lpLocalDcb->fOutxDsrFlow = (handflow.ControlHandShake & SERIAL_DSR_HANDSHAKE) != 0;
472 if (handflow.ControlHandShake & SERIAL_DTR_HANDSHAKE)
474 lpLocalDcb->fDtrControl = DTR_CONTROL_HANDSHAKE;
476 else if (handflow.ControlHandShake & SERIAL_DTR_CONTROL)
478 lpLocalDcb->fDtrControl = DTR_CONTROL_ENABLE;
482 lpLocalDcb->fDtrControl = DTR_CONTROL_DISABLE;
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;
492 if (handflow.FlowReplace & SERIAL_RTS_HANDSHAKE)
494 lpLocalDcb->fRtsControl = RTS_CONTROL_HANDSHAKE;
496 else if (handflow.FlowReplace & SERIAL_RTS_CONTROL)
498 lpLocalDcb->fRtsControl = RTS_CONTROL_ENABLE;
502 lpLocalDcb->fRtsControl = RTS_CONTROL_DISABLE;
507 lpLocalDcb->fAbortOnError = (handflow.ControlHandShake & SERIAL_ERROR_ABORT) != 0;
509 lpLocalDcb->wReserved = 0;
510 lpLocalDcb->XonLim = WINPR_ASSERTING_INT_CAST(WORD, handflow.XonLimit);
511 lpLocalDcb->XoffLim = WINPR_ASSERTING_INT_CAST(WORD, handflow.XoffLimit);
516 if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_GET_LINE_CONTROL,
nullptr, 0, &lineControl,
519 CommLog_Print(WLOG_WARN,
"GetCommState failure: could not get the control settings.");
523 lpLocalDcb->ByteSize = lineControl.WordLength;
524 lpLocalDcb->Parity = lineControl.Parity;
525 lpLocalDcb->StopBits = lineControl.StopBits;
531 if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_GET_CHARS,
nullptr, 0, &serialChars,
534 CommLog_Print(WLOG_WARN,
"GetCommState failure: could not get the serial chars.");
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;
544 memcpy(lpDCB, lpLocalDcb, lpDCB->DCBlength);
563BOOL SetCommState(HANDLE hFile,
LPDCB lpDCB)
565 struct termios upcomingTermios = WINPR_C_ARRAY_INIT;
566 WINPR_COMM* pComm = (WINPR_COMM*)hFile;
567 DWORD bytesReturned = 0;
571 if (!CommIsHandleValid(hFile))
576 SetLastError(ERROR_INVALID_DATA);
584 baudRate.BaudRate = lpDCB->BaudRate;
586 if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_SET_BAUD_RATE, &baudRate,
sizeof(
SERIAL_BAUD_RATE),
587 nullptr, 0, &bytesReturned,
nullptr))
589 CommLog_Print(WLOG_WARN,
"SetCommState failure: could not set the baud rate.");
595 if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_GET_CHARS,
nullptr, 0, &serialChars,
599 CommLog_Print(WLOG_WARN,
"SetCommState failure: could not get the initial serial chars.");
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;
609 if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_SET_CHARS, &serialChars,
sizeof(
SERIAL_CHARS),
610 nullptr, 0, &bytesReturned,
nullptr))
612 CommLog_Print(WLOG_WARN,
"SetCommState failure: could not set the serial chars.");
617 lineControl.StopBits = lpDCB->StopBits;
618 lineControl.Parity = lpDCB->Parity;
619 lineControl.WordLength = lpDCB->ByteSize;
621 if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_SET_LINE_CONTROL, &lineControl,
624 CommLog_Print(WLOG_WARN,
"SetCommState failure: could not set the control settings.");
630 if (lpDCB->fOutxCtsFlow)
632 handflow.ControlHandShake |= SERIAL_CTS_HANDSHAKE;
635 if (lpDCB->fOutxDsrFlow)
637 handflow.ControlHandShake |= SERIAL_DSR_HANDSHAKE;
640 switch (lpDCB->fDtrControl)
642 case SERIAL_DTR_HANDSHAKE:
643 handflow.ControlHandShake |= DTR_CONTROL_HANDSHAKE;
646 case SERIAL_DTR_CONTROL:
647 handflow.ControlHandShake |= DTR_CONTROL_ENABLE;
650 case DTR_CONTROL_DISABLE:
655 CommLog_Print(WLOG_WARN,
"Unexpected fDtrControl value: %" PRId32
"\n",
660 if (lpDCB->fDsrSensitivity)
662 handflow.ControlHandShake |= SERIAL_DSR_SENSITIVITY;
665 if (lpDCB->fTXContinueOnXoff)
667 handflow.FlowReplace |= SERIAL_XOFF_CONTINUE;
672 handflow.FlowReplace |= SERIAL_AUTO_TRANSMIT;
677 handflow.FlowReplace |= SERIAL_AUTO_RECEIVE;
680 if (lpDCB->fErrorChar)
682 handflow.FlowReplace |= SERIAL_ERROR_CHAR;
687 handflow.FlowReplace |= SERIAL_NULL_STRIPPING;
690 switch (lpDCB->fRtsControl)
692 case RTS_CONTROL_TOGGLE:
693 CommLog_Print(WLOG_WARN,
"Unsupported RTS_CONTROL_TOGGLE feature");
697 case RTS_CONTROL_HANDSHAKE:
698 handflow.FlowReplace |= SERIAL_RTS_HANDSHAKE;
701 case RTS_CONTROL_ENABLE:
702 handflow.FlowReplace |= SERIAL_RTS_CONTROL;
705 case RTS_CONTROL_DISABLE:
710 CommLog_Print(WLOG_WARN,
"Unexpected fRtsControl value: %" PRId32
"\n",
715 if (lpDCB->fAbortOnError)
717 handflow.ControlHandShake |= SERIAL_ERROR_ABORT;
722 handflow.XonLimit = lpDCB->XonLim;
723 handflow.XoffLimit = lpDCB->XoffLim;
725 if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_SET_HANDFLOW, &handflow,
sizeof(
SERIAL_HANDFLOW),
726 nullptr, 0, &bytesReturned,
nullptr))
728 CommLog_Print(WLOG_WARN,
"SetCommState failure: could not set the handflow settings.");
734 if (tcgetattr(pComm->fd, &upcomingTermios) <
737 SetLastError(ERROR_IO_DEVICE);
743 upcomingTermios.c_lflag &= (tcflag_t)~ICANON;
747 upcomingTermios.c_lflag |= ICANON;
748 CommLog_Print(WLOG_WARN,
"Unexpected nonbinary mode, consider to unset the ICANON flag.");
753 upcomingTermios.c_iflag |= INPCK;
757 upcomingTermios.c_iflag &= (tcflag_t)~INPCK;
771 if (comm_ioctl_tcsetattr(pComm->fd, TCSANOW, &upcomingTermios) < 0)
773 SetLastError(ERROR_IO_DEVICE);
786 WINPR_COMM* pComm = (WINPR_COMM*)hFile;
787 DWORD bytesReturned = 0;
789 if (!CommIsHandleValid(hFile))
794 if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_GET_TIMEOUTS,
nullptr, 0, lpCommTimeouts,
797 CommLog_Print(WLOG_WARN,
"GetCommTimeouts failure.");
810 WINPR_COMM* pComm = (WINPR_COMM*)hFile;
811 DWORD bytesReturned = 0;
813 if (!CommIsHandleValid(hFile))
818 if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_SET_TIMEOUTS, lpCommTimeouts,
sizeof(
COMMTIMEOUTS),
819 nullptr, 0, &bytesReturned,
nullptr))
821 CommLog_Print(WLOG_WARN,
"SetCommTimeouts failure.");
828BOOL GetDefaultCommConfigA(WINPR_ATTR_UNUSED LPCSTR lpszName, WINPR_ATTR_UNUSED
LPCOMMCONFIG lpCC,
829 WINPR_ATTR_UNUSED LPDWORD lpdwSize)
831 if (!CommInitialized())
835 CommLog_Print(WLOG_ERROR,
"Not implemented");
836 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
840BOOL GetDefaultCommConfigW(WINPR_ATTR_UNUSED LPCWSTR lpszName, WINPR_ATTR_UNUSED
LPCOMMCONFIG lpCC,
841 WINPR_ATTR_UNUSED LPDWORD lpdwSize)
843 if (!CommInitialized())
847 CommLog_Print(WLOG_ERROR,
"Not implemented");
848 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
852BOOL SetDefaultCommConfigA(WINPR_ATTR_UNUSED LPCSTR lpszName, WINPR_ATTR_UNUSED
LPCOMMCONFIG lpCC,
853 WINPR_ATTR_UNUSED DWORD dwSize)
855 if (!CommInitialized())
859 CommLog_Print(WLOG_ERROR,
"Not implemented");
860 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
864BOOL SetDefaultCommConfigW(WINPR_ATTR_UNUSED LPCWSTR lpszName, WINPR_ATTR_UNUSED
LPCOMMCONFIG lpCC,
865 WINPR_ATTR_UNUSED DWORD dwSize)
867 if (!CommInitialized())
871 CommLog_Print(WLOG_ERROR,
"Not implemented");
872 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
876BOOL SetCommBreak(HANDLE hFile)
878 WINPR_COMM* pComm = (WINPR_COMM*)hFile;
880 if (!CommInitialized())
888 CommLog_Print(WLOG_ERROR,
"Not implemented");
889 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
893BOOL ClearCommBreak(HANDLE hFile)
895 WINPR_COMM* pComm = (WINPR_COMM*)hFile;
897 if (!CommInitialized())
905 CommLog_Print(WLOG_ERROR,
"Not implemented");
906 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
910BOOL ClearCommError(HANDLE hFile, WINPR_ATTR_UNUSED PDWORD lpErrors,
913 WINPR_COMM* pComm = (WINPR_COMM*)hFile;
915 if (!CommInitialized())
923 CommLog_Print(WLOG_ERROR,
"Not implemented");
924 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
928BOOL PurgeComm(HANDLE hFile, DWORD dwFlags)
930 WINPR_COMM* pComm = (WINPR_COMM*)hFile;
931 DWORD bytesReturned = 0;
933 if (!CommIsHandleValid(hFile))
936 if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_PURGE, &dwFlags,
sizeof(DWORD),
nullptr, 0,
937 &bytesReturned,
nullptr))
939 CommLog_Print(WLOG_WARN,
"PurgeComm failure.");
946BOOL SetupComm(HANDLE hFile, DWORD dwInQueue, DWORD dwOutQueue)
948 WINPR_COMM* pComm = (WINPR_COMM*)hFile;
950 DWORD bytesReturned = 0;
952 if (!CommIsHandleValid(hFile))
955 queueSize.InSize = dwInQueue;
956 queueSize.OutSize = dwOutQueue;
958 if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_SET_QUEUE_SIZE, &queueSize,
961 CommLog_Print(WLOG_WARN,
"SetCommTimeouts failure.");
968BOOL EscapeCommFunction(HANDLE hFile, WINPR_ATTR_UNUSED DWORD dwFunc)
970 WINPR_COMM* pComm = (WINPR_COMM*)hFile;
972 if (!CommInitialized())
980 CommLog_Print(WLOG_ERROR,
"Not implemented");
981 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
985BOOL TransmitCommChar(HANDLE hFile, WINPR_ATTR_UNUSED
char cChar)
987 WINPR_COMM* pComm = (WINPR_COMM*)hFile;
989 if (!CommInitialized())
997 CommLog_Print(WLOG_ERROR,
"Not implemented");
998 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1002BOOL WaitCommEvent(HANDLE hFile, WINPR_ATTR_UNUSED PDWORD lpEvtMask,
1005 WINPR_COMM* pComm = (WINPR_COMM*)hFile;
1007 if (!CommInitialized())
1015 CommLog_Print(WLOG_ERROR,
"Not implemented");
1016 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1029BOOL DefineCommDevice( LPCTSTR lpDeviceName, LPCTSTR lpTargetPath)
1031 LPTSTR storedDeviceName =
nullptr;
1032 LPTSTR storedTargetPath =
nullptr;
1034 if (!CommInitialized())
1037 EnterCriticalSection(&sCommDevicesLock);
1039 if (sCommDevices ==
nullptr)
1041 SetLastError(ERROR_DLL_INIT_FAILED);
1045 storedDeviceName = _tcsdup(lpDeviceName);
1047 if (storedDeviceName ==
nullptr)
1049 SetLastError(ERROR_OUTOFMEMORY);
1053 storedTargetPath = _tcsdup(lpTargetPath);
1055 if (storedTargetPath ==
nullptr)
1057 SetLastError(ERROR_OUTOFMEMORY);
1063 for (; i < COMM_DEVICE_MAX; i++)
1065 if (sCommDevices[i] !=
nullptr)
1067 if (_tcscmp(sCommDevices[i]->name, storedDeviceName) == 0)
1070 free(sCommDevices[i]->name);
1071 free(sCommDevices[i]->path);
1072 sCommDevices[i]->name = storedDeviceName;
1073 sCommDevices[i]->path = storedTargetPath;
1080 sCommDevices[i] = (COMM_DEVICE*)calloc(1,
sizeof(COMM_DEVICE));
1082 if (sCommDevices[i] ==
nullptr)
1084 SetLastError(ERROR_OUTOFMEMORY);
1088 sCommDevices[i]->name = storedDeviceName;
1089 sCommDevices[i]->path = storedTargetPath;
1094 if (i == COMM_DEVICE_MAX)
1096 SetLastError(ERROR_OUTOFMEMORY);
1101 LeaveCriticalSection(&sCommDevicesLock);
1104 free(storedDeviceName);
1105 free(storedTargetPath);
1106 LeaveCriticalSection(&sCommDevicesLock);
1126DWORD QueryCommDevice(LPCTSTR lpDeviceName, LPTSTR lpTargetPath, DWORD ucchMax)
1128 LPTSTR storedTargetPath =
nullptr;
1129 SetLastError(ERROR_SUCCESS);
1131 if (!CommInitialized())
1134 if (sCommDevices ==
nullptr)
1136 SetLastError(ERROR_DLL_INIT_FAILED);
1140 if (lpDeviceName ==
nullptr || lpTargetPath ==
nullptr)
1142 SetLastError(ERROR_NOT_SUPPORTED);
1146 EnterCriticalSection(&sCommDevicesLock);
1147 storedTargetPath =
nullptr;
1149 for (
int i = 0; i < COMM_DEVICE_MAX; i++)
1151 if (sCommDevices[i] !=
nullptr)
1153 if (_tcscmp(sCommDevices[i]->name, lpDeviceName) == 0)
1155 storedTargetPath = sCommDevices[i]->path;
1165 LeaveCriticalSection(&sCommDevicesLock);
1167 if (storedTargetPath ==
nullptr)
1169 SetLastError(ERROR_INVALID_DATA);
1173 const size_t size = _tcsnlen(storedTargetPath, ucchMax);
1174 if (size + 2 > ucchMax)
1176 SetLastError(ERROR_INSUFFICIENT_BUFFER);
1180 _tcsncpy(lpTargetPath, storedTargetPath, size + 1);
1181 lpTargetPath[size + 2] =
'\0';
1182 return (DWORD)size + 2UL;
1188BOOL IsCommDevice(LPCTSTR lpDeviceName)
1190 TCHAR lpTargetPath[MAX_PATH];
1192 if (!CommInitialized())
1195 if (QueryCommDevice(lpDeviceName, lpTargetPath, MAX_PATH) > 0)
1206void _comm_setServerSerialDriver(HANDLE hComm, SERIAL_DRIVER_ID driverId)
1210 WINPR_COMM* pComm =
nullptr;
1212 if (!CommInitialized())
1215 if (!winpr_Handle_GetInfo(hComm, &Type, &Object))
1217 CommLog_Print(WLOG_WARN,
"_comm_setServerSerialDriver failure");
1221 pComm = (WINPR_COMM*)Object;
1222 pComm->serverSerialDriverId = driverId;
1225static HANDLE_OPS ops = { CommIsHandled, CommCloseHandle, CommGetFd,
nullptr,
1226 nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
1227 nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
1228 nullptr,
nullptr,
nullptr,
nullptr,
nullptr };
1230#if defined(WINPR_HAVE_SYS_EVENTFD_H)
1234static int comm_eventfd_cloexec(
void)
1236 int fd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
1237 if ((fd < 0) && (errno == EINVAL))
1245 ((fcntl(fd, F_SETFL, O_NONBLOCK) < 0) || (fcntl(fd, F_SETFD, FD_CLOEXEC) < 0)))
1280HANDLE CommCreateFileA(LPCSTR lpDeviceName, DWORD dwDesiredAccess, DWORD dwShareMode,
1281 LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition,
1282 DWORD dwFlagsAndAttributes, HANDLE hTemplateFile)
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;
1289 if (!CommInitialized())
1290 return INVALID_HANDLE_VALUE;
1292 if (dwDesiredAccess != (GENERIC_READ | GENERIC_WRITE))
1294 CommLog_Print(WLOG_WARN,
"unexpected access to the device: 0x%08" PRIX32
"",
1298 if (dwShareMode != 0)
1300 SetLastError(ERROR_SHARING_VIOLATION);
1301 return INVALID_HANDLE_VALUE;
1307 if (lpSecurityAttributes !=
nullptr)
1309 CommLog_Print(WLOG_WARN,
"unexpected security attributes, nLength=%" PRIu32
"",
1310 lpSecurityAttributes->nLength);
1313 if (dwCreationDisposition != OPEN_EXISTING)
1315 SetLastError(ERROR_FILE_NOT_FOUND);
1316 return INVALID_HANDLE_VALUE;
1319 if (QueryCommDevice(lpDeviceName, devicePath, MAX_PATH) <= 0)
1322 return INVALID_HANDLE_VALUE;
1325 if (stat(devicePath, &deviceStat) < 0)
1327 CommLog_Print(WLOG_WARN,
"device not found %s", devicePath);
1328 SetLastError(ERROR_FILE_NOT_FOUND);
1329 return INVALID_HANDLE_VALUE;
1332 if (!S_ISCHR(deviceStat.st_mode))
1334 CommLog_Print(WLOG_WARN,
"bad device %s", devicePath);
1335 SetLastError(ERROR_BAD_DEVICE);
1336 return INVALID_HANDLE_VALUE;
1339 if (dwFlagsAndAttributes != 0)
1341 CommLog_Print(WLOG_WARN,
"unexpected flags and attributes: 0x%08" PRIX32
"",
1342 dwFlagsAndAttributes);
1345 if (hTemplateFile !=
nullptr)
1347 SetLastError(ERROR_NOT_SUPPORTED);
1348 return INVALID_HANDLE_VALUE;
1351 pComm = (WINPR_COMM*)calloc(1,
sizeof(WINPR_COMM));
1353 if (pComm ==
nullptr)
1355 SetLastError(ERROR_OUTOFMEMORY);
1356 return INVALID_HANDLE_VALUE;
1359 WINPR_HANDLE_SET_TYPE_AND_MODE(pComm, HANDLE_TYPE_COMM, WINPR_FD_READ);
1360 pComm->common.ops = &ops;
1362 pComm->fd = open(devicePath, O_RDWR | O_NOCTTY | O_NONBLOCK | O_CLOEXEC);
1366 CommLog_Print(WLOG_WARN,
"failed to open device %s", devicePath);
1367 SetLastError(ERROR_BAD_DEVICE);
1371 pComm->fd_read = open(devicePath, O_RDONLY | O_NOCTTY | O_NONBLOCK | O_CLOEXEC);
1373 if (pComm->fd_read < 0)
1375 CommLog_Print(WLOG_WARN,
"failed to open fd_read, device: %s", devicePath);
1376 SetLastError(ERROR_BAD_DEVICE);
1380#if defined(WINPR_HAVE_SYS_EVENTFD_H)
1381 pComm->fd_read_event = comm_eventfd_cloexec();
1384 if (pComm->fd_read_event < 0)
1386 CommLog_Print(WLOG_WARN,
"failed to open fd_read_event, device: %s", devicePath);
1387 SetLastError(ERROR_BAD_DEVICE);
1391 InitializeCriticalSection(&pComm->ReadLock);
1392 pComm->fd_write = open(devicePath, O_WRONLY | O_NOCTTY | O_NONBLOCK | O_CLOEXEC);
1394 if (pComm->fd_write < 0)
1396 CommLog_Print(WLOG_WARN,
"failed to open fd_write, device: %s", devicePath);
1397 SetLastError(ERROR_BAD_DEVICE);
1401#if defined(WINPR_HAVE_SYS_EVENTFD_H)
1402 pComm->fd_write_event = comm_eventfd_cloexec();
1405 if (pComm->fd_write_event < 0)
1407 CommLog_Print(WLOG_WARN,
"failed to open fd_write_event, device: %s", devicePath);
1408 SetLastError(ERROR_BAD_DEVICE);
1412 InitializeCriticalSection(&pComm->WriteLock);
1414 pComm->serverSerialDriverId = SerialDriverUnknown;
1415 InitializeCriticalSection(&pComm->EventsLock);
1417 (void)CommUpdateIOCount(pComm, TRUE);
1422 ZeroMemory(&upcomingTermios,
sizeof(
struct termios));
1424 if (tcgetattr(pComm->fd, &upcomingTermios) < 0)
1426 SetLastError(ERROR_IO_DEVICE);
1430 upcomingTermios.c_iflag &=
1431 (tcflag_t) ~( BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL );
1432 upcomingTermios.c_oflag = 0;
1433 upcomingTermios.c_lflag = 0;
1442 upcomingTermios.c_cflag |= CLOCAL | CREAD;
1444 if (comm_ioctl_tcsetattr(pComm->fd, TCSANOW, &upcomingTermios) < 0)
1446 SetLastError(ERROR_IO_DEVICE);
1450 return (HANDLE)pComm;
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;
1458BOOL CommIsHandled(HANDLE handle)
1460 if (!CommInitialized())
1463 return WINPR_HANDLE_IS_HANDLED(handle, HANDLE_TYPE_COMM, TRUE);
1466BOOL CommIsHandleValid(HANDLE handle)
1468 WINPR_COMM* pComm = (WINPR_COMM*)handle;
1469 if (!CommIsHandled(handle))
1473 SetLastError(ERROR_INVALID_HANDLE);
1479BOOL CommCloseHandle(HANDLE handle)
1481 WINPR_COMM* pComm = (WINPR_COMM*)handle;
1483 if (!CommIsHandled(handle))
1486 DeleteCriticalSection(&pComm->ReadLock);
1487 DeleteCriticalSection(&pComm->WriteLock);
1488 DeleteCriticalSection(&pComm->EventsLock);
1493 if (pComm->fd_write > 0)
1494 close(pComm->fd_write);
1496 if (pComm->fd_write_event > 0)
1497 close(pComm->fd_write_event);
1499 if (pComm->fd_read > 0)
1500 close(pComm->fd_read);
1502 if (pComm->fd_read_event > 0)
1503 close(pComm->fd_read_event);
1508#if defined(WINPR_HAVE_SYS_EVENTFD_H)
1509#ifndef WITH_EVENTFD_READ_WRITE
1510int eventfd_read(
int fd, eventfd_t* value)
1512 return (read(fd, value,
sizeof(*value)) ==
sizeof(*value)) ? 0 : -1;
1515int eventfd_write(
int fd, eventfd_t value)
1517 return (write(fd, &value,
sizeof(value)) ==
sizeof(value)) ? 0 : -1;
1522static const char* CommIoCtlToStr(
unsigned long int io)
1526#if defined(WINPR_HAVE_SERIAL_SUPPORT)
1571#if defined(TIOCEXCL)
1575#if defined(TIOCNXCL)
1579#if defined(TIOCSCTTY)
1583#if defined(TIOCGPGRP)
1587#if defined(TIOCSPGRP)
1591#if defined(TIOCOUTQ)
1599#if defined(TIOCGWINSZ)
1601 return "TIOCGWINSZ";
1603#if defined(TIOCSWINSZ)
1605 return "TIOCSWINSZ";
1607#if defined(TIOCMGET)
1611#if defined(TIOCMBIS)
1615#if defined(TIOCMBIC)
1619#if defined(TIOCMSET)
1623#if defined(TIOCGSOFTCAR)
1625 return "TIOCGSOFTCAR";
1627#if defined(TIOCSSOFTCAR)
1629 return "TIOCSSOFTCAR";
1631#if defined(FIONREAD)
1633 return "FIONREAD/TIOCINQ";
1635#if defined(TIOCLINUX)
1639#if defined(TIOCCONS)
1643#if defined(TIOCGSERIAL)
1645 return "TIOCGSERIAL";
1647#if defined(TIOCSSERIAL)
1649 return "TIOCSSERIAL";
1659#if defined(TIOCNOTTY)
1663#if defined(TIOCSETD)
1667#if defined(TIOCGETD)
1675#if defined(TIOCSBRK)
1679#if defined(TIOCCBRK)
1683#if defined(TIOCGSID)
1687#if defined(TIOCGRS485)
1689 return "TIOCGRS485";
1691#if defined(TIOCSRS485)
1693 return "TIOCSRS485";
1695#if defined(TIOCSPTLCK)
1697 return "TIOCSPTLCK";
1719#if defined(TIOCVHANGUP)
1721 return "TIOCVHANGUP";
1723#if defined(TIOCGPTPEER)
1725 return "TIOCGPTPEER";
1727#if defined(FIONCLEX)
1735#if defined(FIOASYNC)
1739#if defined(TIOCSERCONFIG)
1741 return "TIOCSERCONFIG";
1743#if defined(TIOCSERGWILD)
1745 return "TIOCSERGWILD";
1747#if defined(TIOCSERSWILD)
1749 return "TIOCSERSWILD";
1751#if defined(TIOCGLCKTRMIOS)
1752 case TIOCGLCKTRMIOS:
1753 return "TIOCGLCKTRMIOS";
1755#if defined(TIOCSLCKTRMIOS)
1756 case TIOCSLCKTRMIOS:
1757 return "TIOCSLCKTRMIOS";
1759#if defined(TIOCSERGSTRUCT)
1760 case TIOCSERGSTRUCT:
1761 return "TIOCSERGSTRUCT";
1763#if defined(TIOCSERGETLSR)
1765 return "TIOCSERGETLSR";
1767#if defined(TIOCSERGETMULTI)
1768 case TIOCSERGETMULTI:
1769 return "TIOCSERGETMULTI";
1771#if defined(TIOCSERSETMULTI)
1772 case TIOCSERSETMULTI:
1773 return "TIOCSERSETMULTI";
1775#if defined(TIOCMIWAIT)
1777 return "TIOCMIWAIT";
1779#if defined(TIOCGICOUNT)
1781 return "TIOCGICOUNT";
1783#if defined(FIOQSIZE)
1787#if defined(TIOCPKT_DATA)
1789 return "TIOCPKT_DATA";
1791#if defined(TIOCPKT_FLUSHWRITE)
1792 case TIOCPKT_FLUSHWRITE:
1793 return "TIOCPKT_FLUSHWRITE";
1795#if defined(TIOCPKT_STOP)
1797 return "TIOCPKT_STOP";
1799#if defined(TIOCPKT_START)
1801 return "TIOCPKT_START";
1803#if defined(TIOCPKT_NOSTOP)
1804 case TIOCPKT_NOSTOP:
1805 return "TIOCPKT_NOSTOP";
1807#if defined(TIOCPKT_DOSTOP)
1808 case TIOCPKT_DOSTOP:
1809 return "TIOCPKT_DOSTOP";
1811#if defined(TIOCPKT_IOCTL)
1813 return "TIOCPKT_IOCTL";
1821static BOOL CommStatusErrorEx(WINPR_COMM* pComm,
unsigned long int ctl,
const char* file,
1822 const char* fkt,
size_t line)
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);
1830 if (CommInitialized())
1832 if (WLog_IsLevelActive(sLog, level))
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)));
1841 SetLastError(ERROR_IO_DEVICE);
1846BOOL CommIoCtl_int(WINPR_COMM* pComm,
unsigned long int ctl,
void* data,
const char* file,
1847 const char* fkt,
size_t line)
1849 if (ioctl(pComm->fd, ctl, data) < 0)
1851 if (!CommStatusErrorEx(pComm, ctl, file, fkt, line))
1857BOOL CommUpdateIOCount(WINPR_ATTR_UNUSED HANDLE handle, WINPR_ATTR_UNUSED BOOL checkSupportStatus)
1859 WINPR_COMM* pComm = (WINPR_COMM*)handle;
1860 WINPR_ASSERT(pComm);
1862#if defined(WINPR_HAVE_COMM_COUNTERS)
1863 ZeroMemory(&(pComm->counters),
sizeof(
struct serial_icounter_struct));
1864 if (pComm->TIOCGICOUNTSupported || checkSupportStatus)
1866 const int rc = ioctl(pComm->fd, TIOCGICOUNT, &(pComm->counters));
1867 if (checkSupportStatus)
1868 pComm->TIOCGICOUNTSupported = rc >= 0;
1871 if (!CommStatusErrorEx(pComm, TIOCGICOUNT, __FILE__, __func__, __LINE__))
1879static const char* CommSerialEvFlagString(ULONG flag)
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";
1890 return "SERIAL_EV_CTS ";
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";
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";
1914 return "SERIAL_EV_UNKNOWN";
1918const char* CommSerialEvString(ULONG status,
char* buffer,
size_t size)
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 };
1926 winpr_str_append(
"{", buffer, size,
"");
1928 const char* sep =
"";
1929 for (
size_t x = 0; x < ARRAYSIZE(flags); x++)
1931 const ULONG flag = flags[x];
1934 winpr_str_append(CommSerialEvFlagString(flag), buffer, size, sep);
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,
"");