Milestone 5: deliver embedded RDP sessions and lifecycle hardening

This commit is contained in:
Keith Smith
2026-03-03 18:59:26 -07:00
parent 230a401386
commit 36006bd4aa
2941 changed files with 724359 additions and 77 deletions

View File

@@ -0,0 +1,49 @@
# WinPR: Windows Portable Runtime
# libwinpr-comm cmake build script
#
# Copyright 2014 Marc-Andre Moreau <marcandre.moreau@gmail.com>
#
# 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.
set(MODULE_NAME "winpr-comm")
set(MODULE_PREFIX "WINPR_COMM")
if(NOT WIN32)
set(${MODULE_PREFIX}_SRCS comm.c comm.h)
if(NOT EMSCRIPTEN AND NOT APPLE)
winpr_definition_add(WINPR_HAVE_SERIAL_SUPPORT)
list(
APPEND
${MODULE_PREFIX}_SRCS
comm_io.c
comm_ioctl.c
comm_ioctl.h
comm_serial_sys.c
comm_serial_sys.h
comm_sercx_sys.c
comm_sercx_sys.h
comm_sercx2_sys.c
comm_sercx2_sys.h
)
else()
list(APPEND ${MODULE_PREFIX}_SRCS comm_ioctl_dummy.c comm_ioctl.h)
endif()
winpr_module_add(${${MODULE_PREFIX}_SRCS})
if(NOT EMSCRIPTEN)
if(BUILD_TESTING_INTERNAL AND BUILD_COMM_TESTS)
add_subdirectory(test)
endif()
endif()
endif()

View File

@@ -0,0 +1,9 @@
set(MINWIN_LAYER "1")
set(MINWIN_GROUP "core")
set(MINWIN_MAJOR_VERSION "1")
set(MINWIN_MINOR_VERSION "0")
set(MINWIN_SHORT_NAME "comm")
set(MINWIN_LONG_NAME "Serial Communication API")
set(MODULE_LIBRARY_NAME
"api-ms-win-${MINWIN_GROUP}-${MINWIN_SHORT_NAME}-l${MINWIN_LAYER}-${MINWIN_MAJOR_VERSION}-${MINWIN_MINOR_VERSION}"
)

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,134 @@
/**
* WinPR: Windows Portable Runtime
* Serial Communication API
*
* 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.
*/
#ifndef WINPR_COMM_PRIVATE_H
#define WINPR_COMM_PRIVATE_H
#if defined(__linux__)
#define WINPR_HAVE_COMM_COUNTERS
#include <linux/serial.h>
#endif
#include <winpr/comm.h>
#include "../handle/handle.h"
#include <winpr/config.h>
#if defined(WINPR_HAVE_SYS_EVENTFD_H)
#include <sys/eventfd.h>
#endif
struct winpr_comm
{
WINPR_HANDLE common;
int fd;
int fd_read;
int fd_read_event; /* as of today, only used by _purge() */
CRITICAL_SECTION ReadLock;
int fd_write;
int fd_write_event; /* as of today, only used by _purge() */
CRITICAL_SECTION WriteLock;
/* permissive mode on errors. If TRUE (default is FALSE)
* CommDeviceIoControl always return TRUE.
*
* Not all features are supported yet and an error is then returned when
* an application turns them on (e.g: i/o buffers > 4096). It appeared
* though that devices and applications can be still functional on such
* errors.
*
* see also: comm_ioctl.c
*
* FIXME: getting rid of this flag once all features supported.
*/
BOOL permissive;
SERIAL_DRIVER_ID serverSerialDriverId;
COMMTIMEOUTS timeouts;
CRITICAL_SECTION
EventsLock; /* protects counters, WaitEventMask and PendingEvents */
#if defined(WINPR_HAVE_COMM_COUNTERS)
struct serial_icounter_struct counters;
#endif
ULONG WaitEventMask;
ULONG PendingEvents;
BYTE eventChar;
/* NB: CloseHandle() has to free resources */
ULONG XOnLimit;
ULONG XOffLimit;
#if defined(WINPR_HAVE_COMM_COUNTERS)
BOOL TIOCGICOUNTSupported;
#endif
};
typedef struct winpr_comm WINPR_COMM;
#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
#define SERIAL_EV_WINPR_WAITING 0x4000 /* bit today unused by other SERIAL_EV_* */
#define SERIAL_EV_WINPR_STOP 0x8000 /* bit today unused by other SERIAL_EV_* */
#define WINPR_PURGE_TXABORT 0x00000001 /* abort pending transmission */
#define WINPR_PURGE_RXABORT 0x00000002 /* abort pending reception */
#define CommLog_Print(level, ...) CommLog_PrintEx(level, __FILE__, __LINE__, __func__, __VA_ARGS__)
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, ...);
BOOL CommIsHandled(HANDLE handle);
BOOL CommIsHandleValid(HANDLE handle);
BOOL CommCloseHandle(HANDLE handle);
const HANDLE_CREATOR* GetCommHandleCreator(void);
#define CommIoCtl(pComm, ctl, data) \
CommIoCtl_int((pComm), (ctl), (data), __FILE__, __func__, __LINE__)
BOOL CommIoCtl_int(WINPR_COMM* pComm, unsigned long int ctl, void* data, const char* file,
const char* fkt, size_t line);
BOOL CommUpdateIOCount(HANDLE handle, BOOL checkSupportStatus);
const char* CommSerialEvString(ULONG status, char* buffer, size_t size);
#if defined(WINPR_HAVE_SYS_EVENTFD_H)
#ifndef WITH_EVENTFD_READ_WRITE
int eventfd_read(int fd, eventfd_t* value);
int eventfd_write(int fd, eventfd_t value);
#endif
#endif
#endif /* WINPR_COMM_PRIVATE_H */

View File

@@ -0,0 +1,567 @@
/**
* WinPR: Windows Portable Runtime
* Serial Communication API
*
* 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 <termios.h>
#include <unistd.h>
#include <winpr/io.h>
#include <winpr/wlog.h>
#include <winpr/wtypes.h>
#include "comm.h"
BOOL _comm_set_permissive(HANDLE hDevice, BOOL permissive)
{
WINPR_COMM* pComm = (WINPR_COMM*)hDevice;
if (!CommIsHandled(hDevice))
return FALSE;
pComm->permissive = permissive;
return TRUE;
}
/* Computes VTIME in deciseconds from Ti in milliseconds */
static UCHAR svtime(ULONG Ti)
{
/* FIXME: look for an equivalent math function otherwise let
* do the compiler do the optimization */
if (Ti == 0)
return 0;
else if (Ti < 100)
return 1;
else if (Ti > 25500)
return 255; /* 0xFF */
else
return (UCHAR)(Ti / 100);
}
/**
* ERRORS:
* ERROR_INVALID_HANDLE
* ERROR_NOT_SUPPORTED
* ERROR_INVALID_PARAMETER
* ERROR_TIMEOUT
* ERROR_IO_DEVICE
* ERROR_BAD_DEVICE
*/
BOOL CommReadFile(HANDLE hDevice, LPVOID lpBuffer, DWORD nNumberOfBytesToRead,
LPDWORD lpNumberOfBytesRead, LPOVERLAPPED lpOverlapped)
{
WINPR_COMM* pComm = (WINPR_COMM*)hDevice;
int biggestFd = -1;
fd_set read_set;
int nbFds = 0;
COMMTIMEOUTS* pTimeouts = nullptr;
UCHAR vmin = 0;
UCHAR vtime = 0;
LONGLONG Tmax = 0;
struct timeval tmaxTimeout;
struct timeval* pTmaxTimeout = nullptr;
struct termios currentTermios;
EnterCriticalSection(&pComm->ReadLock); /* KISSer by the function's beginning */
if (!CommIsHandled(hDevice))
goto return_false;
if (lpOverlapped != nullptr)
{
SetLastError(ERROR_NOT_SUPPORTED);
goto return_false;
}
if (lpNumberOfBytesRead == nullptr)
{
SetLastError(
ERROR_INVALID_PARAMETER); /* since we doesn't support lpOverlapped != nullptr */
goto return_false;
}
*lpNumberOfBytesRead = 0; /* will be adjusted if required ... */
if (nNumberOfBytesToRead <= 0) /* N */
{
goto return_true; /* FIXME: or FALSE? */
}
if (tcgetattr(pComm->fd, &currentTermios) < 0)
{
SetLastError(ERROR_IO_DEVICE);
goto return_false;
}
if (currentTermios.c_lflag & ICANON)
{
CommLog_Print(WLOG_WARN, "Canonical mode not supported"); /* the timeout could not be set */
SetLastError(ERROR_NOT_SUPPORTED);
goto return_false;
}
/* http://msdn.microsoft.com/en-us/library/hh439614%28v=vs.85%29.aspx
* http://msdn.microsoft.com/en-us/library/windows/hardware/hh439614%28v=vs.85%29.aspx
*
* ReadIntervalTimeout | ReadTotalTimeoutMultiplier | ReadTotalTimeoutConstant | VMIN | VTIME |
* TMAX | 0 | 0 | 0 | N | 0 |
* INDEF | Blocks for N bytes available. 0< Ti <MAXULONG | 0 | 0 |
* N | Ti | INDEF | Blocks on first byte, then use Ti between bytes. MAXULONG | 0 | 0
* | 0 | 0 | 0 | Returns immediately with bytes available (don't block) MAXULONG |
* MAXULONG | 0< Tc <MAXULONG | N | 0 | Tc | Blocks on first byte
* during Tc or returns immediately with bytes available MAXULONG | m |
* MAXULONG | | Invalid 0 | m | 0< Tc
* <MAXULONG | N | 0 | Tmax | Blocks on first byte during Tmax or returns
* immediately with bytes available 0< Ti <MAXULONG | m | 0<
* Tc <MAXULONG | N | Ti | Tmax | Blocks on first byte, then use Ti between bytes.
* Tmax is used for the whole system call.
*/
/* NB: timeouts are in milliseconds, VTIME are in deciseconds and is an unsigned char */
/* FIXME: double check whether open(pComm->fd_read_event, O_NONBLOCK) doesn't conflict with
* above use cases */
pTimeouts = &(pComm->timeouts);
if ((pTimeouts->ReadIntervalTimeout == MAXULONG) &&
(pTimeouts->ReadTotalTimeoutConstant == MAXULONG))
{
CommLog_Print(
WLOG_WARN,
"ReadIntervalTimeout and ReadTotalTimeoutConstant cannot be both set to MAXULONG");
SetLastError(ERROR_INVALID_PARAMETER);
goto return_false;
}
/* VMIN */
if ((pTimeouts->ReadIntervalTimeout == MAXULONG) &&
(pTimeouts->ReadTotalTimeoutMultiplier == 0) && (pTimeouts->ReadTotalTimeoutConstant == 0))
{
vmin = 0;
}
else
{
/* N */
/* vmin = nNumberOfBytesToRead < 256 ? nNumberOfBytesToRead : 255;*/ /* 0xFF */
/* NB: we might wait endlessly with vmin=N, prefer to
* force vmin=1 and return with bytes
* available. FIXME: is a feature disarded here? */
vmin = 1;
}
/* VTIME */
if ((pTimeouts->ReadIntervalTimeout > 0) && (pTimeouts->ReadIntervalTimeout < MAXULONG))
{
/* Ti */
vtime = svtime(pTimeouts->ReadIntervalTimeout);
}
/* TMAX */
pTmaxTimeout = &tmaxTimeout;
if ((pTimeouts->ReadIntervalTimeout == MAXULONG) &&
(pTimeouts->ReadTotalTimeoutMultiplier == MAXULONG))
{
/* Tc */
Tmax = pTimeouts->ReadTotalTimeoutConstant;
}
else
{
/* Tmax */
Tmax = 1ll * nNumberOfBytesToRead * pTimeouts->ReadTotalTimeoutMultiplier +
1ll * pTimeouts->ReadTotalTimeoutConstant;
/* INDEFinitely */
if ((Tmax == 0) && (pTimeouts->ReadIntervalTimeout < MAXULONG) &&
(pTimeouts->ReadTotalTimeoutMultiplier == 0))
pTmaxTimeout = nullptr;
}
if ((currentTermios.c_cc[VMIN] != vmin) || (currentTermios.c_cc[VTIME] != vtime))
{
currentTermios.c_cc[VMIN] = vmin;
currentTermios.c_cc[VTIME] = vtime;
if (tcsetattr(pComm->fd, TCSANOW, &currentTermios) < 0)
{
CommLog_Print(WLOG_WARN,
"CommReadFile failure, could not apply new timeout values: VMIN=%" PRIu8
", VTIME=%" PRIu8 "",
vmin, vtime);
SetLastError(ERROR_IO_DEVICE);
goto return_false;
}
}
/* wait indefinitely if pTmaxTimeout is nullptr */
if (pTmaxTimeout != nullptr)
{
ZeroMemory(pTmaxTimeout, sizeof(struct timeval));
if (Tmax > 0) /* return immdiately if Tmax == 0 */
{
pTmaxTimeout->tv_sec = Tmax / 1000; /* s */
pTmaxTimeout->tv_usec = (Tmax % 1000) * 1000; /* us */
}
}
/* FIXME: had expected eventfd_write() to return EAGAIN when
* there is no eventfd_read() but this not the case. */
/* discard a possible and no more relevant event */
#if defined(WINPR_HAVE_SYS_EVENTFD_H)
{
eventfd_t val = 0;
(void)eventfd_read(pComm->fd_read_event, &val);
}
#endif
biggestFd = pComm->fd_read;
if (pComm->fd_read_event > biggestFd)
biggestFd = pComm->fd_read_event;
FD_ZERO(&read_set);
WINPR_ASSERT(pComm->fd_read_event < FD_SETSIZE);
WINPR_ASSERT(pComm->fd_read < FD_SETSIZE);
FD_SET(pComm->fd_read_event, &read_set);
FD_SET(pComm->fd_read, &read_set);
nbFds = select(biggestFd + 1, &read_set, nullptr, nullptr, pTmaxTimeout);
if (nbFds < 0)
{
char ebuffer[256] = WINPR_C_ARRAY_INIT;
CommLog_Print(WLOG_WARN, "select() failure, errno=[%d] %s\n", errno,
winpr_strerror(errno, ebuffer, sizeof(ebuffer)));
SetLastError(ERROR_IO_DEVICE);
goto return_false;
}
if (nbFds == 0)
{
/* timeout */
SetLastError(ERROR_TIMEOUT);
goto return_false;
}
/* read_set */
if (FD_ISSET(pComm->fd_read_event, &read_set))
{
#if defined(WINPR_HAVE_SYS_EVENTFD_H)
eventfd_t event = 0;
if (eventfd_read(pComm->fd_read_event, &event) < 0)
{
if (errno == EAGAIN)
{
WINPR_ASSERT(FALSE); /* not quite sure this should ever happen */
/* keep on */
}
else
{
char ebuffer[256] = WINPR_C_ARRAY_INIT;
CommLog_Print(WLOG_WARN,
"unexpected error on reading fd_read_event, errno=[%d] %s\n", errno,
winpr_strerror(errno, ebuffer, sizeof(ebuffer)));
/* FIXME: goto return_false ? */
}
WINPR_ASSERT(errno == EAGAIN);
}
if (event == WINPR_PURGE_RXABORT)
{
SetLastError(ERROR_CANCELLED);
goto return_false;
}
WINPR_ASSERT(event == WINPR_PURGE_RXABORT); /* no other expected event so far */
#endif
}
if (FD_ISSET(pComm->fd_read, &read_set))
{
ssize_t nbRead = read(pComm->fd_read, lpBuffer, nNumberOfBytesToRead);
if ((nbRead < 0) || (nbRead > nNumberOfBytesToRead))
{
char ebuffer[256] = WINPR_C_ARRAY_INIT;
CommLog_Print(WLOG_WARN,
"CommReadFile failed, ReadIntervalTimeout=%" PRIu32
", ReadTotalTimeoutMultiplier=%" PRIu32
", ReadTotalTimeoutConstant=%" PRIu32 " VMIN=%u, VTIME=%u",
pTimeouts->ReadIntervalTimeout, pTimeouts->ReadTotalTimeoutMultiplier,
pTimeouts->ReadTotalTimeoutConstant, currentTermios.c_cc[VMIN],
currentTermios.c_cc[VTIME]);
CommLog_Print(
WLOG_WARN, "CommReadFile failed, nNumberOfBytesToRead=%" PRIu32 ", errno=[%d] %s",
nNumberOfBytesToRead, errno, winpr_strerror(errno, ebuffer, sizeof(ebuffer)));
if (errno == EAGAIN)
{
/* keep on */
goto return_true; /* expect a read-loop to be implemented on the server side */
}
else if (errno == EBADF)
{
SetLastError(ERROR_BAD_DEVICE); /* STATUS_INVALID_DEVICE_REQUEST */
goto return_false;
}
else
{
WINPR_ASSERT(FALSE);
SetLastError(ERROR_IO_DEVICE);
goto return_false;
}
}
if (nbRead == 0)
{
/* termios timeout */
SetLastError(ERROR_TIMEOUT);
goto return_false;
}
*lpNumberOfBytesRead = WINPR_ASSERTING_INT_CAST(UINT32, nbRead);
EnterCriticalSection(&pComm->EventsLock);
if (pComm->PendingEvents & SERIAL_EV_WINPR_WAITING)
{
if (pComm->eventChar != '\0' &&
memchr(lpBuffer, pComm->eventChar, WINPR_ASSERTING_INT_CAST(size_t, nbRead)))
pComm->PendingEvents |= SERIAL_EV_RXCHAR;
}
LeaveCriticalSection(&pComm->EventsLock);
goto return_true;
}
WINPR_ASSERT(FALSE);
*lpNumberOfBytesRead = 0;
return_false:
LeaveCriticalSection(&pComm->ReadLock);
return FALSE;
return_true:
LeaveCriticalSection(&pComm->ReadLock);
return TRUE;
}
/**
* ERRORS:
* ERROR_INVALID_HANDLE
* ERROR_NOT_SUPPORTED
* ERROR_INVALID_PARAMETER
* ERROR_BAD_DEVICE
*/
BOOL CommWriteFile(HANDLE hDevice, LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite,
LPDWORD lpNumberOfBytesWritten, LPOVERLAPPED lpOverlapped)
{
WINPR_COMM* pComm = (WINPR_COMM*)hDevice;
struct timeval tmaxTimeout;
struct timeval* pTmaxTimeout = nullptr;
EnterCriticalSection(&pComm->WriteLock); /* KISSer by the function's beginning */
if (!CommIsHandled(hDevice))
goto return_false;
if (lpOverlapped != nullptr)
{
SetLastError(ERROR_NOT_SUPPORTED);
goto return_false;
}
if (lpNumberOfBytesWritten == nullptr)
{
SetLastError(
ERROR_INVALID_PARAMETER); /* since we doesn't support lpOverlapped != nullptr */
goto return_false;
}
*lpNumberOfBytesWritten = 0; /* will be adjusted if required ... */
if (nNumberOfBytesToWrite <= 0)
{
goto return_true; /* FIXME: or FALSE? */
}
/* FIXME: had expected eventfd_write() to return EAGAIN when
* there is no eventfd_read() but this not the case. */
/* discard a possible and no more relevant event */
#if defined(WINPR_HAVE_SYS_EVENTFD_H)
{
eventfd_t val = 0;
(void)eventfd_read(pComm->fd_write_event, &val);
}
#endif
{
/* ms */
const LONGLONG Tmax =
1ll * nNumberOfBytesToWrite * pComm->timeouts.WriteTotalTimeoutMultiplier +
1ll * pComm->timeouts.WriteTotalTimeoutConstant;
/* NB: select() may update the timeout argument to indicate
* how much time was left. Keep the timeout variable out of
* the while() */
pTmaxTimeout = &tmaxTimeout;
ZeroMemory(pTmaxTimeout, sizeof(struct timeval));
if (Tmax > 0)
{
pTmaxTimeout->tv_sec = Tmax / 1000; /* s */
pTmaxTimeout->tv_usec = (Tmax % 1000) * 1000; /* us */
}
else if ((pComm->timeouts.WriteTotalTimeoutMultiplier == 0) &&
(pComm->timeouts.WriteTotalTimeoutConstant == 0))
{
pTmaxTimeout = nullptr;
}
}
/* else return immdiately */
while (*lpNumberOfBytesWritten < nNumberOfBytesToWrite)
{
int biggestFd = -1;
fd_set event_set;
fd_set write_set;
int nbFds = 0;
biggestFd = pComm->fd_write;
if (pComm->fd_write_event > biggestFd)
biggestFd = pComm->fd_write_event;
FD_ZERO(&event_set);
FD_ZERO(&write_set);
WINPR_ASSERT(pComm->fd_write_event < FD_SETSIZE);
WINPR_ASSERT(pComm->fd_write < FD_SETSIZE);
FD_SET(pComm->fd_write_event, &event_set);
FD_SET(pComm->fd_write, &write_set);
nbFds = select(biggestFd + 1, &event_set, &write_set, nullptr, pTmaxTimeout);
if (nbFds < 0)
{
char ebuffer[256] = WINPR_C_ARRAY_INIT;
CommLog_Print(WLOG_WARN, "select() failure, errno=[%d] %s\n", errno,
winpr_strerror(errno, ebuffer, sizeof(ebuffer)));
SetLastError(ERROR_IO_DEVICE);
goto return_false;
}
if (nbFds == 0)
{
/* timeout */
SetLastError(ERROR_TIMEOUT);
goto return_false;
}
/* event_set */
if (FD_ISSET(pComm->fd_write_event, &event_set))
{
#if defined(WINPR_HAVE_SYS_EVENTFD_H)
eventfd_t event = 0;
if (eventfd_read(pComm->fd_write_event, &event) < 0)
{
if (errno == EAGAIN)
{
WINPR_ASSERT(FALSE); /* not quite sure this should ever happen */
/* keep on */
}
else
{
char ebuffer[256] = WINPR_C_ARRAY_INIT;
CommLog_Print(WLOG_WARN,
"unexpected error on reading fd_write_event, errno=[%d] %s\n",
errno, winpr_strerror(errno, ebuffer, sizeof(ebuffer)));
/* FIXME: goto return_false ? */
}
WINPR_ASSERT(errno == EAGAIN);
}
if (event == WINPR_PURGE_TXABORT)
{
SetLastError(ERROR_CANCELLED);
goto return_false;
}
WINPR_ASSERT(event == WINPR_PURGE_TXABORT); /* no other expected event so far */
#endif
}
/* write_set */
if (FD_ISSET(pComm->fd_write, &write_set))
{
ssize_t nbWritten = 0;
const BYTE* ptr = lpBuffer;
nbWritten = write(pComm->fd_write, &ptr[*lpNumberOfBytesWritten],
nNumberOfBytesToWrite - (*lpNumberOfBytesWritten));
if (nbWritten < 0)
{
char ebuffer[256] = WINPR_C_ARRAY_INIT;
CommLog_Print(WLOG_WARN,
"CommWriteFile failed after %" PRIu32
" bytes written, errno=[%d] %s\n",
*lpNumberOfBytesWritten, errno,
winpr_strerror(errno, ebuffer, sizeof(ebuffer)));
if (errno == EAGAIN)
{
/* keep on */
continue;
}
else if (errno == EBADF)
{
SetLastError(ERROR_BAD_DEVICE); /* STATUS_INVALID_DEVICE_REQUEST */
goto return_false;
}
else
{
WINPR_ASSERT(FALSE);
SetLastError(ERROR_IO_DEVICE);
goto return_false;
}
}
*lpNumberOfBytesWritten += nbWritten;
}
} /* while */
/* FIXME: this call to tcdrain() doesn't look correct and
* might hide a bug but was required while testing a serial
* printer. Its driver was expecting the modem line status
* SERIAL_MSR_DSR true after the sending which was never
* happening otherwise. A purge was also done before each
* Write operation. The serial port was opened with:
* DesiredAccess=0x0012019F. The printer worked fine with
* mstsc. */
tcdrain(pComm->fd_write);
return_true:
LeaveCriticalSection(&pComm->WriteLock);
return TRUE;
return_false:
LeaveCriticalSection(&pComm->WriteLock);
return FALSE;
}

View File

@@ -0,0 +1,759 @@
/**
* 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 <winpr/wlog.h>
#include "comm.h"
#include "comm_ioctl.h"
#include "comm_serial_sys.h"
#include "comm_sercx_sys.h"
#include "comm_sercx2_sys.h"
static const char* comm_ioctl_modem_status_string(ULONG status, char* buffer, size_t size);
/* NB: MS-RDPESP's recommendation:
*
* <2> Section 3.2.5.1.6: Windows Implementations use IOCTL constants
* for IoControlCode values. The content and values of the IOCTLs are
* opaque to the protocol. On the server side, the data contained in
* an IOCTL is simply packaged and sent to the client side. For
* maximum compatibility between the different versions of the Windows
* operating system, the client implementation only singles out
* critical IOCTLs and invokes the applicable Win32 port API. The
* other IOCTLS are passed directly to the client-side driver, and the
* processing of this value depends on the drivers installed on the
* client side. The values and parameters for these IOCTLS can be
* found in [MSFT-W2KDDK] Volume 2, Part 2—Serial and Parallel
* Drivers, and in [MSDN-PORTS].
*/
static BOOL s_CommDeviceIoControl(HANDLE hDevice, DWORD dwIoControlCode, LPVOID lpInBuffer,
DWORD nInBufferSize, LPVOID lpOutBuffer, DWORD nOutBufferSize,
LPDWORD lpBytesReturned, LPOVERLAPPED lpOverlapped)
{
char buffer[128] = WINPR_C_ARRAY_INIT;
WINPR_COMM* pComm = (WINPR_COMM*)hDevice;
const SERIAL_DRIVER* pServerSerialDriver = nullptr;
if (!CommIsHandleValid(hDevice))
return FALSE;
if (lpOverlapped)
{
SetLastError(ERROR_NOT_SUPPORTED);
return FALSE;
}
if (lpBytesReturned == nullptr)
{
SetLastError(
ERROR_INVALID_PARAMETER); /* since we doesn't support lpOverlapped != nullptr */
return FALSE;
}
/* clear any previous last error */
SetLastError(ERROR_SUCCESS);
*lpBytesReturned = 0; /* will be adjusted if required ... */
CommLog_Print(WLOG_DEBUG, "CommDeviceIoControl: IoControlCode: %s [0x%08" PRIx32 "]",
_comm_serial_ioctl_name(dwIoControlCode), dwIoControlCode);
/* remoteSerialDriver to be use ...
*
* FIXME: might prefer to use an automatic rather than static structure
*/
switch (pComm->serverSerialDriverId)
{
case SerialDriverSerialSys:
pServerSerialDriver = SerialSys_s();
break;
case SerialDriverSerCxSys:
pServerSerialDriver = SerCxSys_s();
break;
case SerialDriverSerCx2Sys:
pServerSerialDriver = SerCx2Sys_s();
break;
case SerialDriverUnknown:
default:
CommLog_Print(WLOG_DEBUG, "Unknown remote serial driver (%u), using SerCx2.sys",
pComm->serverSerialDriverId);
pServerSerialDriver = SerCx2Sys_s();
break;
}
WINPR_ASSERT(pServerSerialDriver != nullptr);
switch (dwIoControlCode)
{
case IOCTL_USBPRINT_GET_1284_ID:
{
/* FIXME:
* http://msdn.microsoft.com/en-us/library/windows/hardware/ff551803(v=vs.85).aspx */
*lpBytesReturned = nOutBufferSize; /* an empty OutputBuffer will be returned */
SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
return FALSE;
}
case IOCTL_SERIAL_SET_BAUD_RATE:
{
if (pServerSerialDriver->set_baud_rate)
{
SERIAL_BAUD_RATE* pBaudRate = (SERIAL_BAUD_RATE*)lpInBuffer;
WINPR_ASSERT(nInBufferSize >= sizeof(SERIAL_BAUD_RATE));
if (nInBufferSize < sizeof(SERIAL_BAUD_RATE))
{
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
return pServerSerialDriver->set_baud_rate(pComm, pBaudRate);
}
break;
}
case IOCTL_SERIAL_GET_BAUD_RATE:
{
if (pServerSerialDriver->get_baud_rate)
{
SERIAL_BAUD_RATE* pBaudRate = (SERIAL_BAUD_RATE*)lpOutBuffer;
WINPR_ASSERT(nOutBufferSize >= sizeof(SERIAL_BAUD_RATE));
if (nOutBufferSize < sizeof(SERIAL_BAUD_RATE))
{
SetLastError(ERROR_INSUFFICIENT_BUFFER);
return FALSE;
}
if (!pServerSerialDriver->get_baud_rate(pComm, pBaudRate))
return FALSE;
*lpBytesReturned = sizeof(SERIAL_BAUD_RATE);
return TRUE;
}
break;
}
case IOCTL_SERIAL_GET_PROPERTIES:
{
if (pServerSerialDriver->get_properties)
{
COMMPROP* pProperties = (COMMPROP*)lpOutBuffer;
WINPR_ASSERT(nOutBufferSize >= sizeof(COMMPROP));
if (nOutBufferSize < sizeof(COMMPROP))
{
SetLastError(ERROR_INSUFFICIENT_BUFFER);
return FALSE;
}
if (!pServerSerialDriver->get_properties(pComm, pProperties))
return FALSE;
*lpBytesReturned = sizeof(COMMPROP);
return TRUE;
}
break;
}
case IOCTL_SERIAL_SET_CHARS:
{
if (pServerSerialDriver->set_serial_chars)
{
SERIAL_CHARS* pSerialChars = (SERIAL_CHARS*)lpInBuffer;
WINPR_ASSERT(nInBufferSize >= sizeof(SERIAL_CHARS));
if (nInBufferSize < sizeof(SERIAL_CHARS))
{
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
return pServerSerialDriver->set_serial_chars(pComm, pSerialChars);
}
break;
}
case IOCTL_SERIAL_GET_CHARS:
{
if (pServerSerialDriver->get_serial_chars)
{
SERIAL_CHARS* pSerialChars = (SERIAL_CHARS*)lpOutBuffer;
WINPR_ASSERT(nOutBufferSize >= sizeof(SERIAL_CHARS));
if (nOutBufferSize < sizeof(SERIAL_CHARS))
{
SetLastError(ERROR_INSUFFICIENT_BUFFER);
return FALSE;
}
if (!pServerSerialDriver->get_serial_chars(pComm, pSerialChars))
return FALSE;
*lpBytesReturned = sizeof(SERIAL_CHARS);
return TRUE;
}
break;
}
case IOCTL_SERIAL_SET_LINE_CONTROL:
{
if (pServerSerialDriver->set_line_control)
{
SERIAL_LINE_CONTROL* pLineControl = (SERIAL_LINE_CONTROL*)lpInBuffer;
WINPR_ASSERT(nInBufferSize >= sizeof(SERIAL_LINE_CONTROL));
if (nInBufferSize < sizeof(SERIAL_LINE_CONTROL))
{
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
return pServerSerialDriver->set_line_control(pComm, pLineControl);
}
break;
}
case IOCTL_SERIAL_GET_LINE_CONTROL:
{
if (pServerSerialDriver->get_line_control)
{
SERIAL_LINE_CONTROL* pLineControl = (SERIAL_LINE_CONTROL*)lpOutBuffer;
WINPR_ASSERT(nOutBufferSize >= sizeof(SERIAL_LINE_CONTROL));
if (nOutBufferSize < sizeof(SERIAL_LINE_CONTROL))
{
SetLastError(ERROR_INSUFFICIENT_BUFFER);
return FALSE;
}
if (!pServerSerialDriver->get_line_control(pComm, pLineControl))
return FALSE;
*lpBytesReturned = sizeof(SERIAL_LINE_CONTROL);
return TRUE;
}
break;
}
case IOCTL_SERIAL_SET_HANDFLOW:
{
if (pServerSerialDriver->set_handflow)
{
SERIAL_HANDFLOW* pHandflow = (SERIAL_HANDFLOW*)lpInBuffer;
WINPR_ASSERT(nInBufferSize >= sizeof(SERIAL_HANDFLOW));
if (nInBufferSize < sizeof(SERIAL_HANDFLOW))
{
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
return pServerSerialDriver->set_handflow(pComm, pHandflow);
}
break;
}
case IOCTL_SERIAL_GET_HANDFLOW:
{
if (pServerSerialDriver->get_handflow)
{
SERIAL_HANDFLOW* pHandflow = (SERIAL_HANDFLOW*)lpOutBuffer;
WINPR_ASSERT(nOutBufferSize >= sizeof(SERIAL_HANDFLOW));
if (nOutBufferSize < sizeof(SERIAL_HANDFLOW))
{
SetLastError(ERROR_INSUFFICIENT_BUFFER);
return FALSE;
}
if (!pServerSerialDriver->get_handflow(pComm, pHandflow))
return FALSE;
*lpBytesReturned = sizeof(SERIAL_HANDFLOW);
return TRUE;
}
break;
}
case IOCTL_SERIAL_SET_TIMEOUTS:
{
if (pServerSerialDriver->set_timeouts)
{
SERIAL_TIMEOUTS* pHandflow = (SERIAL_TIMEOUTS*)lpInBuffer;
WINPR_ASSERT(nInBufferSize >= sizeof(SERIAL_TIMEOUTS));
if (nInBufferSize < sizeof(SERIAL_TIMEOUTS))
{
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
return pServerSerialDriver->set_timeouts(pComm, pHandflow);
}
break;
}
case IOCTL_SERIAL_GET_TIMEOUTS:
{
if (pServerSerialDriver->get_timeouts)
{
SERIAL_TIMEOUTS* pHandflow = (SERIAL_TIMEOUTS*)lpOutBuffer;
WINPR_ASSERT(nOutBufferSize >= sizeof(SERIAL_TIMEOUTS));
if (nOutBufferSize < sizeof(SERIAL_TIMEOUTS))
{
SetLastError(ERROR_INSUFFICIENT_BUFFER);
return FALSE;
}
if (!pServerSerialDriver->get_timeouts(pComm, pHandflow))
return FALSE;
*lpBytesReturned = sizeof(SERIAL_TIMEOUTS);
return TRUE;
}
break;
}
case IOCTL_SERIAL_SET_DTR:
{
if (pServerSerialDriver->set_dtr)
{
return pServerSerialDriver->set_dtr(pComm);
}
break;
}
case IOCTL_SERIAL_CLR_DTR:
{
if (pServerSerialDriver->clear_dtr)
{
return pServerSerialDriver->clear_dtr(pComm);
}
break;
}
case IOCTL_SERIAL_SET_RTS:
{
if (pServerSerialDriver->set_rts)
{
return pServerSerialDriver->set_rts(pComm);
}
break;
}
case IOCTL_SERIAL_CLR_RTS:
{
if (pServerSerialDriver->clear_rts)
{
return pServerSerialDriver->clear_rts(pComm);
}
break;
}
case IOCTL_SERIAL_GET_MODEMSTATUS:
{
if (pServerSerialDriver->get_modemstatus)
{
ULONG* pRegister = (ULONG*)lpOutBuffer;
WINPR_ASSERT(nOutBufferSize >= sizeof(ULONG));
if (nOutBufferSize < sizeof(ULONG))
{
SetLastError(ERROR_INSUFFICIENT_BUFFER);
return FALSE;
}
if (!pServerSerialDriver->get_modemstatus(pComm, pRegister))
return FALSE;
CommLog_Print(WLOG_DEBUG, "modem status %s" PRIx32,
comm_ioctl_modem_status_string(*pRegister, buffer, sizeof(buffer)));
*lpBytesReturned = sizeof(ULONG);
return TRUE;
}
break;
}
case IOCTL_SERIAL_SET_WAIT_MASK:
{
if (pServerSerialDriver->set_wait_mask)
{
ULONG* pWaitMask = (ULONG*)lpInBuffer;
WINPR_ASSERT(nInBufferSize >= sizeof(ULONG));
if (nInBufferSize < sizeof(ULONG))
{
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
const BOOL rc = pServerSerialDriver->set_wait_mask(pComm, pWaitMask);
CommLog_Print(WLOG_DEBUG, "set_wait_mask %s -> %d",
CommSerialEvString(*pWaitMask, buffer, sizeof(buffer)), rc);
return rc;
}
break;
}
case IOCTL_SERIAL_GET_WAIT_MASK:
{
if (pServerSerialDriver->get_wait_mask)
{
ULONG* pWaitMask = (ULONG*)lpOutBuffer;
WINPR_ASSERT(nOutBufferSize >= sizeof(ULONG));
if (nOutBufferSize < sizeof(ULONG))
{
SetLastError(ERROR_INSUFFICIENT_BUFFER);
return FALSE;
}
if (!pServerSerialDriver->get_wait_mask(pComm, pWaitMask))
return FALSE;
CommLog_Print(WLOG_DEBUG, "get_wait_mask %s",
CommSerialEvString(*pWaitMask, buffer, sizeof(buffer)));
*lpBytesReturned = sizeof(ULONG);
return TRUE;
}
break;
}
case IOCTL_SERIAL_WAIT_ON_MASK:
{
if (pServerSerialDriver->wait_on_mask)
{
ULONG* pOutputMask = (ULONG*)lpOutBuffer;
WINPR_ASSERT(nOutBufferSize >= sizeof(ULONG));
if (nOutBufferSize < sizeof(ULONG))
{
SetLastError(ERROR_INSUFFICIENT_BUFFER);
return FALSE;
}
const BOOL rc = pServerSerialDriver->wait_on_mask(pComm, pOutputMask);
*lpBytesReturned = sizeof(ULONG);
CommLog_Print(WLOG_DEBUG, "wait_on_mask %s -> %d",
CommSerialEvString(*pOutputMask, buffer, sizeof(buffer)), rc);
return rc;
}
break;
}
case IOCTL_SERIAL_SET_QUEUE_SIZE:
{
if (pServerSerialDriver->set_queue_size)
{
SERIAL_QUEUE_SIZE* pQueueSize = (SERIAL_QUEUE_SIZE*)lpInBuffer;
WINPR_ASSERT(nInBufferSize >= sizeof(SERIAL_QUEUE_SIZE));
if (nInBufferSize < sizeof(SERIAL_QUEUE_SIZE))
{
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
return pServerSerialDriver->set_queue_size(pComm, pQueueSize);
}
break;
}
case IOCTL_SERIAL_PURGE:
{
if (pServerSerialDriver->purge)
{
ULONG* pPurgeMask = (ULONG*)lpInBuffer;
WINPR_ASSERT(nInBufferSize >= sizeof(ULONG));
if (nInBufferSize < sizeof(ULONG))
{
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
return pServerSerialDriver->purge(pComm, pPurgeMask);
}
break;
}
case IOCTL_SERIAL_GET_COMMSTATUS:
{
if (pServerSerialDriver->get_commstatus)
{
SERIAL_STATUS* pCommstatus = (SERIAL_STATUS*)lpOutBuffer;
WINPR_ASSERT(nOutBufferSize >= sizeof(SERIAL_STATUS));
if (nOutBufferSize < sizeof(SERIAL_STATUS))
{
SetLastError(ERROR_INSUFFICIENT_BUFFER);
return FALSE;
}
if (!pServerSerialDriver->get_commstatus(pComm, pCommstatus))
return FALSE;
*lpBytesReturned = sizeof(SERIAL_STATUS);
return TRUE;
}
break;
}
case IOCTL_SERIAL_SET_BREAK_ON:
{
if (pServerSerialDriver->set_break_on)
{
return pServerSerialDriver->set_break_on(pComm);
}
break;
}
case IOCTL_SERIAL_SET_BREAK_OFF:
{
if (pServerSerialDriver->set_break_off)
{
return pServerSerialDriver->set_break_off(pComm);
}
break;
}
case IOCTL_SERIAL_SET_XOFF:
{
if (pServerSerialDriver->set_xoff)
{
return pServerSerialDriver->set_xoff(pComm);
}
break;
}
case IOCTL_SERIAL_SET_XON:
{
if (pServerSerialDriver->set_xon)
{
return pServerSerialDriver->set_xon(pComm);
}
break;
}
case IOCTL_SERIAL_GET_DTRRTS:
{
if (pServerSerialDriver->get_dtrrts)
{
ULONG* pMask = (ULONG*)lpOutBuffer;
WINPR_ASSERT(nOutBufferSize >= sizeof(ULONG));
if (nOutBufferSize < sizeof(ULONG))
{
SetLastError(ERROR_INSUFFICIENT_BUFFER);
return FALSE;
}
if (!pServerSerialDriver->get_dtrrts(pComm, pMask))
return FALSE;
*lpBytesReturned = sizeof(ULONG);
return TRUE;
}
break;
}
case IOCTL_SERIAL_CONFIG_SIZE:
{
if (pServerSerialDriver->config_size)
{
ULONG* pSize = (ULONG*)lpOutBuffer;
WINPR_ASSERT(nOutBufferSize >= sizeof(ULONG));
if (nOutBufferSize < sizeof(ULONG))
{
SetLastError(ERROR_INSUFFICIENT_BUFFER);
return FALSE;
}
if (!pServerSerialDriver->config_size(pComm, pSize))
return FALSE;
*lpBytesReturned = sizeof(ULONG);
return TRUE;
}
break;
}
case IOCTL_SERIAL_IMMEDIATE_CHAR:
{
if (pServerSerialDriver->immediate_char)
{
UCHAR* pChar = (UCHAR*)lpInBuffer;
WINPR_ASSERT(nInBufferSize >= sizeof(UCHAR));
if (nInBufferSize < sizeof(UCHAR))
{
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
return pServerSerialDriver->immediate_char(pComm, pChar);
}
break;
}
case IOCTL_SERIAL_RESET_DEVICE:
{
if (pServerSerialDriver->reset_device)
{
return pServerSerialDriver->reset_device(pComm);
}
break;
}
default:
break;
}
CommLog_Print(
WLOG_WARN, _T("unsupported IoControlCode=[0x%08" PRIX32 "] %s (remote serial driver: %s)"),
dwIoControlCode, _comm_serial_ioctl_name(dwIoControlCode), pServerSerialDriver->name);
SetLastError(ERROR_CALL_NOT_IMPLEMENTED); /* => STATUS_NOT_IMPLEMENTED */
return FALSE;
}
/**
* 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
* ERROR_NOT_SUPPORTED lpOverlapped is not supported
* ERROR_INSUFFICIENT_BUFFER
* ERROR_CALL_NOT_IMPLEMENTED unimplemented ioctl
*/
BOOL CommDeviceIoControl(HANDLE hDevice, DWORD dwIoControlCode, LPVOID lpInBuffer,
DWORD nInBufferSize, LPVOID lpOutBuffer, DWORD nOutBufferSize,
LPDWORD lpBytesReturned, LPOVERLAPPED lpOverlapped)
{
WINPR_COMM* pComm = (WINPR_COMM*)hDevice;
BOOL result = 0;
if (hDevice == INVALID_HANDLE_VALUE)
{
SetLastError(ERROR_INVALID_HANDLE);
return FALSE;
}
if (!CommIsHandled(hDevice))
return FALSE;
if (!pComm->fd)
{
SetLastError(ERROR_INVALID_HANDLE);
return FALSE;
}
result = s_CommDeviceIoControl(hDevice, dwIoControlCode, lpInBuffer, nInBufferSize, lpOutBuffer,
nOutBufferSize, lpBytesReturned, lpOverlapped);
if (lpBytesReturned && *lpBytesReturned != nOutBufferSize)
{
/* This might be a hint for a bug, especially when result==TRUE */
CommLog_Print(WLOG_WARN,
"IoControlCode=[0x%08" PRIX32 "] %s: lpBytesReturned=%" PRIu32
" and nOutBufferSize=%" PRIu32 " are different!",
dwIoControlCode, _comm_serial_ioctl_name(dwIoControlCode), *lpBytesReturned,
nOutBufferSize);
}
if (pComm->permissive)
{
if (!result)
{
CommLog_Print(WLOG_WARN,
"[permissive]: IoControlCode=[0x%08" PRIX32 "] %s failed, ignoring",
dwIoControlCode, _comm_serial_ioctl_name(dwIoControlCode));
}
return TRUE; /* always! */
}
return result;
}
int comm_ioctl_tcsetattr(int fd, int optional_actions, const struct termios* termios_p)
{
struct termios currentState = WINPR_C_ARRAY_INIT;
size_t count = 0;
do
{
const int src = tcsetattr(fd, optional_actions, termios_p);
if (src < 0)
{
char buffer[64] = WINPR_C_ARRAY_INIT;
CommLog_Print(WLOG_WARN, "[%" PRIuz "] tcsetattr failure, errno: %s [%d]", count,
winpr_strerror(errno, buffer, sizeof(buffer)), errno);
return src;
}
/* NB: tcsetattr() can succeed even if not all changes have been applied. */
const int rrc = tcgetattr(fd, &currentState);
if (rrc < 0)
{
char buffer[64] = WINPR_C_ARRAY_INIT;
CommLog_Print(WLOG_WARN, "[%" PRIuz "] tcgetattr failure, errno: %s [%d]", count,
winpr_strerror(errno, buffer, sizeof(buffer)), errno);
return rrc;
}
// NOLINTNEXTLINE(bugprone-suspicious-memory-comparison,cert-exp42-c,cert-flp37-c)
} while ((memcmp(&currentState, termios_p, sizeof(struct termios)) != 0) && (count++ < 2));
return 0;
}
static const char* comm_ioctl_modem_flag_str(ULONG flag)
{
switch (flag)
{
case SERIAL_MSR_DCTS:
return "SERIAL_MSR_DCTS";
case SERIAL_MSR_DDSR:
return "SERIAL_MSR_DDSR";
case SERIAL_MSR_TERI:
return "SERIAL_MSR_TERI";
case SERIAL_MSR_DDCD:
return "SERIAL_MSR_DDCD";
case SERIAL_MSR_CTS:
return "SERIAL_MSR_CTS";
case SERIAL_MSR_DSR:
return "SERIAL_MSR_DSR";
case SERIAL_MSR_RI:
return "SERIAL_MSR_RI";
case SERIAL_MSR_DCD:
return "SERIAL_MSR_DCD";
default:
return "SERIAL_MSR_UNKNOWN";
}
}
const char* comm_ioctl_modem_status_string(ULONG status, char* buffer, size_t size)
{
const ULONG flags[] = { SERIAL_MSR_DCTS, SERIAL_MSR_DDSR, SERIAL_MSR_TERI, SERIAL_MSR_DDCD,
SERIAL_MSR_CTS, SERIAL_MSR_DSR, SERIAL_MSR_RI, SERIAL_MSR_DCD
};
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(comm_ioctl_modem_flag_str(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;
}

View File

@@ -0,0 +1,232 @@
/**
* 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 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.
*/
#ifndef WINPR_COMM_IOCTL_H_
#define WINPR_COMM_IOCTL_H_
#include <termios.h>
#include <winpr/io.h>
#include <winpr/tchar.h>
#include <winpr/wtypes.h>
#include "comm.h"
/* Serial I/O Request Interface: http://msdn.microsoft.com/en-us/library/dn265347%28v=vs.85%29.aspx
* Ntddser.h http://msdn.microsoft.com/en-us/cc308432.aspx
* Ntddpar.h http://msdn.microsoft.com/en-us/cc308431.aspx
*/
#ifdef __cplusplus
extern "C"
{
#endif
/* TODO: defines and types below are very similar to those in comm.h, keep only
* those that differ more than the names */
#define STOP_BIT_1 0
#define STOP_BITS_1_5 1
#define STOP_BITS_2 2
#define NO_PARITY 0
#define ODD_PARITY 1
#define EVEN_PARITY 2
#define MARK_PARITY 3
#define SPACE_PARITY 4
typedef struct
{
ULONG BaudRate;
} SERIAL_BAUD_RATE, *PSERIAL_BAUD_RATE;
typedef struct
{
UCHAR EofChar;
UCHAR ErrorChar;
UCHAR BreakChar;
UCHAR EventChar;
UCHAR XonChar;
UCHAR XoffChar;
} SERIAL_CHARS, *PSERIAL_CHARS;
typedef struct
{
UCHAR StopBits;
UCHAR Parity;
UCHAR WordLength;
} SERIAL_LINE_CONTROL, *PSERIAL_LINE_CONTROL;
typedef struct
{
ULONG ControlHandShake;
ULONG FlowReplace;
ULONG XonLimit;
ULONG XoffLimit;
} SERIAL_HANDFLOW, *PSERIAL_HANDFLOW;
#define SERIAL_DTR_MASK ((ULONG)0x03)
#define SERIAL_DTR_CONTROL ((ULONG)0x01)
#define SERIAL_DTR_HANDSHAKE ((ULONG)0x02)
#define SERIAL_CTS_HANDSHAKE ((ULONG)0x08)
#define SERIAL_DSR_HANDSHAKE ((ULONG)0x10)
#define SERIAL_DCD_HANDSHAKE ((ULONG)0x20)
#define SERIAL_OUT_HANDSHAKEMASK ((ULONG)0x38)
#define SERIAL_DSR_SENSITIVITY ((ULONG)0x40)
#define SERIAL_ERROR_ABORT ((ULONG)0x80000000)
#define SERIAL_CONTROL_INVALID ((ULONG)0x7fffff84)
#define SERIAL_AUTO_TRANSMIT ((ULONG)0x01)
#define SERIAL_AUTO_RECEIVE ((ULONG)0x02)
#define SERIAL_ERROR_CHAR ((ULONG)0x04)
#define SERIAL_NULL_STRIPPING ((ULONG)0x08)
#define SERIAL_BREAK_CHAR ((ULONG)0x10)
#define SERIAL_RTS_MASK ((ULONG)0xc0)
#define SERIAL_RTS_CONTROL ((ULONG)0x40)
#define SERIAL_RTS_HANDSHAKE ((ULONG)0x80)
#define SERIAL_TRANSMIT_TOGGLE ((ULONG)0xc0)
#define SERIAL_XOFF_CONTINUE ((ULONG)0x80000000)
#define SERIAL_FLOW_INVALID ((ULONG)0x7fffff20)
#define SERIAL_SP_SERIALCOMM ((ULONG)0x00000001)
#define SERIAL_SP_UNSPECIFIED ((ULONG)0x00000000)
#define SERIAL_SP_RS232 ((ULONG)0x00000001)
#define SERIAL_SP_PARALLEL ((ULONG)0x00000002)
#define SERIAL_SP_RS422 ((ULONG)0x00000003)
#define SERIAL_SP_RS423 ((ULONG)0x00000004)
#define SERIAL_SP_RS449 ((ULONG)0x00000005)
#define SERIAL_SP_MODEM ((ULONG)0x00000006)
#define SERIAL_SP_FAX ((ULONG)0x00000021)
#define SERIAL_SP_SCANNER ((ULONG)0x00000022)
#define SERIAL_SP_BRIDGE ((ULONG)0x00000100)
#define SERIAL_SP_LAT ((ULONG)0x00000101)
#define SERIAL_SP_TELNET ((ULONG)0x00000102)
#define SERIAL_SP_X25 ((ULONG)0x00000103)
typedef struct
{
ULONG ReadIntervalTimeout;
ULONG ReadTotalTimeoutMultiplier;
ULONG ReadTotalTimeoutConstant;
ULONG WriteTotalTimeoutMultiplier;
ULONG WriteTotalTimeoutConstant;
} SERIAL_TIMEOUTS, *PSERIAL_TIMEOUTS;
#define SERIAL_MSR_DCTS 0x01
#define SERIAL_MSR_DDSR 0x02
#define SERIAL_MSR_TERI 0x04
#define SERIAL_MSR_DDCD 0x08
#define SERIAL_MSR_CTS 0x10
#define SERIAL_MSR_DSR 0x20
#define SERIAL_MSR_RI 0x40
#define SERIAL_MSR_DCD 0x80
typedef struct
{
ULONG InSize;
ULONG OutSize;
} SERIAL_QUEUE_SIZE, *PSERIAL_QUEUE_SIZE;
#define SERIAL_PURGE_TXABORT 0x00000001
#define SERIAL_PURGE_RXABORT 0x00000002
#define SERIAL_PURGE_TXCLEAR 0x00000004
#define SERIAL_PURGE_RXCLEAR 0x00000008
typedef struct
{
ULONG Errors;
ULONG HoldReasons;
ULONG AmountInInQueue;
ULONG AmountInOutQueue;
BOOLEAN EofReceived;
BOOLEAN WaitForImmediate;
} SERIAL_STATUS, *PSERIAL_STATUS;
#define SERIAL_TX_WAITING_FOR_CTS ((ULONG)0x00000001)
#define SERIAL_TX_WAITING_FOR_DSR ((ULONG)0x00000002)
#define SERIAL_TX_WAITING_FOR_DCD ((ULONG)0x00000004)
#define SERIAL_TX_WAITING_FOR_XON ((ULONG)0x00000008)
#define SERIAL_TX_WAITING_XOFF_SENT ((ULONG)0x00000010)
#define SERIAL_TX_WAITING_ON_BREAK ((ULONG)0x00000020)
#define SERIAL_RX_WAITING_FOR_DSR ((ULONG)0x00000040)
#define SERIAL_ERROR_BREAK ((ULONG)0x00000001)
#define SERIAL_ERROR_FRAMING ((ULONG)0x00000002)
#define SERIAL_ERROR_OVERRUN ((ULONG)0x00000004)
#define SERIAL_ERROR_QUEUEOVERRUN ((ULONG)0x00000008)
#define SERIAL_ERROR_PARITY ((ULONG)0x00000010)
#define SERIAL_DTR_STATE ((ULONG)0x00000001)
#define SERIAL_RTS_STATE ((ULONG)0x00000002)
#define SERIAL_CTS_STATE ((ULONG)0x00000010)
#define SERIAL_DSR_STATE ((ULONG)0x00000020)
#define SERIAL_RI_STATE ((ULONG)0x00000040)
#define SERIAL_DCD_STATE ((ULONG)0x00000080)
/**
* A function might be nullptr if not supported by the underlying driver.
*
* FIXME: better have to use input and output buffers for all functions?
*/
typedef struct
{
SERIAL_DRIVER_ID id;
TCHAR* name;
BOOL (*set_baud_rate)(WINPR_COMM* pComm, const SERIAL_BAUD_RATE* pBaudRate);
BOOL (*get_baud_rate)(WINPR_COMM* pComm, SERIAL_BAUD_RATE* pBaudRate);
BOOL (*get_properties)(WINPR_COMM* pComm, COMMPROP* pProperties);
BOOL (*set_serial_chars)(WINPR_COMM* pComm, const SERIAL_CHARS* pSerialChars);
BOOL (*get_serial_chars)(WINPR_COMM* pComm, SERIAL_CHARS* pSerialChars);
BOOL (*set_line_control)(WINPR_COMM* pComm, const SERIAL_LINE_CONTROL* pLineControl);
BOOL (*get_line_control)(WINPR_COMM* pComm, SERIAL_LINE_CONTROL* pLineControl);
BOOL (*set_handflow)(WINPR_COMM* pComm, const SERIAL_HANDFLOW* pHandflow);
BOOL (*get_handflow)(WINPR_COMM* pComm, SERIAL_HANDFLOW* pHandflow);
BOOL (*set_timeouts)(WINPR_COMM* pComm, const SERIAL_TIMEOUTS* pTimeouts);
BOOL (*get_timeouts)(WINPR_COMM* pComm, SERIAL_TIMEOUTS* pTimeouts);
BOOL (*set_dtr)(WINPR_COMM* pComm);
BOOL (*clear_dtr)(WINPR_COMM* pComm);
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);
BOOL (*set_queue_size)(WINPR_COMM* pComm, const SERIAL_QUEUE_SIZE* pQueueSize);
BOOL (*purge)(WINPR_COMM* pComm, const ULONG* pPurgeMask);
BOOL (*get_commstatus)(WINPR_COMM* pComm, SERIAL_STATUS* pCommstatus);
BOOL (*set_break_on)(WINPR_COMM* pComm);
BOOL (*set_break_off)(WINPR_COMM* pComm);
BOOL (*set_xoff)(WINPR_COMM* pComm);
BOOL (*set_xon)(WINPR_COMM* pComm);
BOOL (*get_dtrrts)(WINPR_COMM* pComm, ULONG* pMask);
BOOL (*config_size)(WINPR_COMM* pComm, ULONG* pSize);
BOOL (*immediate_char)(WINPR_COMM* pComm, const UCHAR* pChar);
BOOL (*reset_device)(WINPR_COMM* pComm);
} SERIAL_DRIVER;
int comm_ioctl_tcsetattr(int fd, int optional_actions, const struct termios* termios_p);
#ifdef __cplusplus
}
#endif
#endif /* WINPR_COMM_IOCTL_H_ */

View File

@@ -0,0 +1,87 @@
/**
* WinPR: Windows Portable Runtime
* Serial Communication API - Dummy implementation
*
* Copyright 2024 Armin Novak <anovak@thincast.com>
* Copyright 2024 Thincast Technologies GmbH
*
* 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/comm.h>
#include "comm_ioctl.h"
#include <../log.h>
#define TAG WINPR_TAG("comm")
BOOL CommDeviceIoControl(HANDLE hDevice, DWORD dwIoControlCode, LPVOID lpInBuffer,
DWORD nInBufferSize, LPVOID lpOutBuffer, DWORD nOutBufferSize,
LPDWORD lpBytesReturned, LPOVERLAPPED lpOverlapped)
{
WINPR_UNUSED(hDevice);
WINPR_UNUSED(dwIoControlCode);
WINPR_UNUSED(lpInBuffer);
WINPR_UNUSED(nInBufferSize);
WINPR_UNUSED(lpOutBuffer);
WINPR_UNUSED(nOutBufferSize);
WINPR_UNUSED(lpBytesReturned);
WINPR_UNUSED(lpOverlapped);
WLog_ERR(TAG, "TODO: Function not implemented for this platform");
return FALSE;
}
int comm_ioctl_tcsetattr(int fd, int optional_actions, const struct termios* termios_p)
{
WINPR_UNUSED(fd);
WINPR_UNUSED(optional_actions);
WINPR_UNUSED(termios_p);
WLog_ERR(TAG, "TODO: Function not implemented for this platform");
return -1;
}
BOOL _comm_set_permissive(HANDLE hDevice, BOOL permissive)
{
WINPR_UNUSED(hDevice);
WINPR_UNUSED(permissive);
WLog_ERR(TAG, "TODO: Function not implemented for this platform");
return FALSE;
}
BOOL CommReadFile(HANDLE hDevice, LPVOID lpBuffer, DWORD nNumberOfBytesToRead,
LPDWORD lpNumberOfBytesRead, LPOVERLAPPED lpOverlapped)
{
WINPR_UNUSED(hDevice);
WINPR_UNUSED(lpBuffer);
WINPR_UNUSED(nNumberOfBytesToRead);
WINPR_UNUSED(lpNumberOfBytesRead);
WINPR_UNUSED(lpOverlapped);
WLog_ERR(TAG, "TODO: Function not implemented for this platform");
return FALSE;
}
BOOL CommWriteFile(HANDLE hDevice, LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite,
LPDWORD lpNumberOfBytesWritten, LPOVERLAPPED lpOverlapped)
{
WINPR_UNUSED(hDevice);
WINPR_UNUSED(lpBuffer);
WINPR_UNUSED(nNumberOfBytesToWrite);
WINPR_UNUSED(lpNumberOfBytesWritten);
WINPR_UNUSED(lpOverlapped);
WLog_ERR(TAG, "TODO: Function not implemented for this platform");
return FALSE;
}

View File

@@ -0,0 +1,210 @@
/**
* 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/assert.h>
#include <winpr/wlog.h>
#include "comm_serial_sys.h"
#include "comm_sercx_sys.h"
#include "comm_sercx2_sys.h"
/* http://msdn.microsoft.com/en-us/library/dn265347%28v=vs.85%29.aspx
*
* SerCx2 does not support special characters. SerCx2 always completes
* an IOCTL_SERIAL_SET_CHARS request with a STATUS_SUCCESS status
* code, but does not set any special characters or perform any other
* operation in response to this request. For an
* IOCTL_SERIAL_GET_CHARS request, SerCx2 sets all the character
* values in the SERIAL_CHARS structure to null, and completes the
* request with a STATUS_SUCCESS status code.
*/
static BOOL set_serial_chars(WINPR_ATTR_UNUSED WINPR_COMM* pComm,
WINPR_ATTR_UNUSED const SERIAL_CHARS* pSerialChars)
{
WINPR_ASSERT(pComm);
WINPR_ASSERT(pSerialChars);
return TRUE;
}
static BOOL get_serial_chars(WINPR_ATTR_UNUSED WINPR_COMM* pComm, SERIAL_CHARS* pSerialChars)
{
WINPR_ASSERT(pComm);
WINPR_ASSERT(pSerialChars);
ZeroMemory(pSerialChars, sizeof(SERIAL_CHARS));
return TRUE;
}
/* 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)
{
const SERIAL_DRIVER* pSerialSys = SerialSys_s();
WINPR_ASSERT(pComm);
WINPR_ASSERT(pWaitMask);
WINPR_ASSERT(pSerialSys);
const ULONG possibleMask = *pWaitMask & SERCX2_SYS_SUPPORTED_EV_MASK;
if (possibleMask != *pWaitMask)
{
CommLog_Print(WLOG_WARN,
"Not all wait events supported (SerCx2.sys), requested events= 0x%08" PRIX32
", possible events= 0x%08" PRIX32 "",
*pWaitMask, possibleMask);
/* FIXME: shall we really set the possibleMask and return FALSE? */
pComm->WaitEventMask = possibleMask;
return FALSE;
}
/* NB: All events that are supported by SerCx.sys are supported by Serial.sys*/
return pSerialSys->set_wait_mask(pComm, pWaitMask);
}
static BOOL purge(WINPR_COMM* pComm, const ULONG* pPurgeMask)
{
const SERIAL_DRIVER* pSerialSys = SerialSys_s();
WINPR_ASSERT(pComm);
WINPR_ASSERT(pPurgeMask);
WINPR_ASSERT(pSerialSys);
/* http://msdn.microsoft.com/en-us/library/windows/hardware/ff546655%28v=vs.85%29.aspx */
if ((*pPurgeMask & SERIAL_PURGE_RXCLEAR) && !(*pPurgeMask & SERIAL_PURGE_RXABORT))
{
CommLog_Print(WLOG_WARN,
"Expecting SERIAL_PURGE_RXABORT since SERIAL_PURGE_RXCLEAR is set");
SetLastError(ERROR_INVALID_DEVICE_OBJECT_PARAMETER);
return FALSE;
}
if ((*pPurgeMask & SERIAL_PURGE_TXCLEAR) && !(*pPurgeMask & SERIAL_PURGE_TXABORT))
{
CommLog_Print(WLOG_WARN,
"Expecting SERIAL_PURGE_TXABORT since SERIAL_PURGE_TXCLEAR is set");
SetLastError(ERROR_INVALID_DEVICE_OBJECT_PARAMETER);
return FALSE;
}
return pSerialSys->purge(pComm, pPurgeMask);
}
/* specific functions only */
static SERIAL_DRIVER SerCx2Sys = {
.id = SerialDriverSerCx2Sys,
.name = _T("SerCx2.sys"),
.set_baud_rate = nullptr,
.get_baud_rate = nullptr,
.get_properties = nullptr,
.set_serial_chars = set_serial_chars,
.get_serial_chars = get_serial_chars,
.set_line_control = nullptr,
.get_line_control = nullptr,
.set_handflow = nullptr,
.get_handflow = nullptr,
.set_timeouts = nullptr,
.get_timeouts = nullptr,
.set_dtr = nullptr,
.clear_dtr = nullptr,
.set_rts = nullptr,
.clear_rts = nullptr,
.get_modemstatus = nullptr,
.set_wait_mask = set_wait_mask,
.get_wait_mask = nullptr,
.wait_on_mask = nullptr,
.set_queue_size = nullptr,
.purge = purge,
.get_commstatus = nullptr,
.set_break_on = nullptr,
.set_break_off = nullptr,
.set_xoff = nullptr, /* not supported by SerCx2.sys */
.set_xon = nullptr, /* not supported by SerCx2.sys */
.get_dtrrts = nullptr,
.config_size = nullptr, /* not supported by SerCx2.sys */
.immediate_char = nullptr, /* not supported by SerCx2.sys */
.reset_device = nullptr, /* not supported by SerCx2.sys */
};
const SERIAL_DRIVER* SerCx2Sys_s(void)
{
/* SerCx2Sys completed with inherited functions from SerialSys or SerCxSys */
const SERIAL_DRIVER* pSerialSys = SerialSys_s();
const SERIAL_DRIVER* pSerCxSys = SerCxSys_s();
if (!pSerialSys || !pSerCxSys)
return nullptr;
SerCx2Sys.set_baud_rate = pSerialSys->set_baud_rate;
SerCx2Sys.get_baud_rate = pSerialSys->get_baud_rate;
SerCx2Sys.get_properties = pSerialSys->get_properties;
SerCx2Sys.set_line_control = pSerCxSys->set_line_control;
SerCx2Sys.get_line_control = pSerCxSys->get_line_control;
/* Only SERIAL_CTS_HANDSHAKE, SERIAL_RTS_CONTROL and SERIAL_RTS_HANDSHAKE flags are really
* required by SerCx2.sys http://msdn.microsoft.com/en-us/library/jj680685%28v=vs.85%29.aspx
*/
SerCx2Sys.set_handflow = pSerialSys->set_handflow;
SerCx2Sys.get_handflow = pSerialSys->get_handflow;
SerCx2Sys.set_timeouts = pSerialSys->set_timeouts;
SerCx2Sys.get_timeouts = pSerialSys->get_timeouts;
SerCx2Sys.set_dtr = pSerialSys->set_dtr;
SerCx2Sys.clear_dtr = pSerialSys->clear_dtr;
SerCx2Sys.set_rts = pSerialSys->set_rts;
SerCx2Sys.clear_rts = pSerialSys->clear_rts;
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;
SerCx2Sys.set_queue_size = pSerialSys->set_queue_size;
SerCx2Sys.get_commstatus = pSerialSys->get_commstatus;
SerCx2Sys.set_break_on = pSerialSys->set_break_on;
SerCx2Sys.set_break_off = pSerialSys->set_break_off;
SerCx2Sys.get_dtrrts = pSerialSys->get_dtrrts;
return &SerCx2Sys;
}

View File

@@ -0,0 +1,36 @@
/**
* WinPR: Windows Portable Runtime
* Serial Communication API
*
* 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.
*/
#ifndef COMM_SERCX2_SYS_H
#define COMM_SERCX2_SYS_H
#include "comm_ioctl.h"
#ifdef __cplusplus
extern "C"
{
#endif
const SERIAL_DRIVER* SerCx2Sys_s(void);
#ifdef __cplusplus
}
#endif
#endif /* COMM_SERCX2_SYS_H */

View File

@@ -0,0 +1,261 @@
/**
* 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/assert.h>
#include <termios.h>
#include <winpr/wlog.h>
#include "comm_serial_sys.h"
#include "comm_sercx_sys.h"
static BOOL set_handflow(WINPR_COMM* pComm, const SERIAL_HANDFLOW* pHandflow)
{
SERIAL_HANDFLOW SerCxHandflow;
BOOL result = TRUE;
const SERIAL_DRIVER* pSerialSys = SerialSys_s();
memcpy(&SerCxHandflow, pHandflow, sizeof(SERIAL_HANDFLOW));
/* filter out unsupported bits by SerCx.sys
*
* http://msdn.microsoft.com/en-us/library/windows/hardware/jj680685%28v=vs.85%29.aspx
*/
SerCxHandflow.ControlHandShake =
pHandflow->ControlHandShake &
(SERIAL_DTR_CONTROL | SERIAL_DTR_HANDSHAKE | SERIAL_CTS_HANDSHAKE | SERIAL_DSR_HANDSHAKE);
SerCxHandflow.FlowReplace =
pHandflow->FlowReplace & (SERIAL_RTS_CONTROL | SERIAL_RTS_HANDSHAKE);
if (SerCxHandflow.ControlHandShake != pHandflow->ControlHandShake)
{
if (pHandflow->ControlHandShake & SERIAL_DCD_HANDSHAKE)
{
CommLog_Print(WLOG_WARN,
"SERIAL_DCD_HANDSHAKE not supposed to be implemented by SerCx.sys");
}
if (pHandflow->ControlHandShake & SERIAL_DSR_SENSITIVITY)
{
CommLog_Print(WLOG_WARN,
"SERIAL_DSR_SENSITIVITY not supposed to be implemented by SerCx.sys");
}
if (pHandflow->ControlHandShake & SERIAL_ERROR_ABORT)
{
CommLog_Print(WLOG_WARN,
"SERIAL_ERROR_ABORT not supposed to be implemented by SerCx.sys");
}
SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
result = FALSE;
}
if (SerCxHandflow.FlowReplace != pHandflow->FlowReplace)
{
if (pHandflow->ControlHandShake & SERIAL_AUTO_TRANSMIT)
{
CommLog_Print(WLOG_WARN,
"SERIAL_AUTO_TRANSMIT not supposed to be implemented by SerCx.sys");
}
if (pHandflow->ControlHandShake & SERIAL_AUTO_RECEIVE)
{
CommLog_Print(WLOG_WARN,
"SERIAL_AUTO_RECEIVE not supposed to be implemented by SerCx.sys");
}
if (pHandflow->ControlHandShake & SERIAL_ERROR_CHAR)
{
CommLog_Print(WLOG_WARN,
"SERIAL_ERROR_CHAR not supposed to be implemented by SerCx.sys");
}
if (pHandflow->ControlHandShake & SERIAL_NULL_STRIPPING)
{
CommLog_Print(WLOG_WARN,
"SERIAL_NULL_STRIPPING not supposed to be implemented by SerCx.sys");
}
if (pHandflow->ControlHandShake & SERIAL_BREAK_CHAR)
{
CommLog_Print(WLOG_WARN,
"SERIAL_BREAK_CHAR not supposed to be implemented by SerCx.sys");
}
if (pHandflow->ControlHandShake & SERIAL_XOFF_CONTINUE)
{
CommLog_Print(WLOG_WARN,
"SERIAL_XOFF_CONTINUE not supposed to be implemented by SerCx.sys");
}
SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
result = FALSE;
}
if (!pSerialSys->set_handflow(pComm, &SerCxHandflow))
return FALSE;
return result;
}
static BOOL get_handflow(WINPR_COMM* pComm, SERIAL_HANDFLOW* pHandflow)
{
const SERIAL_DRIVER* pSerialSys = SerialSys_s();
BOOL result = pSerialSys->get_handflow(pComm, pHandflow);
/* filter out unsupported bits by SerCx.sys
*
* http://msdn.microsoft.com/en-us/library/windows/hardware/jj680685%28v=vs.85%29.aspx
*/
pHandflow->ControlHandShake =
pHandflow->ControlHandShake &
(SERIAL_DTR_CONTROL | SERIAL_DTR_HANDSHAKE | SERIAL_CTS_HANDSHAKE | SERIAL_DSR_HANDSHAKE);
pHandflow->FlowReplace = pHandflow->FlowReplace & (SERIAL_RTS_CONTROL | SERIAL_RTS_HANDSHAKE);
return result;
}
/* 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)
{
const SERIAL_DRIVER* pSerialSys = SerialSys_s();
WINPR_ASSERT(pWaitMask);
const ULONG possibleMask = *pWaitMask & SERCX_SYS_SUPPORTED_EV_MASK;
if (possibleMask != *pWaitMask)
{
CommLog_Print(WLOG_WARN,
"Not all wait events supported (SerCx.sys), requested events= 0x%08" PRIX32
", possible events= 0x%08" PRIX32 "",
*pWaitMask, possibleMask);
/* FIXME: shall we really set the possibleMask and return FALSE? */
pComm->WaitEventMask = 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 SERIAL_DRIVER SerCxSys = {
.id = SerialDriverSerCxSys,
.name = _T("SerCx.sys"),
.set_baud_rate = nullptr,
.get_baud_rate = nullptr,
.get_properties = nullptr,
.set_serial_chars = nullptr,
.get_serial_chars = nullptr,
.set_line_control = nullptr,
.get_line_control = nullptr,
.set_handflow = set_handflow,
.get_handflow = get_handflow,
.set_timeouts = nullptr,
.get_timeouts = nullptr,
.set_dtr = nullptr,
.clear_dtr = nullptr,
.set_rts = nullptr,
.clear_rts = nullptr,
.get_modemstatus = nullptr,
.set_wait_mask = set_wait_mask,
.get_wait_mask = nullptr,
.wait_on_mask = nullptr,
.set_queue_size = nullptr,
.purge = nullptr,
.get_commstatus = nullptr,
.set_break_on = nullptr,
.set_break_off = nullptr,
.set_xoff = nullptr,
.set_xon = nullptr,
.get_dtrrts = nullptr,
.config_size = nullptr, /* not supported by SerCx.sys */
.immediate_char = nullptr,
.reset_device = nullptr, /* not supported by SerCx.sys */
};
const SERIAL_DRIVER* SerCxSys_s(void)
{
/* _SerCxSys completed with inherited functions from SerialSys */
const SERIAL_DRIVER* pSerialSys = SerialSys_s();
if (!pSerialSys)
return nullptr;
SerCxSys.set_baud_rate = pSerialSys->set_baud_rate;
SerCxSys.get_baud_rate = pSerialSys->get_baud_rate;
SerCxSys.get_properties = pSerialSys->get_properties;
SerCxSys.set_serial_chars = pSerialSys->set_serial_chars;
SerCxSys.get_serial_chars = pSerialSys->get_serial_chars;
SerCxSys.set_line_control = pSerialSys->set_line_control;
SerCxSys.get_line_control = pSerialSys->get_line_control;
SerCxSys.set_timeouts = pSerialSys->set_timeouts;
SerCxSys.get_timeouts = pSerialSys->get_timeouts;
SerCxSys.set_dtr = pSerialSys->set_dtr;
SerCxSys.clear_dtr = pSerialSys->clear_dtr;
SerCxSys.set_rts = pSerialSys->set_rts;
SerCxSys.clear_rts = pSerialSys->clear_rts;
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;
SerCxSys.set_queue_size = pSerialSys->set_queue_size;
SerCxSys.purge = pSerialSys->purge;
SerCxSys.get_commstatus = pSerialSys->get_commstatus;
SerCxSys.set_break_on = pSerialSys->set_break_on;
SerCxSys.set_break_off = pSerialSys->set_break_off;
SerCxSys.set_xoff = pSerialSys->set_xoff;
SerCxSys.set_xon = pSerialSys->set_xon;
SerCxSys.get_dtrrts = pSerialSys->get_dtrrts;
SerCxSys.immediate_char = pSerialSys->immediate_char;
return &SerCxSys;
}

View File

@@ -0,0 +1,36 @@
/**
* WinPR: Windows Portable Runtime
* Serial Communication API
*
* 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.
*/
#ifndef COMM_SERCX_SYS_H
#define COMM_SERCX_SYS_H
#include "comm_ioctl.h"
#ifdef __cplusplus
extern "C"
{
#endif
const SERIAL_DRIVER* SerCxSys_s(void);
#ifdef __cplusplus
}
#endif
#endif /* COMM_SERCX_SYS_H */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,36 @@
/**
* WinPR: Windows Portable Runtime
* Serial Communication API
*
* 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.
*/
#ifndef COMM_SERIAL_SYS_H
#define COMM_SERIAL_SYS_H
#include "comm_ioctl.h"
#ifdef __cplusplus
extern "C"
{
#endif
const SERIAL_DRIVER* SerialSys_s(void);
#ifdef __cplusplus
}
#endif
#endif /* COMM_SERIAL_SYS_H */

View File

@@ -0,0 +1,34 @@
set(MODULE_NAME "TestComm")
set(MODULE_PREFIX "TEST_COMM")
disable_warnings_for_directory(${CMAKE_CURRENT_BINARY_DIR})
set(${MODULE_PREFIX}_DRIVER ${MODULE_NAME}.c)
set(${MODULE_PREFIX}_TESTS
TestCommDevice.c
TestCommConfig.c
TestGetCommState.c
TestSetCommState.c
TestSerialChars.c
TestControlSettings.c
TestHandflow.c
TestTimeouts.c
TestCommMonitor.c
)
create_test_sourcelist(${MODULE_PREFIX}_SRCS ${${MODULE_PREFIX}_DRIVER} ${${MODULE_PREFIX}_TESTS})
add_executable(${MODULE_NAME} ${${MODULE_PREFIX}_SRCS})
target_link_libraries(${MODULE_NAME} winpr)
set_target_properties(${MODULE_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${TESTING_OUTPUT_DIRECTORY}")
foreach(test ${${MODULE_PREFIX}_TESTS})
get_filename_component(TestName ${test} NAME_WE)
add_test(${TestName} ${TESTING_OUTPUT_DIRECTORY}/${MODULE_NAME} ${TestName})
set_tests_properties(${TestName} PROPERTIES LABELS "comm")
endforeach()
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "WinPR/Test")

View File

@@ -0,0 +1,151 @@
/**
* WinPR: Windows Portable Runtime
* Serial Communication API
*
* 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 <sys/stat.h>
#include <winpr/crt.h>
#include <winpr/comm.h>
#include <winpr/file.h>
#include <winpr/synch.h>
#include <winpr/handle.h>
int TestCommConfig(int argc, char* argv[])
{
DCB dcb = WINPR_C_ARRAY_INIT;
BOOL success = FALSE;
LPCSTR lpFileName = "\\\\.\\COM1";
COMMPROP commProp = WINPR_C_ARRAY_INIT;
struct stat statbuf = WINPR_C_ARRAY_INIT;
HANDLE hComm = CreateFileA(lpFileName, GENERIC_READ | GENERIC_WRITE, 0, nullptr, OPEN_EXISTING,
0, nullptr);
if (hComm && (hComm != INVALID_HANDLE_VALUE))
{
(void)fprintf(
stderr, "CreateFileA failure: could create a handle on a not yet defined device: %s\n",
lpFileName);
return EXIT_FAILURE;
}
if (stat("/dev/ttyS0", &statbuf) < 0)
{
(void)fprintf(stderr, "/dev/ttyS0 not available, making the test to succeed though\n");
return EXIT_SUCCESS;
}
success = DefineCommDevice(lpFileName, "/dev/ttyS0");
if (!success)
{
(void)fprintf(stderr, "DefineCommDevice failure: %s\n", lpFileName);
return EXIT_FAILURE;
}
hComm = CreateFileA(lpFileName, GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_WRITE, /* invalid parameter */
nullptr, CREATE_NEW, /* invalid parameter */
0, (HANDLE)1234); /* invalid parameter */
if (hComm != INVALID_HANDLE_VALUE)
{
(void)fprintf(
stderr, "CreateFileA failure: could create a handle with some invalid parameters %s\n",
lpFileName);
return EXIT_FAILURE;
}
hComm = CreateFileA(lpFileName, GENERIC_READ | GENERIC_WRITE, 0, nullptr, OPEN_EXISTING, 0,
nullptr);
if (!hComm || (hComm == INVALID_HANDLE_VALUE))
{
(void)fprintf(stderr, "CreateFileA failure: %s GetLastError() = 0x%08x\n", lpFileName,
GetLastError());
return EXIT_FAILURE;
}
/* TODO: a second call to CreateFileA should failed and
* GetLastError should return ERROR_SHARING_VIOLATION */
dcb.DCBlength = sizeof(DCB);
success = GetCommState(hComm, &dcb);
if (!success)
{
(void)fprintf(stderr, "GetCommState failure: GetLastError() = Ox%x\n", GetLastError());
return EXIT_FAILURE;
}
(void)fprintf(stderr,
"BaudRate: %" PRIu32 " ByteSize: %" PRIu8 " Parity: %" PRIu8 " StopBits: %" PRIu8
"\n",
dcb.BaudRate, dcb.ByteSize, dcb.Parity, dcb.StopBits);
if (!GetCommProperties(hComm, &commProp))
{
(void)fprintf(stderr, "GetCommProperties failure: GetLastError(): 0x%08x\n",
GetLastError());
return EXIT_FAILURE;
}
if ((commProp.dwSettableBaud & BAUD_57600) <= 0)
{
(void)fprintf(stderr, "BAUD_57600 unsupported!\n");
return EXIT_FAILURE;
}
if ((commProp.dwSettableBaud & BAUD_14400) > 0)
{
(void)fprintf(stderr, "BAUD_14400 supported!\n");
return EXIT_FAILURE;
}
dcb.BaudRate = CBR_57600;
dcb.ByteSize = 8;
dcb.Parity = NOPARITY;
dcb.StopBits = ONESTOPBIT;
success = SetCommState(hComm, &dcb);
if (!success)
{
(void)fprintf(stderr, "SetCommState failure: GetLastError() = 0x%x\n", GetLastError());
return EXIT_FAILURE;
}
success = GetCommState(hComm, &dcb);
if (!success)
{
(void)fprintf(stderr, "GetCommState failure: GetLastError() = 0x%x\n", GetLastError());
return 0;
}
if ((dcb.BaudRate != CBR_57600) || (dcb.ByteSize != 8) || (dcb.Parity != NOPARITY) ||
(dcb.StopBits != ONESTOPBIT))
{
(void)fprintf(stderr,
"Got an unexpected value among: BaudRate: %" PRIu32 " ByteSize: %" PRIu8
" Parity: %" PRIu8 " StopBits: %" PRIu8 "\n",
dcb.BaudRate, dcb.ByteSize, dcb.Parity, dcb.StopBits);
}
(void)CloseHandle(hComm);
return 0;
}

View File

@@ -0,0 +1,115 @@
/**
* WinPR: Windows Portable Runtime
* Serial Communication API
*
* 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 <stdio.h>
#include <winpr/comm.h>
#include <winpr/tchar.h>
static int test_CommDevice(LPCTSTR lpDeviceName, BOOL expectedResult)
{
TCHAR lpTargetPath[MAX_PATH] = WINPR_C_ARRAY_INIT;
BOOL result = DefineCommDevice(lpDeviceName, _T("/dev/test"));
if ((!expectedResult && result) || (expectedResult && !result)) /* logical XOR */
{
_tprintf(_T("DefineCommDevice failure: device name: %s, expected result: %s, result: %s\n"),
lpDeviceName, (expectedResult ? "TRUE" : "FALSE"), (result ? "TRUE" : "FALSE"));
return FALSE;
}
result = IsCommDevice(lpDeviceName);
if ((!expectedResult && result) || (expectedResult && !result)) /* logical XOR */
{
_tprintf(_T("IsCommDevice failure: device name: %s, expected result: %s, result: %s\n"),
lpDeviceName, (expectedResult ? "TRUE" : "FALSE"), (result ? "TRUE" : "FALSE"));
return FALSE;
}
const size_t tclen = QueryCommDevice(lpDeviceName, lpTargetPath, MAX_PATH);
if (expectedResult)
{
const size_t tlen = _tcsnlen(lpTargetPath, ARRAYSIZE(lpTargetPath) - 1);
if (tclen <= tlen) /* at least 2 more TCHAR are expected */
{
_tprintf(_T("QueryCommDevice failure: didn't find the device name: %s\n"),
lpDeviceName);
return FALSE;
}
if (_tcsncmp(_T("/dev/test"), lpTargetPath, ARRAYSIZE(lpTargetPath)) != 0)
{
_tprintf(
_T("QueryCommDevice failure: device name: %s, expected result: %s, result: %s\n"),
lpDeviceName, _T("/dev/test"), lpTargetPath);
return FALSE;
}
if ((tlen >= (ARRAYSIZE(lpTargetPath) - 1)) || (lpTargetPath[tlen + 1] != 0))
{
_tprintf(
_T("QueryCommDevice failure: device name: %s, the second nullptr character is ")
_T("missing at the end of the buffer\n"),
lpDeviceName);
return FALSE;
}
}
else
{
if (tclen > 0)
{
_tprintf(_T("QueryCommDevice failure: device name: %s, expected result: <none>, ")
_T("result: %") _T(PRIuz) _T(" %s\n"),
lpDeviceName, tclen, lpTargetPath);
return FALSE;
}
}
return TRUE;
}
int TestCommDevice(int argc, char* argv[])
{
if (!test_CommDevice(_T("COM0"), FALSE))
return EXIT_FAILURE;
if (!test_CommDevice(_T("COM1"), TRUE))
return EXIT_FAILURE;
if (!test_CommDevice(_T("COM1"), TRUE))
return EXIT_FAILURE;
if (!test_CommDevice(_T("COM10"), FALSE))
return EXIT_FAILURE;
if (!test_CommDevice(_T("\\\\.\\COM5"), TRUE))
return EXIT_FAILURE;
if (!test_CommDevice(_T("\\\\.\\COM10"), TRUE))
return EXIT_FAILURE;
if (!test_CommDevice(_T("\\\\.COM10"), FALSE))
return EXIT_FAILURE;
return 0;
}

View File

@@ -0,0 +1,70 @@
#include <winpr/crt.h>
#include <winpr/comm.h>
#include <winpr/file.h>
#include <winpr/synch.h>
#include <winpr/handle.h>
int TestCommMonitor(int argc, char* argv[])
{
HANDLE hComm = nullptr;
DWORD dwError = 0;
BOOL fSuccess = 0;
DWORD dwEvtMask = 0;
OVERLAPPED overlapped = WINPR_C_ARRAY_INIT;
LPCSTR lpFileName = "\\\\.\\COM1";
hComm = CreateFileA(lpFileName, GENERIC_READ | GENERIC_WRITE, 0, nullptr, OPEN_EXISTING,
FILE_FLAG_OVERLAPPED, nullptr);
if (!hComm || (hComm == INVALID_HANDLE_VALUE))
{
printf("CreateFileA failure: %s\n", lpFileName);
return -1;
}
fSuccess = SetCommMask(hComm, EV_CTS | EV_DSR);
if (!fSuccess)
{
printf("SetCommMask failure: GetLastError() = %" PRIu32 "\n", GetLastError());
return -1;
}
if (!(overlapped.hEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr)))
{
printf("CreateEvent failed: GetLastError() = %" PRIu32 "\n", GetLastError());
return -1;
}
if (WaitCommEvent(hComm, &dwEvtMask, &overlapped))
{
if (dwEvtMask & EV_DSR)
{
printf("EV_DSR\n");
}
if (dwEvtMask & EV_CTS)
{
printf("EV_CTS\n");
}
}
else
{
dwError = GetLastError();
if (dwError == ERROR_IO_PENDING)
{
printf("ERROR_IO_PENDING\n");
}
else
{
printf("WaitCommEvent failure: GetLastError() = %" PRIu32 "\n", dwError);
return -1;
}
}
(void)CloseHandle(hComm);
return 0;
}

View File

@@ -0,0 +1,123 @@
/**
* WinPR: Windows Portable Runtime
* Serial Communication API
*
* 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 <stdio.h>
#include <sys/stat.h>
#include <winpr/comm.h>
#include <winpr/crt.h>
#include "../comm.h"
int TestControlSettings(int argc, char* argv[])
{
struct stat statbuf = WINPR_C_ARRAY_INIT;
BOOL result = 0;
HANDLE hComm = nullptr;
DCB dcb = WINPR_C_ARRAY_INIT;
if (stat("/dev/ttyS0", &statbuf) < 0)
{
(void)fprintf(stderr, "/dev/ttyS0 not available, making the test to succeed though\n");
return EXIT_SUCCESS;
}
result = DefineCommDevice("COM1", "/dev/ttyS0");
if (!result)
{
(void)fprintf(stderr, "DefineCommDevice failure: 0x%x\n", GetLastError());
return EXIT_FAILURE;
}
hComm = CreateFile("COM1", GENERIC_READ | GENERIC_WRITE, 0, nullptr, OPEN_EXISTING, 0, nullptr);
if (hComm == INVALID_HANDLE_VALUE)
{
(void)fprintf(stderr, "CreateFileA failure: 0x%x\n", GetLastError());
return EXIT_FAILURE;
}
ZeroMemory(&dcb, sizeof(DCB));
dcb.DCBlength = sizeof(DCB);
if (!GetCommState(hComm, &dcb))
{
(void)fprintf(stderr, "GetCommState failure; GetLastError(): %08x\n", GetLastError());
return FALSE;
}
/* Test 1 */
dcb.ByteSize = 5;
dcb.StopBits = ONESTOPBIT;
dcb.Parity = MARKPARITY;
if (!SetCommState(hComm, &dcb))
{
(void)fprintf(stderr, "SetCommState failure; GetLastError(): %08x\n", GetLastError());
return FALSE;
}
ZeroMemory(&dcb, sizeof(DCB));
dcb.DCBlength = sizeof(DCB);
if (!GetCommState(hComm, &dcb))
{
(void)fprintf(stderr, "GetCommState failure; GetLastError(): %08x\n", GetLastError());
return FALSE;
}
if ((dcb.ByteSize != 5) || (dcb.StopBits != ONESTOPBIT) || (dcb.Parity != MARKPARITY))
{
(void)fprintf(stderr, "test1 failed.\n");
return FALSE;
}
/* Test 2 */
dcb.ByteSize = 8;
dcb.StopBits = ONESTOPBIT;
dcb.Parity = NOPARITY;
if (!SetCommState(hComm, &dcb))
{
(void)fprintf(stderr, "SetCommState failure; GetLastError(): %08x\n", GetLastError());
return FALSE;
}
ZeroMemory(&dcb, sizeof(DCB));
dcb.DCBlength = sizeof(DCB);
if (!GetCommState(hComm, &dcb))
{
(void)fprintf(stderr, "GetCommState failure; GetLastError(): %08x\n", GetLastError());
return FALSE;
}
if ((dcb.ByteSize != 8) || (dcb.StopBits != ONESTOPBIT) || (dcb.Parity != NOPARITY))
{
(void)fprintf(stderr, "test2 failed.\n");
return FALSE;
}
if (!CloseHandle(hComm))
{
(void)fprintf(stderr, "CloseHandle failure, GetLastError()=%08x\n", GetLastError());
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}

View File

@@ -0,0 +1,140 @@
/**
* WinPR: Windows Portable Runtime
* Serial Communication API
*
* 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 <stdio.h>
#include <sys/stat.h>
#include <winpr/comm.h>
#include <winpr/crt.h>
#include "../comm.h"
static BOOL test_generic(HANDLE hComm)
{
DCB dcb = WINPR_C_ARRAY_INIT;
DCB* pDcb = nullptr;
BOOL result = 0;
ZeroMemory(&dcb, sizeof(DCB));
result = GetCommState(hComm, &dcb);
if (result)
{
printf("GetCommState failure, should have returned false because dcb.DCBlength has been "
"let uninitialized\n");
return FALSE;
}
ZeroMemory(&dcb, sizeof(DCB));
dcb.DCBlength = sizeof(DCB) / 2; /* improper value */
result = GetCommState(hComm, &dcb);
if (result)
{
printf("GetCommState failure, should have return false because dcb.DCBlength was not "
"correctly initialized\n");
return FALSE;
}
ZeroMemory(&dcb, sizeof(DCB));
dcb.DCBlength = sizeof(DCB);
result = GetCommState(hComm, &dcb);
if (!result)
{
printf("GetCommState failure: Ox%x, with adjusted DCBlength\n", GetLastError());
return FALSE;
}
pDcb = (DCB*)calloc(2, sizeof(DCB));
if (!pDcb)
return FALSE;
pDcb->DCBlength = sizeof(DCB) * 2;
result = GetCommState(hComm, pDcb);
result = result && (pDcb->DCBlength == sizeof(DCB) * 2);
free(pDcb);
if (!result)
{
printf("GetCommState failure: 0x%x, with bigger DCBlength\n", GetLastError());
return FALSE;
}
return TRUE;
}
int TestGetCommState(int argc, char* argv[])
{
struct stat statbuf = WINPR_C_ARRAY_INIT;
BOOL result = 0;
HANDLE hComm = nullptr;
if (stat("/dev/ttyS0", &statbuf) < 0)
{
(void)fprintf(stderr, "/dev/ttyS0 not available, making the test to succeed though\n");
return EXIT_SUCCESS;
}
result = DefineCommDevice("COM1", "/dev/ttyS0");
if (!result)
{
printf("DefineCommDevice failure: 0x%x\n", GetLastError());
return EXIT_FAILURE;
}
hComm =
CreateFileA("COM1", GENERIC_READ | GENERIC_WRITE, 0, nullptr, OPEN_EXISTING, 0, nullptr);
if (hComm == INVALID_HANDLE_VALUE)
{
printf("CreateFileA failure: 0x%x\n", GetLastError());
return EXIT_FAILURE;
}
if (!test_generic(hComm))
{
printf("test_generic failure (SerialDriverUnknown)\n");
return EXIT_FAILURE;
}
_comm_setServerSerialDriver(hComm, SerialDriverSerialSys);
if (!test_generic(hComm))
{
printf("test_generic failure (SerialDriverSerialSys)\n");
return EXIT_FAILURE;
}
_comm_setServerSerialDriver(hComm, SerialDriverSerCxSys);
if (!test_generic(hComm))
{
printf("test_generic failure (SerialDriverSerCxSys)\n");
return EXIT_FAILURE;
}
_comm_setServerSerialDriver(hComm, SerialDriverSerCx2Sys);
if (!test_generic(hComm))
{
printf("test_generic failure (SerialDriverSerCx2Sys)\n");
return EXIT_FAILURE;
}
if (!CloseHandle(hComm))
{
(void)fprintf(stderr, "CloseHandle failure, GetLastError()=%08x\n", GetLastError());
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}

View File

@@ -0,0 +1,92 @@
/**
* WinPR: Windows Portable Runtime
* Serial Communication API
*
* 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 <stdio.h>
#include <sys/stat.h>
#ifndef _WIN32
#include <termios.h>
#endif
#include <winpr/comm.h>
#include <winpr/crt.h>
#include "../comm.h"
static BOOL test_SerialSys(HANDLE hComm)
{
// TMP: TODO:
return TRUE;
}
int TestHandflow(int argc, char* argv[])
{
struct stat statbuf = WINPR_C_ARRAY_INIT;
BOOL result = 0;
HANDLE hComm = nullptr;
if (stat("/dev/ttyS0", &statbuf) < 0)
{
(void)fprintf(stderr, "/dev/ttyS0 not available, making the test to succeed though\n");
return EXIT_SUCCESS;
}
result = DefineCommDevice("COM1", "/dev/ttyS0");
if (!result)
{
(void)fprintf(stderr, "DefineCommDevice failure: 0x%x\n", GetLastError());
return EXIT_FAILURE;
}
hComm = CreateFile("COM1", GENERIC_READ | GENERIC_WRITE, 0, nullptr, OPEN_EXISTING, 0, nullptr);
if (hComm == INVALID_HANDLE_VALUE)
{
(void)fprintf(stderr, "CreateFileA failure: 0x%x\n", GetLastError());
return EXIT_FAILURE;
}
_comm_setServerSerialDriver(hComm, SerialDriverSerialSys);
if (!test_SerialSys(hComm))
{
(void)fprintf(stderr, "test_SerCxSys failure\n");
return EXIT_FAILURE;
}
/* _comm_setServerSerialDriver(hComm, SerialDriverSerCxSys); */
/* if (!test_SerCxSys(hComm)) */
/* { */
/* (void)fprintf(stderr, "test_SerCxSys failure\n"); */
/* return EXIT_FAILURE; */
/* } */
/* _comm_setServerSerialDriver(hComm, SerialDriverSerCx2Sys); */
/* if (!test_SerCx2Sys(hComm)) */
/* { */
/* (void)fprintf(stderr, "test_SerCxSys failure\n"); */
/* return EXIT_FAILURE; */
/* } */
if (!CloseHandle(hComm))
{
(void)fprintf(stderr, "CloseHandle failure, GetLastError()=%08x\n", GetLastError());
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}

View File

@@ -0,0 +1,180 @@
/**
* WinPR: Windows Portable Runtime
* Serial Communication API
*
* 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 <stdio.h>
#include <sys/stat.h>
#ifndef _WIN32
#include <termios.h>
#endif
#include <winpr/comm.h>
#include <winpr/crt.h>
#include "../comm.h"
static BOOL test_SerCxSys(HANDLE hComm)
{
DCB dcb = WINPR_C_ARRAY_INIT;
UCHAR XonChar = 0;
UCHAR XoffChar = 0;
struct termios currentTermios = WINPR_C_ARRAY_INIT;
if (tcgetattr(((WINPR_COMM*)hComm)->fd, &currentTermios) < 0)
{
(void)fprintf(stderr, "tcgetattr failure.\n");
return FALSE;
}
dcb.DCBlength = sizeof(DCB);
if (!GetCommState(hComm, &dcb))
{
(void)fprintf(stderr, "GetCommState failure, GetLastError(): 0x%08x\n", GetLastError());
return FALSE;
}
if ((dcb.XonChar == '\0') || (dcb.XoffChar == '\0'))
{
(void)fprintf(stderr, "test_SerCxSys failure, expected XonChar and XoffChar to be set\n");
return FALSE;
}
/* retrieve Xon/Xoff chars */
if ((dcb.XonChar != currentTermios.c_cc[VSTART]) ||
(dcb.XoffChar != currentTermios.c_cc[VSTOP]))
{
(void)fprintf(stderr, "test_SerCxSys failure, could not retrieve XonChar and XoffChar\n");
return FALSE;
}
/* swap XonChar/XoffChar */
XonChar = dcb.XonChar;
XoffChar = dcb.XoffChar;
dcb.XonChar = XoffChar;
dcb.XoffChar = XonChar;
if (!SetCommState(hComm, &dcb))
{
(void)fprintf(stderr, "SetCommState failure, GetLastError(): 0x%08x\n", GetLastError());
return FALSE;
}
ZeroMemory(&dcb, sizeof(DCB));
dcb.DCBlength = sizeof(DCB);
if (!GetCommState(hComm, &dcb))
{
(void)fprintf(stderr, "GetCommState failure, GetLastError(): 0x%08x\n", GetLastError());
return FALSE;
}
if ((dcb.XonChar != XoffChar) || (dcb.XoffChar != XonChar))
{
(void)fprintf(stderr, "test_SerCxSys, expected XonChar and XoffChar to be swapped\n");
return FALSE;
}
/* same XonChar / XoffChar */
dcb.XonChar = dcb.XoffChar;
if (SetCommState(hComm, &dcb))
{
(void)fprintf(stderr,
"test_SerCxSys failure, SetCommState() was supposed to failed because "
"XonChar and XoffChar are the same\n");
return FALSE;
}
if (GetLastError() != ERROR_INVALID_PARAMETER)
{
(void)fprintf(stderr, "test_SerCxSys failure, SetCommState() was supposed to failed with "
"GetLastError()=ERROR_INVALID_PARAMETER\n");
return FALSE;
}
return TRUE;
}
static BOOL test_SerCx2Sys(HANDLE hComm)
{
DCB dcb = WINPR_C_ARRAY_INIT;
dcb.DCBlength = sizeof(DCB);
if (!GetCommState(hComm, &dcb))
{
(void)fprintf(stderr, "GetCommState failure; GetLastError(): %08x\n", GetLastError());
return FALSE;
}
if ((dcb.ErrorChar != '\0') || (dcb.EofChar != '\0') || (dcb.EvtChar != '\0') ||
(dcb.XonChar != '\0') || (dcb.XoffChar != '\0'))
{
(void)fprintf(stderr, "test_SerCx2Sys failure, expected all characters to be: '\\0'\n");
return FALSE;
}
return TRUE;
}
int TestSerialChars(int argc, char* argv[])
{
struct stat statbuf = WINPR_C_ARRAY_INIT;
BOOL result = 0;
HANDLE hComm = nullptr;
if (stat("/dev/ttyS0", &statbuf) < 0)
{
(void)fprintf(stderr, "/dev/ttyS0 not available, making the test to succeed though\n");
return EXIT_SUCCESS;
}
result = DefineCommDevice("COM1", "/dev/ttyS0");
if (!result)
{
(void)fprintf(stderr, "DefineCommDevice failure: 0x%x\n", GetLastError());
return EXIT_FAILURE;
}
hComm = CreateFile("COM1", GENERIC_READ | GENERIC_WRITE, 0, nullptr, OPEN_EXISTING, 0, nullptr);
if (hComm == INVALID_HANDLE_VALUE)
{
(void)fprintf(stderr, "CreateFileA failure: 0x%x\n", GetLastError());
return EXIT_FAILURE;
}
_comm_setServerSerialDriver(hComm, SerialDriverSerCxSys);
if (!test_SerCxSys(hComm))
{
(void)fprintf(stderr, "test_SerCxSys failure\n");
return EXIT_FAILURE;
}
_comm_setServerSerialDriver(hComm, SerialDriverSerCx2Sys);
if (!test_SerCx2Sys(hComm))
{
(void)fprintf(stderr, "test_SerCxSys failure\n");
return EXIT_FAILURE;
}
if (!CloseHandle(hComm))
{
(void)fprintf(stderr, "CloseHandle failure, GetLastError()=%08x\n", GetLastError());
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}

View File

@@ -0,0 +1,335 @@
/**
* WinPR: Windows Portable Runtime
* Serial Communication API
*
* 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 <stdio.h>
#include <sys/stat.h>
#include <winpr/comm.h>
#include <winpr/crt.h>
#include "../comm.h"
static void init_empty_dcb(DCB* pDcb)
{
WINPR_ASSERT(pDcb);
ZeroMemory(pDcb, sizeof(DCB));
pDcb->DCBlength = sizeof(DCB);
pDcb->XonChar = 1;
pDcb->XoffChar = 2;
}
static BOOL test_fParity(HANDLE hComm)
{
DCB dcb = WINPR_C_ARRAY_INIT;
BOOL result = 0;
init_empty_dcb(&dcb);
result = GetCommState(hComm, &dcb);
if (!result)
{
(void)fprintf(stderr, "GetCommState failure: 0x%08" PRIx32 "\n", GetLastError());
return FALSE;
}
/* test 1 */
dcb.fParity = TRUE;
result = SetCommState(hComm, &dcb);
if (!result)
{
(void)fprintf(stderr, "SetCommState failure: 0x%08" PRIx32 "\n", GetLastError());
return FALSE;
}
init_empty_dcb(&dcb);
result = GetCommState(hComm, &dcb);
if (!result)
{
(void)fprintf(stderr, "GetCommState failure: 0x%08" PRIx32 "\n", GetLastError());
return FALSE;
}
if (!dcb.fParity)
{
(void)fprintf(stderr, "unexpected fParity: %" PRIu32 " instead of TRUE\n", dcb.fParity);
return FALSE;
}
/* test 2 */
dcb.fParity = FALSE;
result = SetCommState(hComm, &dcb);
if (!result)
{
(void)fprintf(stderr, "SetCommState failure: 0x%08" PRIx32 "\n", GetLastError());
return FALSE;
}
init_empty_dcb(&dcb);
result = GetCommState(hComm, &dcb);
if (!result)
{
(void)fprintf(stderr, "GetCommState failure: 0x%08" PRIx32 "\n", GetLastError());
return FALSE;
}
if (dcb.fParity)
{
(void)fprintf(stderr, "unexpected fParity: %" PRIu32 " instead of FALSE\n", dcb.fParity);
return FALSE;
}
/* test 3 (redo test 1) */
dcb.fParity = TRUE;
result = SetCommState(hComm, &dcb);
if (!result)
{
(void)fprintf(stderr, "SetCommState failure: 0x%08" PRIx32 "\n", GetLastError());
return FALSE;
}
init_empty_dcb(&dcb);
result = GetCommState(hComm, &dcb);
if (!result)
{
(void)fprintf(stderr, "GetCommState failure: 0x%08" PRIx32 "\n", GetLastError());
return FALSE;
}
if (!dcb.fParity)
{
(void)fprintf(stderr, "unexpected fParity: %" PRIu32 " instead of TRUE\n", dcb.fParity);
return FALSE;
}
return TRUE;
}
static BOOL test_SerialSys(HANDLE hComm)
{
DCB dcb = WINPR_C_ARRAY_INIT;
BOOL result = 0;
init_empty_dcb(&dcb);
result = GetCommState(hComm, &dcb);
if (!result)
{
(void)fprintf(stderr, "GetCommState failure: 0x%x\n", GetLastError());
return FALSE;
}
/* Test 1 */
dcb.BaudRate = CBR_115200;
result = SetCommState(hComm, &dcb);
if (!result)
{
(void)fprintf(stderr, "SetCommState failure: 0x%08x\n", GetLastError());
return FALSE;
}
init_empty_dcb(&dcb);
result = GetCommState(hComm, &dcb);
if (!result)
{
(void)fprintf(stderr, "GetCommState failure: 0x%x\n", GetLastError());
return FALSE;
}
if (dcb.BaudRate != CBR_115200)
{
(void)fprintf(stderr, "SetCommState failure: could not set BaudRate=%d (CBR_115200)\n",
CBR_115200);
return FALSE;
}
/* Test 2 using a different baud rate */
dcb.BaudRate = CBR_57600;
result = SetCommState(hComm, &dcb);
if (!result)
{
(void)fprintf(stderr, "SetCommState failure: 0x%x\n", GetLastError());
return FALSE;
}
init_empty_dcb(&dcb);
result = GetCommState(hComm, &dcb);
if (!result)
{
(void)fprintf(stderr, "GetCommState failure: 0x%x\n", GetLastError());
return FALSE;
}
if (dcb.BaudRate != CBR_57600)
{
(void)fprintf(stderr, "SetCommState failure: could not set BaudRate=%d (CBR_57600)\n",
CBR_57600);
return FALSE;
}
/* Test 3 using an unsupported baud rate on Linux */
dcb.BaudRate = CBR_128000;
result = SetCommState(hComm, &dcb);
if (result)
{
(void)fprintf(stderr,
"SetCommState failure: unexpected support of BaudRate=%d (CBR_128000)\n",
CBR_128000);
return FALSE;
}
return TRUE;
}
static BOOL test_SerCxSys(HANDLE hComm)
{
/* as of today there is no difference */
return test_SerialSys(hComm);
}
static BOOL test_SerCx2Sys(HANDLE hComm)
{
/* as of today there is no difference */
return test_SerialSys(hComm);
}
static BOOL test_generic(HANDLE hComm)
{
DCB dcb = WINPR_C_ARRAY_INIT;
DCB dcb2 = WINPR_C_ARRAY_INIT;
BOOL result = 0;
init_empty_dcb(&dcb);
result = GetCommState(hComm, &dcb);
if (!result)
{
(void)fprintf(stderr, "GetCommState failure: 0x%x\n", GetLastError());
return FALSE;
}
/* Checks whether we get the same information before and after SetCommState */
memcpy(&dcb2, &dcb, sizeof(DCB));
result = SetCommState(hComm, &dcb);
if (!result)
{
(void)fprintf(stderr, "SetCommState failure: 0x%08x\n", GetLastError());
return FALSE;
}
result = GetCommState(hComm, &dcb);
if (!result)
{
(void)fprintf(stderr, "GetCommState failure: 0x%x\n", GetLastError());
return FALSE;
}
if (memcmp(&dcb, &dcb2, sizeof(DCB)) != 0)
{
(void)fprintf(stderr,
"DCB is different after SetCommState() whereas it should have not changed\n");
return FALSE;
}
// TODO: a more complete and generic test using GetCommProperties()
/* TMP: TODO: fBinary tests */
/* fParity tests */
if (!test_fParity(hComm))
{
(void)fprintf(stderr, "test_fParity failure\n");
return FALSE;
}
return TRUE;
}
int TestSetCommState(int argc, char* argv[])
{
struct stat statbuf = WINPR_C_ARRAY_INIT;
BOOL result = 0;
HANDLE hComm = nullptr;
if (stat("/dev/ttyS0", &statbuf) < 0)
{
(void)fprintf(stderr, "/dev/ttyS0 not available, making the test to succeed though\n");
return EXIT_SUCCESS;
}
result = DefineCommDevice("COM1", "/dev/ttyS0");
if (!result)
{
(void)fprintf(stderr, "DefineCommDevice failure: 0x%x\n", GetLastError());
return EXIT_FAILURE;
}
hComm = CreateFile("COM1", GENERIC_READ | GENERIC_WRITE, 0, nullptr, OPEN_EXISTING, 0, nullptr);
if (hComm == INVALID_HANDLE_VALUE)
{
(void)fprintf(stderr, "CreateFileA failure: 0x%x\n", GetLastError());
return EXIT_FAILURE;
}
if (!test_generic(hComm))
{
(void)fprintf(stderr, "test_generic failure (SerialDriverUnknown)\n");
return EXIT_FAILURE;
}
_comm_setServerSerialDriver(hComm, SerialDriverSerialSys);
if (!test_generic(hComm))
{
(void)fprintf(stderr, "test_generic failure (SerialDriverSerialSys)\n");
return EXIT_FAILURE;
}
if (!test_SerialSys(hComm))
{
(void)fprintf(stderr, "test_SerialSys failure\n");
return EXIT_FAILURE;
}
_comm_setServerSerialDriver(hComm, SerialDriverSerCxSys);
if (!test_generic(hComm))
{
(void)fprintf(stderr, "test_generic failure (SerialDriverSerCxSys)\n");
return EXIT_FAILURE;
}
if (!test_SerCxSys(hComm))
{
(void)fprintf(stderr, "test_SerCxSys failure\n");
return EXIT_FAILURE;
}
_comm_setServerSerialDriver(hComm, SerialDriverSerCx2Sys);
if (!test_generic(hComm))
{
(void)fprintf(stderr, "test_generic failure (SerialDriverSerCx2Sys)\n");
return EXIT_FAILURE;
}
if (!test_SerCx2Sys(hComm))
{
(void)fprintf(stderr, "test_SerCx2Sys failure\n");
return EXIT_FAILURE;
}
if (!CloseHandle(hComm))
{
(void)fprintf(stderr, "CloseHandle failure, GetLastError()=%08x\n", GetLastError());
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}

View File

@@ -0,0 +1,141 @@
/**
* WinPR: Windows Portable Runtime
* Serial Communication API
*
* 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 <stdio.h>
#include <sys/stat.h>
#ifndef _WIN32
#include <termios.h>
#endif
#include <winpr/comm.h>
#include <winpr/crt.h>
#include "../comm.h"
static BOOL test_generic(HANDLE hComm)
{
COMMTIMEOUTS timeouts = WINPR_C_ARRAY_INIT;
COMMTIMEOUTS timeouts2 = WINPR_C_ARRAY_INIT;
timeouts.ReadIntervalTimeout = 1;
timeouts.ReadTotalTimeoutMultiplier = 2;
timeouts.ReadTotalTimeoutConstant = 3;
timeouts.WriteTotalTimeoutMultiplier = 4;
timeouts.WriteTotalTimeoutConstant = 5;
if (!SetCommTimeouts(hComm, &timeouts))
{
(void)fprintf(stderr, "SetCommTimeouts failure, GetLastError: 0x%08x\n", GetLastError());
return FALSE;
}
if (!GetCommTimeouts(hComm, &timeouts2))
{
(void)fprintf(stderr, "GetCommTimeouts failure, GetLastError: 0x%08x\n", GetLastError());
return FALSE;
}
if (memcmp(&timeouts, &timeouts2, sizeof(COMMTIMEOUTS)) != 0)
{
(void)fprintf(stderr, "TestTimeouts failure, didn't get back the same timeouts.\n");
return FALSE;
}
/* not supported combination */
timeouts.ReadIntervalTimeout = MAXULONG;
timeouts.ReadTotalTimeoutConstant = MAXULONG;
if (SetCommTimeouts(hComm, &timeouts))
{
(void)fprintf(
stderr,
"SetCommTimeouts succeeded with ReadIntervalTimeout and ReadTotalTimeoutConstant "
"set to MAXULONG. GetLastError: 0x%08x\n",
GetLastError());
return FALSE;
}
if (GetLastError() != ERROR_INVALID_PARAMETER)
{
(void)fprintf(
stderr,
"SetCommTimeouts failure, expected GetLastError to return ERROR_INVALID_PARAMETER "
"and got: 0x%08x\n",
GetLastError());
return FALSE;
}
return TRUE;
}
int TestTimeouts(int argc, char* argv[])
{
struct stat statbuf;
BOOL result = 0;
HANDLE hComm = nullptr;
if (stat("/dev/ttyS0", &statbuf) < 0)
{
(void)fprintf(stderr, "/dev/ttyS0 not available, making the test to succeed though\n");
return EXIT_SUCCESS;
}
result = DefineCommDevice("COM1", "/dev/ttyS0");
if (!result)
{
(void)fprintf(stderr, "DefineCommDevice failure: 0x%x\n", GetLastError());
return EXIT_FAILURE;
}
hComm = CreateFile("COM1", GENERIC_READ | GENERIC_WRITE, 0, nullptr, OPEN_EXISTING, 0, nullptr);
if (hComm == INVALID_HANDLE_VALUE)
{
(void)fprintf(stderr, "CreateFileA failure: 0x%x\n", GetLastError());
return EXIT_FAILURE;
}
_comm_setServerSerialDriver(hComm, SerialDriverSerialSys);
if (!test_generic(hComm))
{
(void)fprintf(stderr, "test_SerialSys failure\n");
return EXIT_FAILURE;
}
_comm_setServerSerialDriver(hComm, SerialDriverSerCxSys);
if (!test_generic(hComm))
{
(void)fprintf(stderr, "test_SerCxSys failure\n");
return EXIT_FAILURE;
}
_comm_setServerSerialDriver(hComm, SerialDriverSerCx2Sys);
if (!test_generic(hComm))
{
(void)fprintf(stderr, "test_SerCx2Sys failure\n");
return EXIT_FAILURE;
}
if (!CloseHandle(hComm))
{
(void)fprintf(stderr, "CloseHandle failure, GetLastError()=%08x\n", GetLastError());
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}