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:
parent
ff4d7d569b
commit
4c743de69c
@ -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")
|
||||
|
@ -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,8 +70,6 @@ struct _SERIAL_DEVICE
|
||||
|
||||
static void serial_process_irp_create(SERIAL_DEVICE* serial, IRP* irp)
|
||||
{
|
||||
UINT32 FileId;
|
||||
|
||||
DWORD DesiredAccess;
|
||||
DWORD SharedAccess;
|
||||
DWORD CreateDisposition;
|
||||
@ -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) */
|
||||
|
||||
if (Length > 0)
|
||||
error_handle:
|
||||
|
||||
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)
|
||||
{
|
||||
@ -281,15 +359,18 @@ static void serial_process_irp_device_control(SERIAL_DEVICE* serial, IRP* irp)
|
||||
goto error_handle;
|
||||
}
|
||||
|
||||
/* FIXME: to be replaced by DeviceIoControl() */
|
||||
Stream_Read(irp->input, InputBuffer, InputBufferLength);
|
||||
|
||||
DEBUG_SVC("CommDeviceIoControl: IoControlCode 0x%x", IoControlCode);
|
||||
|
||||
/* 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);
|
||||
}
|
||||
|
||||
@ -409,7 +502,8 @@ static void serial_free(DEVICE* device)
|
||||
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();
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
||||
|
@ -45,6 +45,7 @@ struct winpr_comm
|
||||
|
||||
int fd;
|
||||
REMOTE_SERIAL_DRIVER_ID remoteSerialDriverId;
|
||||
COMMTIMEOUTS timeouts;
|
||||
|
||||
/* NB: CloseHandle() has to free resources */
|
||||
};
|
||||
|
316
winpr/libwinpr/comm/comm_io.c
Normal file
316
winpr/libwinpr/comm/comm_io.c
Normal 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, ¤tTermios) < 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, ¤tTermios) < 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 */
|
@ -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(¤tState, &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(¤tState, 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 */
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
};
|
||||
|
||||
|
||||
|
@ -12,6 +12,7 @@ set(${MODULE_PREFIX}_TESTS
|
||||
TestSerialChars.c
|
||||
TestControlSettings.c
|
||||
TestHandflow.c
|
||||
TestTimeouts.c
|
||||
TestCommMonitor.c)
|
||||
|
||||
create_test_sourcelist(${MODULE_PREFIX}_SRCS
|
||||
|
126
winpr/libwinpr/comm/test/TestTimeouts.c
Normal file
126
winpr/libwinpr/comm/test/TestTimeouts.c
Normal 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;
|
||||
}
|
Loading…
Reference in New Issue
Block a user