winpr-comm: got IOCTL_SERIAL_SET_TIMEOUTS / IOCTL_SERIAL_GET_TIMEOUTS

serial redirection: use of winpr-comm's functions and not serial_tty.* anymore
This commit is contained in:
Emmanuel Ledoux 2014-05-12 17:33:56 +02:00 committed by Emmanuel Ledoux
parent ff4d7d569b
commit 4c743de69c
14 changed files with 841 additions and 110 deletions

View File

@ -18,9 +18,6 @@
define_channel_client("serial")
set(${MODULE_PREFIX}_SRCS
serial_tty.c
serial_tty.h
serial_constants.h
serial_main.c)
add_channel_client_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} TRUE "DeviceServiceEntry")

View File

@ -42,9 +42,6 @@
#include <unistd.h>
#endif
#include "serial_tty.h"
#include "serial_constants.h"
#include <winpr/collections.h>
#include <winpr/comm.h>
#include <winpr/crt.h>
@ -65,11 +62,7 @@ struct _SERIAL_DEVICE
DEVICE device;
HANDLE* hComm;
// TMP: TBR
char* path;
SERIAL_TTY* tty;
//
// TMP: use of log
wLog* log;
HANDLE thread;
wMessageQueue* IrpQueue;
@ -77,13 +70,11 @@ struct _SERIAL_DEVICE
static void serial_process_irp_create(SERIAL_DEVICE* serial, IRP* irp)
{
UINT32 FileId;
DWORD DesiredAccess;
DWORD SharedAccess;
DWORD CreateDisposition;
UINT32 PathLength;
Stream_Read_UINT32(irp->input, DesiredAccess); /* DesiredAccess (4 bytes) */
Stream_Seek_UINT64(irp->input); /* AllocationSize (8 bytes) */
Stream_Seek_UINT32(irp->input); /* FileAttributes (4 bytes) */
@ -95,16 +86,37 @@ static void serial_process_irp_create(SERIAL_DEVICE* serial, IRP* irp)
assert(PathLength == 0); /* MS-RDPESP 2.2.2.2 */
/* CommCreateFileA current implementation expects nothing else than that */
assert(DesiredAccess == (GENERIC_READ | GENERIC_WRITE));
assert(SharedAccess == 0);
assert(CreateDisposition == OPEN_EXISTING);
#ifndef _WIN32
/* Windows 2012 server sends on a first call :
* DesiredAccess = 0x00100080: SYNCHRONIZE | FILE_READ_ATTRIBUTES
* SharedAccess = 0x00000007: FILE_SHARE_DELETE | FILE_SHARE_WRITE | FILE_SHARE_READ
* CreateDisposition = 0x00000001: CREATE_NEW
*
* then sends :
* DesiredAccess = 0x00120089: SYNCHRONIZE | READ_CONTROL | FILE_READ_ATTRIBUTES | FILE_READ_EA | FILE_READ_DATA
* SharedAccess = 0x00000007: FILE_SHARE_DELETE | FILE_SHARE_WRITE | FILE_SHARE_READ
* CreateDisposition = 0x00000001: CREATE_NEW
*
* assert(DesiredAccess == (GENERIC_READ | GENERIC_WRITE));
* assert(SharedAccess == 0);
* assert(CreateDisposition == OPEN_EXISTING);
*
*/
DEBUG_SVC("DesiredAccess: 0x%0.8x, SharedAccess: 0x%0.8x, CreateDisposition: 0x%0.8x", DesiredAccess, SharedAccess, CreateDisposition);
/* FIXME: As of today only the flags below are supported by CommCreateFileA: */
DesiredAccess = GENERIC_READ | GENERIC_WRITE;
SharedAccess = 0;
CreateDisposition = OPEN_EXISTING;
#endif
serial->hComm = CreateFile(serial->device.name,
DesiredAccess, /* GENERIC_READ | GENERIC_WRITE */
SharedAccess, /* 0 */
DesiredAccess,
SharedAccess,
NULL, /* SecurityAttributes */
CreateDisposition, /* OPEN_EXISTING */
CreateDisposition,
0, /* FlagsAndAttributes */
NULL); /* TemplateFile */
@ -113,17 +125,36 @@ static void serial_process_irp_create(SERIAL_DEVICE* serial, IRP* irp)
DEBUG_WARN("CreateFile failure: %s last-error: Ox%x\n", serial->device.name, GetLastError());
irp->IoStatus = STATUS_UNSUCCESSFUL;
FileId = 0;
goto error_handle;
}
FileId = irp->devman->id_sequence++; // TMP: !?
/* NOTE: binary mode/raw mode required for the redirection. On
* Linux, CommCreateFileA forces this setting.
*/
/* ZeroMemory(&dcb, sizeof(DCB)); */
/* dcb.DCBlength = sizeof(DCB); */
/* GetCommState(serial->hComm, &dcb); */
/* dcb.fBinary = TRUE; */
/* SetCommState(serial->hComm, &dcb); */
// TMP:
COMMTIMEOUTS timeouts;
timeouts.ReadIntervalTimeout = MAXULONG; /* ensures a non blocking state */
timeouts.ReadTotalTimeoutMultiplier = 0;
timeouts.ReadTotalTimeoutConstant = 0;
timeouts.WriteTotalTimeoutMultiplier = 0;
timeouts.WriteTotalTimeoutConstant = 0;
SetCommTimeouts(serial->hComm, &timeouts);
assert(irp->FileId == 0);
irp->FileId = irp->devman->id_sequence++; /* FIXME: why not ((WINPR_COMM*)hComm)->fd? */
irp->IoStatus = STATUS_SUCCESS;
DEBUG_SVC("%s %s (%d) created.", serial->device.name, serial->path, FileId);
DEBUG_SVC("%s (DeviceId: %d, FileId: %d) created.", serial->device.name, irp->device->id, irp->FileId);
error_handle:
Stream_Write_UINT32(irp->output, FileId); /* FileId (4 bytes) */
Stream_Write_UINT32(irp->output, irp->FileId); /* FileId (4 bytes) */
Stream_Write_UINT8(irp->output, 0); /* Information (1 byte) */
irp->Complete(irp);
@ -135,12 +166,12 @@ static void serial_process_irp_close(SERIAL_DEVICE* serial, IRP* irp)
if (!CloseHandle(serial->hComm))
{
DEBUG_WARN("CloseHandle failure: %s %s (%d) closed.", serial->device.name, serial->path, irp->device->id);
DEBUG_WARN("CloseHandle failure: %s (%d) closed.", serial->device.name, irp->device->id);
irp->IoStatus = STATUS_UNSUCCESSFUL;
goto error_handle;
}
DEBUG_SVC("%s %s (%d) closed.", serial->device.name, serial->path, irp->device->id);
DEBUG_SVC("%s (DeviceId: %d, FileId: %d) closed.", serial->device.name, irp->device->id, irp->FileId);
serial->hComm = NULL;
irp->IoStatus = STATUS_SUCCESS;
@ -156,91 +187,146 @@ static void serial_process_irp_read(SERIAL_DEVICE* serial, IRP* irp)
UINT32 Length;
UINT64 Offset;
BYTE* buffer = NULL;
SERIAL_TTY* tty = serial->tty;
DWORD nbRead = 0;
Stream_Read_UINT32(irp->input, Length); /* Length (4 bytes) */
Stream_Read_UINT64(irp->input, Offset); /* Offset (8 bytes) */
Stream_Seek(irp->input, 20); /* Padding (20 bytes) */
if (!tty)
{
irp->IoStatus = STATUS_UNSUCCESSFUL;
Length = 0;
DEBUG_WARN("tty not valid.");
buffer = (BYTE*)calloc(Length, sizeof(BYTE));
if (buffer == NULL)
{
irp->IoStatus = STATUS_NO_MEMORY;
goto error_handle;
}
/* MSRDPESP 3.2.5.1.4: If the Offset field is not set to 0, the value MUST be ignored
* assert(Offset == 0);
*/
DEBUG_SVC("reading %lu bytes from %s", Length, serial->device.name);
/* FIXME: CommReadFile to be replaced by ReadFile */
if (CommReadFile(serial->hComm, buffer, Length, &nbRead, NULL))
{
irp->IoStatus = STATUS_SUCCESS;
}
else
{
buffer = (BYTE*) malloc(Length);
DEBUG_SVC("read failure to %s, nbRead=%d, last-error: 0x%0.8x", serial->device.name, nbRead, GetLastError());
if (!serial_tty_read(tty, buffer, &Length))
switch(GetLastError())
{
irp->IoStatus = STATUS_UNSUCCESSFUL;
free(buffer);
buffer = NULL;
Length = 0;
case ERROR_INVALID_HANDLE:
irp->IoStatus = STATUS_INVALID_DEVICE_REQUEST;
break;
DEBUG_WARN("read %s(%d) failed.", serial->path, tty->id);
}
else
{
DEBUG_SVC("read %llu-%llu from %d", Offset, Offset + Length, tty->id);
case ERROR_NOT_SUPPORTED:
irp->IoStatus = STATUS_NOT_SUPPORTED;
break;
case ERROR_INVALID_PARAMETER:
irp->IoStatus = STATUS_INVALID_PARAMETER;
break;
case ERROR_IO_DEVICE:
irp->IoStatus = STATUS_IO_DEVICE_ERROR;
break;
case ERROR_TIMEOUT:
irp->IoStatus = STATUS_TIMEOUT;
break;
case ERROR_BAD_DEVICE:
irp->IoStatus = STATUS_INVALID_DEVICE_REQUEST;
break;
default:
DEBUG_SVC("unexpected last-error: 0x%x", GetLastError());
irp->IoStatus = STATUS_UNSUCCESSFUL;
break;
}
}
Stream_Write_UINT32(irp->output, Length); /* Length (4 bytes) */
error_handle:
if (Length > 0)
Stream_Write_UINT32(irp->output, nbRead); /* Length (4 bytes) */
if (nbRead > 0)
{
Stream_EnsureRemainingCapacity(irp->output, Length);
Stream_Write(irp->output, buffer, Length);
Stream_EnsureRemainingCapacity(irp->output, nbRead);
Stream_Write(irp->output, buffer, nbRead); /* ReadData */
}
free(buffer);
if (buffer)
free(buffer);
irp->Complete(irp);
}
static void serial_process_irp_write(SERIAL_DEVICE* serial, IRP* irp)
{
int status;
UINT32 Length;
UINT64 Offset;
SERIAL_TTY* tty = serial->tty;
DWORD nbWritten = 0;
Stream_Read_UINT32(irp->input, Length); /* Length (4 bytes) */
Stream_Read_UINT64(irp->input, Offset); /* Offset (8 bytes) */
Stream_Seek(irp->input, 20); /* Padding (20 bytes) */
if (!tty)
assert(Offset == 0); /* not implemented otherwise */
DEBUG_SVC("writing %lu bytes to %s", Length, serial->device.name);
/* FIXME: CommWriteFile to be replaced by WriteFile */
if (CommWriteFile(serial->hComm, Stream_Pointer(irp->input), Length, &nbWritten, NULL))
{
irp->IoStatus = STATUS_UNSUCCESSFUL;
Length = 0;
irp->IoStatus = STATUS_SUCCESS;
}
else
{
DEBUG_SVC("write failure to %s, nbWritten=%d, last-error: 0x%0.8x", serial->device.name, nbWritten, GetLastError());
DEBUG_WARN("tty not valid.");
switch(GetLastError())
{
case ERROR_INVALID_HANDLE:
irp->IoStatus = STATUS_INVALID_DEVICE_REQUEST;
break;
Stream_Write_UINT32(irp->output, Length); /* Length (4 bytes) */
Stream_Write_UINT8(irp->output, 0); /* Padding (1 byte) */
irp->Complete(irp);
return;
case ERROR_NOT_SUPPORTED:
irp->IoStatus = STATUS_NOT_SUPPORTED;
break;
case ERROR_INVALID_PARAMETER:
irp->IoStatus = STATUS_INVALID_PARAMETER;
break;
case ERROR_IO_DEVICE:
irp->IoStatus = STATUS_IO_DEVICE_ERROR;
break;
case ERROR_TIMEOUT:
irp->IoStatus = STATUS_TIMEOUT;
break;
case ERROR_BAD_DEVICE:
irp->IoStatus = STATUS_INVALID_DEVICE_REQUEST;
break;
default:
DEBUG_SVC("unexpected last-error: 0x%x", GetLastError());
irp->IoStatus = STATUS_UNSUCCESSFUL;
break;
}
}
status = serial_tty_write(tty, Stream_Pointer(irp->input), Length);
if (status < 0)
{
irp->IoStatus = STATUS_UNSUCCESSFUL;
Length = 0;
printf("serial_tty_write failure: status: %d, errno: %d\n", status, errno);
Stream_Write_UINT32(irp->output, Length); /* Length (4 bytes) */
Stream_Write_UINT8(irp->output, 0); /* Padding (1 byte) */
irp->Complete(irp);
return;
}
Stream_Write_UINT32(irp->output, Length); /* Length (4 bytes) */
Stream_Write_UINT32(irp->output, nbWritten); /* Length (4 bytes) */
Stream_Write_UINT8(irp->output, 0); /* Padding (1 byte) */
irp->Complete(irp);
}
@ -253,20 +339,12 @@ static void serial_process_irp_device_control(SERIAL_DEVICE* serial, IRP* irp)
UINT32 OutputBufferLength;
BYTE* OutputBuffer = NULL;
DWORD BytesReturned = 0;
SERIAL_TTY* tty = serial->tty;
Stream_Read_UINT32(irp->input, OutputBufferLength); /* OutputBufferLength (4 bytes) */
Stream_Read_UINT32(irp->input, InputBufferLength); /* InputBufferLength (4 bytes) */
Stream_Read_UINT32(irp->input, IoControlCode); /* IoControlCode (4 bytes) */
Stream_Seek(irp->input, 20); /* Padding (20 bytes) */
if (!tty)
{
DEBUG_WARN("tty not valid.");
irp->IoStatus = STATUS_UNSUCCESSFUL;
goto error_handle;
}
OutputBuffer = (BYTE*)calloc(OutputBufferLength, sizeof(BYTE));
if (OutputBuffer == NULL)
{
@ -280,16 +358,19 @@ static void serial_process_irp_device_control(SERIAL_DEVICE* serial, IRP* irp)
irp->IoStatus = STATUS_NO_MEMORY;
goto error_handle;
}
Stream_Read(irp->input, InputBuffer, InputBufferLength);
DEBUG_SVC("CommDeviceIoControl: IoControlCode 0x%x", IoControlCode);
/* FIXME: to be replaced by DeviceIoControl() */
/* FIXME: CommDeviceIoControl to be replaced by DeviceIoControl() */
if (CommDeviceIoControl(serial->hComm, IoControlCode, InputBuffer, InputBufferLength, OutputBuffer, OutputBufferLength, &BytesReturned, NULL))
{
Stream_Write(irp->output, OutputBuffer, BytesReturned);
irp->IoStatus = STATUS_SUCCESS;
}
else
{
DEBUG_SVC("CommDeviceIoControl failure: IoControlCode 0x%x last-error: 0x%x", IoControlCode, GetLastError());
DEBUG_SVC("CommDeviceIoControl failure: IoControlCode 0x%0.8x last-error: 0x%x", IoControlCode, GetLastError());
switch(GetLastError())
{
@ -321,13 +402,25 @@ static void serial_process_irp_device_control(SERIAL_DEVICE* serial, IRP* irp)
}
error_handle:
Stream_Write_UINT32(irp->output, BytesReturned); /* OutputBufferLength (4 bytes) */
if (BytesReturned > 0)
{
Stream_EnsureRemainingCapacity(irp->output, BytesReturned);
Stream_Write(irp->output, OutputBuffer, BytesReturned); /* OutputBuffer */
}
else
{
Stream_Write_UINT8(irp->output, 0); /* Padding (1 byte) */
}
if (InputBuffer != NULL)
free(InputBuffer);
if (OutputBuffer != NULL)
free(OutputBuffer);
// TMP: double check the completion, Information field
irp->Complete(irp);
}
@ -402,14 +495,15 @@ static void serial_irp_request(DEVICE* device, IRP* irp)
static void serial_free(DEVICE* device)
{
SERIAL_DEVICE* serial = (SERIAL_DEVICE*) device;
WLog_Print(serial->log, WLOG_DEBUG, "freeing");
MessageQueue_PostQuit(serial->IrpQueue, 0);
WaitForSingleObject(serial->thread, INFINITE);
CloseHandle(serial->thread);
serial_tty_free(serial->tty);
if (serial->hComm)
CloseHandle(serial->hComm);
/* Clean up resources */
Stream_Free(serial->device.data, TRUE);
@ -442,9 +536,11 @@ int DeviceServiceEntry(PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints)
if ((name && name[0]) && (path && path[0]))
{
DEBUG_SVC("Defining %s as %s", name, path);
if (!DefineCommDevice(name /* eg: COM1 */, path /* eg: /dev/ttyS0 */))
{
DEBUG_SVC("Could not registered: %s as %s", path, name);
DEBUG_SVC("Could not define %s as %s", name, path);
return -1;
}
@ -463,7 +559,6 @@ int DeviceServiceEntry(PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints)
for (i = 0; i <= len; i++)
Stream_Write_UINT8(serial->device.data, name[i] < 0 ? '_' : name[i]);
serial->path = path;
serial->IrpQueue = MessageQueue_New(NULL);
WLog_Init();

View File

@ -375,6 +375,11 @@ WINPR_API BOOL WaitCommEvent(HANDLE hFile, PDWORD lpEvtMask, LPOVERLAPPED lpOver
/* Extended API */
/* FIXME: MAXULONG should be defined arround winpr/limits.h */
#ifndef MAXULONG
#define MAXULONG (4294967295UL)
#endif
/*
* About DefineCommDevice() / QueryDosDevice()
*
@ -402,6 +407,17 @@ WINPR_API HANDLE CommCreateFileA(LPCSTR lpFileName, DWORD dwDesiredAccess, DWORD
BOOL CommDeviceIoControl(HANDLE hDevice, DWORD dwIoControlCode, LPVOID lpInBuffer, DWORD nInBufferSize,
LPVOID lpOutBuffer, DWORD nOutBufferSize, LPDWORD lpBytesReturned, LPOVERLAPPED lpOverlapped);
/**
* FIXME: to be moved in comm_io.h
*/
BOOL CommReadFile(HANDLE hDevice, LPVOID lpBuffer, DWORD nNumberOfBytesToRead,
LPDWORD lpNumberOfBytesRead, LPOVERLAPPED lpOverlapped);
/**
* FIXME: to be moved in comm_io.h
*/
BOOL CommWriteFile(HANDLE hDevice, LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite,
LPDWORD lpNumberOfBytesWritten, LPOVERLAPPED lpOverlapped);
#ifdef __cplusplus
}

View File

@ -21,6 +21,7 @@ set(MODULE_PREFIX "WINPR_COMM")
set(${MODULE_PREFIX}_SRCS
comm.c
comm.h
comm_io.c
comm_ioctl.c
comm_ioctl.h
comm_serial_sys.c

View File

@ -522,7 +522,6 @@ BOOL SetCommState(HANDLE hFile, LPDCB lpDCB)
if (lpDCB->fBinary)
{
upcomingTermios.c_lflag &= ~ICANON;
// TMP: complete the raw mode here?
}
else
{
@ -563,22 +562,55 @@ BOOL SetCommState(HANDLE hFile, LPDCB lpDCB)
return TRUE;
}
/**
* ERRORS:
* ERROR_INVALID_HANDLE
*/
BOOL GetCommTimeouts(HANDLE hFile, LPCOMMTIMEOUTS lpCommTimeouts)
{
WINPR_COMM* pComm = (WINPR_COMM*) hFile;
DWORD bytesReturned;
if (!pComm)
if (!pComm || pComm->Type != HANDLE_TYPE_COMM || !pComm->fd )
{
SetLastError(ERROR_INVALID_HANDLE);
return FALSE;
}
/* as of today, SERIAL_TIMEOUTS and COMMTIMEOUTS structures are identical */
if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_GET_TIMEOUTS, NULL, 0, lpCommTimeouts, sizeof(COMMTIMEOUTS), &bytesReturned, NULL))
{
DEBUG_WARN("GetCommTimeouts failure.");
return FALSE;
}
return TRUE;
}
/**
* ERRORS:
* ERROR_INVALID_HANDLE
*/
BOOL SetCommTimeouts(HANDLE hFile, LPCOMMTIMEOUTS lpCommTimeouts)
{
WINPR_COMM* pComm = (WINPR_COMM*) hFile;
DWORD bytesReturned;
if (!pComm)
if (!pComm || pComm->Type != HANDLE_TYPE_COMM || !pComm->fd )
{
SetLastError(ERROR_INVALID_HANDLE);
return FALSE;
}
/* as of today, SERIAL_TIMEOUTS and COMMTIMEOUTS structures are identical */
if (!CommDeviceIoControl(pComm, IOCTL_SERIAL_SET_TIMEOUTS, lpCommTimeouts, sizeof(COMMTIMEOUTS), NULL, 0, &bytesReturned, NULL))
{
DEBUG_WARN("SetCommTimeouts failure.");
return FALSE;
}
return TRUE;
}
@ -975,7 +1007,7 @@ HANDLE CommCreateFileA(LPCSTR lpDeviceName, DWORD dwDesiredAccess, DWORD dwShare
CHAR devicePath[MAX_PATH];
struct stat deviceStat;
WINPR_COMM* pComm = NULL;
struct termios upcomingTermios;
if (dwDesiredAccess != (GENERIC_READ | GENERIC_WRITE))
{
@ -1052,12 +1084,53 @@ HANDLE CommCreateFileA(LPCSTR lpDeviceName, DWORD dwDesiredAccess, DWORD dwShare
goto error_handle;
}
/* Restore the blocking mode for upcoming read/write operations */
if (fcntl(pComm->fd, F_SETFL, fcntl(pComm->fd, F_GETFL) & ~O_NONBLOCK) < 0)
{
DEBUG_WARN("failed to open device %s, could not restore the O_NONBLOCK flag", devicePath);
SetLastError(ERROR_BAD_DEVICE);
goto error_handle;
}
/* TMP: TODO: FIXME: this information is at least need for
* get/set baud. Is possible to pull this information? to be
* forced with a comand line argument.
/* TMP: TODO: FIXME: this information is at least needed for
* get/set baud functions. Is it possible to pull this
* information? Could be a command line argument.
*/
pComm->remoteSerialDriverId = RemoteSerialDriverUnknown;
pComm->remoteSerialDriverId = RemoteSerialDriverUnknown;
/* The binary/raw mode is required for the redirection but
* only flags that are not handle somewhere-else, except
* ICANON, are forced here. */
ZeroMemory(&upcomingTermios, sizeof(struct termios));
if (tcgetattr(pComm->fd, &upcomingTermios) < 0)
{
SetLastError(ERROR_IO_DEVICE);
goto error_handle;
}
upcomingTermios.c_iflag &= ~(/*IGNBRK |*/ BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL /*| IXON*/);
upcomingTermios.c_oflag = 0; /* <=> &= ~OPOST */
upcomingTermios.c_lflag = 0; /* <=> &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN); */
/* upcomingTermios.c_cflag &= ~(CSIZE | PARENB); */
/* upcomingTermios.c_cflag |= CS8; */
/* About missing missing flags recommended by termios(3)
*
* IGNBRK and IXON, see: IOCTL_SERIAL_SET_HANDFLOW
* CSIZE, PARENB and CS8, see: IOCTL_SERIAL_SET_LINE_CONTROL
*/
/* a few more settings required for the redirection */
upcomingTermios.c_cflag |= CLOCAL | CREAD;
if (_comm_ioctl_tcsetattr(pComm->fd, TCSANOW, &upcomingTermios) < 0)
{
SetLastError(ERROR_IO_DEVICE);
goto error_handle;
}
return (HANDLE)pComm;

View File

@ -45,6 +45,7 @@ struct winpr_comm
int fd;
REMOTE_SERIAL_DRIVER_ID remoteSerialDriverId;
COMMTIMEOUTS timeouts;
/* NB: CloseHandle() has to free resources */
};

View File

@ -0,0 +1,316 @@
/**
* 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.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#ifndef _WIN32
#include <errno.h>
#include <termios.h>
#include <unistd.h>
#include <freerdp/utils/debug.h>
#include <winpr/io.h>
#include <winpr/wtypes.h>
#include "comm.h"
/* Computes Tmax in deciseconds (m and Tcare in milliseconds) */
static UCHAR _tmax(DWORD N, ULONG m, ULONG Tc)
{
/* Tmax = N * m + Tc */
ULONGLONG Tmax = N * m + Tc;
/* FIXME: look for an equivalent math function otherwise let
* do the compiler do the optimization */
if (Tmax == 0)
return 0;
else if (Tmax < 100)
return 1;
else if (Tmax > 25500)
return 255; /* 0xFF */
else
return Tmax/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;
ssize_t nbRead = 0;
COMMTIMEOUTS *pTimeouts;
UCHAR vmin = 0;
UCHAR vtime = 0;
struct termios currentTermios;
if (hDevice == INVALID_HANDLE_VALUE)
{
SetLastError(ERROR_INVALID_HANDLE);
return FALSE;
}
if (!pComm || pComm->Type != HANDLE_TYPE_COMM || !pComm->fd )
{
SetLastError(ERROR_INVALID_HANDLE);
return FALSE;
}
if (lpOverlapped != NULL)
{
SetLastError(ERROR_NOT_SUPPORTED);
return FALSE;
}
if (lpNumberOfBytesRead == NULL)
{
SetLastError(ERROR_INVALID_PARAMETER); /* since we doesn't suppport lpOverlapped != NULL */
return FALSE;
}
*lpNumberOfBytesRead = 0; /* will be ajusted if required ... */
if (nNumberOfBytesToRead <= 0) /* N */
{
return TRUE; /* FIXME: or FALSE? */
}
if (tcgetattr(pComm->fd, &currentTermios) < 0)
{
SetLastError(ERROR_IO_DEVICE);
return FALSE;
}
if (currentTermios.c_lflag & ICANON)
{
DEBUG_WARN("Canonical mode not supported"); /* the timeout could not be set */
SetLastError(ERROR_NOT_SUPPORTED);
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 | 0 | Blocks for N bytes available. FIXME: reduce N to 1?
* 0< Ti <MAXULONG | 0 | 0 | N | Ti | 0 | Block 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 | 0 | Tc | 0 | Blocks on first byte during Tc or returns immediately whith bytes available
* MAXULONG | m | MAXULONG | | Invalid
* 0 | m | 0< Tc <MAXULONG | 0 | Tmax | 0 | Block on first byte during Tmax or returns immediately whith bytes available
* 0< Ti <MAXULONG | m | 0< Tc <MAXULONG | N | Ti | Tmax(1) | Block on first byte, then use Ti between bytes. Tmax is use for the whole system call.
*/
/* Tmax = N * m + Tc */
/* NB: 0<N; 0 < m < MAXULONG */
/* NB: timeout are milliseconds, VTIME are deciseconds and is an unsigned char */
/* FIXME: Use a dedicted timer for Tmax(1). VMIN=0 and VTIME=Tmax is rather used instead of VMIN=N and WTIME=Ti.
* TMP: check whether open(pComm->fd, O_NONBLOCK) doesn't conflict with above use cases
*/
pTimeouts = &(pComm->timeouts);
if ((pTimeouts->ReadIntervalTimeout == MAXULONG) && (pTimeouts->ReadTotalTimeoutConstant == MAXULONG))
{
DEBUG_WARN("ReadIntervalTimeout and ReadTotalTimeoutConstant cannot be both set to MAXULONG");
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
/* VMIN */
if ((pTimeouts->ReadIntervalTimeout < MAXULONG) && (pTimeouts->ReadTotalTimeoutMultiplier == 0) && (pTimeouts->ReadTotalTimeoutConstant == 0))
{
/* should only match the two first cases above */
vmin = nNumberOfBytesToRead < 256 ? nNumberOfBytesToRead : 255; /* 0xFF */
}
/* VTIME */
if ((pTimeouts->ReadIntervalTimeout > 0) && (pTimeouts->ReadTotalTimeoutMultiplier == 0) && (pTimeouts->ReadTotalTimeoutConstant == 0))
{
/* Ti */
vtime = _tmax(0, 0, pTimeouts->ReadIntervalTimeout);
}
else if ((pTimeouts->ReadIntervalTimeout == MAXULONG) && (pTimeouts->ReadTotalTimeoutMultiplier == MAXULONG) && (pTimeouts->ReadTotalTimeoutConstant < MAXULONG))
{
/* Tc */
vtime = _tmax(0, 0, pTimeouts->ReadTotalTimeoutConstant);
}
else if ((pTimeouts->ReadTotalTimeoutMultiplier > 0) || (pTimeouts->ReadTotalTimeoutConstant > 0)) /* <=> Tmax > 0 */
{
/* Tmax and Tmax(1) */
vtime = _tmax(nNumberOfBytesToRead, pTimeouts->ReadTotalTimeoutMultiplier, pTimeouts->ReadTotalTimeoutConstant);
}
if ((currentTermios.c_cc[VMIN] != vmin) || (currentTermios.c_cc[VTIME] != vtime))
{
currentTermios.c_cc[VMIN] = vmin;
currentTermios.c_cc[VTIME] = vtime;
// TMP:
DEBUG_MSG("Applying timeout VMIN=%u, VTIME=%u", vmin, vtime);
if (tcsetattr(pComm->fd, TCSANOW, &currentTermios) < 0)
{
DEBUG_WARN("CommReadFile failure, could not apply new timeout values: VMIN=%u, VTIME=%u", vmin, vtime);
SetLastError(ERROR_IO_DEVICE);
return FALSE;
}
}
// TMP:
DEBUG_MSG("Reading N=%u, VMIN=%u, VTIME=%u", nNumberOfBytesToRead, vmin, vtime);
nbRead = read(pComm->fd, lpBuffer, nNumberOfBytesToRead);
if (nbRead < 0)
{
DEBUG_WARN("CommReadFile failed, ReadIntervalTimeout=%d, ReadTotalTimeoutMultiplier=%d, ReadTotalTimeoutConstant=%d VMIN=%d, VTIME=%d",
pTimeouts->ReadIntervalTimeout, pTimeouts->ReadTotalTimeoutMultiplier, pTimeouts->ReadTotalTimeoutConstant,
currentTermios.c_cc[VMIN], currentTermios.c_cc[VTIME]);
DEBUG_WARN("CommReadFile failed, nNumberOfBytesToRead=%d, errno=[%d] %s", nNumberOfBytesToRead, errno, strerror(errno));
switch (errno)
{
// TMP: TODO:
case EAGAIN: /* EWOULDBLOCK */
nbRead = 0;
break;
case EBADF:
SetLastError(ERROR_BAD_DEVICE); /* STATUS_INVALID_DEVICE_REQUEST */
return FALSE;
default:
return FALSE;
}
}
// SetLastError(ERROR_TIMEOUT)
*lpNumberOfBytesRead = nbRead;
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;
COMMTIMEOUTS *pTimeouts;
if (hDevice == INVALID_HANDLE_VALUE)
{
SetLastError(ERROR_INVALID_HANDLE);
return FALSE;
}
if (!pComm || pComm->Type != HANDLE_TYPE_COMM || !pComm->fd )
{
SetLastError(ERROR_INVALID_HANDLE);
return FALSE;
}
if (lpOverlapped != NULL)
{
SetLastError(ERROR_NOT_SUPPORTED);
return FALSE;
}
if (lpNumberOfBytesWritten == NULL)
{
SetLastError(ERROR_INVALID_PARAMETER); /* since we doesn't suppport lpOverlapped != NULL */
return FALSE;
}
*lpNumberOfBytesWritten = 0; /* will be ajusted if required ... */
if (nNumberOfBytesToWrite <= 0)
{
return TRUE; /* FIXME: or FALSE? */
}
// TMP: TODO: Timeouts
pTimeouts = &(pComm->timeouts);
while (*lpNumberOfBytesWritten < nNumberOfBytesToWrite)
{
ssize_t nbWritten;
nbWritten += write(pComm->fd,
lpBuffer + (*lpNumberOfBytesWritten),
nNumberOfBytesToWrite - (*lpNumberOfBytesWritten));
if (nbWritten < 0)
{
DEBUG_WARN("CommWriteFile failed after %d bytes written, errno=[%d] %s\n", *lpNumberOfBytesWritten, errno, strerror(errno));
switch (errno)
{
case EAGAIN:
/* EWOULDBLOCK */
// TMP: FIXME: fcntl(tty->fd, F_SETFL, O_NONBLOCK) and manage the timeout here?
break;
case EBADF:
SetLastError(ERROR_BAD_DEVICE); /* STATUS_INVALID_DEVICE_REQUEST */
return FALSE;
}
return FALSE;
}
*lpNumberOfBytesWritten += nbWritten;
}
return TRUE;
}
#endif /* _WIN32 */

View File

@ -61,8 +61,10 @@
*
* 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)
@ -71,6 +73,12 @@ BOOL CommDeviceIoControl(HANDLE hDevice, DWORD dwIoControlCode, LPVOID lpInBuffe
WINPR_COMM* pComm = (WINPR_COMM*) hDevice;
REMOTE_SERIAL_DRIVER* pRemoteSerialDriver = NULL;
if (hDevice == INVALID_HANDLE_VALUE)
{
SetLastError(ERROR_INVALID_HANDLE);
return FALSE;
}
if (!pComm || pComm->Type != HANDLE_TYPE_COMM || !pComm->fd )
{
SetLastError(ERROR_INVALID_HANDLE);
@ -85,12 +93,14 @@ BOOL CommDeviceIoControl(HANDLE hDevice, DWORD dwIoControlCode, LPVOID lpInBuffe
if (lpBytesReturned == NULL)
{
SetLastError(ERROR_INVALID_DATA); /* since we doesn't suppport lpOverlapped != NULL */
SetLastError(ERROR_INVALID_PARAMETER); /* since we doesn't suppport lpOverlapped != NULL */
return FALSE;
}
*lpBytesReturned = 0; /* will be ajusted if required ... */
DEBUG_MSG("CommDeviceIoControl: IoControlCode: 0x%0.8x", dwIoControlCode);
/* remoteSerialDriver to be use ...
*
* FIXME: might prefer to use an automatic rather than static structure
@ -111,7 +121,7 @@ BOOL CommDeviceIoControl(HANDLE hDevice, DWORD dwIoControlCode, LPVOID lpInBuffe
case RemoteSerialDriverUnknown:
default:
DEBUG_WARN("Unknown remote serial driver (%d), using SerCx2.sys", pComm->remoteSerialDriverId);
DEBUG_MSG("Unknown remote serial driver (%d), using SerCx2.sys", pComm->remoteSerialDriverId);
pRemoteSerialDriver = SerCx2Sys_s();
break;
}
@ -129,7 +139,7 @@ BOOL CommDeviceIoControl(HANDLE hDevice, DWORD dwIoControlCode, LPVOID lpInBuffe
assert(nInBufferSize >= sizeof(SERIAL_BAUD_RATE));
if (nInBufferSize < sizeof(SERIAL_BAUD_RATE))
{
SetLastError(ERROR_INVALID_DATA);
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
@ -188,7 +198,7 @@ BOOL CommDeviceIoControl(HANDLE hDevice, DWORD dwIoControlCode, LPVOID lpInBuffe
assert(nInBufferSize >= sizeof(SERIAL_CHARS));
if (nInBufferSize < sizeof(SERIAL_CHARS))
{
SetLastError(ERROR_INVALID_DATA);
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
@ -226,7 +236,7 @@ BOOL CommDeviceIoControl(HANDLE hDevice, DWORD dwIoControlCode, LPVOID lpInBuffe
assert(nInBufferSize >= sizeof(SERIAL_LINE_CONTROL));
if (nInBufferSize < sizeof(SERIAL_LINE_CONTROL))
{
SetLastError(ERROR_INVALID_DATA);
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
@ -264,7 +274,7 @@ BOOL CommDeviceIoControl(HANDLE hDevice, DWORD dwIoControlCode, LPVOID lpInBuffe
assert(nInBufferSize >= sizeof(SERIAL_HANDFLOW));
if (nInBufferSize < sizeof(SERIAL_HANDFLOW))
{
SetLastError(ERROR_INVALID_DATA);
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
@ -293,10 +303,49 @@ BOOL CommDeviceIoControl(HANDLE hDevice, DWORD dwIoControlCode, LPVOID lpInBuffe
}
break;
}
case IOCTL_SERIAL_SET_TIMEOUTS:
{
if (pRemoteSerialDriver->set_timeouts)
{
SERIAL_TIMEOUTS *pHandflow = (SERIAL_TIMEOUTS*)lpInBuffer;
assert(nInBufferSize >= sizeof(SERIAL_TIMEOUTS));
if (nInBufferSize < sizeof(SERIAL_TIMEOUTS))
{
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
return pRemoteSerialDriver->set_timeouts(pComm, pHandflow);
}
break;
}
case IOCTL_SERIAL_GET_TIMEOUTS:
{
if (pRemoteSerialDriver->get_timeouts)
{
SERIAL_TIMEOUTS *pHandflow = (SERIAL_TIMEOUTS*)lpOutBuffer;
assert(nOutBufferSize >= sizeof(SERIAL_TIMEOUTS));
if (nOutBufferSize < sizeof(SERIAL_TIMEOUTS))
{
SetLastError(ERROR_INSUFFICIENT_BUFFER);
return FALSE;
}
if (!pRemoteSerialDriver->get_timeouts(pComm, pHandflow))
return FALSE;
*lpBytesReturned = sizeof(SERIAL_TIMEOUTS);
return TRUE;
}
break;
}
}
DEBUG_WARN(_T("unsupported IoControlCode: Ox%0.8x (remote serial driver: %s)"), dwIoControlCode, pRemoteSerialDriver->name);
SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
return FALSE;
}
@ -323,7 +372,7 @@ int _comm_ioctl_tcsetattr(int fd, int optional_actions, const struct termios *te
if (memcmp(&currentState, &termios_p, sizeof(struct termios)) != 0)
{
DEBUG_WARN("all termios parameters were not set, doing a second attempt...");
DEBUG_MSG("all termios parameters are not set yet, doing a second attempt...");
if ((result = tcsetattr(fd, optional_actions, termios_p)) < 0)
{
DEBUG_WARN("2nd tcsetattr failure, errno: %d", errno);
@ -339,7 +388,7 @@ int _comm_ioctl_tcsetattr(int fd, int optional_actions, const struct termios *te
if (memcmp(&currentState, termios_p, sizeof(struct termios)) != 0)
{
DEBUG_WARN("Failure: all parameters were not set on a second attempt.");
DEBUG_WARN("Failure: all termios parameters are still not set on a second attempt");
return -1; /* TMP: double-check whether some parameters can differ anyway */
}
}

View File

@ -45,9 +45,8 @@ extern "C" {
#define IOCTL_SERIAL_GET_BAUD_RATE 0x001B0050
#define IOCTL_SERIAL_SET_LINE_CONTROL 0x001B000C
#define IOCTL_SERIAL_GET_LINE_CONTROL 0x001B0054
/* IOCTL_SERIAL_SET_TIMEOUTS 0x001B001C */
/* IOCTL_SERIAL_GET_TIMEOUTS 0x001B0020 */
#define IOCTL_SERIAL_SET_TIMEOUTS 0x001B001C
#define IOCTL_SERIAL_GET_TIMEOUTS 0x001B0020
/* GET_CHARS and SET_CHARS are swapped in the RDP docs [MS-RDPESP] */
#define IOCTL_SERIAL_GET_CHARS 0x001B0058
#define IOCTL_SERIAL_SET_CHARS 0x001B005C
@ -179,6 +178,17 @@ typedef struct _SERIAL_HANDFLOW
#define SERIAL_SP_TELNET ((ULONG)0x00000102)
#define SERIAL_SP_X25 ((ULONG)0x00000103)
typedef struct _SERIAL_TIMEOUTS
{
ULONG ReadIntervalTimeout;
ULONG ReadTotalTimeoutMultiplier;
ULONG ReadTotalTimeoutConstant;
ULONG WriteTotalTimeoutMultiplier;
ULONG WriteTotalTimeoutConstant;
} SERIAL_TIMEOUTS,*PSERIAL_TIMEOUTS;
/**
* A function might be NULL if not supported by the underlying remote driver.
*
@ -197,6 +207,8 @@ typedef struct _REMOTE_SERIAL_DRIVER
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);
} REMOTE_SERIAL_DRIVER;

View File

@ -66,6 +66,8 @@ static REMOTE_SERIAL_DRIVER _SerCx2Sys =
.get_line_control = NULL,
.set_handflow = NULL,
.get_handflow = NULL,
.set_timeouts = NULL,
.get_timeouts = NULL,
};
@ -89,6 +91,9 @@ REMOTE_SERIAL_DRIVER* SerCx2Sys_s()
_SerCx2Sys.set_handflow = pSerialSys->set_handflow;
_SerCx2Sys.get_handflow = pSerialSys->get_handflow;
_SerCx2Sys.set_timeouts = pSerialSys->set_timeouts;
_SerCx2Sys.get_timeouts = pSerialSys->get_timeouts;
return &_SerCx2Sys;
}

View File

@ -188,7 +188,7 @@ static BOOL _set_baud_rate(WINPR_COMM *pComm, const SERIAL_BAUD_RATE *pBaudRate)
if (_comm_ioctl_tcsetattr(pComm->fd, TCSANOW, &futureState) < 0)
{
DEBUG_WARN("_comm_ioctl_tcsetattr failure: last-error: 0x0.8x", GetLastError());
DEBUG_WARN("_comm_ioctl_tcsetattr failure: last-error: 0x%0.8x", GetLastError());
return FALSE;
}
@ -507,6 +507,8 @@ static REMOTE_SERIAL_DRIVER _SerCxSys =
.get_line_control = NULL,
.set_handflow = _set_handflow,
.get_handflow = _get_handflow,
.set_timeouts = NULL,
.get_timeouts = NULL,
};
@ -521,6 +523,9 @@ REMOTE_SERIAL_DRIVER* SerCxSys_s()
_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;
return &_SerCxSys;
}

View File

@ -827,6 +827,38 @@ static BOOL _get_handflow(WINPR_COMM *pComm, SERIAL_HANDFLOW *pHandflow)
return TRUE;
}
static BOOL _set_timeouts(WINPR_COMM *pComm, const SERIAL_TIMEOUTS *pTimeouts)
{
/* NB: timeouts are applied on system during read/write I/O */
/* http://msdn.microsoft.com/en-us/library/windows/hardware/hh439614%28v=vs.85%29.aspx */
if ((pTimeouts->ReadIntervalTimeout == MAXULONG) && (pTimeouts->ReadTotalTimeoutConstant == MAXULONG))
{
DEBUG_WARN("ReadIntervalTimeout and ReadTotalTimeoutConstant cannot be both set to MAXULONG");
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
pComm->timeouts.ReadIntervalTimeout = pTimeouts->ReadIntervalTimeout;
pComm->timeouts.ReadTotalTimeoutMultiplier = pTimeouts->ReadTotalTimeoutMultiplier;
pComm->timeouts.ReadTotalTimeoutConstant = pTimeouts->ReadTotalTimeoutConstant;
pComm->timeouts.WriteTotalTimeoutMultiplier = pTimeouts->WriteTotalTimeoutMultiplier;
pComm->timeouts.WriteTotalTimeoutConstant = pTimeouts->WriteTotalTimeoutConstant;
return TRUE;
}
static BOOL _get_timeouts(WINPR_COMM *pComm, SERIAL_TIMEOUTS *pTimeouts)
{
pTimeouts->ReadIntervalTimeout = pComm->timeouts.ReadIntervalTimeout;
pTimeouts->ReadTotalTimeoutMultiplier = pComm->timeouts.ReadTotalTimeoutMultiplier;
pTimeouts->ReadTotalTimeoutConstant = pComm->timeouts.ReadTotalTimeoutConstant;
pTimeouts->WriteTotalTimeoutMultiplier = pComm->timeouts.WriteTotalTimeoutMultiplier;
pTimeouts->WriteTotalTimeoutConstant = pComm->timeouts.WriteTotalTimeoutConstant;
return TRUE;
}
static REMOTE_SERIAL_DRIVER _SerialSys =
{
@ -841,6 +873,8 @@ static REMOTE_SERIAL_DRIVER _SerialSys =
.get_line_control = _get_line_control,
.set_handflow = _set_handflow,
.get_handflow = _get_handflow,
.set_timeouts = _set_timeouts,
.get_timeouts = _get_timeouts,
};

View File

@ -12,6 +12,7 @@ set(${MODULE_PREFIX}_TESTS
TestSerialChars.c
TestControlSettings.c
TestHandflow.c
TestTimeouts.c
TestCommMonitor.c)
create_test_sourcelist(${MODULE_PREFIX}_SRCS

View File

@ -0,0 +1,126 @@
/**
* 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 <termios.h>
#include <winpr/comm.h>
#include <winpr/crt.h>
#include "../comm.h"
static BOOL test_generic(HANDLE hComm)
{
COMMTIMEOUTS timeouts, timeouts2;
timeouts.ReadIntervalTimeout = 1;
timeouts.ReadTotalTimeoutMultiplier = 2;
timeouts.ReadTotalTimeoutConstant = 3;
timeouts.WriteTotalTimeoutMultiplier = 4;
timeouts.WriteTotalTimeoutConstant = 5;
if (!SetCommTimeouts(hComm, &timeouts))
{
fprintf(stderr, "SetCommTimeouts failure, GetLastError: 0x%0.8x\n", GetLastError());
return FALSE;
}
ZeroMemory(&timeouts2, sizeof(COMMTIMEOUTS));
if (!GetCommTimeouts(hComm, &timeouts2))
{
fprintf(stderr, "GetCommTimeouts failure, GetLastError: 0x%0.8x\n", GetLastError());
return FALSE;
}
if (memcmp(&timeouts, &timeouts2, sizeof(COMMTIMEOUTS)) != 0)
{
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))
{
fprintf(stderr, "SetCommTimeouts succeeded with ReadIntervalTimeout and ReadTotalTimeoutConstant set to MAXULONG. GetLastError: 0x%0.8x\n", GetLastError());
return FALSE;
}
if (GetLastError() != ERROR_INVALID_PARAMETER)
{
fprintf(stderr, "SetCommTimeouts failure, expected GetLastError to return ERROR_INVALID_PARAMETER and got: 0x%0.8x\n", GetLastError());
return FALSE;
}
return TRUE;
}
int TestTimeouts(int argc, char* argv[])
{
BOOL result;
HANDLE hComm;
// TMP: FIXME: check if we can proceed with tests on the actual device, skip and warn otherwise but don't fail
result = DefineCommDevice("COM1", "/dev/ttyS0");
if (!result)
{
fprintf(stderr, "DefineCommDevice failure: 0x%x\n", GetLastError());
return EXIT_FAILURE;
}
hComm = CreateFile("COM1",
GENERIC_READ | GENERIC_WRITE,
0, NULL, OPEN_EXISTING, 0, NULL);
if (hComm == INVALID_HANDLE_VALUE)
{
fprintf(stderr, "CreateFileA failure: 0x%x\n", GetLastError());
return EXIT_FAILURE;
}
_comm_setRemoteSerialDriver(hComm, RemoteSerialDriverSerialSys);
if (!test_generic(hComm))
{
fprintf(stderr, "test_SerialSys failure\n");
return EXIT_FAILURE;
}
_comm_setRemoteSerialDriver(hComm, RemoteSerialDriverSerCxSys);
if (!test_generic(hComm))
{
fprintf(stderr, "test_SerCxSys failure\n");
return EXIT_FAILURE;
}
_comm_setRemoteSerialDriver(hComm, RemoteSerialDriverSerCx2Sys);
if (!test_generic(hComm))
{
fprintf(stderr, "test_SerCx2Sys failure\n");
return EXIT_FAILURE;
}
if (!CloseHandle(hComm))
{
fprintf(stderr, "CloseHandle failure, GetLastError()=%0.8x\n", GetLastError());
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}