From 4243928c2e348789fd0024df49751206c6362d8a Mon Sep 17 00:00:00 2001 From: Emmanuel Ledoux Date: Wed, 14 May 2014 16:29:10 +0200 Subject: [PATCH] winpr-comm: fixed _set_lines() / _clear_lines() winpr-comm: got IOCTL_SERIAL_SET_WAIT_MASK / IOCTL_SERIAL_GET_WAIT_MASK and a non-blocking version of IOCTL_SERIAL_WAIT_ON_MASK --- channels/serial/client/serial_main.c | 14 +- winpr/include/winpr/comm.h | 123 ++++++++++ winpr/libwinpr/comm/comm.c | 12 + winpr/libwinpr/comm/comm.h | 6 + winpr/libwinpr/comm/comm_ioctl.c | 83 ++++++- winpr/libwinpr/comm/comm_ioctl.h | 70 ++---- winpr/libwinpr/comm/comm_sercx2_sys.c | 48 ++++ winpr/libwinpr/comm/comm_sercx_sys.c | 44 ++++ winpr/libwinpr/comm/comm_serial_sys.c | 328 ++++++++++++++++++++++++-- 9 files changed, 645 insertions(+), 83 deletions(-) diff --git a/channels/serial/client/serial_main.c b/channels/serial/client/serial_main.c index 98e84d5a4..1b366c9d1 100644 --- a/channels/serial/client/serial_main.c +++ b/channels/serial/client/serial_main.c @@ -364,7 +364,7 @@ static void serial_process_irp_device_control(SERIAL_DEVICE* serial, IRP* irp) Stream_Read(irp->input, InputBuffer, InputBufferLength); - DEBUG_SVC("CommDeviceIoControl: IoControlCode 0x%x", IoControlCode); + DEBUG_SVC("CommDeviceIoControl: IoControlCode=[0x%x] %s", IoControlCode, _comm_serial_ioctl_name(IoControlCode)); /* FIXME: CommDeviceIoControl to be replaced by DeviceIoControl() */ if (CommDeviceIoControl(serial->hComm, IoControlCode, InputBuffer, InputBufferLength, OutputBuffer, OutputBufferLength, &BytesReturned, NULL)) @@ -373,7 +373,10 @@ static void serial_process_irp_device_control(SERIAL_DEVICE* serial, IRP* irp) } else { - DEBUG_SVC("CommDeviceIoControl failure: IoControlCode 0x%0.8x last-error: 0x%x", IoControlCode, GetLastError()); + DEBUG_SVC("CommDeviceIoControl failure: IoControlCode=[0x%0.8x] %s, last-error: 0x%x", + IoControlCode, _comm_serial_ioctl_name(IoControlCode), GetLastError()); + + // TMP: TODO: Status code to be reviewed according: http://msdn.microsoft.com/en-us/library/ff547466%28v=vs.85%29.aspx#generic_status_values_for_serial_device_control_requests switch(GetLastError()) { @@ -397,9 +400,14 @@ static void serial_process_irp_device_control(SERIAL_DEVICE* serial, IRP* irp) irp->IoStatus = STATUS_NOT_IMPLEMENTED; break; + case ERROR_IO_PENDING: + irp->IoStatus = STATUS_PENDING; + break; + default: DEBUG_SVC("unexpected last-error: 0x%x", GetLastError()); - irp->IoStatus = STATUS_UNSUCCESSFUL; + //irp->IoStatus = STATUS_UNSUCCESSFUL; + irp->IoStatus = STATUS_CANCELLED; break; } } diff --git a/winpr/include/winpr/comm.h b/winpr/include/winpr/comm.h index b373fe6f2..ed79b4c0f 100644 --- a/winpr/include/winpr/comm.h +++ b/winpr/include/winpr/comm.h @@ -401,6 +401,129 @@ WINPR_API HANDLE CommCreateFileA(LPCSTR lpFileName, DWORD dwDesiredAccess, DWORD DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, HANDLE hTemplateFile); + +#define IOCTL_SERIAL_SET_BAUD_RATE 0x001B0004 +#define IOCTL_SERIAL_GET_BAUD_RATE 0x001B0050 +#define IOCTL_SERIAL_SET_LINE_CONTROL 0x001B000C +#define IOCTL_SERIAL_GET_LINE_CONTROL 0x001B0054 +#define IOCTL_SERIAL_SET_TIMEOUTS 0x001B001C +#define IOCTL_SERIAL_GET_TIMEOUTS 0x001B0020 +/* GET_CHARS and SET_CHARS are swapped in the RDP docs [MS-RDPESP] */ +#define IOCTL_SERIAL_GET_CHARS 0x001B0058 +#define IOCTL_SERIAL_SET_CHARS 0x001B005C + +#define IOCTL_SERIAL_SET_DTR 0x001B0024 +#define IOCTL_SERIAL_CLR_DTR 0x001B0028 +/* IOCTL_SERIAL_RESET_DEVICE 0x001B002C */ +#define IOCTL_SERIAL_SET_RTS 0x001B0030 +#define IOCTL_SERIAL_CLR_RTS 0x001B0034 +/* IOCTL_SERIAL_SET_XOFF 0x001B0038 */ +/* IOCTL_SERIAL_SET_XON 0x001B003C */ +/* IOCTL_SERIAL_SET_BREAK_ON 0x001B0010 */ +/* IOCTL_SERIAL_SET_BREAK_OFF 0x001B0014 */ +/* IOCTL_SERIAL_SET_QUEUE_SIZE 0x001B0008 */ +#define IOCTL_SERIAL_GET_WAIT_MASK 0x001B0040 +#define IOCTL_SERIAL_SET_WAIT_MASK 0x001B0044 +#define IOCTL_SERIAL_WAIT_ON_MASK 0x001B0048 +/* IOCTL_SERIAL_IMMEDIATE_CHAR 0x001B0018 */ +/* IOCTL_SERIAL_PURGE 0x001B004C */ +#define IOCTL_SERIAL_GET_HANDFLOW 0x001B0060 +#define IOCTL_SERIAL_SET_HANDFLOW 0x001B0064 +#define IOCTL_SERIAL_GET_MODEMSTATUS 0x001B0068 +/* IOCTL_SERIAL_GET_DTRRTS 0x001B0078 */ +/* IOCTL_SERIAL_GET_COMMSTATUS 0x001B0084 */ +#define IOCTL_SERIAL_GET_PROPERTIES 0x001B0074 +/* IOCTL_SERIAL_XOFF_COUNTER 0x001B0070 */ +/* IOCTL_SERIAL_LSRMST_INSERT 0x001B007C */ +/* IOCTL_SERIAL_CONFIG_SIZE 0x001B0080 */ +/* IOCTL_SERIAL_GET_STATS 0x001B008C */ +/* IOCTL_SERIAL_CLEAR_STATS 0x001B0090 */ +/* IOCTL_SERIAL_GET_MODEM_CONTROL 0x001B0094 */ +/* IOCTL_SERIAL_SET_MODEM_CONTROL 0x001B0098 */ +/* IOCTL_SERIAL_SET_FIFO_CONTROL 0x001B009C */ + +/* IOCTL_PAR_QUERY_INFORMATION 0x00160004 */ +/* IOCTL_PAR_SET_INFORMATION 0x00160008 */ +/* IOCTL_PAR_QUERY_DEVICE_ID 0x0016000C */ +/* IOCTL_PAR_QUERY_DEVICE_ID_SIZE 0x00160010 */ +/* IOCTL_IEEE1284_GET_MODE 0x00160014 */ +/* IOCTL_IEEE1284_NEGOTIATE 0x00160018 */ +/* IOCTL_PAR_SET_WRITE_ADDRESS 0x0016001C */ +/* IOCTL_PAR_SET_READ_ADDRESS 0x00160020 */ +/* IOCTL_PAR_GET_DEVICE_CAPS 0x00160024 */ +/* IOCTL_PAR_GET_DEFAULT_MODES 0x00160028 */ +/* IOCTL_PAR_QUERY_RAW_DEVICE_ID 0x00160030 */ +/* IOCTL_PAR_IS_PORT_FREE 0x00160054 */ + + +typedef struct __SERIAL_IOCTL_NAME +{ + ULONG number; + const char* name; +} _SERIAL_IOCTL_NAME; + +static const _SERIAL_IOCTL_NAME _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"}, + + {0, NULL} +}; + +/** + * FIXME: got a proper function name and place + */ +const char* _comm_serial_ioctl_name(ULONG number); + + /** * FIXME: to be moved in comm_ioctl.h */ diff --git a/winpr/libwinpr/comm/comm.c b/winpr/libwinpr/comm/comm.c index 8feb72464..f594fb377 100644 --- a/winpr/libwinpr/comm/comm.c +++ b/winpr/libwinpr/comm/comm.c @@ -27,6 +27,7 @@ #ifndef _WIN32 #include +#include #include #include #include @@ -1099,6 +1100,17 @@ HANDLE CommCreateFileA(LPCSTR lpDeviceName, DWORD dwDesiredAccess, DWORD dwShare */ pComm->remoteSerialDriverId = RemoteSerialDriverUnknown; + if (ioctl(pComm->fd, TIOCGICOUNT, &(pComm->counters)) < 0) + { + DEBUG_WARN("TIOCGICOUNT ioctl failed, errno=[%d] %s", errno, strerror(errno)); + SetLastError(ERROR_IO_DEVICE); + goto error_handle; + } + + + + + /* The binary/raw mode is required for the redirection but * only flags that are not handle somewhere-else, except diff --git a/winpr/libwinpr/comm/comm.h b/winpr/libwinpr/comm/comm.h index 116f85dc4..272eaa5bc 100644 --- a/winpr/libwinpr/comm/comm.h +++ b/winpr/libwinpr/comm/comm.h @@ -23,6 +23,8 @@ #ifndef _WIN32 +#include + #include #include "../handle/handle.h" @@ -46,6 +48,10 @@ struct winpr_comm int fd; REMOTE_SERIAL_DRIVER_ID remoteSerialDriverId; COMMTIMEOUTS timeouts; + + struct serial_icounter_struct counters; + ULONG waitMask; + ULONG pendingEvents; /* NB: CloseHandle() has to free resources */ }; diff --git a/winpr/libwinpr/comm/comm_ioctl.c b/winpr/libwinpr/comm/comm_ioctl.c index 89de82871..8583fa8d3 100644 --- a/winpr/libwinpr/comm/comm_ioctl.c +++ b/winpr/libwinpr/comm/comm_ioctl.c @@ -56,9 +56,27 @@ */ +const char* _comm_serial_ioctl_name(ULONG number) +{ + int i; + + for (i=0; _SERIAL_IOCTL_NAMES[i].number != 0; i++) + { + if (_SERIAL_IOCTL_NAMES[i].number == number) + { + return _SERIAL_IOCTL_NAMES[i].name; + } + } + + return "(unknown ioctl name)"; +} + + /** * FIXME: to be used through winpr-io's DeviceIoControl * + * Any previous error as returned by GetLastError is cleared. + * * ERRORS: * ERROR_INVALID_HANDLE * ERROR_INVALID_PARAMETER @@ -73,6 +91,9 @@ BOOL CommDeviceIoControl(HANDLE hDevice, DWORD dwIoControlCode, LPVOID lpInBuffe WINPR_COMM* pComm = (WINPR_COMM*) hDevice; REMOTE_SERIAL_DRIVER* pRemoteSerialDriver = NULL; + /* clear any previous last error */ + SetLastError(ERROR_SUCCESS); + if (hDevice == INVALID_HANDLE_VALUE) { SetLastError(ERROR_INVALID_HANDLE); @@ -394,9 +415,69 @@ BOOL CommDeviceIoControl(HANDLE hDevice, DWORD dwIoControlCode, LPVOID lpInBuffe } break; } + case IOCTL_SERIAL_SET_WAIT_MASK: + { + if (pRemoteSerialDriver->set_wait_mask) + { + ULONG *pWaitMask = (ULONG*)lpInBuffer; + + assert(nInBufferSize >= sizeof(ULONG)); + if (nInBufferSize < sizeof(ULONG)) + { + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + + return pRemoteSerialDriver->set_wait_mask(pComm, pWaitMask); + } + break; + } + case IOCTL_SERIAL_GET_WAIT_MASK: + { + if (pRemoteSerialDriver->get_wait_mask) + { + ULONG *pWaitMask = (ULONG*)lpOutBuffer; + + assert(nOutBufferSize >= sizeof(ULONG)); + if (nOutBufferSize < sizeof(ULONG)) + { + SetLastError(ERROR_INSUFFICIENT_BUFFER); + return FALSE; + } + + if (!pRemoteSerialDriver->get_wait_mask(pComm, pWaitMask)) + return FALSE; + + *lpBytesReturned = sizeof(ULONG); + return TRUE; + } + break; + } + case IOCTL_SERIAL_WAIT_ON_MASK: + { + if (pRemoteSerialDriver->wait_on_mask) + { + ULONG *pOutputMask = (ULONG*)lpOutBuffer; + + assert(nOutBufferSize >= sizeof(ULONG)); + if (nOutBufferSize < sizeof(ULONG)) + { + SetLastError(ERROR_INSUFFICIENT_BUFFER); + return FALSE; + } + + if (!pRemoteSerialDriver->wait_on_mask(pComm, pOutputMask)) + return FALSE; + + *lpBytesReturned = sizeof(ULONG); + return TRUE; + } + break; + } } - DEBUG_WARN(_T("unsupported IoControlCode: Ox%0.8x (remote serial driver: %s)"), dwIoControlCode, pRemoteSerialDriver->name); + DEBUG_WARN(_T("unsupported IoControlCode=[Ox%0.8x] %s (remote serial driver: %s)"), + dwIoControlCode, _comm_serial_ioctl_name(dwIoControlCode), pRemoteSerialDriver->name); SetLastError(ERROR_CALL_NOT_IMPLEMENTED); return FALSE; diff --git a/winpr/libwinpr/comm/comm_ioctl.h b/winpr/libwinpr/comm/comm_ioctl.h index 8ae137fc6..3c582edde 100644 --- a/winpr/libwinpr/comm/comm_ioctl.h +++ b/winpr/libwinpr/comm/comm_ioctl.h @@ -42,59 +42,6 @@ extern "C" { #endif -#define IOCTL_SERIAL_SET_BAUD_RATE 0x001B0004 -#define IOCTL_SERIAL_GET_BAUD_RATE 0x001B0050 -#define IOCTL_SERIAL_SET_LINE_CONTROL 0x001B000C -#define IOCTL_SERIAL_GET_LINE_CONTROL 0x001B0054 -#define IOCTL_SERIAL_SET_TIMEOUTS 0x001B001C -#define IOCTL_SERIAL_GET_TIMEOUTS 0x001B0020 -/* GET_CHARS and SET_CHARS are swapped in the RDP docs [MS-RDPESP] */ -#define IOCTL_SERIAL_GET_CHARS 0x001B0058 -#define IOCTL_SERIAL_SET_CHARS 0x001B005C - -#define IOCTL_SERIAL_SET_DTR 0x001B0024 -#define IOCTL_SERIAL_CLR_DTR 0x001B0028 -/* IOCTL_SERIAL_RESET_DEVICE 0x001B002C */ -#define IOCTL_SERIAL_SET_RTS 0x001B0030 -#define IOCTL_SERIAL_CLR_RTS 0x001B0034 -/* IOCTL_SERIAL_SET_XOFF 0x001B0038 */ -/* IOCTL_SERIAL_SET_XON 0x001B003C */ -/* IOCTL_SERIAL_SET_BREAK_ON 0x001B0010 */ -/* IOCTL_SERIAL_SET_BREAK_OFF 0x001B0014 */ -/* IOCTL_SERIAL_SET_QUEUE_SIZE 0x001B0008 */ -/* IOCTL_SERIAL_GET_WAIT_MASK 0x001B0040 */ -/* IOCTL_SERIAL_SET_WAIT_MASK 0x001B0044 */ -/* IOCTL_SERIAL_WAIT_ON_MASK 0x001B0048 */ -/* IOCTL_SERIAL_IMMEDIATE_CHAR 0x001B0018 */ -/* IOCTL_SERIAL_PURGE 0x001B004C */ -#define IOCTL_SERIAL_GET_HANDFLOW 0x001B0060 -#define IOCTL_SERIAL_SET_HANDFLOW 0x001B0064 -#define IOCTL_SERIAL_GET_MODEMSTATUS 0x001B0068 -/* IOCTL_SERIAL_GET_DTRRTS 0x001B0078 */ -/* IOCTL_SERIAL_GET_COMMSTATUS 0x001B0084 */ -#define IOCTL_SERIAL_GET_PROPERTIES 0x001B0074 -/* IOCTL_SERIAL_XOFF_COUNTER 0x001B0070 */ -/* IOCTL_SERIAL_LSRMST_INSERT 0x001B007C */ -/* IOCTL_SERIAL_CONFIG_SIZE 0x001B0080 */ -/* IOCTL_SERIAL_GET_STATS 0x001B008C */ -/* IOCTL_SERIAL_CLEAR_STATS 0x001B0090 */ -/* IOCTL_SERIAL_GET_MODEM_CONTROL 0x001B0094 */ -/* IOCTL_SERIAL_SET_MODEM_CONTROL 0x001B0098 */ -/* IOCTL_SERIAL_SET_FIFO_CONTROL 0x001B009C */ - -/* IOCTL_PAR_QUERY_INFORMATION 0x00160004 */ -/* IOCTL_PAR_SET_INFORMATION 0x00160008 */ -/* IOCTL_PAR_QUERY_DEVICE_ID 0x0016000C */ -/* IOCTL_PAR_QUERY_DEVICE_ID_SIZE 0x00160010 */ -/* IOCTL_IEEE1284_GET_MODE 0x00160014 */ -/* IOCTL_IEEE1284_NEGOTIATE 0x00160018 */ -/* IOCTL_PAR_SET_WRITE_ADDRESS 0x0016001C */ -/* IOCTL_PAR_SET_READ_ADDRESS 0x00160020 */ -/* IOCTL_PAR_GET_DEVICE_CAPS 0x00160024 */ -/* IOCTL_PAR_GET_DEFAULT_MODES 0x00160028 */ -/* IOCTL_PAR_QUERY_RAW_DEVICE_ID 0x00160030 */ -/* IOCTL_PAR_IS_PORT_FREE 0x00160054 */ - #define STOP_BIT_1 0 #define STOP_BITS_1_5 1 @@ -200,6 +147,20 @@ typedef struct _SERIAL_TIMEOUTS #define SERIAL_MSR_DCD 0x80 +#define SERIAL_EV_RXCHAR 0x0001 +#define SERIAL_EV_RXFLAG 0x0002 +#define SERIAL_EV_TXEMPTY 0x0004 +#define SERIAL_EV_CTS 0x0008 +#define SERIAL_EV_DSR 0x0010 +#define SERIAL_EV_RLSD 0x0020 +#define SERIAL_EV_BREAK 0x0040 +#define SERIAL_EV_ERR 0x0080 +#define SERIAL_EV_RING 0x0100 +#define SERIAL_EV_PERR 0x0200 +#define SERIAL_EV_RX80FULL 0x0400 +#define SERIAL_EV_EVENT1 0x0800 +#define SERIAL_EV_EVENT2 0x1000 + /** * A function might be NULL if not supported by the underlying remote driver. * @@ -225,6 +186,9 @@ typedef struct _REMOTE_SERIAL_DRIVER BOOL (*set_rts)(WINPR_COMM *pComm); BOOL (*clear_rts)(WINPR_COMM *pComm); BOOL (*get_modemstatus)(WINPR_COMM *pComm, ULONG *pRegister); + BOOL (*set_wait_mask)(WINPR_COMM *pComm, const ULONG *pWaitMask); + BOOL (*get_wait_mask)(WINPR_COMM *pComm, ULONG *pWaitMask); + BOOL (*wait_on_mask)(WINPR_COMM *pComm, ULONG *pOutputMask); } REMOTE_SERIAL_DRIVER; diff --git a/winpr/libwinpr/comm/comm_sercx2_sys.c b/winpr/libwinpr/comm/comm_sercx2_sys.c index 517430c6d..04e9960a3 100644 --- a/winpr/libwinpr/comm/comm_sercx2_sys.c +++ b/winpr/libwinpr/comm/comm_sercx2_sys.c @@ -22,6 +22,8 @@ #ifndef _WIN32 +#include + #include "comm_serial_sys.h" #include "comm_sercx_sys.h" @@ -52,6 +54,45 @@ static BOOL _get_serial_chars(WINPR_COMM* pComm, SERIAL_CHARS* pSerialChars) } +/* http://msdn.microsoft.com/en-us/library/windows/hardware/hh439605%28v=vs.85%29.aspx */ +/* FIXME: only using the Serial.sys' events, complete the support of the remaining events */ +static const ULONG _SERCX2_SYS_SUPPORTED_EV_MASK = + 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*/; + +/* use Serial.sys for basis (not SerCx.sys) */ +static BOOL _set_wait_mask(WINPR_COMM *pComm, const ULONG *pWaitMask) +{ + ULONG possibleMask; + REMOTE_SERIAL_DRIVER* pSerialSys = SerialSys_s(); + + possibleMask = *pWaitMask & _SERCX2_SYS_SUPPORTED_EV_MASK; + + if (possibleMask != *pWaitMask) + { + DEBUG_WARN("Not all wait events supported (SerCx2.sys), requested events= 0X%0.4X, possible events= 0X%0.4X", *pWaitMask, possibleMask); + + /* FIXME: shall we really set the possibleMask and return FALSE? */ + pComm->waitMask = possibleMask; + return FALSE; + } + + /* NB: All events that are supported by SerCx.sys are supported by Serial.sys*/ + return pSerialSys->set_wait_mask(pComm, pWaitMask); +} + + /* specific functions only */ static REMOTE_SERIAL_DRIVER _SerCx2Sys = { @@ -73,6 +114,9 @@ static REMOTE_SERIAL_DRIVER _SerCx2Sys = .set_rts = NULL, .clear_rts = NULL, .get_modemstatus = NULL, + .set_wait_mask = _set_wait_mask, + .get_wait_mask = NULL, + .wait_on_mask = NULL, }; @@ -107,6 +151,10 @@ REMOTE_SERIAL_DRIVER* SerCx2Sys_s() _SerCx2Sys.get_modemstatus = pSerialSys->get_modemstatus; + _SerCx2Sys.set_wait_mask = pSerialSys->set_wait_mask; + _SerCx2Sys.get_wait_mask = pSerialSys->get_wait_mask; + _SerCx2Sys.wait_on_mask = pSerialSys->wait_on_mask; + return &_SerCx2Sys; } diff --git a/winpr/libwinpr/comm/comm_sercx_sys.c b/winpr/libwinpr/comm/comm_sercx_sys.c index fef0339f2..c00357eda 100644 --- a/winpr/libwinpr/comm/comm_sercx_sys.c +++ b/winpr/libwinpr/comm/comm_sercx_sys.c @@ -492,6 +492,43 @@ static BOOL _get_handflow(WINPR_COMM *pComm, SERIAL_HANDFLOW *pHandflow) } +/* http://msdn.microsoft.com/en-us/library/windows/hardware/hh439605%28v=vs.85%29.aspx */ +static const ULONG _SERCX_SYS_SUPPORTED_EV_MASK = + 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*/; + + +static BOOL _set_wait_mask(WINPR_COMM *pComm, const ULONG *pWaitMask) +{ + ULONG possibleMask; + REMOTE_SERIAL_DRIVER* pSerialSys = SerialSys_s(); + + possibleMask = *pWaitMask & _SERCX_SYS_SUPPORTED_EV_MASK; + + if (possibleMask != *pWaitMask) + { + DEBUG_WARN("Not all wait events supported (SerCx.sys), requested events= 0X%0.4X, possible events= 0X%0.4X", *pWaitMask, possibleMask); + + /* FIXME: shall we really set the possibleMask and return FALSE? */ + pComm->waitMask = possibleMask; + return FALSE; + } + + /* NB: All events that are supported by SerCx.sys are supported by Serial.sys*/ + return pSerialSys->set_wait_mask(pComm, pWaitMask); +} + /* specific functions only */ static REMOTE_SERIAL_DRIVER _SerCxSys = @@ -514,6 +551,9 @@ static REMOTE_SERIAL_DRIVER _SerCxSys = .set_rts = NULL, .clear_rts = NULL, .get_modemstatus = NULL, + .set_wait_mask = _set_wait_mask, + .get_wait_mask = NULL, + .wait_on_mask = NULL, }; @@ -539,6 +579,10 @@ REMOTE_SERIAL_DRIVER* SerCxSys_s() _SerCxSys.get_modemstatus = pSerialSys->get_modemstatus; + _SerCxSys.set_wait_mask = pSerialSys->set_wait_mask; + _SerCxSys.get_wait_mask = pSerialSys->get_wait_mask; + _SerCxSys.wait_on_mask = pSerialSys->wait_on_mask; + return &_SerCxSys; } diff --git a/winpr/libwinpr/comm/comm_serial_sys.c b/winpr/libwinpr/comm/comm_serial_sys.c index cdc5d6486..ca004233f 100644 --- a/winpr/libwinpr/comm/comm_serial_sys.c +++ b/winpr/libwinpr/comm/comm_serial_sys.c @@ -23,6 +23,7 @@ #ifndef _WIN32 #include +#include #include #include @@ -140,6 +141,7 @@ static const speed_t _SERIAL_SYS_BAUD_TABLE[][2] = { #define _SERIAL_MAX_BAUD B115200 + static BOOL _get_properties(WINPR_COMM *pComm, COMMPROP *pProperties) { int i; @@ -559,6 +561,7 @@ static BOOL _get_line_control(WINPR_COMM *pComm, SERIAL_LINE_CONTROL *pLineContr /* hard-coded in N_TTY */ #define TTY_THRESHOLD_THROTTLE 128 /* now based on remaining room */ #define TTY_THRESHOLD_UNTHROTTLE 128 +#define N_TTY_BUF_SIZE 4096 static BOOL _set_handflow(WINPR_COMM *pComm, const SERIAL_HANDFLOW *pHandflow) { @@ -862,16 +865,28 @@ static BOOL _get_timeouts(WINPR_COMM *pComm, SERIAL_TIMEOUTS *pTimeouts) } -static BOOL _set_line(WINPR_COMM *pComm, UINT32 line) +static BOOL _set_lines(WINPR_COMM *pComm, UINT32 lines) { - ioctl(pComm->fd, TIOCMBIS, line); + if (ioctl(pComm->fd, TIOCMBIS, &lines) < 0) + { + DEBUG_WARN("TIOCMBIS ioctl failed, lines=0x%0.4X, errno=[%d] %s", lines, errno, strerror(errno)); + SetLastError(ERROR_IO_DEVICE); + return FALSE; + } + return TRUE; } -static BOOL _clear_line(WINPR_COMM *pComm, UINT32 line) +static BOOL _clear_lines(WINPR_COMM *pComm, UINT32 lines) { - ioctl(pComm->fd, TIOCMBIC, line); + if (ioctl(pComm->fd, TIOCMBIC, &lines) < 0) + { + DEBUG_WARN("TIOCMBIC ioctl failed, lines=0x%0.4X, errno=[%d] %s", lines, errno, strerror(errno)); + SetLastError(ERROR_IO_DEVICE); + return FALSE; + } + return TRUE; } @@ -891,7 +906,7 @@ static BOOL _set_dtr(WINPR_COMM *pComm) return FALSE; } - return _set_line(pComm, TIOCM_DTR); + return _set_lines(pComm, TIOCM_DTR); } static BOOL _clear_dtr(WINPR_COMM *pComm) @@ -909,37 +924,39 @@ static BOOL _clear_dtr(WINPR_COMM *pComm) return FALSE; } - return _clear_line(pComm, TIOCM_DTR); + return _clear_lines(pComm, TIOCM_DTR); } static BOOL _set_rts(WINPR_COMM *pComm) { - SERIAL_HANDFLOW handflow; - if (!_get_handflow(pComm, &handflow)) - return FALSE; + // TMP: really required? + /* SERIAL_HANDFLOW handflow; */ + /* if (!_get_handflow(pComm, &handflow)) */ + /* return FALSE; */ - if (handflow.FlowReplace & SERIAL_RTS_HANDSHAKE) - { - SetLastError(ERROR_INVALID_PARAMETER); - return FALSE; - } + /* if (handflow.FlowReplace & SERIAL_RTS_HANDSHAKE) */ + /* { */ + /* SetLastError(ERROR_INVALID_PARAMETER); */ + /* return FALSE; */ + /* } */ - return _set_line(pComm, TIOCM_RTS); + return _set_lines(pComm, TIOCM_RTS); } static BOOL _clear_rts(WINPR_COMM *pComm) { - SERIAL_HANDFLOW handflow; - if (!_get_handflow(pComm, &handflow)) - return FALSE; + // TMP: really required? + /* SERIAL_HANDFLOW handflow; */ + /* if (!_get_handflow(pComm, &handflow)) */ + /* return FALSE; */ - if (handflow.FlowReplace & SERIAL_RTS_HANDSHAKE) - { - SetLastError(ERROR_INVALID_PARAMETER); - return FALSE; - } + /* if (handflow.FlowReplace & SERIAL_RTS_HANDSHAKE) */ + /* { */ + /* SetLastError(ERROR_INVALID_PARAMETER); */ + /* return FALSE; */ + /* } */ - return _clear_line(pComm, TIOCM_RTS); + return _clear_lines(pComm, TIOCM_RTS); } @@ -947,8 +964,13 @@ static BOOL _clear_rts(WINPR_COMM *pComm) static BOOL _get_modemstatus(WINPR_COMM *pComm, ULONG *pRegister) { UINT32 lines=0; - ioctl(pComm->fd, TIOCMGET, &lines); - + if (ioctl(pComm->fd, TIOCMGET, &lines) < 0) + { + DEBUG_WARN("TIOCMGET ioctl failed, errno=[%d] %s", errno, strerror(errno)); + SetLastError(ERROR_IO_DEVICE); + return FALSE; + } + ZeroMemory(pRegister, sizeof(ULONG)); /* TODO: FIXME: how to get a direct access from the user space @@ -972,6 +994,257 @@ static BOOL _get_modemstatus(WINPR_COMM *pComm, ULONG *pRegister) return TRUE; } +/* http://msdn.microsoft.com/en-us/library/windows/hardware/hh439605%28v=vs.85%29.aspx */ +static const ULONG _SERIAL_SYS_SUPPORTED_EV_MASK = + 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*/; + + +static BOOL _set_wait_mask(WINPR_COMM *pComm, const ULONG *pWaitMask) +{ + ULONG possibleMask; + + if (*pWaitMask == 0) + { + /* clearing pending events */ + + // TMP: TODO: + + if (ioctl(pComm->fd, TIOCGICOUNT, &(pComm->counters)) < 0) + { + DEBUG_WARN("TIOCGICOUNT ioctl failed, errno=[%d] %s", errno, strerror(errno)); + SetLastError(ERROR_IO_DEVICE); + return FALSE; + } + + pComm->pendingEvents = 0; + } + + // TMP: TODO: + // pending wait_on_mask must be stopped with STATUS_SUCCESS + // http://msdn.microsoft.com/en-us/library/ff546805%28v=vs.85%29.aspx + + + possibleMask = *pWaitMask & _SERIAL_SYS_SUPPORTED_EV_MASK; + + if (possibleMask != *pWaitMask) + { + DEBUG_WARN("Not all wait events supported (Serial.sys), requested events= 0X%0.4X, possible events= 0X%0.4X", *pWaitMask, possibleMask); + + /* FIXME: shall we really set the possibleMask and return FALSE? */ + pComm->waitMask = possibleMask; + return FALSE; + } + + pComm->waitMask = possibleMask; + return TRUE; +} + + +static BOOL _get_wait_mask(WINPR_COMM *pComm, ULONG *pWaitMask) +{ + *pWaitMask = pComm->waitMask; + return TRUE; +} + + +static BOOL _wait_on_mask(WINPR_COMM *pComm, ULONG *pOutputMask) +{ + assert(*pOutputMask == 0); + + + /* TMP: TODO: while (TRUE) */ + { + int nbBytesToBeRead = 0; + int nbBytesToBeWritten = 0; + struct serial_icounter_struct currentCounters; + ULONG tiocmiwaitMask = 0; /* TIOCMIWAIT can wait for the 4 lines: TIOCM_RNG/DSR/CD/CTS */ + + if (ioctl(pComm->fd, TIOCINQ, &nbBytesToBeRead) < 0) + { + DEBUG_WARN("TIOCINQ ioctl failed, errno=[%d] %s", errno, strerror(errno)); + SetLastError(ERROR_IO_DEVICE); + return FALSE; + } + + if (ioctl(pComm->fd, TIOCOUTQ, &nbBytesToBeWritten) < 0) + { + DEBUG_WARN("TIOCOUTQ ioctl failed, errno=[%d] %s", errno, strerror(errno)); + SetLastError(ERROR_IO_DEVICE); + return FALSE; + } + + ZeroMemory(¤tCounters, sizeof(struct serial_icounter_struct)); + if (ioctl(pComm->fd, TIOCGICOUNT, ¤tCounters) < 0) + { + DEBUG_WARN("TIOCGICOUNT ioctl failed, errno=[%d] %s", errno, strerror(errno)); + SetLastError(ERROR_IO_DEVICE); + return FALSE; + } + + /* NB: preferred below "currentCounters.* != pComm->counters.*" over "currentCounters.* > pComm->counters.*" thinking the counters can loop */ + + + /* events */ + + if (pComm->waitMask & SERIAL_EV_RXCHAR) + { + if (nbBytesToBeRead > 0) + { + /* at least one character is pending to be read */ + *pOutputMask |= SERIAL_EV_RXCHAR; + } + } + + if (pComm->waitMask & SERIAL_EV_RXFLAG) + { + if (pComm->pendingEvents & SERIAL_EV_RXFLAG) // TMP: to be done in the ReadThread + { + /* the event character was received FIXME: is the character supposed to be still in the input buffer? */ + + /* event consumption */ + pComm->pendingEvents &= ~SERIAL_EV_RXFLAG; + *pOutputMask |= SERIAL_EV_RXFLAG; + } + } + + if (pComm->waitMask & SERIAL_EV_TXEMPTY) + { + if (nbBytesToBeWritten == 0) + { + /* NB: as of today CommWriteFile still blocks and uses the same thread than CommDeviceIoControl, + * it should be enough to just check nbBytesToBeWritten + */ + + /* the output buffer is empty */ + *pOutputMask |= SERIAL_EV_TXEMPTY; + } + } + + if (pComm->waitMask & SERIAL_EV_CTS) + { + tiocmiwaitMask |= TIOCM_CTS; + } + + if (pComm->waitMask & SERIAL_EV_DSR) + { + tiocmiwaitMask |= TIOCM_DSR; + } + + if (pComm->waitMask & SERIAL_EV_RLSD) + { + tiocmiwaitMask |= TIOCM_CD; + } + + if (pComm->waitMask & SERIAL_EV_BREAK) + { + if (currentCounters.brk != pComm->counters.brk) + { + *pOutputMask |= SERIAL_EV_BREAK; + + /* event consumption */ + pComm->counters.brk = currentCounters.brk; + } + } + + if (pComm->waitMask & SERIAL_EV_ERR) + { + if ((currentCounters.frame != pComm->counters.frame) || + (currentCounters.overrun != pComm->counters.overrun) || + (currentCounters.parity != pComm->counters.parity)) + { + *pOutputMask |= SERIAL_EV_ERR; + + /* event consumption */ + pComm->counters.frame = currentCounters.frame; + pComm->counters.overrun = currentCounters.overrun; + pComm->counters.parity = currentCounters.parity; + } + } + + if (pComm->waitMask & SERIAL_EV_RING) + { + tiocmiwaitMask |= TIOCM_RNG; + } + + if (pComm->waitMask & SERIAL_EV_RX80FULL) + { + if (nbBytesToBeRead > (0.8 * N_TTY_BUF_SIZE)) + *pOutputMask |= SERIAL_EV_RX80FULL; + } + + if ((*pOutputMask == 0) && /* don't need to wait more if at least an event already occured */ + (tiocmiwaitMask > 0)) + { + if ((pComm->waitMask & SERIAL_EV_CTS) && currentCounters.cts != pComm->counters.cts) + { + *pOutputMask |= SERIAL_EV_CTS; + + /* event consumption */ + pComm->counters.cts = currentCounters.cts; + } + + if ((pComm->waitMask & SERIAL_EV_DSR) && currentCounters.dsr != pComm->counters.dsr) + { + *pOutputMask |= SERIAL_EV_DSR; + + /* event consumption */ + pComm->counters.dsr = currentCounters.dsr; + } + + if ((pComm->waitMask & SERIAL_EV_RLSD) && currentCounters.dcd != pComm->counters.dcd) + { + *pOutputMask |= SERIAL_EV_RLSD; + + /* event consumption */ + pComm->counters.dcd = currentCounters.dcd; + } + + if ((pComm->waitMask & SERIAL_EV_RING) && currentCounters.rng != pComm->counters.rng) + { + *pOutputMask |= SERIAL_EV_RING; + + /* event consumption */ + pComm->counters.rng = currentCounters.rng; + } + + + /* FIXME: TIOCMIWAIT could be possible if _wait_on_mask gets its own thread */ + /* if (*pOutputMask == 0) */ + /* { */ + /* if (ioctl(pComm->fd, TIOCMIWAIT, &tiocmiwaitMask) < 0) */ + /* { */ + /* DEBUG_WARN("TIOCMIWAIT ioctl failed, errno=[%d] %s", errno, strerror(errno)); */ + /* SetLastError(ERROR_IO_DEVICE); */ + /* return FALSE; */ + /* } */ + /* /\* TODO: check counters again after TIOCMIWAIT *\/ */ + /* } */ + } + + if (*pOutputMask != 0) + { + /* at least an event occurred */ + return TRUE; + } + } + + DEBUG_WARN("_wait_on_mask pending on events:0X%0.4X", pComm->waitMask); + SetLastError(ERROR_IO_PENDING); /* see: WaitCommEvent's help */ + return FALSE; +} + static REMOTE_SERIAL_DRIVER _SerialSys = { @@ -993,6 +1266,9 @@ static REMOTE_SERIAL_DRIVER _SerialSys = .set_rts = _set_rts, .clear_rts = _clear_rts, .get_modemstatus = _get_modemstatus, + .set_wait_mask = _set_wait_mask, + .get_wait_mask = _get_wait_mask, + .wait_on_mask = _wait_on_mask, };