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") define_channel_client("serial")
set(${MODULE_PREFIX}_SRCS set(${MODULE_PREFIX}_SRCS
serial_tty.c
serial_tty.h
serial_constants.h
serial_main.c) serial_main.c)
add_channel_client_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} TRUE "DeviceServiceEntry") add_channel_client_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} TRUE "DeviceServiceEntry")

View File

@ -42,9 +42,6 @@
#include <unistd.h> #include <unistd.h>
#endif #endif
#include "serial_tty.h"
#include "serial_constants.h"
#include <winpr/collections.h> #include <winpr/collections.h>
#include <winpr/comm.h> #include <winpr/comm.h>
#include <winpr/crt.h> #include <winpr/crt.h>
@ -65,11 +62,7 @@ struct _SERIAL_DEVICE
DEVICE device; DEVICE device;
HANDLE* hComm; HANDLE* hComm;
// TMP: TBR // TMP: use of log
char* path;
SERIAL_TTY* tty;
//
wLog* log; wLog* log;
HANDLE thread; HANDLE thread;
wMessageQueue* IrpQueue; wMessageQueue* IrpQueue;
@ -77,13 +70,11 @@ struct _SERIAL_DEVICE
static void serial_process_irp_create(SERIAL_DEVICE* serial, IRP* irp) static void serial_process_irp_create(SERIAL_DEVICE* serial, IRP* irp)
{ {
UINT32 FileId;
DWORD DesiredAccess; DWORD DesiredAccess;
DWORD SharedAccess; DWORD SharedAccess;
DWORD CreateDisposition; DWORD CreateDisposition;
UINT32 PathLength; UINT32 PathLength;
Stream_Read_UINT32(irp->input, DesiredAccess); /* DesiredAccess (4 bytes) */ Stream_Read_UINT32(irp->input, DesiredAccess); /* DesiredAccess (4 bytes) */
Stream_Seek_UINT64(irp->input); /* AllocationSize (8 bytes) */ Stream_Seek_UINT64(irp->input); /* AllocationSize (8 bytes) */
Stream_Seek_UINT32(irp->input); /* FileAttributes (4 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 */ assert(PathLength == 0); /* MS-RDPESP 2.2.2.2 */
/* CommCreateFileA current implementation expects nothing else than that */
assert(DesiredAccess == (GENERIC_READ | GENERIC_WRITE)); #ifndef _WIN32
assert(SharedAccess == 0); /* Windows 2012 server sends on a first call :
assert(CreateDisposition == OPEN_EXISTING); * 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, serial->hComm = CreateFile(serial->device.name,
DesiredAccess, /* GENERIC_READ | GENERIC_WRITE */ DesiredAccess,
SharedAccess, /* 0 */ SharedAccess,
NULL, /* SecurityAttributes */ NULL, /* SecurityAttributes */
CreateDisposition, /* OPEN_EXISTING */ CreateDisposition,
0, /* FlagsAndAttributes */ 0, /* FlagsAndAttributes */
NULL); /* TemplateFile */ 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()); DEBUG_WARN("CreateFile failure: %s last-error: Ox%x\n", serial->device.name, GetLastError());
irp->IoStatus = STATUS_UNSUCCESSFUL; irp->IoStatus = STATUS_UNSUCCESSFUL;
FileId = 0;
goto error_handle; 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; 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: 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) */ Stream_Write_UINT8(irp->output, 0); /* Information (1 byte) */
irp->Complete(irp); irp->Complete(irp);
@ -135,12 +166,12 @@ static void serial_process_irp_close(SERIAL_DEVICE* serial, IRP* irp)
if (!CloseHandle(serial->hComm)) 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; irp->IoStatus = STATUS_UNSUCCESSFUL;
goto error_handle; 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; serial->hComm = NULL;
irp->IoStatus = STATUS_SUCCESS; irp->IoStatus = STATUS_SUCCESS;
@ -156,91 +187,146 @@ static void serial_process_irp_read(SERIAL_DEVICE* serial, IRP* irp)
UINT32 Length; UINT32 Length;
UINT64 Offset; UINT64 Offset;
BYTE* buffer = NULL; BYTE* buffer = NULL;
SERIAL_TTY* tty = serial->tty; DWORD nbRead = 0;
Stream_Read_UINT32(irp->input, Length); /* Length (4 bytes) */ Stream_Read_UINT32(irp->input, Length); /* Length (4 bytes) */
Stream_Read_UINT64(irp->input, Offset); /* Offset (8 bytes) */ Stream_Read_UINT64(irp->input, Offset); /* Offset (8 bytes) */
Stream_Seek(irp->input, 20); /* Padding (20 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 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; case ERROR_INVALID_HANDLE:
free(buffer); irp->IoStatus = STATUS_INVALID_DEVICE_REQUEST;
buffer = NULL; break;
Length = 0;
DEBUG_WARN("read %s(%d) failed.", serial->path, tty->id); case ERROR_NOT_SUPPORTED:
} irp->IoStatus = STATUS_NOT_SUPPORTED;
else break;
{
DEBUG_SVC("read %llu-%llu from %d", Offset, Offset + Length, tty->id); 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_EnsureRemainingCapacity(irp->output, nbRead);
Stream_Write(irp->output, buffer, Length); Stream_Write(irp->output, buffer, nbRead); /* ReadData */
} }
free(buffer);
if (buffer)
free(buffer);
irp->Complete(irp); irp->Complete(irp);
} }
static void serial_process_irp_write(SERIAL_DEVICE* serial, IRP* irp) static void serial_process_irp_write(SERIAL_DEVICE* serial, IRP* irp)
{ {
int status;
UINT32 Length; UINT32 Length;
UINT64 Offset; UINT64 Offset;
SERIAL_TTY* tty = serial->tty; DWORD nbWritten = 0;
Stream_Read_UINT32(irp->input, Length); /* Length (4 bytes) */ Stream_Read_UINT32(irp->input, Length); /* Length (4 bytes) */
Stream_Read_UINT64(irp->input, Offset); /* Offset (8 bytes) */ Stream_Read_UINT64(irp->input, Offset); /* Offset (8 bytes) */
Stream_Seek(irp->input, 20); /* Padding (20 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; irp->IoStatus = STATUS_SUCCESS;
Length = 0; }
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) */ case ERROR_NOT_SUPPORTED:
Stream_Write_UINT8(irp->output, 0); /* Padding (1 byte) */ irp->IoStatus = STATUS_NOT_SUPPORTED;
irp->Complete(irp); break;
return;
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) Stream_Write_UINT32(irp->output, nbWritten); /* Length (4 bytes) */
{
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_UINT8(irp->output, 0); /* Padding (1 byte) */ Stream_Write_UINT8(irp->output, 0); /* Padding (1 byte) */
irp->Complete(irp); irp->Complete(irp);
} }
@ -253,20 +339,12 @@ static void serial_process_irp_device_control(SERIAL_DEVICE* serial, IRP* irp)
UINT32 OutputBufferLength; UINT32 OutputBufferLength;
BYTE* OutputBuffer = NULL; BYTE* OutputBuffer = NULL;
DWORD BytesReturned = 0; DWORD BytesReturned = 0;
SERIAL_TTY* tty = serial->tty;
Stream_Read_UINT32(irp->input, OutputBufferLength); /* OutputBufferLength (4 bytes) */ Stream_Read_UINT32(irp->input, OutputBufferLength); /* OutputBufferLength (4 bytes) */
Stream_Read_UINT32(irp->input, InputBufferLength); /* InputBufferLength (4 bytes) */ Stream_Read_UINT32(irp->input, InputBufferLength); /* InputBufferLength (4 bytes) */
Stream_Read_UINT32(irp->input, IoControlCode); /* IoControlCode (4 bytes) */ Stream_Read_UINT32(irp->input, IoControlCode); /* IoControlCode (4 bytes) */
Stream_Seek(irp->input, 20); /* Padding (20 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)); OutputBuffer = (BYTE*)calloc(OutputBufferLength, sizeof(BYTE));
if (OutputBuffer == NULL) if (OutputBuffer == NULL)
{ {
@ -280,16 +358,19 @@ static void serial_process_irp_device_control(SERIAL_DEVICE* serial, IRP* irp)
irp->IoStatus = STATUS_NO_MEMORY; irp->IoStatus = STATUS_NO_MEMORY;
goto error_handle; 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)) if (CommDeviceIoControl(serial->hComm, IoControlCode, InputBuffer, InputBufferLength, OutputBuffer, OutputBufferLength, &BytesReturned, NULL))
{ {
Stream_Write(irp->output, OutputBuffer, BytesReturned);
irp->IoStatus = STATUS_SUCCESS; irp->IoStatus = STATUS_SUCCESS;
} }
else 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()) switch(GetLastError())
{ {
@ -321,13 +402,25 @@ static void serial_process_irp_device_control(SERIAL_DEVICE* serial, IRP* irp)
} }
error_handle: 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) if (InputBuffer != NULL)
free(InputBuffer); free(InputBuffer);
if (OutputBuffer != NULL) if (OutputBuffer != NULL)
free(OutputBuffer); free(OutputBuffer);
// TMP: double check the completion, Information field
irp->Complete(irp); irp->Complete(irp);
} }
@ -402,14 +495,15 @@ static void serial_irp_request(DEVICE* device, IRP* irp)
static void serial_free(DEVICE* device) static void serial_free(DEVICE* device)
{ {
SERIAL_DEVICE* serial = (SERIAL_DEVICE*) device; SERIAL_DEVICE* serial = (SERIAL_DEVICE*) device;
WLog_Print(serial->log, WLOG_DEBUG, "freeing"); WLog_Print(serial->log, WLOG_DEBUG, "freeing");
MessageQueue_PostQuit(serial->IrpQueue, 0); MessageQueue_PostQuit(serial->IrpQueue, 0);
WaitForSingleObject(serial->thread, INFINITE); WaitForSingleObject(serial->thread, INFINITE);
CloseHandle(serial->thread); CloseHandle(serial->thread);
serial_tty_free(serial->tty); if (serial->hComm)
CloseHandle(serial->hComm);
/* Clean up resources */ /* Clean up resources */
Stream_Free(serial->device.data, TRUE); Stream_Free(serial->device.data, TRUE);
@ -442,9 +536,11 @@ int DeviceServiceEntry(PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints)
if ((name && name[0]) && (path && path[0])) if ((name && name[0]) && (path && path[0]))
{ {
DEBUG_SVC("Defining %s as %s", name, path);
if (!DefineCommDevice(name /* eg: COM1 */, path /* eg: /dev/ttyS0 */)) 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; return -1;
} }
@ -463,7 +559,6 @@ int DeviceServiceEntry(PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints)
for (i = 0; i <= len; i++) for (i = 0; i <= len; i++)
Stream_Write_UINT8(serial->device.data, name[i] < 0 ? '_' : name[i]); Stream_Write_UINT8(serial->device.data, name[i] < 0 ? '_' : name[i]);
serial->path = path;
serial->IrpQueue = MessageQueue_New(NULL); serial->IrpQueue = MessageQueue_New(NULL);
WLog_Init(); WLog_Init();

View File

@ -375,6 +375,11 @@ WINPR_API BOOL WaitCommEvent(HANDLE hFile, PDWORD lpEvtMask, LPOVERLAPPED lpOver
/* Extended API */ /* Extended API */
/* FIXME: MAXULONG should be defined arround winpr/limits.h */
#ifndef MAXULONG
#define MAXULONG (4294967295UL)
#endif
/* /*
* About DefineCommDevice() / QueryDosDevice() * 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, BOOL CommDeviceIoControl(HANDLE hDevice, DWORD dwIoControlCode, LPVOID lpInBuffer, DWORD nInBufferSize,
LPVOID lpOutBuffer, DWORD nOutBufferSize, LPDWORD lpBytesReturned, LPOVERLAPPED lpOverlapped); 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 #ifdef __cplusplus
} }

View File

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

View File

@ -522,7 +522,6 @@ BOOL SetCommState(HANDLE hFile, LPDCB lpDCB)
if (lpDCB->fBinary) if (lpDCB->fBinary)
{ {
upcomingTermios.c_lflag &= ~ICANON; upcomingTermios.c_lflag &= ~ICANON;
// TMP: complete the raw mode here?
} }
else else
{ {
@ -563,22 +562,55 @@ BOOL SetCommState(HANDLE hFile, LPDCB lpDCB)
return TRUE; return TRUE;
} }
/**
* ERRORS:
* ERROR_INVALID_HANDLE
*/
BOOL GetCommTimeouts(HANDLE hFile, LPCOMMTIMEOUTS lpCommTimeouts) BOOL GetCommTimeouts(HANDLE hFile, LPCOMMTIMEOUTS lpCommTimeouts)
{ {
WINPR_COMM* pComm = (WINPR_COMM*) hFile; 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; 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; return TRUE;
} }
/**
* ERRORS:
* ERROR_INVALID_HANDLE
*/
BOOL SetCommTimeouts(HANDLE hFile, LPCOMMTIMEOUTS lpCommTimeouts) BOOL SetCommTimeouts(HANDLE hFile, LPCOMMTIMEOUTS lpCommTimeouts)
{ {
WINPR_COMM* pComm = (WINPR_COMM*) hFile; 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; 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; return TRUE;
} }
@ -975,7 +1007,7 @@ HANDLE CommCreateFileA(LPCSTR lpDeviceName, DWORD dwDesiredAccess, DWORD dwShare
CHAR devicePath[MAX_PATH]; CHAR devicePath[MAX_PATH];
struct stat deviceStat; struct stat deviceStat;
WINPR_COMM* pComm = NULL; WINPR_COMM* pComm = NULL;
struct termios upcomingTermios;
if (dwDesiredAccess != (GENERIC_READ | GENERIC_WRITE)) if (dwDesiredAccess != (GENERIC_READ | GENERIC_WRITE))
{ {
@ -1052,12 +1084,53 @@ HANDLE CommCreateFileA(LPCSTR lpDeviceName, DWORD dwDesiredAccess, DWORD dwShare
goto error_handle; 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 /* TMP: TODO: FIXME: this information is at least needed for
* forced with a comand line argument. * 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; return (HANDLE)pComm;

View File

@ -45,6 +45,7 @@ struct winpr_comm
int fd; int fd;
REMOTE_SERIAL_DRIVER_ID remoteSerialDriverId; REMOTE_SERIAL_DRIVER_ID remoteSerialDriverId;
COMMTIMEOUTS timeouts;
/* NB: CloseHandle() has to free resources */ /* 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: * ERRORS:
* ERROR_INVALID_HANDLE * ERROR_INVALID_HANDLE
* ERROR_INVALID_PARAMETER
* ERROR_NOT_SUPPORTED lpOverlapped is not supported * ERROR_NOT_SUPPORTED lpOverlapped is not supported
* ERROR_INSUFFICIENT_BUFFER * ERROR_INSUFFICIENT_BUFFER
* ERROR_CALL_NOT_IMPLEMENTED unimplemented ioctl
*/ */
BOOL CommDeviceIoControl(HANDLE hDevice, DWORD dwIoControlCode, LPVOID lpInBuffer, DWORD nInBufferSize, BOOL CommDeviceIoControl(HANDLE hDevice, DWORD dwIoControlCode, LPVOID lpInBuffer, DWORD nInBufferSize,
LPVOID lpOutBuffer, DWORD nOutBufferSize, LPDWORD lpBytesReturned, LPOVERLAPPED lpOverlapped) 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; WINPR_COMM* pComm = (WINPR_COMM*) hDevice;
REMOTE_SERIAL_DRIVER* pRemoteSerialDriver = NULL; 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 ) if (!pComm || pComm->Type != HANDLE_TYPE_COMM || !pComm->fd )
{ {
SetLastError(ERROR_INVALID_HANDLE); SetLastError(ERROR_INVALID_HANDLE);
@ -85,12 +93,14 @@ BOOL CommDeviceIoControl(HANDLE hDevice, DWORD dwIoControlCode, LPVOID lpInBuffe
if (lpBytesReturned == NULL) 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; return FALSE;
} }
*lpBytesReturned = 0; /* will be ajusted if required ... */ *lpBytesReturned = 0; /* will be ajusted if required ... */
DEBUG_MSG("CommDeviceIoControl: IoControlCode: 0x%0.8x", dwIoControlCode);
/* remoteSerialDriver to be use ... /* remoteSerialDriver to be use ...
* *
* FIXME: might prefer to use an automatic rather than static structure * 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: case RemoteSerialDriverUnknown:
default: 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(); pRemoteSerialDriver = SerCx2Sys_s();
break; break;
} }
@ -129,7 +139,7 @@ BOOL CommDeviceIoControl(HANDLE hDevice, DWORD dwIoControlCode, LPVOID lpInBuffe
assert(nInBufferSize >= sizeof(SERIAL_BAUD_RATE)); assert(nInBufferSize >= sizeof(SERIAL_BAUD_RATE));
if (nInBufferSize < sizeof(SERIAL_BAUD_RATE)) if (nInBufferSize < sizeof(SERIAL_BAUD_RATE))
{ {
SetLastError(ERROR_INVALID_DATA); SetLastError(ERROR_INVALID_PARAMETER);
return FALSE; return FALSE;
} }
@ -188,7 +198,7 @@ BOOL CommDeviceIoControl(HANDLE hDevice, DWORD dwIoControlCode, LPVOID lpInBuffe
assert(nInBufferSize >= sizeof(SERIAL_CHARS)); assert(nInBufferSize >= sizeof(SERIAL_CHARS));
if (nInBufferSize < sizeof(SERIAL_CHARS)) if (nInBufferSize < sizeof(SERIAL_CHARS))
{ {
SetLastError(ERROR_INVALID_DATA); SetLastError(ERROR_INVALID_PARAMETER);
return FALSE; return FALSE;
} }
@ -226,7 +236,7 @@ BOOL CommDeviceIoControl(HANDLE hDevice, DWORD dwIoControlCode, LPVOID lpInBuffe
assert(nInBufferSize >= sizeof(SERIAL_LINE_CONTROL)); assert(nInBufferSize >= sizeof(SERIAL_LINE_CONTROL));
if (nInBufferSize < sizeof(SERIAL_LINE_CONTROL)) if (nInBufferSize < sizeof(SERIAL_LINE_CONTROL))
{ {
SetLastError(ERROR_INVALID_DATA); SetLastError(ERROR_INVALID_PARAMETER);
return FALSE; return FALSE;
} }
@ -264,7 +274,7 @@ BOOL CommDeviceIoControl(HANDLE hDevice, DWORD dwIoControlCode, LPVOID lpInBuffe
assert(nInBufferSize >= sizeof(SERIAL_HANDFLOW)); assert(nInBufferSize >= sizeof(SERIAL_HANDFLOW));
if (nInBufferSize < sizeof(SERIAL_HANDFLOW)) if (nInBufferSize < sizeof(SERIAL_HANDFLOW))
{ {
SetLastError(ERROR_INVALID_DATA); SetLastError(ERROR_INVALID_PARAMETER);
return FALSE; return FALSE;
} }
@ -293,10 +303,49 @@ BOOL CommDeviceIoControl(HANDLE hDevice, DWORD dwIoControlCode, LPVOID lpInBuffe
} }
break; 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); DEBUG_WARN(_T("unsupported IoControlCode: Ox%0.8x (remote serial driver: %s)"), dwIoControlCode, pRemoteSerialDriver->name);
SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
return FALSE; 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) 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) if ((result = tcsetattr(fd, optional_actions, termios_p)) < 0)
{ {
DEBUG_WARN("2nd tcsetattr failure, errno: %d", errno); 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) 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 */ 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_GET_BAUD_RATE 0x001B0050
#define IOCTL_SERIAL_SET_LINE_CONTROL 0x001B000C #define IOCTL_SERIAL_SET_LINE_CONTROL 0x001B000C
#define IOCTL_SERIAL_GET_LINE_CONTROL 0x001B0054 #define IOCTL_SERIAL_GET_LINE_CONTROL 0x001B0054
/* IOCTL_SERIAL_SET_TIMEOUTS 0x001B001C */ #define IOCTL_SERIAL_SET_TIMEOUTS 0x001B001C
/* IOCTL_SERIAL_GET_TIMEOUTS 0x001B0020 */ #define IOCTL_SERIAL_GET_TIMEOUTS 0x001B0020
/* GET_CHARS and SET_CHARS are swapped in the RDP docs [MS-RDPESP] */ /* GET_CHARS and SET_CHARS are swapped in the RDP docs [MS-RDPESP] */
#define IOCTL_SERIAL_GET_CHARS 0x001B0058 #define IOCTL_SERIAL_GET_CHARS 0x001B0058
#define IOCTL_SERIAL_SET_CHARS 0x001B005C #define IOCTL_SERIAL_SET_CHARS 0x001B005C
@ -179,6 +178,17 @@ typedef struct _SERIAL_HANDFLOW
#define SERIAL_SP_TELNET ((ULONG)0x00000102) #define SERIAL_SP_TELNET ((ULONG)0x00000102)
#define SERIAL_SP_X25 ((ULONG)0x00000103) #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. * 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 (*get_line_control)(WINPR_COMM *pComm, SERIAL_LINE_CONTROL *pLineControl);
BOOL (*set_handflow)(WINPR_COMM *pComm, const SERIAL_HANDFLOW *pHandflow); BOOL (*set_handflow)(WINPR_COMM *pComm, const SERIAL_HANDFLOW *pHandflow);
BOOL (*get_handflow)(WINPR_COMM *pComm, 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; } REMOTE_SERIAL_DRIVER;

View File

@ -66,6 +66,8 @@ static REMOTE_SERIAL_DRIVER _SerCx2Sys =
.get_line_control = NULL, .get_line_control = NULL,
.set_handflow = NULL, .set_handflow = NULL,
.get_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.set_handflow = pSerialSys->set_handflow;
_SerCx2Sys.get_handflow = pSerialSys->get_handflow; _SerCx2Sys.get_handflow = pSerialSys->get_handflow;
_SerCx2Sys.set_timeouts = pSerialSys->set_timeouts;
_SerCx2Sys.get_timeouts = pSerialSys->get_timeouts;
return &_SerCx2Sys; 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) 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; return FALSE;
} }
@ -507,6 +507,8 @@ static REMOTE_SERIAL_DRIVER _SerCxSys =
.get_line_control = NULL, .get_line_control = NULL,
.set_handflow = _set_handflow, .set_handflow = _set_handflow,
.get_handflow = _get_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.set_line_control = pSerialSys->set_line_control;
_SerCxSys.get_line_control = pSerialSys->get_line_control; _SerCxSys.get_line_control = pSerialSys->get_line_control;
_SerCxSys.set_timeouts = pSerialSys->set_timeouts;
_SerCxSys.get_timeouts = pSerialSys->get_timeouts;
return &_SerCxSys; return &_SerCxSys;
} }

View File

@ -827,6 +827,38 @@ static BOOL _get_handflow(WINPR_COMM *pComm, SERIAL_HANDFLOW *pHandflow)
return TRUE; 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 = static REMOTE_SERIAL_DRIVER _SerialSys =
{ {
@ -841,6 +873,8 @@ static REMOTE_SERIAL_DRIVER _SerialSys =
.get_line_control = _get_line_control, .get_line_control = _get_line_control,
.set_handflow = _set_handflow, .set_handflow = _set_handflow,
.get_handflow = _get_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 TestSerialChars.c
TestControlSettings.c TestControlSettings.c
TestHandflow.c TestHandflow.c
TestTimeouts.c
TestCommMonitor.c) TestCommMonitor.c)
create_test_sourcelist(${MODULE_PREFIX}_SRCS 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;
}