summaryrefslogtreecommitdiffstats
path: root/winpr/libwinpr/comm/comm.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--winpr/libwinpr/comm/comm.c1426
1 files changed, 1426 insertions, 0 deletions
diff --git a/winpr/libwinpr/comm/comm.c b/winpr/libwinpr/comm/comm.c
new file mode 100644
index 0000000..c4e828e
--- /dev/null
+++ b/winpr/libwinpr/comm/comm.c
@@ -0,0 +1,1426 @@
+/**
+ * WinPR: Windows Portable Runtime
+ * Serial Communication API
+ *
+ * Copyright 2011 O.S. Systems Software Ltda.
+ * Copyright 2011 Eduardo Fiss Beloni <beloni@ossystems.com.br>
+ * Copyright 2014 Marc-Andre Moreau <marcandre.moreau@gmail.com>
+ * Copyright 2014 Hewlett-Packard Development Company, L.P.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <winpr/config.h>
+
+#if defined __linux__ && !defined ANDROID
+
+#include <winpr/assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <stdarg.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include <winpr/crt.h>
+#include <winpr/comm.h>
+#include <winpr/tchar.h>
+#include <winpr/wlog.h>
+#include <winpr/handle.h>
+
+#include "comm_ioctl.h"
+
+/**
+ * Communication Resources:
+ * http://msdn.microsoft.com/en-us/library/windows/desktop/aa363196/
+ */
+
+#include "comm.h"
+
+static wLog* _Log = NULL;
+
+struct comm_device
+{
+ LPTSTR name;
+ LPTSTR path;
+};
+
+typedef struct comm_device COMM_DEVICE;
+
+/* FIXME: get a clever data structure, see also io.h functions */
+/* _CommDevices is a NULL-terminated array with a maximun of COMM_DEVICE_MAX COMM_DEVICE */
+#define COMM_DEVICE_MAX 128
+static COMM_DEVICE** _CommDevices = NULL;
+static CRITICAL_SECTION _CommDevicesLock;
+
+static HANDLE_CREATOR _CommHandleCreator;
+
+static pthread_once_t _CommInitialized = PTHREAD_ONCE_INIT;
+
+static int CommGetFd(HANDLE handle)
+{
+ WINPR_COMM* comm = (WINPR_COMM*)handle;
+
+ if (!CommIsHandled(handle))
+ return -1;
+
+ return comm->fd;
+}
+
+HANDLE_CREATOR* GetCommHandleCreator(void)
+{
+ _CommHandleCreator.IsHandled = IsCommDevice;
+ _CommHandleCreator.CreateFileA = CommCreateFileA;
+ return &_CommHandleCreator;
+}
+
+static void _CommInit(void)
+{
+ /* NB: error management to be done outside of this function */
+ WINPR_ASSERT(_Log == NULL);
+ WINPR_ASSERT(_CommDevices == NULL);
+ _CommDevices = (COMM_DEVICE**)calloc(COMM_DEVICE_MAX + 1, sizeof(COMM_DEVICE*));
+
+ if (!_CommDevices)
+ return;
+
+ if (!InitializeCriticalSectionEx(&_CommDevicesLock, 0, 0))
+ {
+ free(_CommDevices);
+ _CommDevices = NULL;
+ return;
+ }
+
+ _Log = WLog_Get("com.winpr.comm");
+ WINPR_ASSERT(_Log != NULL);
+}
+
+/**
+ * Returns TRUE when the comm module is correctly intialized, FALSE otherwise
+ * with ERROR_DLL_INIT_FAILED set as the last error.
+ */
+static BOOL CommInitialized(void)
+{
+ if (pthread_once(&_CommInitialized, _CommInit) != 0)
+ {
+ SetLastError(ERROR_DLL_INIT_FAILED);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+void CommLog_Print(DWORD level, ...)
+{
+ if (!CommInitialized())
+ return;
+
+ va_list ap;
+ va_start(ap, level);
+ WLog_PrintVA(_Log, level, ap);
+ va_end(ap);
+}
+
+BOOL BuildCommDCBA(LPCSTR lpDef, LPDCB lpDCB)
+{
+ if (!CommInitialized())
+ return FALSE;
+
+ /* TODO: not implemented */
+ CommLog_Print(WLOG_ERROR, "Not implemented");
+ SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
+ return FALSE;
+}
+
+BOOL BuildCommDCBW(LPCWSTR lpDef, LPDCB lpDCB)
+{
+ if (!CommInitialized())
+ return FALSE;
+
+ /* TODO: not implemented */
+ CommLog_Print(WLOG_ERROR, "Not implemented");
+ SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
+ return FALSE;
+}
+
+BOOL BuildCommDCBAndTimeoutsA(LPCSTR lpDef, LPDCB lpDCB, LPCOMMTIMEOUTS lpCommTimeouts)
+{
+ if (!CommInitialized())
+ return FALSE;
+
+ /* TODO: not implemented */
+ CommLog_Print(WLOG_ERROR, "Not implemented");
+ SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
+ return FALSE;
+}
+
+BOOL BuildCommDCBAndTimeoutsW(LPCWSTR lpDef, LPDCB lpDCB, LPCOMMTIMEOUTS lpCommTimeouts)
+{
+ if (!CommInitialized())
+ return FALSE;
+
+ /* TODO: not implemented */
+ CommLog_Print(WLOG_ERROR, "Not implemented");
+ SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
+ return FALSE;
+}
+
+BOOL CommConfigDialogA(LPCSTR lpszName, HWND hWnd, LPCOMMCONFIG lpCC)
+{
+ if (!CommInitialized())
+ return FALSE;
+
+ /* TODO: not implemented */
+ CommLog_Print(WLOG_ERROR, "Not implemented");
+ SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
+ return FALSE;
+}
+
+BOOL CommConfigDialogW(LPCWSTR lpszName, HWND hWnd, LPCOMMCONFIG lpCC)
+{
+ if (!CommInitialized())
+ return FALSE;
+
+ /* TODO: not implemented */
+ CommLog_Print(WLOG_ERROR, "Not implemented");
+ SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
+ return FALSE;
+}
+
+BOOL GetCommConfig(HANDLE hCommDev, LPCOMMCONFIG lpCC, LPDWORD lpdwSize)
+{
+ WINPR_COMM* pComm = (WINPR_COMM*)hCommDev;
+
+ if (!CommInitialized())
+ return FALSE;
+
+ /* TODO: not implemented */
+
+ if (!pComm)
+ return FALSE;
+
+ CommLog_Print(WLOG_ERROR, "Not implemented");
+ SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
+ return FALSE;
+}
+
+BOOL SetCommConfig(HANDLE hCommDev, LPCOMMCONFIG lpCC, DWORD dwSize)
+{
+ WINPR_COMM* pComm = (WINPR_COMM*)hCommDev;
+
+ if (!CommInitialized())
+ return FALSE;
+
+ /* TODO: not implemented */
+
+ if (!pComm)
+ return FALSE;
+
+ CommLog_Print(WLOG_ERROR, "Not implemented");
+ SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
+ return FALSE;
+}
+
+BOOL GetCommMask(HANDLE hFile, PDWORD lpEvtMask)
+{
+ WINPR_COMM* pComm = (WINPR_COMM*)hFile;
+
+ if (!CommInitialized())
+ return FALSE;
+
+ /* TODO: not implemented */
+
+ if (!pComm)
+ return FALSE;
+
+ CommLog_Print(WLOG_ERROR, "Not implemented");
+ SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
+ return FALSE;
+}
+
+BOOL SetCommMask(HANDLE hFile, DWORD dwEvtMask)
+{
+ WINPR_COMM* pComm = (WINPR_COMM*)hFile;
+
+ if (!CommInitialized())
+ return FALSE;
+
+ /* TODO: not implemented */
+
+ if (!pComm)
+ return FALSE;
+
+ CommLog_Print(WLOG_ERROR, "Not implemented");
+ SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
+ return FALSE;
+}
+
+BOOL GetCommModemStatus(HANDLE hFile, PDWORD lpModemStat)
+{
+ WINPR_COMM* pComm = (WINPR_COMM*)hFile;
+
+ if (!CommInitialized())
+ return FALSE;
+
+ /* TODO: not implemented */
+
+ if (!pComm)
+ return FALSE;
+
+ CommLog_Print(WLOG_ERROR, "Not implemented");
+ SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
+ return FALSE;
+}
+
+/**
+ * ERRORS:
+ * ERROR_DLL_INIT_FAILED
+ * ERROR_INVALID_HANDLE
+ */
+BOOL GetCommProperties(HANDLE hFile, LPCOMMPROP lpCommProp)
+{
+ WINPR_COMM* pComm = (WINPR_COMM*)hFile;
+ DWORD bytesReturned = 0;
+
+ if (!CommIsHandleValid(hFile))
+ return FALSE;
+
+ if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_GET_PROPERTIES, NULL, 0, lpCommProp,
+ sizeof(COMMPROP), &bytesReturned, NULL))
+ {
+ CommLog_Print(WLOG_WARN, "GetCommProperties failure.");
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/**
+ *
+ *
+ * ERRORS:
+ * ERROR_INVALID_HANDLE
+ * ERROR_INVALID_DATA
+ * ERROR_IO_DEVICE
+ * ERROR_OUTOFMEMORY
+ */
+BOOL GetCommState(HANDLE hFile, LPDCB lpDCB)
+{
+ DCB* lpLocalDcb = NULL;
+ struct termios currentState;
+ WINPR_COMM* pComm = (WINPR_COMM*)hFile;
+ DWORD bytesReturned = 0;
+
+ if (!CommIsHandleValid(hFile))
+ return FALSE;
+
+ if (!lpDCB)
+ {
+ SetLastError(ERROR_INVALID_DATA);
+ return FALSE;
+ }
+
+ if (lpDCB->DCBlength < sizeof(DCB))
+ {
+ SetLastError(ERROR_INVALID_DATA);
+ return FALSE;
+ }
+
+ if (tcgetattr(pComm->fd, &currentState) < 0)
+ {
+ SetLastError(ERROR_IO_DEVICE);
+ return FALSE;
+ }
+
+ lpLocalDcb = (DCB*)calloc(1, lpDCB->DCBlength);
+
+ if (lpLocalDcb == NULL)
+ {
+ SetLastError(ERROR_OUTOFMEMORY);
+ return FALSE;
+ }
+
+ /* error_handle */
+ lpLocalDcb->DCBlength = lpDCB->DCBlength;
+ SERIAL_BAUD_RATE baudRate;
+
+ if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_GET_BAUD_RATE, NULL, 0, &baudRate,
+ sizeof(SERIAL_BAUD_RATE), &bytesReturned, NULL))
+ {
+ CommLog_Print(WLOG_WARN, "GetCommState failure: could not get the baud rate.");
+ goto error_handle;
+ }
+
+ lpLocalDcb->BaudRate = baudRate.BaudRate;
+ lpLocalDcb->fBinary = (currentState.c_cflag & ICANON) == 0;
+
+ if (!lpLocalDcb->fBinary)
+ {
+ CommLog_Print(WLOG_WARN, "Unexpected nonbinary mode, consider to unset the ICANON flag.");
+ }
+
+ lpLocalDcb->fParity = (currentState.c_iflag & INPCK) != 0;
+ SERIAL_HANDFLOW handflow;
+
+ if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_GET_HANDFLOW, NULL, 0, &handflow,
+ sizeof(SERIAL_HANDFLOW), &bytesReturned, NULL))
+ {
+ CommLog_Print(WLOG_WARN, "GetCommState failure: could not get the handflow settings.");
+ goto error_handle;
+ }
+
+ lpLocalDcb->fOutxCtsFlow = (handflow.ControlHandShake & SERIAL_CTS_HANDSHAKE) != 0;
+ lpLocalDcb->fOutxDsrFlow = (handflow.ControlHandShake & SERIAL_DSR_HANDSHAKE) != 0;
+
+ if (handflow.ControlHandShake & SERIAL_DTR_HANDSHAKE)
+ {
+ lpLocalDcb->fDtrControl = DTR_CONTROL_HANDSHAKE;
+ }
+ else if (handflow.ControlHandShake & SERIAL_DTR_CONTROL)
+ {
+ lpLocalDcb->fDtrControl = DTR_CONTROL_ENABLE;
+ }
+ else
+ {
+ lpLocalDcb->fDtrControl = DTR_CONTROL_DISABLE;
+ }
+
+ lpLocalDcb->fDsrSensitivity = (handflow.ControlHandShake & SERIAL_DSR_SENSITIVITY) != 0;
+ lpLocalDcb->fTXContinueOnXoff = (handflow.FlowReplace & SERIAL_XOFF_CONTINUE) != 0;
+ lpLocalDcb->fOutX = (handflow.FlowReplace & SERIAL_AUTO_TRANSMIT) != 0;
+ lpLocalDcb->fInX = (handflow.FlowReplace & SERIAL_AUTO_RECEIVE) != 0;
+ lpLocalDcb->fErrorChar = (handflow.FlowReplace & SERIAL_ERROR_CHAR) != 0;
+ lpLocalDcb->fNull = (handflow.FlowReplace & SERIAL_NULL_STRIPPING) != 0;
+
+ if (handflow.FlowReplace & SERIAL_RTS_HANDSHAKE)
+ {
+ lpLocalDcb->fRtsControl = RTS_CONTROL_HANDSHAKE;
+ }
+ else if (handflow.FlowReplace & SERIAL_RTS_CONTROL)
+ {
+ lpLocalDcb->fRtsControl = RTS_CONTROL_ENABLE;
+ }
+ else
+ {
+ lpLocalDcb->fRtsControl = RTS_CONTROL_DISABLE;
+ }
+
+ // FIXME: how to get the RTS_CONTROL_TOGGLE state? Does it match the UART 16750's Autoflow
+ // Control Enabled bit in its Modem Control Register (MCR)
+ lpLocalDcb->fAbortOnError = (handflow.ControlHandShake & SERIAL_ERROR_ABORT) != 0;
+ /* lpLocalDcb->fDummy2 not used */
+ lpLocalDcb->wReserved = 0; /* must be zero */
+ lpLocalDcb->XonLim = handflow.XonLimit;
+ lpLocalDcb->XoffLim = handflow.XoffLimit;
+ SERIAL_LINE_CONTROL lineControl;
+
+ if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_GET_LINE_CONTROL, NULL, 0, &lineControl,
+ sizeof(SERIAL_LINE_CONTROL), &bytesReturned, NULL))
+ {
+ CommLog_Print(WLOG_WARN, "GetCommState failure: could not get the control settings.");
+ goto error_handle;
+ }
+
+ lpLocalDcb->ByteSize = lineControl.WordLength;
+ lpLocalDcb->Parity = lineControl.Parity;
+ lpLocalDcb->StopBits = lineControl.StopBits;
+ SERIAL_CHARS serialChars;
+
+ if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_GET_CHARS, NULL, 0, &serialChars,
+ sizeof(SERIAL_CHARS), &bytesReturned, NULL))
+ {
+ CommLog_Print(WLOG_WARN, "GetCommState failure: could not get the serial chars.");
+ goto error_handle;
+ }
+
+ lpLocalDcb->XonChar = serialChars.XonChar;
+ lpLocalDcb->XoffChar = serialChars.XoffChar;
+ lpLocalDcb->ErrorChar = serialChars.ErrorChar;
+ lpLocalDcb->EofChar = serialChars.EofChar;
+ lpLocalDcb->EvtChar = serialChars.EventChar;
+ memcpy(lpDCB, lpLocalDcb, lpDCB->DCBlength);
+ free(lpLocalDcb);
+ return TRUE;
+error_handle:
+ free(lpLocalDcb);
+ return FALSE;
+}
+
+/**
+ * @return TRUE on success, FALSE otherwise.
+ *
+ * As of today, SetCommState() can fail half-way with some settings
+ * applied and some others not. SetCommState() returns on the first
+ * failure met. FIXME: or is it correct?
+ *
+ * ERRORS:
+ * ERROR_INVALID_HANDLE
+ * ERROR_IO_DEVICE
+ */
+BOOL SetCommState(HANDLE hFile, LPDCB lpDCB)
+{
+ struct termios upcomingTermios = { 0 };
+ WINPR_COMM* pComm = (WINPR_COMM*)hFile;
+ DWORD bytesReturned = 0;
+
+ /* FIXME: validate changes according GetCommProperties? */
+
+ if (!CommIsHandleValid(hFile))
+ return FALSE;
+
+ if (!lpDCB)
+ {
+ SetLastError(ERROR_INVALID_DATA);
+ return FALSE;
+ }
+
+ /* NB: did the choice to call ioctls first when available and
+ then to setup upcomingTermios. Don't mix both stages. */
+ /** ioctl calls stage **/
+ SERIAL_BAUD_RATE baudRate;
+ baudRate.BaudRate = lpDCB->BaudRate;
+
+ if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_SET_BAUD_RATE, &baudRate, sizeof(SERIAL_BAUD_RATE),
+ NULL, 0, &bytesReturned, NULL))
+ {
+ CommLog_Print(WLOG_WARN, "SetCommState failure: could not set the baud rate.");
+ return FALSE;
+ }
+
+ SERIAL_CHARS serialChars;
+
+ if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_GET_CHARS, NULL, 0, &serialChars,
+ sizeof(SERIAL_CHARS), &bytesReturned,
+ NULL)) /* as of today, required for BreakChar */
+ {
+ CommLog_Print(WLOG_WARN, "SetCommState failure: could not get the initial serial chars.");
+ return FALSE;
+ }
+
+ serialChars.XonChar = lpDCB->XonChar;
+ serialChars.XoffChar = lpDCB->XoffChar;
+ serialChars.ErrorChar = lpDCB->ErrorChar;
+ serialChars.EofChar = lpDCB->EofChar;
+ serialChars.EventChar = lpDCB->EvtChar;
+
+ if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_SET_CHARS, &serialChars, sizeof(SERIAL_CHARS),
+ NULL, 0, &bytesReturned, NULL))
+ {
+ CommLog_Print(WLOG_WARN, "SetCommState failure: could not set the serial chars.");
+ return FALSE;
+ }
+
+ SERIAL_LINE_CONTROL lineControl;
+ lineControl.StopBits = lpDCB->StopBits;
+ lineControl.Parity = lpDCB->Parity;
+ lineControl.WordLength = lpDCB->ByteSize;
+
+ if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_SET_LINE_CONTROL, &lineControl,
+ sizeof(SERIAL_LINE_CONTROL), NULL, 0, &bytesReturned, NULL))
+ {
+ CommLog_Print(WLOG_WARN, "SetCommState failure: could not set the control settings.");
+ return FALSE;
+ }
+
+ SERIAL_HANDFLOW handflow = { 0 };
+
+ if (lpDCB->fOutxCtsFlow)
+ {
+ handflow.ControlHandShake |= SERIAL_CTS_HANDSHAKE;
+ }
+
+ if (lpDCB->fOutxDsrFlow)
+ {
+ handflow.ControlHandShake |= SERIAL_DSR_HANDSHAKE;
+ }
+
+ switch (lpDCB->fDtrControl)
+ {
+ case SERIAL_DTR_HANDSHAKE:
+ handflow.ControlHandShake |= DTR_CONTROL_HANDSHAKE;
+ break;
+
+ case SERIAL_DTR_CONTROL:
+ handflow.ControlHandShake |= DTR_CONTROL_ENABLE;
+ break;
+
+ case DTR_CONTROL_DISABLE:
+ /* do nothing since handflow is init-zeroed */
+ break;
+
+ default:
+ CommLog_Print(WLOG_WARN, "Unexpected fDtrControl value: %" PRIu32 "\n",
+ lpDCB->fDtrControl);
+ return FALSE;
+ }
+
+ if (lpDCB->fDsrSensitivity)
+ {
+ handflow.ControlHandShake |= SERIAL_DSR_SENSITIVITY;
+ }
+
+ if (lpDCB->fTXContinueOnXoff)
+ {
+ handflow.FlowReplace |= SERIAL_XOFF_CONTINUE;
+ }
+
+ if (lpDCB->fOutX)
+ {
+ handflow.FlowReplace |= SERIAL_AUTO_TRANSMIT;
+ }
+
+ if (lpDCB->fInX)
+ {
+ handflow.FlowReplace |= SERIAL_AUTO_RECEIVE;
+ }
+
+ if (lpDCB->fErrorChar)
+ {
+ handflow.FlowReplace |= SERIAL_ERROR_CHAR;
+ }
+
+ if (lpDCB->fNull)
+ {
+ handflow.FlowReplace |= SERIAL_NULL_STRIPPING;
+ }
+
+ switch (lpDCB->fRtsControl)
+ {
+ case RTS_CONTROL_TOGGLE:
+ CommLog_Print(WLOG_WARN, "Unsupported RTS_CONTROL_TOGGLE feature");
+ // FIXME: see also GetCommState()
+ return FALSE;
+
+ case RTS_CONTROL_HANDSHAKE:
+ handflow.FlowReplace |= SERIAL_RTS_HANDSHAKE;
+ break;
+
+ case RTS_CONTROL_ENABLE:
+ handflow.FlowReplace |= SERIAL_RTS_CONTROL;
+ break;
+
+ case RTS_CONTROL_DISABLE:
+ /* do nothing since handflow is init-zeroed */
+ break;
+
+ default:
+ CommLog_Print(WLOG_WARN, "Unexpected fRtsControl value: %" PRIu32 "\n",
+ lpDCB->fRtsControl);
+ return FALSE;
+ }
+
+ if (lpDCB->fAbortOnError)
+ {
+ handflow.ControlHandShake |= SERIAL_ERROR_ABORT;
+ }
+
+ /* lpDCB->fDummy2 not used */
+ /* lpLocalDcb->wReserved ignored */
+ handflow.XonLimit = lpDCB->XonLim;
+ handflow.XoffLimit = lpDCB->XoffLim;
+
+ if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_SET_HANDFLOW, &handflow, sizeof(SERIAL_HANDFLOW),
+ NULL, 0, &bytesReturned, NULL))
+ {
+ CommLog_Print(WLOG_WARN, "SetCommState failure: could not set the handflow settings.");
+ return FALSE;
+ }
+
+ /** upcomingTermios stage **/
+
+ if (tcgetattr(pComm->fd, &upcomingTermios) <
+ 0) /* NB: preserves current settings not directly handled by the Communication Functions */
+ {
+ SetLastError(ERROR_IO_DEVICE);
+ return FALSE;
+ }
+
+ if (lpDCB->fBinary)
+ {
+ upcomingTermios.c_lflag &= ~ICANON;
+ }
+ else
+ {
+ upcomingTermios.c_lflag |= ICANON;
+ CommLog_Print(WLOG_WARN, "Unexpected nonbinary mode, consider to unset the ICANON flag.");
+ }
+
+ if (lpDCB->fParity)
+ {
+ upcomingTermios.c_iflag |= INPCK;
+ }
+ else
+ {
+ upcomingTermios.c_iflag &= ~INPCK;
+ }
+
+ /* http://msdn.microsoft.com/en-us/library/windows/desktop/aa363423%28v=vs.85%29.aspx
+ *
+ * The SetCommState function reconfigures the communications
+ * resource, but it does not affect the internal output and
+ * input buffers of the specified driver. The buffers are not
+ * flushed, and pending read and write operations are not
+ * terminated prematurely.
+ *
+ * TCSANOW matches the best this definition
+ */
+
+ if (_comm_ioctl_tcsetattr(pComm->fd, TCSANOW, &upcomingTermios) < 0)
+ {
+ SetLastError(ERROR_IO_DEVICE);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/**
+ * ERRORS:
+ * ERROR_INVALID_HANDLE
+ */
+BOOL GetCommTimeouts(HANDLE hFile, LPCOMMTIMEOUTS lpCommTimeouts)
+{
+ WINPR_COMM* pComm = (WINPR_COMM*)hFile;
+ DWORD bytesReturned = 0;
+
+ if (!CommIsHandleValid(hFile))
+ return FALSE;
+
+ /* as of today, SERIAL_TIMEOUTS and COMMTIMEOUTS structures are identical */
+
+ if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_GET_TIMEOUTS, NULL, 0, lpCommTimeouts,
+ sizeof(COMMTIMEOUTS), &bytesReturned, NULL))
+ {
+ CommLog_Print(WLOG_WARN, "GetCommTimeouts failure.");
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/**
+ * ERRORS:
+ * ERROR_INVALID_HANDLE
+ */
+BOOL SetCommTimeouts(HANDLE hFile, LPCOMMTIMEOUTS lpCommTimeouts)
+{
+ WINPR_COMM* pComm = (WINPR_COMM*)hFile;
+ DWORD bytesReturned = 0;
+
+ if (!CommIsHandleValid(hFile))
+ return FALSE;
+
+ /* as of today, SERIAL_TIMEOUTS and COMMTIMEOUTS structures are identical */
+
+ if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_SET_TIMEOUTS, lpCommTimeouts, sizeof(COMMTIMEOUTS),
+ NULL, 0, &bytesReturned, NULL))
+ {
+ CommLog_Print(WLOG_WARN, "SetCommTimeouts failure.");
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+BOOL GetDefaultCommConfigA(LPCSTR lpszName, LPCOMMCONFIG lpCC, LPDWORD lpdwSize)
+{
+ if (!CommInitialized())
+ return FALSE;
+
+ /* TODO: not implemented */
+ CommLog_Print(WLOG_ERROR, "Not implemented");
+ SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
+ return FALSE;
+}
+
+BOOL GetDefaultCommConfigW(LPCWSTR lpszName, LPCOMMCONFIG lpCC, LPDWORD lpdwSize)
+{
+ if (!CommInitialized())
+ return FALSE;
+
+ /* TODO: not implemented */
+ CommLog_Print(WLOG_ERROR, "Not implemented");
+ SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
+ return FALSE;
+}
+
+BOOL SetDefaultCommConfigA(LPCSTR lpszName, LPCOMMCONFIG lpCC, DWORD dwSize)
+{
+ if (!CommInitialized())
+ return FALSE;
+
+ /* TODO: not implemented */
+ CommLog_Print(WLOG_ERROR, "Not implemented");
+ SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
+ return FALSE;
+}
+
+BOOL SetDefaultCommConfigW(LPCWSTR lpszName, LPCOMMCONFIG lpCC, DWORD dwSize)
+{
+ if (!CommInitialized())
+ return FALSE;
+
+ /* TODO: not implemented */
+ CommLog_Print(WLOG_ERROR, "Not implemented");
+ SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
+ return FALSE;
+}
+
+BOOL SetCommBreak(HANDLE hFile)
+{
+ WINPR_COMM* pComm = (WINPR_COMM*)hFile;
+
+ if (!CommInitialized())
+ return FALSE;
+
+ /* TODO: not implemented */
+
+ if (!pComm)
+ return FALSE;
+
+ CommLog_Print(WLOG_ERROR, "Not implemented");
+ SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
+ return FALSE;
+}
+
+BOOL ClearCommBreak(HANDLE hFile)
+{
+ WINPR_COMM* pComm = (WINPR_COMM*)hFile;
+
+ if (!CommInitialized())
+ return FALSE;
+
+ /* TODO: not implemented */
+
+ if (!pComm)
+ return FALSE;
+
+ CommLog_Print(WLOG_ERROR, "Not implemented");
+ SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
+ return FALSE;
+}
+
+BOOL ClearCommError(HANDLE hFile, PDWORD lpErrors, LPCOMSTAT lpStat)
+{
+ WINPR_COMM* pComm = (WINPR_COMM*)hFile;
+
+ if (!CommInitialized())
+ return FALSE;
+
+ /* TODO: not implemented */
+
+ if (!pComm)
+ return FALSE;
+
+ CommLog_Print(WLOG_ERROR, "Not implemented");
+ SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
+ return FALSE;
+}
+
+BOOL PurgeComm(HANDLE hFile, DWORD dwFlags)
+{
+ WINPR_COMM* pComm = (WINPR_COMM*)hFile;
+ DWORD bytesReturned = 0;
+
+ if (!CommIsHandleValid(hFile))
+ return FALSE;
+
+ if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_PURGE, &dwFlags, sizeof(DWORD), NULL, 0,
+ &bytesReturned, NULL))
+ {
+ CommLog_Print(WLOG_WARN, "PurgeComm failure.");
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+BOOL SetupComm(HANDLE hFile, DWORD dwInQueue, DWORD dwOutQueue)
+{
+ WINPR_COMM* pComm = (WINPR_COMM*)hFile;
+ SERIAL_QUEUE_SIZE queueSize;
+ DWORD bytesReturned = 0;
+
+ if (!CommIsHandleValid(hFile))
+ return FALSE;
+
+ queueSize.InSize = dwInQueue;
+ queueSize.OutSize = dwOutQueue;
+
+ if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_SET_QUEUE_SIZE, &queueSize,
+ sizeof(SERIAL_QUEUE_SIZE), NULL, 0, &bytesReturned, NULL))
+ {
+ CommLog_Print(WLOG_WARN, "SetCommTimeouts failure.");
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+BOOL EscapeCommFunction(HANDLE hFile, DWORD dwFunc)
+{
+ WINPR_COMM* pComm = (WINPR_COMM*)hFile;
+
+ if (!CommInitialized())
+ return FALSE;
+
+ /* TODO: not implemented */
+
+ if (!pComm)
+ return FALSE;
+
+ CommLog_Print(WLOG_ERROR, "Not implemented");
+ SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
+ return FALSE;
+}
+
+BOOL TransmitCommChar(HANDLE hFile, char cChar)
+{
+ WINPR_COMM* pComm = (WINPR_COMM*)hFile;
+
+ if (!CommInitialized())
+ return FALSE;
+
+ /* TODO: not implemented */
+
+ if (!pComm)
+ return FALSE;
+
+ CommLog_Print(WLOG_ERROR, "Not implemented");
+ SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
+ return FALSE;
+}
+
+BOOL WaitCommEvent(HANDLE hFile, PDWORD lpEvtMask, LPOVERLAPPED lpOverlapped)
+{
+ WINPR_COMM* pComm = (WINPR_COMM*)hFile;
+
+ if (!CommInitialized())
+ return FALSE;
+
+ /* TODO: not implemented */
+
+ if (!pComm)
+ return FALSE;
+
+ CommLog_Print(WLOG_ERROR, "Not implemented");
+ SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
+ return FALSE;
+}
+
+/**
+ * Returns TRUE on success, FALSE otherwise. To get extended error
+ * information, call GetLastError.
+ *
+ * ERRORS:
+ * ERROR_DLL_INIT_FAILED
+ * ERROR_OUTOFMEMORY was not possible to get mappings.
+ * ERROR_INVALID_DATA was not possible to add the device.
+ */
+BOOL DefineCommDevice(/* DWORD dwFlags,*/ LPCTSTR lpDeviceName, LPCTSTR lpTargetPath)
+{
+ LPTSTR storedDeviceName = NULL;
+ LPTSTR storedTargetPath = NULL;
+
+ if (!CommInitialized())
+ return FALSE;
+
+ EnterCriticalSection(&_CommDevicesLock);
+
+ if (_CommDevices == NULL)
+ {
+ SetLastError(ERROR_DLL_INIT_FAILED);
+ goto error_handle;
+ }
+
+ storedDeviceName = _tcsdup(lpDeviceName);
+
+ if (storedDeviceName == NULL)
+ {
+ SetLastError(ERROR_OUTOFMEMORY);
+ goto error_handle;
+ }
+
+ storedTargetPath = _tcsdup(lpTargetPath);
+
+ if (storedTargetPath == NULL)
+ {
+ SetLastError(ERROR_OUTOFMEMORY);
+ goto error_handle;
+ }
+
+ int i = 0;
+ for (; i < COMM_DEVICE_MAX; i++)
+ {
+ if (_CommDevices[i] != NULL)
+ {
+ if (_tcscmp(_CommDevices[i]->name, storedDeviceName) == 0)
+ {
+ /* take over the emplacement */
+ free(_CommDevices[i]->name);
+ free(_CommDevices[i]->path);
+ _CommDevices[i]->name = storedDeviceName;
+ _CommDevices[i]->path = storedTargetPath;
+ break;
+ }
+ }
+ else
+ {
+ /* new emplacement */
+ _CommDevices[i] = (COMM_DEVICE*)calloc(1, sizeof(COMM_DEVICE));
+
+ if (_CommDevices[i] == NULL)
+ {
+ SetLastError(ERROR_OUTOFMEMORY);
+ goto error_handle;
+ }
+
+ _CommDevices[i]->name = storedDeviceName;
+ _CommDevices[i]->path = storedTargetPath;
+ break;
+ }
+ }
+
+ if (i == COMM_DEVICE_MAX)
+ {
+ SetLastError(ERROR_OUTOFMEMORY);
+ goto error_handle;
+ }
+
+ LeaveCriticalSection(&_CommDevicesLock);
+ return TRUE;
+error_handle:
+ free(storedDeviceName);
+ free(storedTargetPath);
+ LeaveCriticalSection(&_CommDevicesLock);
+ return FALSE;
+}
+
+/**
+ * Returns the number of target paths in the buffer pointed to by
+ * lpTargetPath.
+ *
+ * The current implementation returns in any case 0 and 1 target
+ * path. A NULL lpDeviceName is not supported yet to get all the
+ * paths.
+ *
+ * ERRORS:
+ * ERROR_SUCCESS
+ * ERROR_DLL_INIT_FAILED
+ * ERROR_OUTOFMEMORY was not possible to get mappings.
+ * ERROR_NOT_SUPPORTED equivalent QueryDosDevice feature not supported.
+ * ERROR_INVALID_DATA was not possible to retrieve any device information.
+ * ERROR_INSUFFICIENT_BUFFER too small lpTargetPath
+ */
+DWORD QueryCommDevice(LPCTSTR lpDeviceName, LPTSTR lpTargetPath, DWORD ucchMax)
+{
+ LPTSTR storedTargetPath = NULL;
+ SetLastError(ERROR_SUCCESS);
+
+ if (!CommInitialized())
+ return 0;
+
+ if (_CommDevices == NULL)
+ {
+ SetLastError(ERROR_DLL_INIT_FAILED);
+ return 0;
+ }
+
+ if (lpDeviceName == NULL || lpTargetPath == NULL)
+ {
+ SetLastError(ERROR_NOT_SUPPORTED);
+ return 0;
+ }
+
+ EnterCriticalSection(&_CommDevicesLock);
+ storedTargetPath = NULL;
+
+ for (int i = 0; i < COMM_DEVICE_MAX; i++)
+ {
+ if (_CommDevices[i] != NULL)
+ {
+ if (_tcscmp(_CommDevices[i]->name, lpDeviceName) == 0)
+ {
+ storedTargetPath = _CommDevices[i]->path;
+ break;
+ }
+
+ continue;
+ }
+
+ break;
+ }
+
+ LeaveCriticalSection(&_CommDevicesLock);
+
+ if (storedTargetPath == NULL)
+ {
+ SetLastError(ERROR_INVALID_DATA);
+ return 0;
+ }
+
+ if (_tcslen(storedTargetPath) + 2 > ucchMax)
+ {
+ SetLastError(ERROR_INSUFFICIENT_BUFFER);
+ return 0;
+ }
+
+ _tcscpy(lpTargetPath, storedTargetPath);
+ lpTargetPath[_tcslen(storedTargetPath) + 1] = '\0'; /* 2nd final '\0' */
+ return _tcslen(lpTargetPath) + 2;
+}
+
+/**
+ * Checks whether lpDeviceName is a valid and registered Communication device.
+ */
+BOOL IsCommDevice(LPCTSTR lpDeviceName)
+{
+ TCHAR lpTargetPath[MAX_PATH];
+
+ if (!CommInitialized())
+ return FALSE;
+
+ if (QueryCommDevice(lpDeviceName, lpTargetPath, MAX_PATH) > 0)
+ {
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/**
+ * Sets
+ */
+void _comm_setServerSerialDriver(HANDLE hComm, SERIAL_DRIVER_ID driverId)
+{
+ ULONG Type = 0;
+ WINPR_HANDLE* Object = NULL;
+ WINPR_COMM* pComm = NULL;
+
+ if (!CommInitialized())
+ return;
+
+ if (!winpr_Handle_GetInfo(hComm, &Type, &Object))
+ {
+ CommLog_Print(WLOG_WARN, "_comm_setServerSerialDriver failure");
+ return;
+ }
+
+ pComm = (WINPR_COMM*)Object;
+ pComm->serverSerialDriverId = driverId;
+}
+
+static HANDLE_OPS ops = { CommIsHandled, CommCloseHandle,
+ CommGetFd, NULL, /* CleanupHandle */
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL };
+
+/**
+ * http://msdn.microsoft.com/en-us/library/windows/desktop/aa363198%28v=vs.85%29.aspx
+ *
+ * @param lpDeviceName e.g. COM1, ...
+ *
+ * @param dwDesiredAccess expects GENERIC_READ | GENERIC_WRITE, a
+ * warning message is printed otherwise. TODO: better support.
+ *
+ * @param dwShareMode must be zero, INVALID_HANDLE_VALUE is returned
+ * otherwise and GetLastError() should return ERROR_SHARING_VIOLATION.
+ *
+ * @param lpSecurityAttributes NULL expected, a warning message is printed
+ * otherwise. TODO: better support.
+ *
+ * @param dwCreationDisposition must be OPEN_EXISTING. If the
+ * communication device doesn't exist INVALID_HANDLE_VALUE is returned
+ * and GetLastError() returns ERROR_FILE_NOT_FOUND.
+ *
+ * @param dwFlagsAndAttributes zero expected, a warning message is
+ * printed otherwise.
+ *
+ * @param hTemplateFile must be NULL.
+ *
+ * @return INVALID_HANDLE_VALUE on error.
+ */
+HANDLE CommCreateFileA(LPCSTR lpDeviceName, DWORD dwDesiredAccess, DWORD dwShareMode,
+ LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition,
+ DWORD dwFlagsAndAttributes, HANDLE hTemplateFile)
+{
+ CHAR devicePath[MAX_PATH];
+ struct stat deviceStat;
+ WINPR_COMM* pComm = NULL;
+ struct termios upcomingTermios;
+
+ if (!CommInitialized())
+ return INVALID_HANDLE_VALUE;
+
+ if (dwDesiredAccess != (GENERIC_READ | GENERIC_WRITE))
+ {
+ CommLog_Print(WLOG_WARN, "unexpected access to the device: 0x%08" PRIX32 "",
+ dwDesiredAccess);
+ }
+
+ if (dwShareMode != 0)
+ {
+ SetLastError(ERROR_SHARING_VIOLATION);
+ return INVALID_HANDLE_VALUE;
+ }
+
+ /* TODO: Prevents other processes from opening a file or
+ * device if they request delete, read, or write access. */
+
+ if (lpSecurityAttributes != NULL)
+ {
+ CommLog_Print(WLOG_WARN, "unexpected security attributes, nLength=%" PRIu32 "",
+ lpSecurityAttributes->nLength);
+ }
+
+ if (dwCreationDisposition != OPEN_EXISTING)
+ {
+ SetLastError(ERROR_FILE_NOT_FOUND); /* FIXME: ERROR_NOT_SUPPORTED better? */
+ return INVALID_HANDLE_VALUE;
+ }
+
+ if (QueryCommDevice(lpDeviceName, devicePath, MAX_PATH) <= 0)
+ {
+ /* SetLastError(GetLastError()); */
+ return INVALID_HANDLE_VALUE;
+ }
+
+ if (stat(devicePath, &deviceStat) < 0)
+ {
+ CommLog_Print(WLOG_WARN, "device not found %s", devicePath);
+ SetLastError(ERROR_FILE_NOT_FOUND);
+ return INVALID_HANDLE_VALUE;
+ }
+
+ if (!S_ISCHR(deviceStat.st_mode))
+ {
+ CommLog_Print(WLOG_WARN, "bad device %s", devicePath);
+ SetLastError(ERROR_BAD_DEVICE);
+ return INVALID_HANDLE_VALUE;
+ }
+
+ if (dwFlagsAndAttributes != 0)
+ {
+ CommLog_Print(WLOG_WARN, "unexpected flags and attributes: 0x%08" PRIX32 "",
+ dwFlagsAndAttributes);
+ }
+
+ if (hTemplateFile != NULL)
+ {
+ SetLastError(ERROR_NOT_SUPPORTED); /* FIXME: other proper error? */
+ return INVALID_HANDLE_VALUE;
+ }
+
+ pComm = (WINPR_COMM*)calloc(1, sizeof(WINPR_COMM));
+
+ if (pComm == NULL)
+ {
+ SetLastError(ERROR_OUTOFMEMORY);
+ return INVALID_HANDLE_VALUE;
+ }
+
+ WINPR_HANDLE_SET_TYPE_AND_MODE(pComm, HANDLE_TYPE_COMM, WINPR_FD_READ);
+ pComm->common.ops = &ops;
+ /* error_handle */
+ pComm->fd = open(devicePath, O_RDWR | O_NOCTTY | O_NONBLOCK);
+
+ if (pComm->fd < 0)
+ {
+ CommLog_Print(WLOG_WARN, "failed to open device %s", devicePath);
+ SetLastError(ERROR_BAD_DEVICE);
+ goto error_handle;
+ }
+
+ pComm->fd_read = open(devicePath, O_RDONLY | O_NOCTTY | O_NONBLOCK);
+
+ if (pComm->fd_read < 0)
+ {
+ CommLog_Print(WLOG_WARN, "failed to open fd_read, device: %s", devicePath);
+ SetLastError(ERROR_BAD_DEVICE);
+ goto error_handle;
+ }
+
+ pComm->fd_read_event = eventfd(
+ 0, EFD_NONBLOCK); /* EFD_NONBLOCK required because a read() is not always expected */
+
+ if (pComm->fd_read_event < 0)
+ {
+ CommLog_Print(WLOG_WARN, "failed to open fd_read_event, device: %s", devicePath);
+ SetLastError(ERROR_BAD_DEVICE);
+ goto error_handle;
+ }
+
+ InitializeCriticalSection(&pComm->ReadLock);
+ pComm->fd_write = open(devicePath, O_WRONLY | O_NOCTTY | O_NONBLOCK);
+
+ if (pComm->fd_write < 0)
+ {
+ CommLog_Print(WLOG_WARN, "failed to open fd_write, device: %s", devicePath);
+ SetLastError(ERROR_BAD_DEVICE);
+ goto error_handle;
+ }
+
+ pComm->fd_write_event = eventfd(
+ 0, EFD_NONBLOCK); /* EFD_NONBLOCK required because a read() is not always expected */
+
+ if (pComm->fd_write_event < 0)
+ {
+ CommLog_Print(WLOG_WARN, "failed to open fd_write_event, device: %s", devicePath);
+ SetLastError(ERROR_BAD_DEVICE);
+ goto error_handle;
+ }
+
+ InitializeCriticalSection(&pComm->WriteLock);
+ /* can also be setup later on with _comm_setServerSerialDriver() */
+ pComm->serverSerialDriverId = SerialDriverUnknown;
+ InitializeCriticalSection(&pComm->EventsLock);
+
+ if (ioctl(pComm->fd, TIOCGICOUNT, &(pComm->counters)) < 0)
+ {
+ char ebuffer[256] = { 0 };
+ CommLog_Print(WLOG_WARN, "TIOCGICOUNT ioctl failed, errno=[%d] %s.", errno,
+ winpr_strerror(errno, ebuffer, sizeof(ebuffer)));
+ CommLog_Print(WLOG_WARN, "could not read counters.");
+ /* could not initialize counters but keep on.
+ *
+ * Not all drivers, especially for USB to serial
+ * adapters (e.g. those based on pl2303), does support
+ * this call.
+ */
+ ZeroMemory(&(pComm->counters), sizeof(struct serial_icounter_struct));
+ }
+
+ /* The binary/raw mode is required for the redirection but
+ * only flags that are not handle somewhere-else, except
+ * ICANON, are forced here. */
+ ZeroMemory(&upcomingTermios, sizeof(struct termios));
+
+ if (tcgetattr(pComm->fd, &upcomingTermios) < 0)
+ {
+ SetLastError(ERROR_IO_DEVICE);
+ goto error_handle;
+ }
+
+ upcomingTermios.c_iflag &=
+ ~(/*IGNBRK |*/ BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL /*| IXON*/);
+ upcomingTermios.c_oflag = 0; /* <=> &= ~OPOST */
+ upcomingTermios.c_lflag = 0; /* <=> &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN); */
+ /* upcomingTermios.c_cflag &= ~(CSIZE | PARENB); */
+ /* upcomingTermios.c_cflag |= CS8; */
+ /* About missing flags recommended by termios(3):
+ *
+ * IGNBRK and IXON, see: IOCTL_SERIAL_SET_HANDFLOW
+ * CSIZE, PARENB and CS8, see: IOCTL_SERIAL_SET_LINE_CONTROL
+ */
+ /* a few more settings required for the redirection */
+ upcomingTermios.c_cflag |= CLOCAL | CREAD;
+
+ if (_comm_ioctl_tcsetattr(pComm->fd, TCSANOW, &upcomingTermios) < 0)
+ {
+ SetLastError(ERROR_IO_DEVICE);
+ goto error_handle;
+ }
+
+ return (HANDLE)pComm;
+error_handle:
+ CloseHandle(pComm);
+ return INVALID_HANDLE_VALUE;
+}
+
+BOOL CommIsHandled(HANDLE handle)
+{
+ if (!CommInitialized())
+ return FALSE;
+
+ return WINPR_HANDLE_IS_HANDLED(handle, HANDLE_TYPE_COMM, TRUE);
+}
+
+BOOL CommIsHandleValid(HANDLE handle)
+{
+ WINPR_COMM* pComm = (WINPR_COMM*)handle;
+ if (!CommIsHandled(handle))
+ return FALSE;
+ if (pComm->fd <= 0)
+ {
+ SetLastError(ERROR_INVALID_HANDLE);
+ return FALSE;
+ }
+ return TRUE;
+}
+
+BOOL CommCloseHandle(HANDLE handle)
+{
+ WINPR_COMM* pComm = (WINPR_COMM*)handle;
+
+ if (!CommIsHandled(handle))
+ return FALSE;
+
+ if (pComm->PendingEvents & SERIAL_EV_WINPR_WAITING)
+ {
+ ULONG WaitMask = 0;
+ DWORD BytesReturned = 0;
+
+ /* ensures to gracefully stop the WAIT_ON_MASK's loop */
+ if (!CommDeviceIoControl(handle, IOCTL_SERIAL_SET_WAIT_MASK, &WaitMask, sizeof(ULONG), NULL,
+ 0, &BytesReturned, NULL))
+ {
+ CommLog_Print(WLOG_WARN, "failure to WAIT_ON_MASK's loop!");
+ }
+ }
+
+ DeleteCriticalSection(&pComm->ReadLock);
+ DeleteCriticalSection(&pComm->WriteLock);
+ DeleteCriticalSection(&pComm->EventsLock);
+
+ if (pComm->fd > 0)
+ close(pComm->fd);
+
+ if (pComm->fd_write > 0)
+ close(pComm->fd_write);
+
+ if (pComm->fd_write_event > 0)
+ close(pComm->fd_write_event);
+
+ if (pComm->fd_read > 0)
+ close(pComm->fd_read);
+
+ if (pComm->fd_read_event > 0)
+ close(pComm->fd_read_event);
+
+ free(pComm);
+ return TRUE;
+}
+
+#ifndef WITH_EVENTFD_READ_WRITE
+int eventfd_read(int fd, eventfd_t* value)
+{
+ return (read(fd, value, sizeof(*value)) == sizeof(*value)) ? 0 : -1;
+}
+
+int eventfd_write(int fd, eventfd_t value)
+{
+ return (write(fd, &value, sizeof(value)) == sizeof(value)) ? 0 : -1;
+}
+#endif
+
+#endif /* __linux__ */