1921 lines
47 KiB
C
1921 lines
47 KiB
C
/**
|
|
* 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>
|
|
|
|
#include <winpr/assert.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <pthread.h>
|
|
#include <stdarg.h>
|
|
#if defined(WINPR_HAVE_SYS_EVENTFD_H)
|
|
#include <sys/eventfd.h>
|
|
#endif
|
|
#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"
|
|
|
|
#include "../log.h"
|
|
#define TAG WINPR_TAG("comm")
|
|
|
|
/**
|
|
* Communication Resources:
|
|
* http://msdn.microsoft.com/en-us/library/windows/desktop/aa363196/
|
|
*/
|
|
|
|
#include "comm.h"
|
|
|
|
static wLog* sLog = nullptr;
|
|
|
|
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 nullptr-terminated array with a maximum of COMM_DEVICE_MAX COMM_DEVICE */
|
|
#define COMM_DEVICE_MAX 128
|
|
static COMM_DEVICE** sCommDevices = nullptr;
|
|
static CRITICAL_SECTION sCommDevicesLock = WINPR_C_ARRAY_INIT;
|
|
|
|
static pthread_once_t sCommInitialized = PTHREAD_ONCE_INIT;
|
|
|
|
static const _SERIAL_IOCTL_NAME S_SERIAL_IOCTL_NAMES[] = {
|
|
{ IOCTL_SERIAL_SET_BAUD_RATE, "IOCTL_SERIAL_SET_BAUD_RATE" },
|
|
{ IOCTL_SERIAL_GET_BAUD_RATE, "IOCTL_SERIAL_GET_BAUD_RATE" },
|
|
{ IOCTL_SERIAL_SET_LINE_CONTROL, "IOCTL_SERIAL_SET_LINE_CONTROL" },
|
|
{ IOCTL_SERIAL_GET_LINE_CONTROL, "IOCTL_SERIAL_GET_LINE_CONTROL" },
|
|
{ IOCTL_SERIAL_SET_TIMEOUTS, "IOCTL_SERIAL_SET_TIMEOUTS" },
|
|
{ IOCTL_SERIAL_GET_TIMEOUTS, "IOCTL_SERIAL_GET_TIMEOUTS" },
|
|
{ IOCTL_SERIAL_GET_CHARS, "IOCTL_SERIAL_GET_CHARS" },
|
|
{ IOCTL_SERIAL_SET_CHARS, "IOCTL_SERIAL_SET_CHARS" },
|
|
{ IOCTL_SERIAL_SET_DTR, "IOCTL_SERIAL_SET_DTR" },
|
|
{ IOCTL_SERIAL_CLR_DTR, "IOCTL_SERIAL_CLR_DTR" },
|
|
{ IOCTL_SERIAL_RESET_DEVICE, "IOCTL_SERIAL_RESET_DEVICE" },
|
|
{ IOCTL_SERIAL_SET_RTS, "IOCTL_SERIAL_SET_RTS" },
|
|
{ IOCTL_SERIAL_CLR_RTS, "IOCTL_SERIAL_CLR_RTS" },
|
|
{ IOCTL_SERIAL_SET_XOFF, "IOCTL_SERIAL_SET_XOFF" },
|
|
{ IOCTL_SERIAL_SET_XON, "IOCTL_SERIAL_SET_XON" },
|
|
{ IOCTL_SERIAL_SET_BREAK_ON, "IOCTL_SERIAL_SET_BREAK_ON" },
|
|
{ IOCTL_SERIAL_SET_BREAK_OFF, "IOCTL_SERIAL_SET_BREAK_OFF" },
|
|
{ IOCTL_SERIAL_SET_QUEUE_SIZE, "IOCTL_SERIAL_SET_QUEUE_SIZE" },
|
|
{ IOCTL_SERIAL_GET_WAIT_MASK, "IOCTL_SERIAL_GET_WAIT_MASK" },
|
|
{ IOCTL_SERIAL_SET_WAIT_MASK, "IOCTL_SERIAL_SET_WAIT_MASK" },
|
|
{ IOCTL_SERIAL_WAIT_ON_MASK, "IOCTL_SERIAL_WAIT_ON_MASK" },
|
|
{ IOCTL_SERIAL_IMMEDIATE_CHAR, "IOCTL_SERIAL_IMMEDIATE_CHAR" },
|
|
{ IOCTL_SERIAL_PURGE, "IOCTL_SERIAL_PURGE" },
|
|
{ IOCTL_SERIAL_GET_HANDFLOW, "IOCTL_SERIAL_GET_HANDFLOW" },
|
|
{ IOCTL_SERIAL_SET_HANDFLOW, "IOCTL_SERIAL_SET_HANDFLOW" },
|
|
{ IOCTL_SERIAL_GET_MODEMSTATUS, "IOCTL_SERIAL_GET_MODEMSTATUS" },
|
|
{ IOCTL_SERIAL_GET_DTRRTS, "IOCTL_SERIAL_GET_DTRRTS" },
|
|
{ IOCTL_SERIAL_GET_COMMSTATUS, "IOCTL_SERIAL_GET_COMMSTATUS" },
|
|
{ IOCTL_SERIAL_GET_PROPERTIES, "IOCTL_SERIAL_GET_PROPERTIES" },
|
|
// {IOCTL_SERIAL_XOFF_COUNTER, "IOCTL_SERIAL_XOFF_COUNTER"},
|
|
// {IOCTL_SERIAL_LSRMST_INSERT, "IOCTL_SERIAL_LSRMST_INSERT"},
|
|
{ IOCTL_SERIAL_CONFIG_SIZE, "IOCTL_SERIAL_CONFIG_SIZE" },
|
|
// {IOCTL_SERIAL_GET_STATS, "IOCTL_SERIAL_GET_STATS"},
|
|
// {IOCTL_SERIAL_CLEAR_STATS, "IOCTL_SERIAL_CLEAR_STATS"},
|
|
// {IOCTL_SERIAL_GET_MODEM_CONTROL,"IOCTL_SERIAL_GET_MODEM_CONTROL"},
|
|
// {IOCTL_SERIAL_SET_MODEM_CONTROL,"IOCTL_SERIAL_SET_MODEM_CONTROL"},
|
|
// {IOCTL_SERIAL_SET_FIFO_CONTROL, "IOCTL_SERIAL_SET_FIFO_CONTROL"},
|
|
|
|
// {IOCTL_PAR_QUERY_INFORMATION, "IOCTL_PAR_QUERY_INFORMATION"},
|
|
// {IOCTL_PAR_SET_INFORMATION, "IOCTL_PAR_SET_INFORMATION"},
|
|
// {IOCTL_PAR_QUERY_DEVICE_ID, "IOCTL_PAR_QUERY_DEVICE_ID"},
|
|
// {IOCTL_PAR_QUERY_DEVICE_ID_SIZE,"IOCTL_PAR_QUERY_DEVICE_ID_SIZE"},
|
|
// {IOCTL_IEEE1284_GET_MODE, "IOCTL_IEEE1284_GET_MODE"},
|
|
// {IOCTL_IEEE1284_NEGOTIATE, "IOCTL_IEEE1284_NEGOTIATE"},
|
|
// {IOCTL_PAR_SET_WRITE_ADDRESS, "IOCTL_PAR_SET_WRITE_ADDRESS"},
|
|
// {IOCTL_PAR_SET_READ_ADDRESS, "IOCTL_PAR_SET_READ_ADDRESS"},
|
|
// {IOCTL_PAR_GET_DEVICE_CAPS, "IOCTL_PAR_GET_DEVICE_CAPS"},
|
|
// {IOCTL_PAR_GET_DEFAULT_MODES, "IOCTL_PAR_GET_DEFAULT_MODES"},
|
|
// {IOCTL_PAR_QUERY_RAW_DEVICE_ID, "IOCTL_PAR_QUERY_RAW_DEVICE_ID"},
|
|
// {IOCTL_PAR_IS_PORT_FREE, "IOCTL_PAR_IS_PORT_FREE"},
|
|
|
|
{ IOCTL_USBPRINT_GET_1284_ID, "IOCTL_USBPRINT_GET_1284_ID" }
|
|
};
|
|
|
|
const char* _comm_serial_ioctl_name(ULONG number)
|
|
{
|
|
for (size_t x = 0; x < ARRAYSIZE(S_SERIAL_IOCTL_NAMES); x++)
|
|
{
|
|
const _SERIAL_IOCTL_NAME* cur = &S_SERIAL_IOCTL_NAMES[x];
|
|
if (cur->number == number)
|
|
return cur->name;
|
|
}
|
|
|
|
return "(unknown ioctl name)";
|
|
}
|
|
|
|
static int CommGetFd(HANDLE handle)
|
|
{
|
|
WINPR_COMM* comm = (WINPR_COMM*)handle;
|
|
|
|
if (!CommIsHandled(handle))
|
|
return -1;
|
|
|
|
return comm->fd;
|
|
}
|
|
|
|
const HANDLE_CREATOR* GetCommHandleCreator(void)
|
|
{
|
|
#if defined(WINPR_HAVE_SERIAL_SUPPORT)
|
|
static const HANDLE_CREATOR sCommHandleCreator = { .IsHandled = IsCommDevice,
|
|
.CreateFileA = CommCreateFileA };
|
|
return &sCommHandleCreator;
|
|
#else
|
|
return nullptr;
|
|
#endif
|
|
}
|
|
|
|
static void CommInit(void)
|
|
{
|
|
/* NB: error management to be done outside of this function */
|
|
WINPR_ASSERT(sLog == nullptr);
|
|
WINPR_ASSERT(sCommDevices == nullptr);
|
|
sCommDevices = (COMM_DEVICE**)calloc(COMM_DEVICE_MAX + 1, sizeof(COMM_DEVICE*));
|
|
|
|
if (!sCommDevices)
|
|
return;
|
|
|
|
if (!InitializeCriticalSectionEx(&sCommDevicesLock, 0, 0))
|
|
{
|
|
free((void*)sCommDevices);
|
|
sCommDevices = nullptr;
|
|
return;
|
|
}
|
|
|
|
sLog = WLog_Get(TAG);
|
|
WINPR_ASSERT(sLog != nullptr);
|
|
}
|
|
|
|
/**
|
|
* Returns TRUE when the comm module is correctly initialized, FALSE otherwise
|
|
* with ERROR_DLL_INIT_FAILED set as the last error.
|
|
*/
|
|
static BOOL CommInitialized(void)
|
|
{
|
|
if (pthread_once(&sCommInitialized, CommInit) != 0)
|
|
{
|
|
SetLastError(ERROR_DLL_INIT_FAILED);
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
WINPR_ATTR_FORMAT_ARG(5, 6)
|
|
void CommLog_PrintEx(DWORD level, const char* file, size_t line, const char* fkt,
|
|
WINPR_FORMAT_ARG const char* fmt, ...)
|
|
{
|
|
if (!CommInitialized())
|
|
return;
|
|
|
|
if (!WLog_IsLevelActive(sLog, level))
|
|
return;
|
|
va_list ap = WINPR_C_ARRAY_INIT;
|
|
va_start(ap, fmt);
|
|
WLog_PrintTextMessageVA(sLog, level, line, file, fkt, fmt, ap);
|
|
va_end(ap);
|
|
}
|
|
|
|
BOOL BuildCommDCBA(WINPR_ATTR_UNUSED LPCSTR lpDef, WINPR_ATTR_UNUSED LPDCB lpDCB)
|
|
{
|
|
if (!CommInitialized())
|
|
return FALSE;
|
|
|
|
/* TODO: not implemented */
|
|
CommLog_Print(WLOG_ERROR, "Not implemented");
|
|
SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL BuildCommDCBW(WINPR_ATTR_UNUSED LPCWSTR lpDef, WINPR_ATTR_UNUSED LPDCB lpDCB)
|
|
{
|
|
if (!CommInitialized())
|
|
return FALSE;
|
|
|
|
/* TODO: not implemented */
|
|
CommLog_Print(WLOG_ERROR, "Not implemented");
|
|
SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL BuildCommDCBAndTimeoutsA(WINPR_ATTR_UNUSED LPCSTR lpDef, WINPR_ATTR_UNUSED LPDCB lpDCB,
|
|
WINPR_ATTR_UNUSED LPCOMMTIMEOUTS lpCommTimeouts)
|
|
{
|
|
if (!CommInitialized())
|
|
return FALSE;
|
|
|
|
/* TODO: not implemented */
|
|
CommLog_Print(WLOG_ERROR, "Not implemented");
|
|
SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL BuildCommDCBAndTimeoutsW(WINPR_ATTR_UNUSED LPCWSTR lpDef, WINPR_ATTR_UNUSED LPDCB lpDCB,
|
|
WINPR_ATTR_UNUSED LPCOMMTIMEOUTS lpCommTimeouts)
|
|
{
|
|
if (!CommInitialized())
|
|
return FALSE;
|
|
|
|
/* TODO: not implemented */
|
|
CommLog_Print(WLOG_ERROR, "Not implemented");
|
|
SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL CommConfigDialogA(WINPR_ATTR_UNUSED LPCSTR lpszName, WINPR_ATTR_UNUSED HWND hWnd,
|
|
WINPR_ATTR_UNUSED LPCOMMCONFIG lpCC)
|
|
{
|
|
if (!CommInitialized())
|
|
return FALSE;
|
|
|
|
/* TODO: not implemented */
|
|
CommLog_Print(WLOG_ERROR, "Not implemented");
|
|
SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL CommConfigDialogW(WINPR_ATTR_UNUSED LPCWSTR lpszName, WINPR_ATTR_UNUSED HWND hWnd,
|
|
WINPR_ATTR_UNUSED 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, WINPR_ATTR_UNUSED LPCOMMCONFIG lpCC,
|
|
WINPR_ATTR_UNUSED 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, WINPR_ATTR_UNUSED LPCOMMCONFIG lpCC,
|
|
WINPR_ATTR_UNUSED 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, WINPR_ATTR_UNUSED 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, WINPR_ATTR_UNUSED 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, WINPR_ATTR_UNUSED 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, nullptr, 0, lpCommProp,
|
|
sizeof(COMMPROP), &bytesReturned, nullptr))
|
|
{
|
|
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 = nullptr;
|
|
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, ¤tState) < 0)
|
|
{
|
|
SetLastError(ERROR_IO_DEVICE);
|
|
return FALSE;
|
|
}
|
|
|
|
lpLocalDcb = (DCB*)calloc(1, lpDCB->DCBlength);
|
|
|
|
if (lpLocalDcb == nullptr)
|
|
{
|
|
SetLastError(ERROR_OUTOFMEMORY);
|
|
return FALSE;
|
|
}
|
|
|
|
/* error_handle */
|
|
lpLocalDcb->DCBlength = lpDCB->DCBlength;
|
|
SERIAL_BAUD_RATE baudRate;
|
|
|
|
if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_GET_BAUD_RATE, nullptr, 0, &baudRate,
|
|
sizeof(SERIAL_BAUD_RATE), &bytesReturned, nullptr))
|
|
{
|
|
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, nullptr, 0, &handflow,
|
|
sizeof(SERIAL_HANDFLOW), &bytesReturned, nullptr))
|
|
{
|
|
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 = WINPR_ASSERTING_INT_CAST(WORD, handflow.XonLimit);
|
|
lpLocalDcb->XoffLim = WINPR_ASSERTING_INT_CAST(WORD, handflow.XoffLimit);
|
|
|
|
{
|
|
SERIAL_LINE_CONTROL lineControl = WINPR_C_ARRAY_INIT;
|
|
|
|
if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_GET_LINE_CONTROL, nullptr, 0, &lineControl,
|
|
sizeof(SERIAL_LINE_CONTROL), &bytesReturned, nullptr))
|
|
{
|
|
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 = WINPR_C_ARRAY_INIT;
|
|
|
|
if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_GET_CHARS, nullptr, 0, &serialChars,
|
|
sizeof(SERIAL_CHARS), &bytesReturned, nullptr))
|
|
{
|
|
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 = WINPR_C_ARRAY_INIT;
|
|
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),
|
|
nullptr, 0, &bytesReturned, nullptr))
|
|
{
|
|
CommLog_Print(WLOG_WARN, "SetCommState failure: could not set the baud rate.");
|
|
return FALSE;
|
|
}
|
|
|
|
SERIAL_CHARS serialChars;
|
|
|
|
if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_GET_CHARS, nullptr, 0, &serialChars,
|
|
sizeof(SERIAL_CHARS), &bytesReturned,
|
|
nullptr)) /* 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),
|
|
nullptr, 0, &bytesReturned, nullptr))
|
|
{
|
|
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), nullptr, 0, &bytesReturned, nullptr))
|
|
{
|
|
CommLog_Print(WLOG_WARN, "SetCommState failure: could not set the control settings.");
|
|
return FALSE;
|
|
}
|
|
|
|
SERIAL_HANDFLOW handflow = WINPR_C_ARRAY_INIT;
|
|
|
|
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: %" PRId32 "\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: %" PRId32 "\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),
|
|
nullptr, 0, &bytesReturned, nullptr))
|
|
{
|
|
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 &= (tcflag_t)~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 &= (tcflag_t)~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, nullptr, 0, lpCommTimeouts,
|
|
sizeof(COMMTIMEOUTS), &bytesReturned, nullptr))
|
|
{
|
|
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),
|
|
nullptr, 0, &bytesReturned, nullptr))
|
|
{
|
|
CommLog_Print(WLOG_WARN, "SetCommTimeouts failure.");
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL GetDefaultCommConfigA(WINPR_ATTR_UNUSED LPCSTR lpszName, WINPR_ATTR_UNUSED LPCOMMCONFIG lpCC,
|
|
WINPR_ATTR_UNUSED LPDWORD lpdwSize)
|
|
{
|
|
if (!CommInitialized())
|
|
return FALSE;
|
|
|
|
/* TODO: not implemented */
|
|
CommLog_Print(WLOG_ERROR, "Not implemented");
|
|
SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL GetDefaultCommConfigW(WINPR_ATTR_UNUSED LPCWSTR lpszName, WINPR_ATTR_UNUSED LPCOMMCONFIG lpCC,
|
|
WINPR_ATTR_UNUSED LPDWORD lpdwSize)
|
|
{
|
|
if (!CommInitialized())
|
|
return FALSE;
|
|
|
|
/* TODO: not implemented */
|
|
CommLog_Print(WLOG_ERROR, "Not implemented");
|
|
SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL SetDefaultCommConfigA(WINPR_ATTR_UNUSED LPCSTR lpszName, WINPR_ATTR_UNUSED LPCOMMCONFIG lpCC,
|
|
WINPR_ATTR_UNUSED DWORD dwSize)
|
|
{
|
|
if (!CommInitialized())
|
|
return FALSE;
|
|
|
|
/* TODO: not implemented */
|
|
CommLog_Print(WLOG_ERROR, "Not implemented");
|
|
SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL SetDefaultCommConfigW(WINPR_ATTR_UNUSED LPCWSTR lpszName, WINPR_ATTR_UNUSED LPCOMMCONFIG lpCC,
|
|
WINPR_ATTR_UNUSED 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, WINPR_ATTR_UNUSED PDWORD lpErrors,
|
|
WINPR_ATTR_UNUSED 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), nullptr, 0,
|
|
&bytesReturned, nullptr))
|
|
{
|
|
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), nullptr, 0, &bytesReturned, nullptr))
|
|
{
|
|
CommLog_Print(WLOG_WARN, "SetCommTimeouts failure.");
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL EscapeCommFunction(HANDLE hFile, WINPR_ATTR_UNUSED 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, WINPR_ATTR_UNUSED 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, WINPR_ATTR_UNUSED PDWORD lpEvtMask,
|
|
WINPR_ATTR_UNUSED 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 = nullptr;
|
|
LPTSTR storedTargetPath = nullptr;
|
|
|
|
if (!CommInitialized())
|
|
return FALSE;
|
|
|
|
EnterCriticalSection(&sCommDevicesLock);
|
|
|
|
if (sCommDevices == nullptr)
|
|
{
|
|
SetLastError(ERROR_DLL_INIT_FAILED);
|
|
goto error_handle;
|
|
}
|
|
|
|
storedDeviceName = _tcsdup(lpDeviceName);
|
|
|
|
if (storedDeviceName == nullptr)
|
|
{
|
|
SetLastError(ERROR_OUTOFMEMORY);
|
|
goto error_handle;
|
|
}
|
|
|
|
storedTargetPath = _tcsdup(lpTargetPath);
|
|
|
|
if (storedTargetPath == nullptr)
|
|
{
|
|
SetLastError(ERROR_OUTOFMEMORY);
|
|
goto error_handle;
|
|
}
|
|
|
|
{
|
|
int i = 0;
|
|
for (; i < COMM_DEVICE_MAX; i++)
|
|
{
|
|
if (sCommDevices[i] != nullptr)
|
|
{
|
|
if (_tcscmp(sCommDevices[i]->name, storedDeviceName) == 0)
|
|
{
|
|
/* take over the emplacement */
|
|
free(sCommDevices[i]->name);
|
|
free(sCommDevices[i]->path);
|
|
sCommDevices[i]->name = storedDeviceName;
|
|
sCommDevices[i]->path = storedTargetPath;
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* new emplacement */
|
|
sCommDevices[i] = (COMM_DEVICE*)calloc(1, sizeof(COMM_DEVICE));
|
|
|
|
if (sCommDevices[i] == nullptr)
|
|
{
|
|
SetLastError(ERROR_OUTOFMEMORY);
|
|
goto error_handle;
|
|
}
|
|
|
|
sCommDevices[i]->name = storedDeviceName;
|
|
sCommDevices[i]->path = storedTargetPath;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (i == COMM_DEVICE_MAX)
|
|
{
|
|
SetLastError(ERROR_OUTOFMEMORY);
|
|
goto error_handle;
|
|
}
|
|
}
|
|
|
|
LeaveCriticalSection(&sCommDevicesLock);
|
|
return TRUE;
|
|
error_handle:
|
|
free(storedDeviceName);
|
|
free(storedTargetPath);
|
|
LeaveCriticalSection(&sCommDevicesLock);
|
|
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 nullptr 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 = nullptr;
|
|
SetLastError(ERROR_SUCCESS);
|
|
|
|
if (!CommInitialized())
|
|
return 0;
|
|
|
|
if (sCommDevices == nullptr)
|
|
{
|
|
SetLastError(ERROR_DLL_INIT_FAILED);
|
|
return 0;
|
|
}
|
|
|
|
if (lpDeviceName == nullptr || lpTargetPath == nullptr)
|
|
{
|
|
SetLastError(ERROR_NOT_SUPPORTED);
|
|
return 0;
|
|
}
|
|
|
|
EnterCriticalSection(&sCommDevicesLock);
|
|
storedTargetPath = nullptr;
|
|
|
|
for (int i = 0; i < COMM_DEVICE_MAX; i++)
|
|
{
|
|
if (sCommDevices[i] != nullptr)
|
|
{
|
|
if (_tcscmp(sCommDevices[i]->name, lpDeviceName) == 0)
|
|
{
|
|
storedTargetPath = sCommDevices[i]->path;
|
|
break;
|
|
}
|
|
|
|
continue;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
LeaveCriticalSection(&sCommDevicesLock);
|
|
|
|
if (storedTargetPath == nullptr)
|
|
{
|
|
SetLastError(ERROR_INVALID_DATA);
|
|
return 0;
|
|
}
|
|
|
|
const size_t size = _tcsnlen(storedTargetPath, ucchMax);
|
|
if (size + 2 > ucchMax)
|
|
{
|
|
SetLastError(ERROR_INSUFFICIENT_BUFFER);
|
|
return 0;
|
|
}
|
|
|
|
_tcsncpy(lpTargetPath, storedTargetPath, size + 1);
|
|
lpTargetPath[size + 2] = '\0'; /* 2nd final '\0' */
|
|
return (DWORD)size + 2UL;
|
|
}
|
|
|
|
/**
|
|
* 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 = nullptr;
|
|
WINPR_COMM* pComm = nullptr;
|
|
|
|
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, nullptr, /* CleanupHandle */
|
|
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
|
|
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
|
|
nullptr, nullptr, nullptr, nullptr, nullptr };
|
|
|
|
/**
|
|
* 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 nullptr 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 nullptr.
|
|
*
|
|
* @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] = WINPR_C_ARRAY_INIT;
|
|
struct stat deviceStat = WINPR_C_ARRAY_INIT;
|
|
WINPR_COMM* pComm = nullptr;
|
|
struct termios upcomingTermios = WINPR_C_ARRAY_INIT;
|
|
|
|
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 != nullptr)
|
|
{
|
|
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 != nullptr)
|
|
{
|
|
SetLastError(ERROR_NOT_SUPPORTED); /* FIXME: other proper error? */
|
|
return INVALID_HANDLE_VALUE;
|
|
}
|
|
|
|
pComm = (WINPR_COMM*)calloc(1, sizeof(WINPR_COMM));
|
|
|
|
if (pComm == nullptr)
|
|
{
|
|
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;
|
|
}
|
|
|
|
#if defined(WINPR_HAVE_SYS_EVENTFD_H)
|
|
pComm->fd_read_event = eventfd(
|
|
0, EFD_NONBLOCK); /* EFD_NONBLOCK required because a read() is not always expected */
|
|
#endif
|
|
|
|
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;
|
|
}
|
|
|
|
#if defined(WINPR_HAVE_SYS_EVENTFD_H)
|
|
pComm->fd_write_event = eventfd(
|
|
0, EFD_NONBLOCK); /* EFD_NONBLOCK required because a read() is not always expected */
|
|
#endif
|
|
|
|
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);
|
|
|
|
(void)CommUpdateIOCount(pComm, TRUE);
|
|
|
|
/* 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 &=
|
|
(tcflag_t) ~(/*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:
|
|
WINPR_PRAGMA_DIAG_PUSH
|
|
WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC(void) CloseHandle(pComm);
|
|
WINPR_PRAGMA_DIAG_POP
|
|
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;
|
|
|
|
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;
|
|
}
|
|
|
|
#if defined(WINPR_HAVE_SYS_EVENTFD_H)
|
|
#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
|
|
|
|
static const char* CommIoCtlToStr(unsigned long int io)
|
|
{
|
|
switch (io)
|
|
{
|
|
#if defined(WINPR_HAVE_SERIAL_SUPPORT)
|
|
#if defined(TCGETS)
|
|
case TCGETS:
|
|
return "TCGETS";
|
|
#endif
|
|
#if defined(TCSETS)
|
|
case TCSETS:
|
|
return "TCSETS";
|
|
#endif
|
|
#if defined(TCSETSW)
|
|
case TCSETSW:
|
|
return "TCSETSW";
|
|
#endif
|
|
#if defined(TCSETSF)
|
|
case TCSETSF:
|
|
return "TCSETSF";
|
|
#endif
|
|
#if defined(TCGETA)
|
|
case TCGETA:
|
|
return "TCGETA";
|
|
#endif
|
|
#if defined(TCSETA)
|
|
case TCSETA:
|
|
return "TCSETA";
|
|
#endif
|
|
#if defined(TCSETAW)
|
|
case TCSETAW:
|
|
return "TCSETAW";
|
|
#endif
|
|
#if defined(TCSETAF)
|
|
case TCSETAF:
|
|
return "TCSETAF";
|
|
#endif
|
|
#if defined(TCSBRK)
|
|
case TCSBRK:
|
|
return "TCSBRK";
|
|
#endif
|
|
#if defined(TCXONC)
|
|
case TCXONC:
|
|
return "TCXONC";
|
|
#endif
|
|
#if defined(TCFLSH)
|
|
case TCFLSH:
|
|
return "TCFLSH";
|
|
#endif
|
|
#if defined(TIOCEXCL)
|
|
case TIOCEXCL:
|
|
return "TIOCEXCL";
|
|
#endif
|
|
#if defined(TIOCNXCL)
|
|
case TIOCNXCL:
|
|
return "TIOCNXCL";
|
|
#endif
|
|
#if defined(TIOCSCTTY)
|
|
case TIOCSCTTY:
|
|
return "TIOCSCTTY";
|
|
#endif
|
|
#if defined(TIOCGPGRP)
|
|
case TIOCGPGRP:
|
|
return "TIOCGPGRP";
|
|
#endif
|
|
#if defined(TIOCSPGRP)
|
|
case TIOCSPGRP:
|
|
return "TIOCSPGRP";
|
|
#endif
|
|
#if defined(TIOCOUTQ)
|
|
case TIOCOUTQ:
|
|
return "TIOCOUTQ";
|
|
#endif
|
|
#if defined(TIOCSTI)
|
|
case TIOCSTI:
|
|
return "TIOCSTI";
|
|
#endif
|
|
#if defined(TIOCGWINSZ)
|
|
case TIOCGWINSZ:
|
|
return "TIOCGWINSZ";
|
|
#endif
|
|
#if defined(TIOCSWINSZ)
|
|
case TIOCSWINSZ:
|
|
return "TIOCSWINSZ";
|
|
#endif
|
|
#if defined(TIOCMGET)
|
|
case TIOCMGET:
|
|
return "TIOCMGET";
|
|
#endif
|
|
#if defined(TIOCMBIS)
|
|
case TIOCMBIS:
|
|
return "TIOCMBIS";
|
|
#endif
|
|
#if defined(TIOCMBIC)
|
|
case TIOCMBIC:
|
|
return "TIOCMBIC";
|
|
#endif
|
|
#if defined(TIOCMSET)
|
|
case TIOCMSET:
|
|
return "TIOCMSET";
|
|
#endif
|
|
#if defined(TIOCGSOFTCAR)
|
|
case TIOCGSOFTCAR:
|
|
return "TIOCGSOFTCAR";
|
|
#endif
|
|
#if defined(TIOCSSOFTCAR)
|
|
case TIOCSSOFTCAR:
|
|
return "TIOCSSOFTCAR";
|
|
#endif
|
|
#if defined(FIONREAD)
|
|
case FIONREAD:
|
|
return "FIONREAD/TIOCINQ";
|
|
#endif
|
|
#if defined(TIOCLINUX)
|
|
case TIOCLINUX:
|
|
return "TIOCLINUX";
|
|
#endif
|
|
#if defined(TIOCCONS)
|
|
case TIOCCONS:
|
|
return "TIOCCONS";
|
|
#endif
|
|
#if defined(TIOCGSERIAL)
|
|
case TIOCGSERIAL:
|
|
return "TIOCGSERIAL";
|
|
#endif
|
|
#if defined(TIOCSSERIAL)
|
|
case TIOCSSERIAL:
|
|
return "TIOCSSERIAL";
|
|
#endif
|
|
#if defined(TIOCPKT)
|
|
case TIOCPKT:
|
|
return "TIOCPKT";
|
|
#endif
|
|
#if defined(FIONBIO)
|
|
case FIONBIO:
|
|
return "FIONBIO";
|
|
#endif
|
|
#if defined(TIOCNOTTY)
|
|
case TIOCNOTTY:
|
|
return "TIOCNOTTY";
|
|
#endif
|
|
#if defined(TIOCSETD)
|
|
case TIOCSETD:
|
|
return "TIOCSETD";
|
|
#endif
|
|
#if defined(TIOCGETD)
|
|
case TIOCGETD:
|
|
return "TIOCGETD";
|
|
#endif
|
|
#if defined(TCSBRKP)
|
|
case TCSBRKP:
|
|
return "TCSBRKP";
|
|
#endif
|
|
#if defined(TIOCSBRK)
|
|
case TIOCSBRK:
|
|
return "TIOCSBRK";
|
|
#endif
|
|
#if defined(TIOCCBRK)
|
|
case TIOCCBRK:
|
|
return "TIOCCBRK";
|
|
#endif
|
|
#if defined(TIOCGSID)
|
|
case TIOCGSID:
|
|
return "TIOCGSID";
|
|
#endif
|
|
#if defined(TIOCGRS485)
|
|
case TIOCGRS485:
|
|
return "TIOCGRS485";
|
|
#endif
|
|
#if defined(TIOCSRS485)
|
|
case TIOCSRS485:
|
|
return "TIOCSRS485";
|
|
#endif
|
|
#if defined(TIOCSPTLCK)
|
|
case TIOCSPTLCK:
|
|
return "TIOCSPTLCK";
|
|
#endif
|
|
#if defined(TCGETX)
|
|
case TCGETX:
|
|
return "TCGETX";
|
|
#endif
|
|
#if defined(TCSETX)
|
|
case TCSETX:
|
|
return "TCSETX";
|
|
#endif
|
|
#if defined(TCSETXF)
|
|
case TCSETXF:
|
|
return "TCSETXF";
|
|
#endif
|
|
#if defined(TCSETXW)
|
|
case TCSETXW:
|
|
return "TCSETXW";
|
|
#endif
|
|
#if defined(TIOCSIG)
|
|
case TIOCSIG:
|
|
return "TIOCSIG";
|
|
#endif
|
|
#if defined(TIOCVHANGUP)
|
|
case TIOCVHANGUP:
|
|
return "TIOCVHANGUP";
|
|
#endif
|
|
#if defined(TIOCGPTPEER)
|
|
case TIOCGPTPEER:
|
|
return "TIOCGPTPEER";
|
|
#endif
|
|
#if defined(FIONCLEX)
|
|
case FIONCLEX:
|
|
return "FIONCLEX";
|
|
#endif
|
|
#if defined(FIOCLEX)
|
|
case FIOCLEX:
|
|
return "FIOCLEX";
|
|
#endif
|
|
#if defined(FIOASYNC)
|
|
case FIOASYNC:
|
|
return "FIOASYNC";
|
|
#endif
|
|
#if defined(TIOCSERCONFIG)
|
|
case TIOCSERCONFIG:
|
|
return "TIOCSERCONFIG";
|
|
#endif
|
|
#if defined(TIOCSERGWILD)
|
|
case TIOCSERGWILD:
|
|
return "TIOCSERGWILD";
|
|
#endif
|
|
#if defined(TIOCSERSWILD)
|
|
case TIOCSERSWILD:
|
|
return "TIOCSERSWILD";
|
|
#endif
|
|
#if defined(TIOCGLCKTRMIOS)
|
|
case TIOCGLCKTRMIOS:
|
|
return "TIOCGLCKTRMIOS";
|
|
#endif
|
|
#if defined(TIOCSLCKTRMIOS)
|
|
case TIOCSLCKTRMIOS:
|
|
return "TIOCSLCKTRMIOS";
|
|
#endif
|
|
#if defined(TIOCSERGSTRUCT)
|
|
case TIOCSERGSTRUCT:
|
|
return "TIOCSERGSTRUCT";
|
|
#endif
|
|
#if defined(TIOCSERGETLSR)
|
|
case TIOCSERGETLSR:
|
|
return "TIOCSERGETLSR";
|
|
#endif
|
|
#if defined(TIOCSERGETMULTI)
|
|
case TIOCSERGETMULTI:
|
|
return "TIOCSERGETMULTI";
|
|
#endif
|
|
#if defined(TIOCSERSETMULTI)
|
|
case TIOCSERSETMULTI:
|
|
return "TIOCSERSETMULTI";
|
|
#endif
|
|
#if defined(TIOCMIWAIT)
|
|
case TIOCMIWAIT:
|
|
return "TIOCMIWAIT";
|
|
#endif
|
|
#if defined(TIOCGICOUNT)
|
|
case TIOCGICOUNT:
|
|
return "TIOCGICOUNT";
|
|
#endif
|
|
#if defined(FIOQSIZE)
|
|
case FIOQSIZE:
|
|
return "FIOQSIZE";
|
|
#endif
|
|
#if defined(TIOCPKT_DATA)
|
|
case TIOCPKT_DATA:
|
|
return "TIOCPKT_DATA";
|
|
#endif
|
|
#if defined(TIOCPKT_FLUSHWRITE)
|
|
case TIOCPKT_FLUSHWRITE:
|
|
return "TIOCPKT_FLUSHWRITE";
|
|
#endif
|
|
#if defined(TIOCPKT_STOP)
|
|
case TIOCPKT_STOP:
|
|
return "TIOCPKT_STOP";
|
|
#endif
|
|
#if defined(TIOCPKT_START)
|
|
case TIOCPKT_START:
|
|
return "TIOCPKT_START";
|
|
#endif
|
|
#if defined(TIOCPKT_NOSTOP)
|
|
case TIOCPKT_NOSTOP:
|
|
return "TIOCPKT_NOSTOP";
|
|
#endif
|
|
#if defined(TIOCPKT_DOSTOP)
|
|
case TIOCPKT_DOSTOP:
|
|
return "TIOCPKT_DOSTOP";
|
|
#endif
|
|
#if defined(TIOCPKT_IOCTL)
|
|
case TIOCPKT_IOCTL:
|
|
return "TIOCPKT_IOCTL";
|
|
#endif
|
|
#endif
|
|
default:
|
|
return "UNKNOWN";
|
|
}
|
|
}
|
|
|
|
static BOOL CommStatusErrorEx(WINPR_COMM* pComm, unsigned long int ctl, const char* file,
|
|
const char* fkt, size_t line)
|
|
{
|
|
WINPR_ASSERT(pComm);
|
|
BOOL rc = (pComm->permissive);
|
|
const DWORD level = rc ? WLOG_DEBUG : WLOG_WARN;
|
|
char ebuffer[256] = WINPR_C_ARRAY_INIT;
|
|
const char* str = CommIoCtlToStr(ctl);
|
|
|
|
if (CommInitialized())
|
|
{
|
|
if (WLog_IsLevelActive(sLog, level))
|
|
{
|
|
WLog_PrintTextMessage(sLog, level, line, file, fkt,
|
|
"%s [0x%08lx] ioctl failed, errno=[%d] %s.", str, ctl, errno,
|
|
winpr_strerror(errno, ebuffer, sizeof(ebuffer)));
|
|
}
|
|
}
|
|
|
|
if (!rc)
|
|
SetLastError(ERROR_IO_DEVICE);
|
|
|
|
return rc;
|
|
}
|
|
|
|
BOOL CommIoCtl_int(WINPR_COMM* pComm, unsigned long int ctl, void* data, const char* file,
|
|
const char* fkt, size_t line)
|
|
{
|
|
if (ioctl(pComm->fd, ctl, data) < 0)
|
|
{
|
|
if (!CommStatusErrorEx(pComm, ctl, file, fkt, line))
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL CommUpdateIOCount(WINPR_ATTR_UNUSED HANDLE handle, WINPR_ATTR_UNUSED BOOL checkSupportStatus)
|
|
{
|
|
WINPR_COMM* pComm = (WINPR_COMM*)handle;
|
|
WINPR_ASSERT(pComm);
|
|
|
|
#if defined(WINPR_HAVE_COMM_COUNTERS)
|
|
ZeroMemory(&(pComm->counters), sizeof(struct serial_icounter_struct));
|
|
if (pComm->TIOCGICOUNTSupported || checkSupportStatus)
|
|
{
|
|
const int rc = ioctl(pComm->fd, TIOCGICOUNT, &(pComm->counters));
|
|
if (checkSupportStatus)
|
|
pComm->TIOCGICOUNTSupported = rc >= 0;
|
|
else if (rc < 0)
|
|
{
|
|
if (!CommStatusErrorEx(pComm, TIOCGICOUNT, __FILE__, __func__, __LINE__))
|
|
return FALSE;
|
|
}
|
|
}
|
|
#endif
|
|
return TRUE;
|
|
}
|
|
|
|
static const char* CommSerialEvFlagString(ULONG flag)
|
|
{
|
|
switch (flag)
|
|
{
|
|
case SERIAL_EV_RXCHAR:
|
|
return "SERIAL_EV_RXCHAR";
|
|
case SERIAL_EV_RXFLAG:
|
|
return "SERIAL_EV_RXFLAG";
|
|
case SERIAL_EV_TXEMPTY:
|
|
return "SERIAL_EV_TXEMPTY";
|
|
case SERIAL_EV_CTS:
|
|
return "SERIAL_EV_CTS ";
|
|
case SERIAL_EV_DSR:
|
|
return "SERIAL_EV_DSR ";
|
|
case SERIAL_EV_RLSD:
|
|
return "SERIAL_EV_RLSD";
|
|
case SERIAL_EV_BREAK:
|
|
return "SERIAL_EV_BREAK";
|
|
case SERIAL_EV_ERR:
|
|
return "SERIAL_EV_ERR ";
|
|
case SERIAL_EV_RING:
|
|
return "SERIAL_EV_RING";
|
|
case SERIAL_EV_PERR:
|
|
return "SERIAL_EV_PERR";
|
|
case SERIAL_EV_RX80FULL:
|
|
return "SERIAL_EV_RX80FULL";
|
|
case SERIAL_EV_EVENT1:
|
|
return "SERIAL_EV_EVENT1";
|
|
case SERIAL_EV_EVENT2:
|
|
return "SERIAL_EV_EVENT2";
|
|
case SERIAL_EV_WINPR_WAITING:
|
|
return "SERIAL_EV_WINPR_WAITING";
|
|
case SERIAL_EV_WINPR_STOP:
|
|
return "SERIAL_EV_WINPR_STOP";
|
|
default:
|
|
return "SERIAL_EV_UNKNOWN";
|
|
}
|
|
}
|
|
|
|
const char* CommSerialEvString(ULONG status, char* buffer, size_t size)
|
|
{
|
|
const ULONG flags[] = { SERIAL_EV_RXCHAR, SERIAL_EV_RXFLAG, SERIAL_EV_TXEMPTY,
|
|
SERIAL_EV_CTS, SERIAL_EV_DSR, SERIAL_EV_RLSD,
|
|
SERIAL_EV_BREAK, SERIAL_EV_ERR, SERIAL_EV_RING,
|
|
SERIAL_EV_PERR, SERIAL_EV_RX80FULL, SERIAL_EV_EVENT1,
|
|
SERIAL_EV_EVENT2, SERIAL_EV_WINPR_WAITING, SERIAL_EV_WINPR_STOP };
|
|
|
|
winpr_str_append("{", buffer, size, "");
|
|
|
|
const char* sep = "";
|
|
for (size_t x = 0; x < ARRAYSIZE(flags); x++)
|
|
{
|
|
const ULONG flag = flags[x];
|
|
if (status & flag)
|
|
{
|
|
winpr_str_append(CommSerialEvFlagString(flag), buffer, size, sep);
|
|
sep = "|";
|
|
}
|
|
}
|
|
|
|
char number[32] = WINPR_C_ARRAY_INIT;
|
|
(void)_snprintf(number, sizeof(number), "}[0x%08" PRIx32 "]", status);
|
|
winpr_str_append(number, buffer, size, "");
|
|
return buffer;
|
|
}
|