Merge branch 'ports'

Conflicts:
	channels/serial/client/serial_tty.c
This commit is contained in:
Emmanuel Ledoux 2014-06-30 17:22:15 +02:00 committed by Emmanuel Ledoux
commit 9fc225ac5d
40 changed files with 8123 additions and 1382 deletions

3
.gitignore vendored
View File

@ -122,3 +122,6 @@ default.log
*.txt.user
*.autosave
# etags
TAGS

View File

@ -18,9 +18,6 @@
define_channel_client("serial")
set(${MODULE_PREFIX}_SRCS
serial_tty.c
serial_tty.h
serial_constants.h
serial_main.c)
add_channel_client_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} TRUE "DeviceServiceEntry")
@ -32,6 +29,11 @@ set_complex_link_libraries(VARIABLE ${MODULE_PREFIX}_LIBS
MODULE freerdp
MODULES freerdp-utils)
set_complex_link_libraries(VARIABLE ${MODULE_PREFIX}_LIBS
MONOLITHIC ${MONOLITHIC_BUILD}
MODULE winpr
MODULES winpr-comm)
target_link_libraries(${MODULE_NAME} ${${MODULE_PREFIX}_LIBS})
install(TARGETS ${MODULE_NAME} DESTINATION ${FREERDP_ADDIN_PATH} EXPORT FreeRDPTargets)

View File

@ -1,154 +0,0 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* Serial Port Device Service Virtual Channel
*
* Copyright 2011 O.S. Systems Software Ltda.
* Copyright 2011 Eduardo Fiss Beloni <beloni@ossystems.com.br>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef __SERIAL_CONSTANTS_H
#define __SERIAL_CONSTANTS_H
/* http://www.codeproject.com/KB/system/chaiyasit_t.aspx */
#define SERIAL_TIMEOUT_MAX 4294967295u
/* DR_CONTROL_REQ.IoControlCode */
enum DR_PORT_CONTROL_REQ
{
IOCTL_SERIAL_SET_BAUD_RATE = 0x001B0004,
IOCTL_SERIAL_GET_BAUD_RATE = 0x001B0050,
IOCTL_SERIAL_SET_LINE_CONTROL = 0x001B000C,
IOCTL_SERIAL_GET_LINE_CONTROL = 0x001B0054,
IOCTL_SERIAL_SET_TIMEOUTS = 0x001B001C,
IOCTL_SERIAL_GET_TIMEOUTS = 0x001B0020,
/* GET_CHARS and SET_CHARS are swapped in the RDP docs [MS-RDPESP] */
IOCTL_SERIAL_GET_CHARS = 0x001B0058,
IOCTL_SERIAL_SET_CHARS = 0x001B005C,
IOCTL_SERIAL_SET_DTR = 0x001B0024,
IOCTL_SERIAL_CLR_DTR = 0x001B0028,
IOCTL_SERIAL_RESET_DEVICE = 0x001B002C,
IOCTL_SERIAL_SET_RTS = 0x001B0030,
IOCTL_SERIAL_CLR_RTS = 0x001B0034,
IOCTL_SERIAL_SET_XOFF = 0x001B0038,
IOCTL_SERIAL_SET_XON = 0x001B003C,
IOCTL_SERIAL_SET_BREAK_ON = 0x001B0010,
IOCTL_SERIAL_SET_BREAK_OFF = 0x001B0014,
IOCTL_SERIAL_SET_QUEUE_SIZE = 0x001B0008,
IOCTL_SERIAL_GET_WAIT_MASK = 0x001B0040,
IOCTL_SERIAL_SET_WAIT_MASK = 0x001B0044,
IOCTL_SERIAL_WAIT_ON_MASK = 0x001B0048,
IOCTL_SERIAL_IMMEDIATE_CHAR = 0x001B0018,
IOCTL_SERIAL_PURGE = 0x001B004C,
IOCTL_SERIAL_GET_HANDFLOW = 0x001B0060,
IOCTL_SERIAL_SET_HANDFLOW = 0x001B0064,
IOCTL_SERIAL_GET_MODEMSTATUS = 0x001B0068,
IOCTL_SERIAL_GET_DTRRTS = 0x001B0078,
/* according to [MS-RDPESP] it should be 0x001B0084, but servers send 0x001B006C */
IOCTL_SERIAL_GET_COMMSTATUS = 0x001B006C,
IOCTL_SERIAL_GET_PROPERTIES = 0x001B0074,
IOCTL_SERIAL_XOFF_COUNTER = 0x001B0070,
IOCTL_SERIAL_LSRMST_INSERT = 0x001B007C,
IOCTL_SERIAL_CONFIG_SIZE = 0x001B0080,
IOCTL_SERIAL_GET_STATS = 0x001B008C,
IOCTL_SERIAL_CLEAR_STATS = 0x001B0090,
IOCTL_SERIAL_GET_MODEM_CONTROL = 0x001B0094,
IOCTL_SERIAL_SET_MODEM_CONTROL = 0x001B0098,
IOCTL_SERIAL_SET_FIFO_CONTROL = 0x001B009C,
};
enum SERIAL_PURGE_MASK
{
SERIAL_PURGE_TXABORT = 0x00000001,
SERIAL_PURGE_RXABORT = 0x00000002,
SERIAL_PURGE_TXCLEAR = 0x00000004,
SERIAL_PURGE_RXCLEAR = 0x00000008,
};
enum SERIAL_WAIT_MASK
{
SERIAL_EV_RXCHAR = 0x0001, /* Any Character received */
SERIAL_EV_RXFLAG = 0x0002, /* Received certain character */
SERIAL_EV_TXEMPTY = 0x0004, /* Transmitt Queue Empty */
SERIAL_EV_CTS = 0x0008, /* CTS changed state */
SERIAL_EV_DSR = 0x0010, /* DSR changed state */
SERIAL_EV_RLSD = 0x0020, /* RLSD changed state */
SERIAL_EV_BREAK = 0x0040, /* BREAK received */
SERIAL_EV_ERR = 0x0080, /* Line status error occurred */
SERIAL_EV_RING = 0x0100, /* Ring signal detected */
SERIAL_EV_PERR = 0x0200, /* Printer error occured */
SERIAL_EV_RX80FULL = 0x0400,/* Receive buffer is 80 percent full */
SERIAL_EV_EVENT1 = 0x0800, /* Provider specific event 1 */
SERIAL_EV_EVENT2 = 0x1000, /* Provider specific event 2 */
};
enum SERIAL_MODEM_STATUS
{
SERIAL_MS_DTR = 0x01,
SERIAL_MS_RTS = 0x02,
SERIAL_MS_CTS = 0x10,
SERIAL_MS_DSR = 0x20,
SERIAL_MS_RNG = 0x40,
SERIAL_MS_CAR = 0x80,
};
enum SERIAL_HANDFLOW
{
SERIAL_DTR_CONTROL = 0x01,
SERIAL_CTS_HANDSHAKE = 0x08,
SERIAL_ERROR_ABORT = 0x80000000,
};
enum SERIAL_FLOW_CONTROL
{
SERIAL_XON_HANDSHAKE = 0x01,
SERIAL_XOFF_HANDSHAKE = 0x02,
SERIAL_DSR_SENSITIVITY = 0x40,
};
enum SERIAL_CHARS
{
SERIAL_CHAR_EOF = 0,
SERIAL_CHAR_ERROR = 1,
SERIAL_CHAR_BREAK = 2,
SERIAL_CHAR_EVENT = 3,
SERIAL_CHAR_XON = 4,
SERIAL_CHAR_XOFF = 5,
};
enum SERIAL_ABORT_IO
{
SERIAL_ABORT_IO_NONE = 0,
SERIAL_ABORT_IO_WRITE = 1,
SERIAL_ABORT_IO_READ = 2,
};
enum SERIAL_STOP_BITS
{
SERIAL_STOP_BITS_1 = 0,
SERIAL_STOP_BITS_2 = 2,
};
enum SERIAL_PARITY
{
SERIAL_NO_PARITY = 0,
SERIAL_ODD_PARITY = 1,
SERIAL_EVEN_PARITY = 2,
};
#endif

View File

@ -4,6 +4,7 @@
*
* Copyright 2011 O.S. Systems Software Ltda.
* Copyright 2011 Eduardo Fiss Beloni <beloni@ossystems.com.br>
* Copyright 2014 Hewlett-Packard Development Company, L.P.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -41,97 +42,201 @@
#include <unistd.h>
#endif
#include "serial_tty.h"
#include "serial_constants.h"
#include <winpr/collections.h>
#include <winpr/comm.h>
#include <winpr/crt.h>
#include <winpr/wlog.h>
#include <winpr/stream.h>
#include <winpr/synch.h>
#include <winpr/thread.h>
#include <winpr/stream.h>
#include <winpr/collections.h>
/* #include <winpr/wlog.h> */
#include <freerdp/freerdp.h>
#include <freerdp/channels/rdpdr.h>
#define MAX_IRP_THREADS 5
typedef struct _SERIAL_DEVICE SERIAL_DEVICE;
struct _SERIAL_DEVICE
{
DEVICE device;
SERIAL_DRIVER_ID ServerSerialDriverId;
HANDLE* hComm;
char* path;
SERIAL_TTY* tty;
/* TODO: use of log (prefered the old fashion DEBUG_SVC and
* DEBUG_WARN macros for backward compatibility resaons)
*/
/* wLog* log; */
HANDLE MainThread;
wMessageQueue* MainIrpQueue;
wLog* log;
HANDLE thread;
wMessageQueue* IrpQueue;
/* one thread per pending IRP and indexed according their CompletionId */
wListDictionary *IrpThreads;
UINT32 IrpThreadToBeTerminatedCount;
CRITICAL_SECTION TerminatingIrpThreadsLock;
};
typedef struct _IRP_THREAD_DATA IRP_THREAD_DATA;
struct _IRP_THREAD_DATA
{
SERIAL_DEVICE *serial;
IRP *irp;
};
static UINT32 _GetLastErrorToIoStatus()
{
/* http://msdn.microsoft.com/en-us/library/ff547466%28v=vs.85%29.aspx#generic_status_values_for_serial_device_control_requests */
switch(GetLastError())
{
case ERROR_BAD_DEVICE:
return STATUS_INVALID_DEVICE_REQUEST;
case ERROR_CALL_NOT_IMPLEMENTED:
return STATUS_NOT_IMPLEMENTED;
case ERROR_CANCELLED:
return STATUS_CANCELLED;
case ERROR_INSUFFICIENT_BUFFER:
return STATUS_BUFFER_TOO_SMALL; /* NB: STATUS_BUFFER_SIZE_TOO_SMALL not defined */
case ERROR_INVALID_DEVICE_OBJECT_PARAMETER: /* eg: SerCx2.sys' _purge() */
return STATUS_INVALID_DEVICE_STATE;
case ERROR_INVALID_HANDLE:
return STATUS_INVALID_DEVICE_REQUEST;
case ERROR_INVALID_PARAMETER:
return STATUS_INVALID_PARAMETER;
case ERROR_IO_DEVICE:
return STATUS_IO_DEVICE_ERROR;
case ERROR_IO_PENDING:
return STATUS_PENDING;
case ERROR_NOT_SUPPORTED:
return STATUS_NOT_SUPPORTED;
case ERROR_TIMEOUT:
return STATUS_TIMEOUT;
/* no default */
}
DEBUG_SVC("unexpected last-error: 0x%lx", GetLastError());
return STATUS_UNSUCCESSFUL;
}
static void serial_process_irp_create(SERIAL_DEVICE* serial, IRP* irp)
{
int status;
UINT32 FileId;
DWORD DesiredAccess;
DWORD SharedAccess;
DWORD CreateDisposition;
UINT32 PathLength;
char* path = NULL;
SERIAL_TTY* tty;
Stream_Seek_UINT32(irp->input); /* DesiredAccess (4 bytes) */
Stream_Seek_UINT64(irp->input); /* AllocationSize (8 bytes) */
Stream_Seek_UINT32(irp->input); /* FileAttributes (4 bytes) */
Stream_Seek_UINT32(irp->input); /* SharedAccess (4 bytes) */
Stream_Seek_UINT32(irp->input); /* CreateDisposition (4 bytes) */
Stream_Seek_UINT32(irp->input); /* CreateOptions (4 bytes) */
Stream_Read_UINT32(irp->input, DesiredAccess); /* DesiredAccess (4 bytes) */
Stream_Seek_UINT64(irp->input); /* AllocationSize (8 bytes) */
Stream_Seek_UINT32(irp->input); /* FileAttributes (4 bytes) */
Stream_Read_UINT32(irp->input, SharedAccess); /* SharedAccess (4 bytes) */
Stream_Read_UINT32(irp->input, CreateDisposition); /* CreateDisposition (4 bytes) */
Stream_Seek_UINT32(irp->input); /* CreateOptions (4 bytes) */
Stream_Read_UINT32(irp->input, PathLength); /* PathLength (4 bytes) */
Stream_Seek(irp->input, PathLength); /* Path (variable) */
Stream_Read_UINT32(irp->input, PathLength); /* PathLength (4 bytes) */
assert(PathLength == 0); /* MS-RDPESP 2.2.2.2 */
status = ConvertFromUnicode(CP_UTF8, 0, (WCHAR*) Stream_Pointer(irp->input),
PathLength / 2, &path, 0, NULL, NULL);
if (status < 1)
path = (char*) calloc(1, 1);
#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 Windows 2012 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);
*
*/
FileId = irp->devman->id_sequence++;
DEBUG_SVC("DesiredAccess: 0x%lX, SharedAccess: 0x%lX, CreateDisposition: 0x%lX", DesiredAccess, SharedAccess, CreateDisposition);
tty = serial_tty_new(serial->path, FileId);
/* FIXME: As of today only the flags below are supported by CommCreateFileA: */
DesiredAccess = GENERIC_READ | GENERIC_WRITE;
SharedAccess = 0;
CreateDisposition = OPEN_EXISTING;
#endif
if (!tty)
serial->hComm = CreateFile(serial->device.name,
DesiredAccess,
SharedAccess,
NULL, /* SecurityAttributes */
CreateDisposition,
0, /* FlagsAndAttributes */
NULL); /* TemplateFile */
if (!serial->hComm || (serial->hComm == INVALID_HANDLE_VALUE))
{
DEBUG_WARN("CreateFile failure: %s last-error: Ox%lX\n", serial->device.name, GetLastError());
irp->IoStatus = STATUS_UNSUCCESSFUL;
FileId = 0;
}
else
{
serial->tty = tty;
goto error_handle;
}
Stream_Write_UINT32(irp->output, FileId); /* FileId (4 bytes) */
Stream_Write_UINT8(irp->output, 0); /* Information (1 byte) */
_comm_setServerSerialDriver(serial->hComm, serial->ServerSerialDriverId);
free(path);
/* FIXME: Appeared to be useful to setup some devices. Guess
* the device driver asked to setup some unsupported feature
* that were not eventually used. TODO: collecting more
* details, a command line argument? */
/* _comm_set_permissive(serial->hComm, TRUE); */
irp->Complete(irp);
/* 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); */
assert(irp->FileId == 0);
irp->FileId = irp->devman->id_sequence++; /* FIXME: why not ((WINPR_COMM*)hComm)->fd? */
irp->IoStatus = STATUS_SUCCESS;
DEBUG_SVC("%s (DeviceId: %d, FileId: %d) created.", serial->device.name, irp->device->id, irp->FileId);
error_handle:
Stream_Write_UINT32(irp->output, irp->FileId); /* FileId (4 bytes) */
Stream_Write_UINT8(irp->output, 0); /* Information (1 byte) */
}
static void serial_process_irp_close(SERIAL_DEVICE* serial, IRP* irp)
{
SERIAL_TTY* tty = serial->tty;
Stream_Seek(irp->input, 32); /* Padding (32 bytes) */
if (!tty)
if (!CloseHandle(serial->hComm))
{
DEBUG_WARN("CloseHandle failure: %s (%d) closed.", serial->device.name, irp->device->id);
irp->IoStatus = STATUS_UNSUCCESSFUL;
}
else
{
serial_tty_free(tty);
serial->tty = NULL;
goto error_handle;
}
DEBUG_SVC("%s (DeviceId: %d, FileId: %d) closed.", serial->device.name, irp->device->id, irp->FileId);
serial->hComm = NULL;
irp->IoStatus = STATUS_SUCCESS;
error_handle:
Stream_Zero(irp->output, 5); /* Padding (5 bytes) */
irp->Complete(irp);
}
static void serial_process_irp_read(SERIAL_DEVICE* serial, IRP* irp)
@ -139,119 +244,175 @@ 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)
buffer = (BYTE*)calloc(Length, sizeof(BYTE));
if (buffer == NULL)
{
irp->IoStatus = STATUS_UNSUCCESSFUL;
Length = 0;
irp->IoStatus = STATUS_NO_MEMORY;
goto error_handle;
}
/* MS-RDPESP 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 %d 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=%ld, last-error: 0x%lX", serial->device.name, nbRead, GetLastError());
if (!serial_tty_read(tty, buffer, &Length))
{
irp->IoStatus = STATUS_UNSUCCESSFUL;
free(buffer);
buffer = NULL;
Length = 0;
}
else
{
}
irp->IoStatus = _GetLastErrorToIoStatus();
}
Stream_Write_UINT32(irp->output, Length); /* Length (4 bytes) */
DEBUG_SVC("%lu bytes read from %s", nbRead, serial->device.name);
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);
irp->Complete(irp);
if (buffer)
free(buffer);
}
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)
{
irp->IoStatus = STATUS_UNSUCCESSFUL;
Length = 0;
/* MS-RDPESP 3.2.5.1.5: The Offset field is ignored
* assert(Offset == 0);
*
* Using a serial printer, noticed though this field could be
* set.
*/
Stream_Write_UINT32(irp->output, Length); /* Length (4 bytes) */
Stream_Write_UINT8(irp->output, 0); /* Padding (1 byte) */
irp->Complete(irp);
return;
DEBUG_SVC("writing %d 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_SUCCESS;
}
else
{
DEBUG_SVC("write failure to %s, nbWritten=%ld, last-error: 0x%lX", serial->device.name, nbWritten, GetLastError());
irp->IoStatus = _GetLastErrorToIoStatus();
}
status = serial_tty_write(tty, Stream_Pointer(irp->input), Length);
DEBUG_SVC("%lu bytes written to %s", nbWritten, serial->device.name);
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);
}
static void serial_process_irp_device_control(SERIAL_DEVICE* serial, IRP* irp)
{
UINT32 IoControlCode;
UINT32 InputBufferLength;
BYTE* InputBuffer = NULL;
UINT32 OutputBufferLength;
UINT32 abortIo = SERIAL_ABORT_IO_NONE;
SERIAL_TTY* tty = serial->tty;
BYTE* OutputBuffer = NULL;
DWORD BytesReturned = 0;
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)
OutputBuffer = (BYTE*)calloc(OutputBufferLength, sizeof(BYTE));
if (OutputBuffer == NULL)
{
irp->IoStatus = STATUS_UNSUCCESSFUL;
OutputBufferLength = 0;
irp->IoStatus = STATUS_NO_MEMORY;
goto error_handle;
}
InputBuffer = (BYTE*)calloc(InputBufferLength, sizeof(BYTE));
if (InputBuffer == NULL)
{
irp->IoStatus = STATUS_NO_MEMORY;
goto error_handle;
}
Stream_Read(irp->input, InputBuffer, InputBufferLength);
DEBUG_SVC("CommDeviceIoControl: CompletionId=%d, IoControlCode=[0x%X] %s", irp->CompletionId, IoControlCode, _comm_serial_ioctl_name(IoControlCode));
/* FIXME: CommDeviceIoControl to be replaced by DeviceIoControl() */
if (CommDeviceIoControl(serial->hComm, IoControlCode, InputBuffer, InputBufferLength, OutputBuffer, OutputBufferLength, &BytesReturned, NULL))
{
/* DEBUG_SVC("CommDeviceIoControl: CompletionId=%d, IoControlCode=[0x%X] %s done", irp->CompletionId, IoControlCode, _comm_serial_ioctl_name(IoControlCode)); */
irp->IoStatus = STATUS_SUCCESS;
}
else
{
irp->IoStatus = serial_tty_control(tty, IoControlCode, irp->input, irp->output, &abortIo);
DEBUG_SVC("CommDeviceIoControl failure: IoControlCode=[0x%X] %s, last-error: 0x%lX",
IoControlCode, _comm_serial_ioctl_name(IoControlCode), GetLastError());
irp->IoStatus = _GetLastErrorToIoStatus();
}
irp->Complete(irp);
error_handle:
/* FIXME: find out whether it's required or not to get
* BytesReturned == OutputBufferLength when
* CommDeviceIoControl returns FALSE */
assert(OutputBufferLength == BytesReturned);
Stream_Write_UINT32(irp->output, BytesReturned); /* OutputBufferLength (4 bytes) */
if (BytesReturned > 0)
{
Stream_EnsureRemainingCapacity(irp->output, BytesReturned);
Stream_Write(irp->output, OutputBuffer, BytesReturned); /* OutputBuffer */
}
/* FIXME: Why at least Windows 2008R2 gets lost with this
* extra byte and likely on a IOCTL_SERIAL_SET_BAUD_RATE? The
* extra byte is well required according MS-RDPEFS
* 2.2.1.5.5 */
/* else */
/* { */
/* Stream_Write_UINT8(irp->output, 0); /\* Padding (1 byte) *\/ */
/* } */
if (InputBuffer != NULL)
free(InputBuffer);
if (OutputBuffer != NULL)
free(OutputBuffer);
}
static void serial_process_irp(SERIAL_DEVICE* serial, IRP* irp)
{
WLog_Print(serial->log, WLOG_DEBUG, "IRP MajorFunction: 0x%04X MinorFunction: 0x%04X\n",
irp->MajorFunction, irp->MinorFunction);
/* WLog_Print(serial->log, WLOG_DEBUG, "IRP MajorFunction: 0x%04X MinorFunction: 0x%04X\n", */
/* irp->MajorFunction, irp->MinorFunction); */
switch (irp->MajorFunction)
{
@ -277,59 +438,300 @@ static void serial_process_irp(SERIAL_DEVICE* serial, IRP* irp)
default:
irp->IoStatus = STATUS_NOT_SUPPORTED;
irp->Complete(irp);
break;
}
}
static void* irp_thread_func(void* arg)
{
IRP_THREAD_DATA *data = (IRP_THREAD_DATA*)arg;
/* blocks until the end of the request */
serial_process_irp(data->serial, data->irp);
EnterCriticalSection(&data->serial->TerminatingIrpThreadsLock);
data->serial->IrpThreadToBeTerminatedCount++;
data->irp->Complete(data->irp);
LeaveCriticalSection(&data->serial->TerminatingIrpThreadsLock);
/* NB: At this point, the server might already being reusing
* the CompletionId whereas the thread is not yet
* terminated */
free(data);
ExitThread(0);
return NULL;
}
static void create_irp_thread(SERIAL_DEVICE *serial, IRP *irp)
{
IRP_THREAD_DATA *data = NULL;
HANDLE irpThread = INVALID_HANDLE_VALUE;
HANDLE previousIrpThread;
/* for a test/debug purpose, uncomment the code below to get a
* single thread for all IRPs. NB: two IRPs could not be
* processed at the same time, typically two concurent
* Read/Write operations could block each other. */
/* serial_process_irp(serial, irp); */
/* irp->Complete(irp); */
/* return; */
/* NOTE: for good or bad, this implementation relies on the
* server to avoid a flooding of requests. see also _purge().
*/
EnterCriticalSection(&serial->TerminatingIrpThreadsLock);
while (serial->IrpThreadToBeTerminatedCount > 0)
{
/* Cleaning up termitating and pending irp
* threads. See also: irp_thread_func() */
HANDLE irpThread;
ULONG_PTR *ids;
int i, nbIds;
nbIds = ListDictionary_GetKeys(serial->IrpThreads, &ids);
for (i=0; i<nbIds; i++)
{
/* Checking if ids[i] is terminating or pending */
DWORD waitResult;
ULONG_PTR id = ids[i];
irpThread = ListDictionary_GetItemValue(serial->IrpThreads, (void*)id);
/* FIXME: not quite sure a zero timeout is a good thing to check whether a thread is stil alived or not */
waitResult = WaitForSingleObject(irpThread, 0);
if (waitResult == WAIT_OBJECT_0)
{
/* terminating thread */
/* DEBUG_SVC("IRP thread with CompletionId=%d naturally died", id); */
CloseHandle(irpThread);
ListDictionary_Remove(serial->IrpThreads, (void*)id);
serial->IrpThreadToBeTerminatedCount--;
}
else if (waitResult != WAIT_TIMEOUT)
{
/* unexpected thread state */
DEBUG_WARN("WaitForSingleObject, got an unexpected result=0x%lX\n", waitResult);
assert(FALSE);
}
/* pending thread (but not yet terminating thread) if waitResult == WAIT_TIMEOUT */
}
if (serial->IrpThreadToBeTerminatedCount > 0)
{
DEBUG_SVC("%d IRP thread(s) not yet terminated", serial->IrpThreadToBeTerminatedCount);
Sleep(1); /* 1 ms */
}
}
LeaveCriticalSection(&serial->TerminatingIrpThreadsLock);
/* NB: At this point and thanks to the synchronization we're
* sure that the incoming IRP uses well a recycled
* CompletionId or the server sent again an IRP already posted
* which didn't get yet a response (this later server behavior
* at least observed with IOCTL_SERIAL_WAIT_ON_MASK and
* mstsc.exe).
*
* FIXME: behavior documented somewhere? behavior not yet
* observed with FreeRDP).
*/
previousIrpThread = ListDictionary_GetItemValue(serial->IrpThreads, (void*)irp->CompletionId);
if (previousIrpThread)
{
/* Thread still alived <=> Request still pending */
DEBUG_SVC("IRP recall: IRP with the CompletionId=%d not yet completed!", irp->CompletionId);
assert(FALSE); /* unimplemented */
/* TODO: asserts that previousIrpThread handles well
* the same request by checking more details. Need an
* access to the IRP object used by previousIrpThread
*/
/* TODO: taking over the pending IRP or sending a kind
* of wake up signal to accelerate the pending
* request
*
* To be considered:
* if (IoControlCode == IOCTL_SERIAL_WAIT_ON_MASK) {
* pComm->PendingEvents |= SERIAL_EV_FREERDP_*;
* }
*/
irp->Discard(irp);
return;
}
if (ListDictionary_Count(serial->IrpThreads) >= MAX_IRP_THREADS)
{
DEBUG_WARN("Number of IRP threads threshold reached: %d, keep on anyway", ListDictionary_Count(serial->IrpThreads));
assert(FALSE); /* unimplemented */
/* TODO: MAX_IRP_THREADS has been thought to avoid a
* flooding of pending requests. Use
* WaitForMultipleObjects() when available in winpr
* for threads.
*/
}
/* error_handle to be used ... */
data = (IRP_THREAD_DATA*)calloc(1, sizeof(IRP_THREAD_DATA));
if (data == NULL)
{
DEBUG_WARN("Could not allocate a new IRP_THREAD_DATA.");
goto error_handle;
}
data->serial = serial;
data->irp = irp;
/* data freed by irp_thread_func */
irpThread = CreateThread(NULL,
0,
(LPTHREAD_START_ROUTINE)irp_thread_func,
(void*)data,
0,
NULL);
if (irpThread == INVALID_HANDLE_VALUE)
{
DEBUG_WARN("Could not allocate a new IRP thread.");
goto error_handle;
}
ListDictionary_Add(serial->IrpThreads, (void*)irp->CompletionId, irpThread);
return;
error_handle:
irp->IoStatus = STATUS_NO_MEMORY;
irp->Complete(irp);
if (data)
free(data);
}
static void terminate_pending_irp_threads(SERIAL_DEVICE *serial)
{
ULONG_PTR *ids;
int i, nbIds;
nbIds = ListDictionary_GetKeys(serial->IrpThreads, &ids);
DEBUG_SVC("Terminating %d IRP thread(s)", nbIds);
for (i=0; i<nbIds; i++)
{
HANDLE irpThread;
ULONG_PTR id = ids[i];
irpThread = ListDictionary_GetItemValue(serial->IrpThreads, (void*)id);
TerminateThread(irpThread, 0);
WaitForSingleObject(irpThread, INFINITE);
CloseHandle(irpThread);
DEBUG_SVC("IRP thread terminated, CompletionId %d", id);
}
ListDictionary_Clear(serial->IrpThreads);
}
static void* serial_thread_func(void* arg)
{
IRP* irp;
wMessage message;
SERIAL_DEVICE* drive = (SERIAL_DEVICE*) arg;
SERIAL_DEVICE* serial = (SERIAL_DEVICE*) arg;
while (1)
{
if (!MessageQueue_Wait(drive->IrpQueue))
if (!MessageQueue_Wait(serial->MainIrpQueue))
break;
if (!MessageQueue_Peek(drive->IrpQueue, &message, TRUE))
if (!MessageQueue_Peek(serial->MainIrpQueue, &message, TRUE))
break;
if (message.id == WMQ_QUIT)
{
terminate_pending_irp_threads(serial);
break;
}
irp = (IRP*) message.wParam;
if (irp)
serial_process_irp(drive, irp);
create_irp_thread(serial, irp);
}
ExitThread(0);
return NULL;
}
static void serial_irp_request(DEVICE* device, IRP* irp)
{
SERIAL_DEVICE* serial = (SERIAL_DEVICE*) device;
MessageQueue_Post(serial->IrpQueue, NULL, 0, (void*) irp, NULL);
assert(irp != NULL);
if (irp == NULL)
return;
/* NB: ENABLE_ASYNCIO is set, (MS-RDPEFS 2.2.2.7.2) this
* allows the server to send multiple simultaneous read or
* write requests.
*/
MessageQueue_Post(serial->MainIrpQueue, NULL, 0, (void*) irp, NULL);
}
static void serial_free(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);
WaitForSingleObject(serial->thread, INFINITE);
CloseHandle(serial->thread);
MessageQueue_PostQuit(serial->MainIrpQueue, 0);
WaitForSingleObject(serial->MainThread, INFINITE);
CloseHandle(serial->MainThread);
serial_tty_free(serial->tty);
if (serial->hComm)
CloseHandle(serial->hComm);
/* Clean up resources */
Stream_Free(serial->device.data, TRUE);
MessageQueue_Free(serial->IrpQueue);
MessageQueue_Free(serial->MainIrpQueue);
ListDictionary_Free(serial->IrpThreads);
DeleteCriticalSection(&serial->TerminatingIrpThreadsLock);
free(serial);
}
@ -343,23 +745,32 @@ int DeviceServiceEntry(PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints)
int i, len;
char* name;
char* path;
char* driver;
RDPDR_SERIAL* device;
SERIAL_DEVICE* serial;
device = (RDPDR_SERIAL*) pEntryPoints->device;
name = device->Name;
path = device->Path;
driver = device->Driver;
if (!name || (name[0] == '*'))
{
/* TODO: implement auto detection of parallel ports */
/* TODO: implement auto detection of serial ports */
return 0;
}
if ((name && name[0]) && (path && path[0]))
{
serial = (SERIAL_DEVICE*) calloc(1, sizeof(SERIAL_DEVICE));
DEBUG_SVC("Defining %s as %s", name, path);
if (!DefineCommDevice(name /* eg: COM1 */, path /* eg: /dev/ttyS0 */))
{
DEBUG_SVC("Could not define %s as %s", name, path);
return -1;
}
serial = (SERIAL_DEVICE*) calloc(1, sizeof(SERIAL_DEVICE));
if (!serial)
return -1;
@ -374,17 +785,50 @@ 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);
if (driver != NULL)
{
if (_stricmp(driver, "Serial") == 0)
serial->ServerSerialDriverId = SerialDriverSerialSys;
else if (_stricmp(driver, "SerCx") == 0)
serial->ServerSerialDriverId = SerialDriverSerCxSys;
else if (_stricmp(driver, "SerCx2") == 0)
serial->ServerSerialDriverId = SerialDriverSerCx2Sys;
else
{
assert(FALSE);
WLog_Init();
serial->log = WLog_Get("com.freerdp.channel.serial.client");
WLog_Print(serial->log, WLOG_DEBUG, "initializing");
DEBUG_SVC("Unknown server's serial driver: %s. SerCx2 will be used", driver);
serial->ServerSerialDriverId = SerialDriverSerCx2Sys;
}
}
else
{
/* default driver */
serial->ServerSerialDriverId = SerialDriverSerCx2Sys;
}
DEBUG_SVC("Server's serial driver: %s (id: %d)", driver, serial->ServerSerialDriverId);
/* TODO: implement auto detection of the server's serial driver */
serial->MainIrpQueue = MessageQueue_New(NULL);
/* IrpThreads content only modified by create_irp_thread() */
serial->IrpThreads = ListDictionary_New(FALSE);
serial->IrpThreadToBeTerminatedCount = 0;
InitializeCriticalSection(&serial->TerminatingIrpThreadsLock);
/* WLog_Init(); */
/* serial->log = WLog_Get("com.freerdp.channel.serial.client"); */
/* WLog_Print(serial->log, WLOG_DEBUG, "initializing"); */
pEntryPoints->RegisterDevice(pEntryPoints->devman, (DEVICE*) serial);
serial->thread = CreateThread(NULL, 0,
(LPTHREAD_START_ROUTINE) serial_thread_func, (void*) serial, 0, NULL);
serial->MainThread = CreateThread(NULL,
0,
(LPTHREAD_START_ROUTINE) serial_thread_func,
(void*) serial,
0,
NULL);
}
return 0;

File diff suppressed because it is too large Load Diff

View File

@ -1,80 +0,0 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* Serial Port Device Service Virtual Channel
*
* Copyright 2011 O.S. Systems Software Ltda.
* Copyright 2011 Eduardo Fiss Beloni <beloni@ossystems.com.br>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef __SERIAL_TTY_H
#define __SERIAL_TTY_H
#include <sys/types.h>
#include <sys/stat.h>
#ifndef _WIN32
#include <dirent.h>
#endif
#include <freerdp/types.h>
#include <winpr/stream.h>
typedef struct _SERIAL_TTY SERIAL_TTY;
struct _SERIAL_TTY
{
UINT32 id;
int fd;
int dtr;
int rts;
UINT32 control;
UINT32 xonoff;
UINT32 onlimit;
UINT32 offlimit;
UINT32 baud_rate;
UINT32 queue_in_size;
UINT32 queue_out_size;
UINT32 wait_mask;
UINT32 read_interval_timeout;
UINT32 read_total_timeout_multiplier;
UINT32 read_total_timeout_constant;
UINT32 write_total_timeout_multiplier;
UINT32 write_total_timeout_constant;
BYTE stop_bits;
BYTE parity;
BYTE word_length;
BYTE chars[6];
struct termios* ptermios;
struct termios* pold_termios;
int event_txempty;
int event_cts;
int event_dsr;
int event_rlsd;
int event_pending;
long timeout;
};
SERIAL_TTY* serial_tty_new(const char* path, UINT32 id);
void serial_tty_free(SERIAL_TTY* tty);
BOOL serial_tty_read(SERIAL_TTY* tty, BYTE* buffer, UINT32* Length);
int serial_tty_write(SERIAL_TTY* tty, BYTE* buffer, UINT32 Length);
UINT32 serial_tty_control(SERIAL_TTY* tty, UINT32 IoControlCode, wStream* input, wStream* output, UINT32* abort_io);
BOOL serial_tty_get_event(SERIAL_TTY* tty, UINT32* result);
#endif /* __SERIAL_TTY_H */

View File

@ -400,6 +400,9 @@ int freerdp_client_add_device_channel(rdpSettings* settings, int count, char** p
if (count > 2)
serial->Path = _strdup(params[2]);
if (count > 3)
serial->Driver = _strdup(params[3]);
freerdp_device_collection_add(settings, (RDPDR_DEVICE*) serial);
return 1;

View File

@ -466,6 +466,7 @@ struct _RDPDR_SERIAL
UINT32 Type;
char* Name;
char* Path;
char* Driver;
};
typedef struct _RDPDR_SERIAL RDPDR_SERIAL;

View File

@ -287,8 +287,17 @@ out_smartc_name_error:
goto out_serial_path_error;
}
if (serial->Driver)
{
_serial->Driver = _strdup(serial->Driver);
if (!_serial->Driver)
goto out_serial_driver_error;
}
return (RDPDR_DEVICE*) _serial;
out_serial_driver_error:
free(_serial->Path);
out_serial_path_error:
free(_serial->Name);
out_serial_name_error:
@ -360,6 +369,7 @@ void freerdp_device_collection_free(rdpSettings* settings)
else if (settings->DeviceArray[index]->Type == RDPDR_DTYP_SERIAL)
{
free(((RDPDR_SERIAL*) device)->Path);
free(((RDPDR_SERIAL*) device)->Driver);
}
else if (settings->DeviceArray[index]->Type == RDPDR_DTYP_PARALLEL)
{

589
winpr/include/winpr/comm.h Normal file
View File

@ -0,0 +1,589 @@
/**
* WinPR: Windows Portable Runtime
* Serial Communication API
*
* Copyright 2011 O.S. Systems Software Ltda.
* Copyright 2011 Eduardo Fiss Beloni <beloni@ossystems.com.br>
* Copyright 2014 Marc-Andre Moreau <marcandre.moreau@gmail.com>
* Copyright 2014 Hewlett-Packard Development Company, L.P.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef WINPR_COMM_H
#define WINPR_COMM_H
#include <winpr/collections.h>
#include <winpr/file.h>
#include <winpr/winpr.h>
#include <winpr/wtypes.h>
#ifndef _WIN32
#define NOPARITY 0
#define ODDPARITY 1
#define EVENPARITY 2
#define MARKPARITY 3
#define SPACEPARITY 4
#define ONESTOPBIT 0
#define ONE5STOPBITS 1
#define TWOSTOPBITS 2
#ifndef IGNORE
#define IGNORE 0
#endif
#define CBR_110 110
#define CBR_300 300
#define CBR_600 600
#define CBR_1200 1200
#define CBR_2400 2400
#define CBR_4800 4800
#define CBR_9600 9600
#define CBR_14400 14400
#define CBR_19200 19200
#define CBR_38400 38400
#define CBR_56000 56000
#define CBR_57600 57600
#define CBR_115200 115200
#define CBR_128000 128000
#define CBR_256000 256000
#define CE_RXOVER 0x0001
#define CE_OVERRUN 0x0002
#define CE_RXPARITY 0x0004
#define CE_FRAME 0x0008
#define CE_BREAK 0x0010
#define CE_TXFULL 0x0100
#define CE_PTO 0x0200
#define CE_IOE 0x0400
#define CE_DNS 0x0800
#define CE_OOP 0x1000
#define CE_MODE 0x8000
#define IE_BADID (-1)
#define IE_OPEN (-2)
#define IE_NOPEN (-3)
#define IE_MEMORY (-4)
#define IE_DEFAULT (-5)
#define IE_HARDWARE (-10)
#define IE_BYTESIZE (-11)
#define IE_BAUDRATE (-12)
#define EV_RXCHAR 0x0001
#define EV_RXFLAG 0x0002
#define EV_TXEMPTY 0x0004
#define EV_CTS 0x0008
#define EV_DSR 0x0010
#define EV_RLSD 0x0020
#define EV_BREAK 0x0040
#define EV_ERR 0x0080
#define EV_RING 0x0100
#define EV_PERR 0x0200
#define EV_RX80FULL 0x0400
#define EV_EVENT1 0x0800
#define EV_EVENT2 0x1000
#define SETXOFF 1
#define SETXON 2
#define SETRTS 3
#define CLRRTS 4
#define SETDTR 5
#define CLRDTR 6
#define RESETDEV 7
#define SETBREAK 8
#define CLRBREAK 9
#define PURGE_TXABORT 0x0001
#define PURGE_RXABORT 0x0002
#define PURGE_TXCLEAR 0x0004
#define PURGE_RXCLEAR 0x0008
#define LPTx 0x80
#define MS_CTS_ON ((DWORD)0x0010)
#define MS_DSR_ON ((DWORD)0x0020)
#define MS_RING_ON ((DWORD)0x0040)
#define MS_RLSD_ON ((DWORD)0x0080)
#define SP_SERIALCOMM ((DWORD)0x00000001)
#define PST_UNSPECIFIED ((DWORD)0x00000000)
#define PST_RS232 ((DWORD)0x00000001)
#define PST_PARALLELPORT ((DWORD)0x00000002)
#define PST_RS422 ((DWORD)0x00000003)
#define PST_RS423 ((DWORD)0x00000004)
#define PST_RS449 ((DWORD)0x00000005)
#define PST_MODEM ((DWORD)0x00000006)
#define PST_FAX ((DWORD)0x00000021)
#define PST_SCANNER ((DWORD)0x00000022)
#define PST_NETWORK_BRIDGE ((DWORD)0x00000100)
#define PST_LAT ((DWORD)0x00000101)
#define PST_TCPIP_TELNET ((DWORD)0x00000102)
#define PST_X25 ((DWORD)0x00000103)
#define PCF_DTRDSR ((DWORD)0x0001)
#define PCF_RTSCTS ((DWORD)0x0002)
#define PCF_RLSD ((DWORD)0x0004)
#define PCF_PARITY_CHECK ((DWORD)0x0008)
#define PCF_XONXOFF ((DWORD)0x0010)
#define PCF_SETXCHAR ((DWORD)0x0020)
#define PCF_TOTALTIMEOUTS ((DWORD)0x0040)
#define PCF_INTTIMEOUTS ((DWORD)0x0080)
#define PCF_SPECIALCHARS ((DWORD)0x0100)
#define PCF_16BITMODE ((DWORD)0x0200)
#define SP_PARITY ((DWORD)0x0001)
#define SP_BAUD ((DWORD)0x0002)
#define SP_DATABITS ((DWORD)0x0004)
#define SP_STOPBITS ((DWORD)0x0008)
#define SP_HANDSHAKING ((DWORD)0x0010)
#define SP_PARITY_CHECK ((DWORD)0x0020)
#define SP_RLSD ((DWORD)0x0040)
#define BAUD_075 ((DWORD)0x00000001)
#define BAUD_110 ((DWORD)0x00000002)
#define BAUD_134_5 ((DWORD)0x00000004)
#define BAUD_150 ((DWORD)0x00000008)
#define BAUD_300 ((DWORD)0x00000010)
#define BAUD_600 ((DWORD)0x00000020)
#define BAUD_1200 ((DWORD)0x00000040)
#define BAUD_1800 ((DWORD)0x00000080)
#define BAUD_2400 ((DWORD)0x00000100)
#define BAUD_4800 ((DWORD)0x00000200)
#define BAUD_7200 ((DWORD)0x00000400)
#define BAUD_9600 ((DWORD)0x00000800)
#define BAUD_14400 ((DWORD)0x00001000)
#define BAUD_19200 ((DWORD)0x00002000)
#define BAUD_38400 ((DWORD)0x00004000)
#define BAUD_56K ((DWORD)0x00008000)
#define BAUD_128K ((DWORD)0x00010000)
#define BAUD_115200 ((DWORD)0x00020000)
#define BAUD_57600 ((DWORD)0x00040000)
#define BAUD_USER ((DWORD)0x10000000)
/* Ntddser.h: http://msdn.microsoft.com/en-us/cc308432.aspx */
#define SERIAL_BAUD_075 ((ULONG)0x00000001)
#define SERIAL_BAUD_110 ((ULONG)0x00000002)
#define SERIAL_BAUD_134_5 ((ULONG)0x00000004)
#define SERIAL_BAUD_150 ((ULONG)0x00000008)
#define SERIAL_BAUD_300 ((ULONG)0x00000010)
#define SERIAL_BAUD_600 ((ULONG)0x00000020)
#define SERIAL_BAUD_1200 ((ULONG)0x00000040)
#define SERIAL_BAUD_1800 ((ULONG)0x00000080)
#define SERIAL_BAUD_2400 ((ULONG)0x00000100)
#define SERIAL_BAUD_4800 ((ULONG)0x00000200)
#define SERIAL_BAUD_7200 ((ULONG)0x00000400)
#define SERIAL_BAUD_9600 ((ULONG)0x00000800)
#define SERIAL_BAUD_14400 ((ULONG)0x00001000)
#define SERIAL_BAUD_19200 ((ULONG)0x00002000)
#define SERIAL_BAUD_38400 ((ULONG)0x00004000)
#define SERIAL_BAUD_56K ((ULONG)0x00008000)
#define SERIAL_BAUD_128K ((ULONG)0x00010000)
#define SERIAL_BAUD_115200 ((ULONG)0x00020000)
#define SERIAL_BAUD_57600 ((ULONG)0x00040000)
#define SERIAL_BAUD_USER ((ULONG)0x10000000)
#define DATABITS_5 ((WORD)0x0001)
#define DATABITS_6 ((WORD)0x0002)
#define DATABITS_7 ((WORD)0x0004)
#define DATABITS_8 ((WORD)0x0008)
#define DATABITS_16 ((WORD)0x0010)
#define DATABITS_16X ((WORD)0x0020)
#define STOPBITS_10 ((WORD)0x0001)
#define STOPBITS_15 ((WORD)0x0002)
#define STOPBITS_20 ((WORD)0x0004)
#define PARITY_NONE ((WORD)0x0100)
#define PARITY_ODD ((WORD)0x0200)
#define PARITY_EVEN ((WORD)0x0400)
#define PARITY_MARK ((WORD)0x0800)
#define PARITY_SPACE ((WORD)0x1000)
#define COMMPROP_INITIALIZED ((DWORD)0xE73CF52E)
#define DTR_CONTROL_DISABLE 0x00
#define DTR_CONTROL_ENABLE 0x01
#define DTR_CONTROL_HANDSHAKE 0x02
#define RTS_CONTROL_DISABLE 0x00
#define RTS_CONTROL_ENABLE 0x01
#define RTS_CONTROL_HANDSHAKE 0x02
#define RTS_CONTROL_TOGGLE 0x03
// http://msdn.microsoft.com/en-us/library/windows/desktop/aa363214%28v=vs.85%29.aspx
typedef struct _DCB
{
DWORD DCBlength;
DWORD BaudRate;
DWORD fBinary:1;
DWORD fParity:1;
DWORD fOutxCtsFlow:1;
DWORD fOutxDsrFlow:1;
DWORD fDtrControl:2;
DWORD fDsrSensitivity:1;
DWORD fTXContinueOnXoff:1;
DWORD fOutX:1;
DWORD fInX:1;
DWORD fErrorChar:1;
DWORD fNull:1;
DWORD fRtsControl:2;
DWORD fAbortOnError:1;
DWORD fDummy2:17;
WORD wReserved;
WORD XonLim;
WORD XoffLim;
BYTE ByteSize;
BYTE Parity;
BYTE StopBits;
char XonChar;
char XoffChar;
char ErrorChar;
char EofChar;
char EvtChar;
WORD wReserved1;
} DCB, *LPDCB;
typedef struct _COMM_CONFIG
{
DWORD dwSize;
WORD wVersion;
WORD wReserved;
DCB dcb;
DWORD dwProviderSubType;
DWORD dwProviderOffset;
DWORD dwProviderSize;
WCHAR wcProviderData[1];
} COMMCONFIG, *LPCOMMCONFIG;
typedef struct _COMMPROP
{
WORD wPacketLength;
WORD wPacketVersion;
DWORD dwServiceMask;
DWORD dwReserved1;
DWORD dwMaxTxQueue;
DWORD dwMaxRxQueue;
DWORD dwMaxBaud;
DWORD dwProvSubType;
DWORD dwProvCapabilities;
DWORD dwSettableParams;
DWORD dwSettableBaud;
WORD wSettableData;
WORD wSettableStopParity;
DWORD dwCurrentTxQueue;
DWORD dwCurrentRxQueue;
DWORD dwProvSpec1;
DWORD dwProvSpec2;
WCHAR wcProvChar[1];
} COMMPROP, *LPCOMMPROP;
typedef struct _COMMTIMEOUTS
{
DWORD ReadIntervalTimeout;
DWORD ReadTotalTimeoutMultiplier;
DWORD ReadTotalTimeoutConstant;
DWORD WriteTotalTimeoutMultiplier;
DWORD WriteTotalTimeoutConstant;
} COMMTIMEOUTS, *LPCOMMTIMEOUTS;
typedef struct _COMSTAT
{
DWORD fCtsHold:1;
DWORD fDsrHold:1;
DWORD fRlsdHold:1;
DWORD fXoffHold:1;
DWORD fXoffSent:1;
DWORD fEof:1;
DWORD fTxim:1;
DWORD fReserved:25;
DWORD cbInQue;
DWORD cbOutQue;
} COMSTAT, *LPCOMSTAT;
#ifdef __cplusplus
extern "C" {
#endif
WINPR_API BOOL BuildCommDCBA(LPCSTR lpDef, LPDCB lpDCB);
WINPR_API BOOL BuildCommDCBW(LPCWSTR lpDef, LPDCB lpDCB);
WINPR_API BOOL BuildCommDCBAndTimeoutsA(LPCSTR lpDef, LPDCB lpDCB, LPCOMMTIMEOUTS lpCommTimeouts);
WINPR_API BOOL BuildCommDCBAndTimeoutsW(LPCWSTR lpDef, LPDCB lpDCB, LPCOMMTIMEOUTS lpCommTimeouts);
WINPR_API BOOL CommConfigDialogA(LPCSTR lpszName, HWND hWnd, LPCOMMCONFIG lpCC);
WINPR_API BOOL CommConfigDialogW(LPCWSTR lpszName, HWND hWnd, LPCOMMCONFIG lpCC);
WINPR_API BOOL GetCommConfig(HANDLE hCommDev, LPCOMMCONFIG lpCC, LPDWORD lpdwSize);
WINPR_API BOOL SetCommConfig(HANDLE hCommDev, LPCOMMCONFIG lpCC, DWORD dwSize);
WINPR_API BOOL GetCommMask(HANDLE hFile, PDWORD lpEvtMask);
WINPR_API BOOL SetCommMask(HANDLE hFile, DWORD dwEvtMask);
WINPR_API BOOL GetCommModemStatus(HANDLE hFile, PDWORD lpModemStat);
WINPR_API BOOL GetCommProperties(HANDLE hFile, LPCOMMPROP lpCommProp);
WINPR_API BOOL GetCommState(HANDLE hFile, LPDCB lpDCB);
WINPR_API BOOL SetCommState(HANDLE hFile, LPDCB lpDCB);
WINPR_API BOOL GetCommTimeouts(HANDLE hFile, LPCOMMTIMEOUTS lpCommTimeouts);
WINPR_API BOOL SetCommTimeouts(HANDLE hFile, LPCOMMTIMEOUTS lpCommTimeouts);
WINPR_API BOOL GetDefaultCommConfigA(LPCSTR lpszName, LPCOMMCONFIG lpCC, LPDWORD lpdwSize);
WINPR_API BOOL GetDefaultCommConfigW(LPCWSTR lpszName, LPCOMMCONFIG lpCC, LPDWORD lpdwSize);
WINPR_API BOOL SetDefaultCommConfigA(LPCSTR lpszName, LPCOMMCONFIG lpCC, DWORD dwSize);
WINPR_API BOOL SetDefaultCommConfigW(LPCWSTR lpszName, LPCOMMCONFIG lpCC, DWORD dwSize);
WINPR_API BOOL SetCommBreak(HANDLE hFile);
WINPR_API BOOL ClearCommBreak(HANDLE hFile);
WINPR_API BOOL ClearCommError(HANDLE hFile, PDWORD lpErrors, LPCOMSTAT lpStat);
WINPR_API BOOL PurgeComm(HANDLE hFile, DWORD dwFlags);
WINPR_API BOOL SetupComm(HANDLE hFile, DWORD dwInQueue, DWORD dwOutQueue);
WINPR_API BOOL EscapeCommFunction(HANDLE hFile, DWORD dwFunc);
WINPR_API BOOL TransmitCommChar(HANDLE hFile, char cChar);
WINPR_API BOOL WaitCommEvent(HANDLE hFile, PDWORD lpEvtMask, LPOVERLAPPED lpOverlapped);
#ifdef UNICODE
#define BuildCommDCB BuildCommDCBW
#define BuildCommDCBAndTimeouts BuildCommDCBAndTimeoutsW
#define CommConfigDialog CommConfigDialogW
#define GetDefaultCommConfig GetDefaultCommConfigW
#define SetDefaultCommConfig SetDefaultCommConfigW
#else
#define BuildCommDCB BuildCommDCBA
#define BuildCommDCBAndTimeouts BuildCommDCBAndTimeoutsA
#define CommConfigDialog CommConfigDialogA
#define GetDefaultCommConfig GetDefaultCommConfigA
#define SetDefaultCommConfig SetDefaultCommConfigA
#endif
/* Extended API */
/* FIXME: MAXULONG should be defined arround winpr/limits.h */
#ifndef MAXULONG
#define MAXULONG (4294967295UL)
#endif
/**
* IOCTLs table according the server's serial driver:
* http://msdn.microsoft.com/en-us/library/windows/hardware/dn265347%28v=vs.85%29.aspx
*/
typedef enum _SERIAL_DRIVER_ID
{
SerialDriverUnknown = 0,
SerialDriverSerialSys,
SerialDriverSerCxSys,
SerialDriverSerCx2Sys /* default fallback, see also CommDeviceIoControl() */
} SERIAL_DRIVER_ID;
/*
* About DefineCommDevice() / QueryDosDevice()
*
* Did something close to QueryDosDevice() and DefineDosDevice() but with
* folowing constraints:
* - mappings are stored in a static array.
* - QueryCommDevice returns only the mappings that have been defined through DefineCommDevice()
*/
WINPR_API BOOL DefineCommDevice(/* DWORD dwFlags,*/ LPCTSTR lpDeviceName, LPCTSTR lpTargetPath);
WINPR_API DWORD QueryCommDevice(LPCTSTR lpDeviceName, LPTSTR lpTargetPath, DWORD ucchMax);
WINPR_API BOOL IsCommDevice(LPCTSTR lpDeviceName);
/**
* A handle can only be created on defined devices with DefineCommDevice(). This
* also ensures that CommCreateFileA() has been registered through
* RegisterHandleCreator().
*/
WINPR_API HANDLE CommCreateFileA(LPCSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode, LPSECURITY_ATTRIBUTES lpSecurityAttributes,
DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, HANDLE hTemplateFile);
#define IOCTL_SERIAL_SET_BAUD_RATE 0x001B0004
#define IOCTL_SERIAL_GET_BAUD_RATE 0x001B0050
#define IOCTL_SERIAL_SET_LINE_CONTROL 0x001B000C
#define IOCTL_SERIAL_GET_LINE_CONTROL 0x001B0054
#define IOCTL_SERIAL_SET_TIMEOUTS 0x001B001C
#define IOCTL_SERIAL_GET_TIMEOUTS 0x001B0020
/* GET_CHARS and SET_CHARS are swapped in the RDP docs [MS-RDPESP] */
#define IOCTL_SERIAL_GET_CHARS 0x001B0058
#define IOCTL_SERIAL_SET_CHARS 0x001B005C
#define IOCTL_SERIAL_SET_DTR 0x001B0024
#define IOCTL_SERIAL_CLR_DTR 0x001B0028
#define IOCTL_SERIAL_RESET_DEVICE 0x001B002C
#define IOCTL_SERIAL_SET_RTS 0x001B0030
#define IOCTL_SERIAL_CLR_RTS 0x001B0034
#define IOCTL_SERIAL_SET_XOFF 0x001B0038
#define IOCTL_SERIAL_SET_XON 0x001B003C
#define IOCTL_SERIAL_SET_BREAK_ON 0x001B0010
#define IOCTL_SERIAL_SET_BREAK_OFF 0x001B0014
#define IOCTL_SERIAL_SET_QUEUE_SIZE 0x001B0008
#define IOCTL_SERIAL_GET_WAIT_MASK 0x001B0040
#define IOCTL_SERIAL_SET_WAIT_MASK 0x001B0044
#define IOCTL_SERIAL_WAIT_ON_MASK 0x001B0048
#define IOCTL_SERIAL_IMMEDIATE_CHAR 0x001B0018
#define IOCTL_SERIAL_PURGE 0x001B004C
#define IOCTL_SERIAL_GET_HANDFLOW 0x001B0060
#define IOCTL_SERIAL_SET_HANDFLOW 0x001B0064
#define IOCTL_SERIAL_GET_MODEMSTATUS 0x001B0068
#define IOCTL_SERIAL_GET_DTRRTS 0x001B0078
/* according to [MS-RDPESP] it should be 0x001B0084, but servers send 0x001B006C */
#define IOCTL_SERIAL_GET_COMMSTATUS 0x001B006C
#define IOCTL_SERIAL_GET_PROPERTIES 0x001B0074
/* IOCTL_SERIAL_XOFF_COUNTER 0x001B0070 */
/* IOCTL_SERIAL_LSRMST_INSERT 0x001B007C */
#define IOCTL_SERIAL_CONFIG_SIZE 0x001B0080
/* IOCTL_SERIAL_GET_STATS 0x001B008C */
/* IOCTL_SERIAL_CLEAR_STATS 0x001B0090 */
/* IOCTL_SERIAL_GET_MODEM_CONTROL 0x001B0094 */
/* IOCTL_SERIAL_SET_MODEM_CONTROL 0x001B0098 */
/* IOCTL_SERIAL_SET_FIFO_CONTROL 0x001B009C */
/* IOCTL_PAR_QUERY_INFORMATION 0x00160004 */
/* IOCTL_PAR_SET_INFORMATION 0x00160008 */
/* IOCTL_PAR_QUERY_DEVICE_ID 0x0016000C */
/* IOCTL_PAR_QUERY_DEVICE_ID_SIZE 0x00160010 */
/* IOCTL_IEEE1284_GET_MODE 0x00160014 */
/* IOCTL_IEEE1284_NEGOTIATE 0x00160018 */
/* IOCTL_PAR_SET_WRITE_ADDRESS 0x0016001C */
/* IOCTL_PAR_SET_READ_ADDRESS 0x00160020 */
/* IOCTL_PAR_GET_DEVICE_CAPS 0x00160024 */
/* IOCTL_PAR_GET_DEFAULT_MODES 0x00160028 */
/* IOCTL_PAR_QUERY_RAW_DEVICE_ID 0x00160030 */
/* IOCTL_PAR_IS_PORT_FREE 0x00160054 */
/* http://msdn.microsoft.com/en-us/library/windows/hardware/ff551803(v=vs.85).aspx */
#define IOCTL_USBPRINT_GET_1284_ID 0x220034
typedef struct __SERIAL_IOCTL_NAME
{
ULONG number;
const char* name;
} _SERIAL_IOCTL_NAME;
static const _SERIAL_IOCTL_NAME _SERIAL_IOCTL_NAMES[] =
{
{IOCTL_SERIAL_SET_BAUD_RATE, "IOCTL_SERIAL_SET_BAUD_RATE"},
{IOCTL_SERIAL_GET_BAUD_RATE, "IOCTL_SERIAL_GET_BAUD_RATE"},
{IOCTL_SERIAL_SET_LINE_CONTROL, "IOCTL_SERIAL_SET_LINE_CONTROL"},
{IOCTL_SERIAL_GET_LINE_CONTROL, "IOCTL_SERIAL_GET_LINE_CONTROL"},
{IOCTL_SERIAL_SET_TIMEOUTS, "IOCTL_SERIAL_SET_TIMEOUTS"},
{IOCTL_SERIAL_GET_TIMEOUTS, "IOCTL_SERIAL_GET_TIMEOUTS"},
{IOCTL_SERIAL_GET_CHARS, "IOCTL_SERIAL_GET_CHARS"},
{IOCTL_SERIAL_SET_CHARS, "IOCTL_SERIAL_SET_CHARS"},
{IOCTL_SERIAL_SET_DTR, "IOCTL_SERIAL_SET_DTR"},
{IOCTL_SERIAL_CLR_DTR, "IOCTL_SERIAL_CLR_DTR"},
{IOCTL_SERIAL_RESET_DEVICE, "IOCTL_SERIAL_RESET_DEVICE"},
{IOCTL_SERIAL_SET_RTS, "IOCTL_SERIAL_SET_RTS"},
{IOCTL_SERIAL_CLR_RTS, "IOCTL_SERIAL_CLR_RTS"},
{IOCTL_SERIAL_SET_XOFF, "IOCTL_SERIAL_SET_XOFF"},
{IOCTL_SERIAL_SET_XON, "IOCTL_SERIAL_SET_XON"},
{IOCTL_SERIAL_SET_BREAK_ON, "IOCTL_SERIAL_SET_BREAK_ON"},
{IOCTL_SERIAL_SET_BREAK_OFF, "IOCTL_SERIAL_SET_BREAK_OFF"},
{IOCTL_SERIAL_SET_QUEUE_SIZE, "IOCTL_SERIAL_SET_QUEUE_SIZE"},
{IOCTL_SERIAL_GET_WAIT_MASK, "IOCTL_SERIAL_GET_WAIT_MASK"},
{IOCTL_SERIAL_SET_WAIT_MASK, "IOCTL_SERIAL_SET_WAIT_MASK"},
{IOCTL_SERIAL_WAIT_ON_MASK, "IOCTL_SERIAL_WAIT_ON_MASK"},
{IOCTL_SERIAL_IMMEDIATE_CHAR, "IOCTL_SERIAL_IMMEDIATE_CHAR"},
{IOCTL_SERIAL_PURGE, "IOCTL_SERIAL_PURGE"},
{IOCTL_SERIAL_GET_HANDFLOW, "IOCTL_SERIAL_GET_HANDFLOW"},
{IOCTL_SERIAL_SET_HANDFLOW, "IOCTL_SERIAL_SET_HANDFLOW"},
{IOCTL_SERIAL_GET_MODEMSTATUS, "IOCTL_SERIAL_GET_MODEMSTATUS"},
{IOCTL_SERIAL_GET_DTRRTS, "IOCTL_SERIAL_GET_DTRRTS"},
{IOCTL_SERIAL_GET_COMMSTATUS, "IOCTL_SERIAL_GET_COMMSTATUS"},
{IOCTL_SERIAL_GET_PROPERTIES, "IOCTL_SERIAL_GET_PROPERTIES"},
// {IOCTL_SERIAL_XOFF_COUNTER, "IOCTL_SERIAL_XOFF_COUNTER"},
// {IOCTL_SERIAL_LSRMST_INSERT, "IOCTL_SERIAL_LSRMST_INSERT"},
{IOCTL_SERIAL_CONFIG_SIZE, "IOCTL_SERIAL_CONFIG_SIZE"},
// {IOCTL_SERIAL_GET_STATS, "IOCTL_SERIAL_GET_STATS"},
// {IOCTL_SERIAL_CLEAR_STATS, "IOCTL_SERIAL_CLEAR_STATS"},
// {IOCTL_SERIAL_GET_MODEM_CONTROL,"IOCTL_SERIAL_GET_MODEM_CONTROL"},
// {IOCTL_SERIAL_SET_MODEM_CONTROL,"IOCTL_SERIAL_SET_MODEM_CONTROL"},
// {IOCTL_SERIAL_SET_FIFO_CONTROL, "IOCTL_SERIAL_SET_FIFO_CONTROL"},
// {IOCTL_PAR_QUERY_INFORMATION, "IOCTL_PAR_QUERY_INFORMATION"},
// {IOCTL_PAR_SET_INFORMATION, "IOCTL_PAR_SET_INFORMATION"},
// {IOCTL_PAR_QUERY_DEVICE_ID, "IOCTL_PAR_QUERY_DEVICE_ID"},
// {IOCTL_PAR_QUERY_DEVICE_ID_SIZE,"IOCTL_PAR_QUERY_DEVICE_ID_SIZE"},
// {IOCTL_IEEE1284_GET_MODE, "IOCTL_IEEE1284_GET_MODE"},
// {IOCTL_IEEE1284_NEGOTIATE, "IOCTL_IEEE1284_NEGOTIATE"},
// {IOCTL_PAR_SET_WRITE_ADDRESS, "IOCTL_PAR_SET_WRITE_ADDRESS"},
// {IOCTL_PAR_SET_READ_ADDRESS, "IOCTL_PAR_SET_READ_ADDRESS"},
// {IOCTL_PAR_GET_DEVICE_CAPS, "IOCTL_PAR_GET_DEVICE_CAPS"},
// {IOCTL_PAR_GET_DEFAULT_MODES, "IOCTL_PAR_GET_DEFAULT_MODES"},
// {IOCTL_PAR_QUERY_RAW_DEVICE_ID, "IOCTL_PAR_QUERY_RAW_DEVICE_ID"},
// {IOCTL_PAR_IS_PORT_FREE, "IOCTL_PAR_IS_PORT_FREE"},
{IOCTL_USBPRINT_GET_1284_ID, "IOCTL_USBPRINT_GET_1284_ID"},
{0, NULL}
};
/**
* FIXME: got a proper function name and place
*/
const char* _comm_serial_ioctl_name(ULONG number);
/**
* FIXME: got a proper function name and place
*/
void _comm_setServerSerialDriver(HANDLE hComm, SERIAL_DRIVER_ID);
/**
* FIXME: got a proper function name and place
*
* permissive mode is disabled by default.
*/
BOOL _comm_set_permissive(HANDLE hDevice, BOOL permissive);
/**
* FIXME: to be moved in comm_ioctl.h
*/
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
}
#endif
#endif /* _WIN32 */
#endif /* WINPR_COMM_H */

View File

@ -311,10 +311,23 @@ WINPR_API BOOL CreateDirectoryW(LPCWSTR lpPathName, LPSECURITY_ATTRIBUTES lpSecu
#define CreateDirectory CreateDirectoryA
#endif
#endif
/* Extra Functions */
typedef BOOL (*pcIsFileHandled)(LPCSTR lpFileName);
typedef HANDLE (*pcCreateFileA)(LPCSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode, LPSECURITY_ATTRIBUTES lpSecurityAttributes,
DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, HANDLE hTemplateFile);
typedef struct _HANDLE_CREATOR
{
pcIsFileHandled IsHandled;
pcCreateFileA CreateFileA;
} HANDLE_CREATOR, *PHANDLE_CREATOR, *LPHANDLE_CREATOR;
BOOL RegisterHandleCreator(PHANDLE_CREATOR pHandleCreator);
#endif /* _WIN32 */
#define WILDCARD_STAR 0x00000001
#define WILDCARD_QM 0x00000002
#define WILDCARD_DOS 0x00000100
@ -331,6 +344,7 @@ WINPR_API LPSTR FilePatternFindNextWildcardA(LPCSTR lpPattern, DWORD* pFlags);
WINPR_API int UnixChangeFileMode(const char* filename, int flags);
WINPR_API BOOL IsNamedPipeFileNameA(LPCSTR lpName);
WINPR_API char* GetNamedPipeNameWithoutPrefixA(LPCSTR lpName);
WINPR_API char* GetNamedPipeUnixDomainSocketBaseFilePathA(void);
WINPR_API char* GetNamedPipeUnixDomainSocketFilePathA(LPCSTR lpName);
@ -342,4 +356,3 @@ WINPR_API int GetNamePipeFileDescriptor(HANDLE hNamedPipe);
#endif
#endif /* WINPR_FILE_H */

View File

@ -40,6 +40,7 @@ typedef CHAR TCHAR;
#define _tcslen _wcslen
#define _tcsdup _wcsdup
#define _tcscmp wcscmp
#define _tcsncmp wcsncmp
#define _tcscpy wcscpy
#define _tcscat wcscat
#define _tcschr wcschr
@ -51,6 +52,7 @@ typedef CHAR TCHAR;
#define _tcslen strlen
#define _tcsdup _strdup
#define _tcscmp strcmp
#define _tcsncmp strncmp
#define _tcscpy strcpy
#define _tcscat strcat
#define _tcschr strchr

View File

@ -0,0 +1,61 @@
# WinPR: Windows Portable Runtime
# libwinpr-comm cmake build script
#
# Copyright 2014 Marc-Andre Moreau <marcandre.moreau@gmail.com>
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
set(MODULE_NAME "winpr-comm")
set(MODULE_PREFIX "WINPR_COMM")
set(${MODULE_PREFIX}_SRCS
comm.c
comm.h
comm_io.c
comm_ioctl.c
comm_ioctl.h
comm_serial_sys.c
comm_serial_sys.h
comm_sercx_sys.c
comm_sercx_sys.h
comm_sercx2_sys.c
comm_sercx2_sys.h)
if(MSVC AND (NOT MONOLITHIC_BUILD))
set(${MODULE_PREFIX}_SRCS ${${MODULE_PREFIX}_SRCS} module.def)
endif()
add_complex_library(MODULE ${MODULE_NAME} TYPE "OBJECT"
MONOLITHIC ${MONOLITHIC_BUILD}
SOURCES ${${MODULE_PREFIX}_SRCS})
set_target_properties(${MODULE_NAME} PROPERTIES VERSION ${WINPR_VERSION_FULL} SOVERSION ${WINPR_VERSION} PREFIX "lib")
set_complex_link_libraries(VARIABLE ${MODULE_PREFIX}_LIBS
MONOLITHIC ${MONOLITHIC_BUILD} INTERNAL
MODULE winpr
MODULES winpr-crt winpr-file winpr-utils)
if(MONOLITHIC_BUILD)
set(WINPR_LIBS ${WINPR_LIBS} ${${MODULE_PREFIX}_LIBS} PARENT_SCOPE)
else()
target_link_libraries(${MODULE_NAME} ${${MODULE_PREFIX}_LIBS})
install(TARGETS ${MODULE_NAME} DESTINATION ${CMAKE_INSTALL_LIBDIR} EXPORT WinPRTargets)
endif()
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "WinPR")
if(BUILD_TESTING)
add_subdirectory(test)
endif()

View File

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

1312
winpr/libwinpr/comm/comm.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,94 @@
/**
* WinPR: Windows Portable Runtime
* Serial Communication API
*
* Copyright 2014 Marc-Andre Moreau <marcandre.moreau@gmail.com>
* Copyright 2014 Hewlett-Packard Development Company, L.P.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef WINPR_COMM_PRIVATE_H
#define WINPR_COMM_PRIVATE_H
#ifndef _WIN32
#include <linux/serial.h>
#include <sys/eventfd.h>
#include <winpr/comm.h>
#include "../handle/handle.h"
struct winpr_comm
{
WINPR_HANDLE_DEF();
int fd;
int fd_read;
int fd_read_event; /* as of today, only used by _purge() */
CRITICAL_SECTION ReadLock;
int fd_write;
int fd_write_event; /* as of today, only used by _purge() */
CRITICAL_SECTION WriteLock;
/* permissive mode on errors if TRUE (default is FALSE).
*
* Since not all features are supported, some devices and applications
* can still be functional on such errors.
*
* TODO: command line switch or getting rid of it.
*/
BOOL permissive;
SERIAL_DRIVER_ID serverSerialDriverId;
COMMTIMEOUTS timeouts;
CRITICAL_SECTION EventsLock; /* protects counters, WaitEventMask and PendingEvents */
struct serial_icounter_struct counters;
ULONG WaitEventMask;
ULONG PendingEvents;
/* NB: CloseHandle() has to free resources */
};
typedef struct winpr_comm WINPR_COMM;
#define SERIAL_EV_RXCHAR 0x0001
#define SERIAL_EV_RXFLAG 0x0002
#define SERIAL_EV_TXEMPTY 0x0004
#define SERIAL_EV_CTS 0x0008
#define SERIAL_EV_DSR 0x0010
#define SERIAL_EV_RLSD 0x0020
#define SERIAL_EV_BREAK 0x0040
#define SERIAL_EV_ERR 0x0080
#define SERIAL_EV_RING 0x0100
#define SERIAL_EV_PERR 0x0200
#define SERIAL_EV_RX80FULL 0x0400
#define SERIAL_EV_EVENT1 0x0800
#define SERIAL_EV_EVENT2 0x1000
#define SERIAL_EV_FREERDP_WAITING 0x4000 /* bit today unused by other SERIAL_EV_* */
#define SERIAL_EV_FREERDP_STOP 0x8000 /* bit today unused by other SERIAL_EV_* */
#define FREERDP_PURGE_TXABORT 0x00000001 /* abort pending transmission */
#define FREERDP_PURGE_RXABORT 0x00000002 /* abort pending reception */
BOOL CommIsHandled(HANDLE handle);
BOOL CommCloseHandle(HANDLE handle);
#endif /* _WIN32 */
#endif /* WINPR_COMM_PRIVATE_H */

View File

@ -0,0 +1,551 @@
/**
* 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 <assert.h>
#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"
BOOL _comm_set_permissive(HANDLE hDevice, BOOL permissive)
{
WINPR_COMM* pComm = (WINPR_COMM*) hDevice;
if (hDevice == INVALID_HANDLE_VALUE)
{
SetLastError(ERROR_INVALID_HANDLE);
return FALSE;
}
if (!pComm || pComm->Type != HANDLE_TYPE_COMM)
{
SetLastError(ERROR_INVALID_HANDLE);
return FALSE;
}
pComm->permissive = permissive;
return TRUE;
}
/* Computes VMIN in deciseconds from Ti in milliseconds */
static UCHAR _vtime(ULONG Ti)
{
/* FIXME: look for an equivalent math function otherwise let
* do the compiler do the optimization */
if (Ti == 0)
return 0;
else if (Ti < 100)
return 1;
else if (Ti > 25500)
return 255; /* 0xFF */
else
return Ti/100;
}
/**
* ERRORS:
* ERROR_INVALID_HANDLE
* ERROR_NOT_SUPPORTED
* ERROR_INVALID_PARAMETER
* ERROR_TIMEOUT
* ERROR_IO_DEVICE
* ERROR_BAD_DEVICE
*/
BOOL CommReadFile(HANDLE hDevice, LPVOID lpBuffer, DWORD nNumberOfBytesToRead,
LPDWORD lpNumberOfBytesRead, LPOVERLAPPED lpOverlapped)
{
WINPR_COMM* pComm = (WINPR_COMM*) hDevice;
int biggestFd = -1;
fd_set read_set;
int nbFds;
COMMTIMEOUTS *pTimeouts;
UCHAR vmin = 0;
UCHAR vtime = 0;
ULONGLONG Tmax = 0;
struct timeval tmaxTimeout, *pTmaxTimeout;
struct termios currentTermios;
EnterCriticalSection(&pComm->ReadLock); /* KISSer by the function's beginning */
if (hDevice == INVALID_HANDLE_VALUE)
{
SetLastError(ERROR_INVALID_HANDLE);
goto return_false;
}
if (!pComm || pComm->Type != HANDLE_TYPE_COMM)
{
SetLastError(ERROR_INVALID_HANDLE);
goto return_false;
}
if (lpOverlapped != NULL)
{
SetLastError(ERROR_NOT_SUPPORTED);
goto return_false;
}
if (lpNumberOfBytesRead == NULL)
{
SetLastError(ERROR_INVALID_PARAMETER); /* since we doesn't suppport lpOverlapped != NULL */
goto return_false;
}
*lpNumberOfBytesRead = 0; /* will be ajusted if required ... */
if (nNumberOfBytesToRead <= 0) /* N */
{
goto return_true; /* FIXME: or FALSE? */
}
if (tcgetattr(pComm->fd, &currentTermios) < 0)
{
SetLastError(ERROR_IO_DEVICE);
goto return_false;
}
if (currentTermios.c_lflag & ICANON)
{
DEBUG_WARN("Canonical mode not supported"); /* the timeout could not be set */
SetLastError(ERROR_NOT_SUPPORTED);
goto return_false;
}
/* http://msdn.microsoft.com/en-us/library/hh439614%28v=vs.85%29.aspx
* http://msdn.microsoft.com/en-us/library/windows/hardware/hh439614%28v=vs.85%29.aspx
*
* ReadIntervalTimeout | ReadTotalTimeoutMultiplier | ReadTotalTimeoutConstant | VMIN | VTIME | TMAX |
* 0 | 0 | 0 | N | 0 | 0 | Blocks for N bytes available.
* 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 | N | 0 | Tc | Blocks on first byte during Tc or returns immediately whith bytes available
* MAXULONG | m | MAXULONG | | Invalid
* 0 | m | 0< Tc <MAXULONG | N | 0 | Tmax | Block on first byte during Tmax or returns immediately whith bytes available
* 0< Ti <MAXULONG | m | 0< Tc <MAXULONG | N | Ti | Tmax | Block on first byte, then use Ti between bytes. Tmax is use for the whole system call.
*/
/* NB: timeouts are in milliseconds, VTIME are in deciseconds and is an unsigned char */
/* FIXME: double check whether open(pComm->fd_read_event, O_NONBLOCK) doesn't conflict with above use cases */
pTimeouts = &(pComm->timeouts);
if ((pTimeouts->ReadIntervalTimeout == MAXULONG) && (pTimeouts->ReadTotalTimeoutConstant == MAXULONG))
{
DEBUG_WARN("ReadIntervalTimeout and ReadTotalTimeoutConstant cannot be both set to MAXULONG");
SetLastError(ERROR_INVALID_PARAMETER);
goto return_false;
}
/* VMIN */
if ((pTimeouts->ReadIntervalTimeout == MAXULONG) && (pTimeouts->ReadTotalTimeoutMultiplier == 0) && (pTimeouts->ReadTotalTimeoutConstant == 0))
{
vmin = 0;
}
else
{
/* N */
/* vmin = nNumberOfBytesToRead < 256 ? nNumberOfBytesToRead : 255;*/ /* 0xFF */
/* NB: we might wait endlessly with vmin=N, prefer to
* force vmin=1 and return with bytes
* available. FIXME: is a feature disarded here? */
vmin = 1;
}
/* VTIME */
if ((pTimeouts->ReadIntervalTimeout > 0) && (pTimeouts->ReadIntervalTimeout < MAXULONG))
{
/* Ti */
vtime = _vtime(pTimeouts->ReadIntervalTimeout);
}
/* TMAX */
if ((pTimeouts->ReadIntervalTimeout == MAXULONG) && (pTimeouts->ReadTotalTimeoutMultiplier == MAXULONG))
{
/* Tc */
Tmax = pTimeouts->ReadTotalTimeoutConstant;
}
else
{
/* Tmax */
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;
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);
goto return_false;
}
}
pTmaxTimeout = NULL; /* no timeout if Tmax == 0 */
if (Tmax > 0)
{
ZeroMemory(&tmaxTimeout, sizeof(struct timeval));
tmaxTimeout.tv_sec = Tmax / 1000; /* s */
tmaxTimeout.tv_usec = (Tmax % 1000) * 1000; /* us */
pTmaxTimeout = &tmaxTimeout;
}
/* FIXME: had expected eventfd_write() to return EAGAIN when
* there is no eventfd_read() but this not the case. */
/* discard a possible and no more relevant event */
eventfd_read(pComm->fd_read_event, NULL);
biggestFd = pComm->fd_read;
if (pComm->fd_read_event > biggestFd)
biggestFd = pComm->fd_read_event;
FD_ZERO(&read_set);
assert(pComm->fd_read_event < FD_SETSIZE);
assert(pComm->fd_read < FD_SETSIZE);
FD_SET(pComm->fd_read_event, &read_set);
FD_SET(pComm->fd_read, &read_set);
nbFds = select(biggestFd+1, &read_set, NULL, NULL, pTmaxTimeout);
if (nbFds < 0)
{
DEBUG_WARN("select() failure, errno=[%d] %s\n", errno, strerror(errno));
SetLastError(ERROR_IO_DEVICE);
goto return_false;
}
if (nbFds == 0)
{
/* timeout */
SetLastError(ERROR_TIMEOUT);
goto return_false;
}
/* read_set */
if (FD_ISSET(pComm->fd_read_event, &read_set))
{
eventfd_t event = 0;
if (eventfd_read(pComm->fd_read_event, &event) < 0)
{
if (errno == EAGAIN)
{
assert(FALSE); /* not quite sure this should ever happen */
/* keep on */
}
else
{
DEBUG_WARN("unexpected error on reading fd_read_event, errno=[%d] %s\n", errno, strerror(errno));
/* FIXME: goto return_false ? */
}
assert(errno == EAGAIN);
}
if (event == FREERDP_PURGE_RXABORT)
{
SetLastError(ERROR_CANCELLED);
goto return_false;
}
assert(event == FREERDP_PURGE_RXABORT); /* no other expected event so far */
}
if (FD_ISSET(pComm->fd_read, &read_set))
{
ssize_t nbRead = 0;
nbRead = read(pComm->fd_read, lpBuffer, nNumberOfBytesToRead);
if (nbRead < 0)
{
DEBUG_WARN("CommReadFile failed, ReadIntervalTimeout=%lu, ReadTotalTimeoutMultiplier=%lu, ReadTotalTimeoutConstant=%lu VMIN=%u, VTIME=%u",
pTimeouts->ReadIntervalTimeout, pTimeouts->ReadTotalTimeoutMultiplier, pTimeouts->ReadTotalTimeoutConstant,
currentTermios.c_cc[VMIN], currentTermios.c_cc[VTIME]);
DEBUG_WARN("CommReadFile failed, nNumberOfBytesToRead=%lu, errno=[%d] %s", nNumberOfBytesToRead, errno, strerror(errno));
if (errno == EAGAIN)
{
/* keep on */
goto return_true; /* expect a read-loop to be implemented on the server side */
}
else if (errno == EBADF)
{
SetLastError(ERROR_BAD_DEVICE); /* STATUS_INVALID_DEVICE_REQUEST */
goto return_false;
}
else
{
assert(FALSE);
SetLastError(ERROR_IO_DEVICE);
goto return_false;
}
}
if (nbRead == 0)
{
/* termios timeout */
SetLastError(ERROR_TIMEOUT);
goto return_false;
}
*lpNumberOfBytesRead = nbRead;
goto return_true;
}
assert(FALSE);
*lpNumberOfBytesRead = 0;
return_false:
LeaveCriticalSection(&pComm->ReadLock);
return FALSE;
return_true:
LeaveCriticalSection(&pComm->ReadLock);
return TRUE;
}
/**
* ERRORS:
* ERROR_INVALID_HANDLE
* ERROR_NOT_SUPPORTED
* ERROR_INVALID_PARAMETER
* ERROR_BAD_DEVICE
*/
BOOL CommWriteFile(HANDLE hDevice, LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite,
LPDWORD lpNumberOfBytesWritten, LPOVERLAPPED lpOverlapped)
{
WINPR_COMM* pComm = (WINPR_COMM*) hDevice;
struct timeval tmaxTimeout, *pTmaxTimeout;
EnterCriticalSection(&pComm->WriteLock); /* KISSer by the function's beginning */
if (hDevice == INVALID_HANDLE_VALUE)
{
SetLastError(ERROR_INVALID_HANDLE);
goto return_false;
}
if (!pComm || pComm->Type != HANDLE_TYPE_COMM)
{
SetLastError(ERROR_INVALID_HANDLE);
goto return_false;
}
if (lpOverlapped != NULL)
{
SetLastError(ERROR_NOT_SUPPORTED);
goto return_false;
}
if (lpNumberOfBytesWritten == NULL)
{
SetLastError(ERROR_INVALID_PARAMETER); /* since we doesn't suppport lpOverlapped != NULL */
goto return_false;
}
*lpNumberOfBytesWritten = 0; /* will be ajusted if required ... */
if (nNumberOfBytesToWrite <= 0)
{
goto return_true; /* FIXME: or FALSE? */
}
/* FIXME: had expected eventfd_write() to return EAGAIN when
* there is no eventfd_read() but this not the case. */
/* discard a possible and no more relevant event */
eventfd_read(pComm->fd_write_event, NULL);
/* ms */
ULONGLONG Tmax = nNumberOfBytesToWrite * pComm->timeouts.WriteTotalTimeoutMultiplier + pComm->timeouts.WriteTotalTimeoutConstant;
/* NB: select() may update the timeout argument to indicate
* how much time was left. Keep the timeout variable out of
* the while() */
pTmaxTimeout = NULL; /* no timeout if Tmax == 0 */
if (Tmax > 0)
{
ZeroMemory(&tmaxTimeout, sizeof(struct timeval));
tmaxTimeout.tv_sec = Tmax / 1000; /* s */
tmaxTimeout.tv_usec = (Tmax % 1000) * 1000; /* us */
pTmaxTimeout = &tmaxTimeout;
}
while (*lpNumberOfBytesWritten < nNumberOfBytesToWrite)
{
int biggestFd = -1;
fd_set event_set, write_set;
int nbFds;
biggestFd = pComm->fd_write;
if (pComm->fd_write_event > biggestFd)
biggestFd = pComm->fd_write_event;
FD_ZERO(&event_set);
FD_ZERO(&write_set);
assert(pComm->fd_write_event < FD_SETSIZE);
assert(pComm->fd_write < FD_SETSIZE);
FD_SET(pComm->fd_write_event, &event_set);
FD_SET(pComm->fd_write, &write_set);
nbFds = select(biggestFd+1, &event_set, &write_set, NULL, pTmaxTimeout);
if (nbFds < 0)
{
DEBUG_WARN("select() failure, errno=[%d] %s\n", errno, strerror(errno));
SetLastError(ERROR_IO_DEVICE);
goto return_false;
}
if (nbFds == 0)
{
/* timeout */
SetLastError(ERROR_TIMEOUT);
goto return_false;
}
/* event_set */
if (FD_ISSET(pComm->fd_write_event, &event_set))
{
eventfd_t event = 0;
if (eventfd_read(pComm->fd_write_event, &event) < 0)
{
if (errno == EAGAIN)
{
assert(FALSE); /* not quite sure this should ever happen */
/* keep on */
}
else
{
DEBUG_WARN("unexpected error on reading fd_write_event, errno=[%d] %s\n", errno, strerror(errno));
/* FIXME: goto return_false ? */
}
assert(errno == EAGAIN);
}
if (event == FREERDP_PURGE_TXABORT)
{
SetLastError(ERROR_CANCELLED);
goto return_false;
}
assert(event == FREERDP_PURGE_TXABORT); /* no other expected event so far */
}
/* write_set */
if (FD_ISSET(pComm->fd_write, &write_set))
{
ssize_t nbWritten;
nbWritten = write(pComm->fd_write,
lpBuffer + (*lpNumberOfBytesWritten),
nNumberOfBytesToWrite - (*lpNumberOfBytesWritten));
if (nbWritten < 0)
{
DEBUG_WARN("CommWriteFile failed after %lu bytes written, errno=[%d] %s\n", *lpNumberOfBytesWritten, errno, strerror(errno));
if (errno == EAGAIN)
{
/* keep on */
continue;
}
else if (errno == EBADF)
{
SetLastError(ERROR_BAD_DEVICE); /* STATUS_INVALID_DEVICE_REQUEST */
goto return_false;
}
else
{
assert(FALSE);
SetLastError(ERROR_IO_DEVICE);
goto return_false;
}
}
*lpNumberOfBytesWritten += nbWritten;
}
} /* while */
/* FIXME: this call to tcdrain() doesn't look correct and
* might hide a bug but was required while testing a serial
* printer. Its driver was expecting the modem line status
* SERIAL_MSR_DSR true after the sending which was never
* happenning otherwise. A purge was also done before each
* Write operation. The serial port was oppened with:
* DesiredAccess=0x0012019F. The printer worked fine with
* mstsc. */
tcdrain(pComm->fd_write);
return_true:
LeaveCriticalSection(&pComm->WriteLock);
return TRUE;
return_false:
LeaveCriticalSection(&pComm->WriteLock);
return FALSE;
}
#endif /* _WIN32 */

View File

@ -0,0 +1,739 @@
/**
* WinPR: Windows Portable Runtime
* Serial Communication API
*
* Copyright 2011 O.S. Systems Software Ltda.
* Copyright 2011 Eduardo Fiss Beloni <beloni@ossystems.com.br>
* Copyright 2014 Marc-Andre Moreau <marcandre.moreau@gmail.com>
* Copyright 2014 Hewlett-Packard Development Company, L.P.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#ifndef _WIN32
#include <assert.h>
#include <errno.h>
#include <freerdp/utils/debug.h>
#include "comm.h"
#include "comm_ioctl.h"
#include "comm_serial_sys.h"
#include "comm_sercx_sys.h"
#include "comm_sercx2_sys.h"
/* NB: MS-RDPESP's recommendation:
*
* <2> Section 3.2.5.1.6: Windows Implementations use IOCTL constants
* for IoControlCode values. The content and values of the IOCTLs are
* opaque to the protocol. On the server side, the data contained in
* an IOCTL is simply packaged and sent to the client side. For
* maximum compatibility between the different versions of the Windows
* operating system, the client implementation only singles out
* critical IOCTLs and invokes the applicable Win32 port API. The
* other IOCTLS are passed directly to the client-side driver, and the
* processing of this value depends on the drivers installed on the
* client side. The values and parameters for these IOCTLS can be
* found in [MSFT-W2KDDK] Volume 2, Part 2Serial and Parallel
* Drivers, and in [MSDN-PORTS].
*/
const char* _comm_serial_ioctl_name(ULONG number)
{
int i;
for (i=0; _SERIAL_IOCTL_NAMES[i].number != 0; i++)
{
if (_SERIAL_IOCTL_NAMES[i].number == number)
{
return _SERIAL_IOCTL_NAMES[i].name;
}
}
return "(unknown ioctl name)";
}
static BOOL _CommDeviceIoControl(HANDLE hDevice, DWORD dwIoControlCode, LPVOID lpInBuffer, DWORD nInBufferSize,
LPVOID lpOutBuffer, DWORD nOutBufferSize, LPDWORD lpBytesReturned, LPOVERLAPPED lpOverlapped)
{
WINPR_COMM* pComm = (WINPR_COMM*) hDevice;
SERIAL_DRIVER* pServerSerialDriver = NULL;
/* clear any previous last error */
SetLastError(ERROR_SUCCESS);
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 (lpBytesReturned == 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
*/
switch (pComm->serverSerialDriverId)
{
case SerialDriverSerialSys:
pServerSerialDriver = SerialSys_s();
break;
case SerialDriverSerCxSys:
pServerSerialDriver = SerCxSys_s();
break;
case SerialDriverSerCx2Sys:
pServerSerialDriver = SerCx2Sys_s();
break;
case SerialDriverUnknown:
default:
DEBUG_MSG("Unknown remote serial driver (%d), using SerCx2.sys", pComm->serverSerialDriverId);
pServerSerialDriver = SerCx2Sys_s();
break;
}
assert(pServerSerialDriver != NULL);
switch (dwIoControlCode)
{
case IOCTL_USBPRINT_GET_1284_ID:
{
/* FIXME: http://msdn.microsoft.com/en-us/library/windows/hardware/ff551803(v=vs.85).aspx */
*lpBytesReturned = nOutBufferSize; /* an empty OutputBuffer will be returned */
SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
return FALSE;
}
case IOCTL_SERIAL_SET_BAUD_RATE:
{
if (pServerSerialDriver->set_baud_rate)
{
SERIAL_BAUD_RATE *pBaudRate = (SERIAL_BAUD_RATE*)lpInBuffer;
assert(nInBufferSize >= sizeof(SERIAL_BAUD_RATE));
if (nInBufferSize < sizeof(SERIAL_BAUD_RATE))
{
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
return pServerSerialDriver->set_baud_rate(pComm, pBaudRate);
}
break;
}
case IOCTL_SERIAL_GET_BAUD_RATE:
{
if (pServerSerialDriver->get_baud_rate)
{
SERIAL_BAUD_RATE *pBaudRate = (SERIAL_BAUD_RATE*)lpOutBuffer;
assert(nOutBufferSize >= sizeof(SERIAL_BAUD_RATE));
if (nOutBufferSize < sizeof(SERIAL_BAUD_RATE))
{
SetLastError(ERROR_INSUFFICIENT_BUFFER);
return FALSE;
}
if (!pServerSerialDriver->get_baud_rate(pComm, pBaudRate))
return FALSE;
*lpBytesReturned = sizeof(SERIAL_BAUD_RATE);
return TRUE;
}
break;
}
case IOCTL_SERIAL_GET_PROPERTIES:
{
if (pServerSerialDriver->get_properties)
{
COMMPROP *pProperties = (COMMPROP*)lpOutBuffer;
assert(nOutBufferSize >= sizeof(COMMPROP));
if (nOutBufferSize < sizeof(COMMPROP))
{
SetLastError(ERROR_INSUFFICIENT_BUFFER);
return FALSE;
}
if (!pServerSerialDriver->get_properties(pComm, pProperties))
return FALSE;
*lpBytesReturned = sizeof(COMMPROP);
return TRUE;
}
break;
}
case IOCTL_SERIAL_SET_CHARS:
{
if (pServerSerialDriver->set_serial_chars)
{
SERIAL_CHARS *pSerialChars = (SERIAL_CHARS*)lpInBuffer;
assert(nInBufferSize >= sizeof(SERIAL_CHARS));
if (nInBufferSize < sizeof(SERIAL_CHARS))
{
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
return pServerSerialDriver->set_serial_chars(pComm, pSerialChars);
}
break;
}
case IOCTL_SERIAL_GET_CHARS:
{
if (pServerSerialDriver->get_serial_chars)
{
SERIAL_CHARS *pSerialChars = (SERIAL_CHARS*)lpOutBuffer;
assert(nOutBufferSize >= sizeof(SERIAL_CHARS));
if (nOutBufferSize < sizeof(SERIAL_CHARS))
{
SetLastError(ERROR_INSUFFICIENT_BUFFER);
return FALSE;
}
if (!pServerSerialDriver->get_serial_chars(pComm, pSerialChars))
return FALSE;
*lpBytesReturned = sizeof(SERIAL_CHARS);
return TRUE;
}
break;
}
case IOCTL_SERIAL_SET_LINE_CONTROL:
{
if (pServerSerialDriver->set_line_control)
{
SERIAL_LINE_CONTROL *pLineControl = (SERIAL_LINE_CONTROL*)lpInBuffer;
assert(nInBufferSize >= sizeof(SERIAL_LINE_CONTROL));
if (nInBufferSize < sizeof(SERIAL_LINE_CONTROL))
{
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
return pServerSerialDriver->set_line_control(pComm, pLineControl);
}
break;
}
case IOCTL_SERIAL_GET_LINE_CONTROL:
{
if (pServerSerialDriver->get_line_control)
{
SERIAL_LINE_CONTROL *pLineControl = (SERIAL_LINE_CONTROL*)lpOutBuffer;
assert(nOutBufferSize >= sizeof(SERIAL_LINE_CONTROL));
if (nOutBufferSize < sizeof(SERIAL_LINE_CONTROL))
{
SetLastError(ERROR_INSUFFICIENT_BUFFER);
return FALSE;
}
if (!pServerSerialDriver->get_line_control(pComm, pLineControl))
return FALSE;
*lpBytesReturned = sizeof(SERIAL_LINE_CONTROL);
return TRUE;
}
break;
}
case IOCTL_SERIAL_SET_HANDFLOW:
{
if (pServerSerialDriver->set_handflow)
{
SERIAL_HANDFLOW *pHandflow = (SERIAL_HANDFLOW*)lpInBuffer;
assert(nInBufferSize >= sizeof(SERIAL_HANDFLOW));
if (nInBufferSize < sizeof(SERIAL_HANDFLOW))
{
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
return pServerSerialDriver->set_handflow(pComm, pHandflow);
}
break;
}
case IOCTL_SERIAL_GET_HANDFLOW:
{
if (pServerSerialDriver->get_handflow)
{
SERIAL_HANDFLOW *pHandflow = (SERIAL_HANDFLOW*)lpOutBuffer;
assert(nOutBufferSize >= sizeof(SERIAL_HANDFLOW));
if (nOutBufferSize < sizeof(SERIAL_HANDFLOW))
{
SetLastError(ERROR_INSUFFICIENT_BUFFER);
return FALSE;
}
if (!pServerSerialDriver->get_handflow(pComm, pHandflow))
return FALSE;
*lpBytesReturned = sizeof(SERIAL_HANDFLOW);
return TRUE;
}
break;
}
case IOCTL_SERIAL_SET_TIMEOUTS:
{
if (pServerSerialDriver->set_timeouts)
{
SERIAL_TIMEOUTS *pHandflow = (SERIAL_TIMEOUTS*)lpInBuffer;
assert(nInBufferSize >= sizeof(SERIAL_TIMEOUTS));
if (nInBufferSize < sizeof(SERIAL_TIMEOUTS))
{
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
return pServerSerialDriver->set_timeouts(pComm, pHandflow);
}
break;
}
case IOCTL_SERIAL_GET_TIMEOUTS:
{
if (pServerSerialDriver->get_timeouts)
{
SERIAL_TIMEOUTS *pHandflow = (SERIAL_TIMEOUTS*)lpOutBuffer;
assert(nOutBufferSize >= sizeof(SERIAL_TIMEOUTS));
if (nOutBufferSize < sizeof(SERIAL_TIMEOUTS))
{
SetLastError(ERROR_INSUFFICIENT_BUFFER);
return FALSE;
}
if (!pServerSerialDriver->get_timeouts(pComm, pHandflow))
return FALSE;
*lpBytesReturned = sizeof(SERIAL_TIMEOUTS);
return TRUE;
}
break;
}
case IOCTL_SERIAL_SET_DTR:
{
if (pServerSerialDriver->set_dtr)
{
return pServerSerialDriver->set_dtr(pComm);
}
break;
}
case IOCTL_SERIAL_CLR_DTR:
{
if (pServerSerialDriver->clear_dtr)
{
return pServerSerialDriver->clear_dtr(pComm);
}
break;
}
case IOCTL_SERIAL_SET_RTS:
{
if (pServerSerialDriver->set_rts)
{
return pServerSerialDriver->set_rts(pComm);
}
break;
}
case IOCTL_SERIAL_CLR_RTS:
{
if (pServerSerialDriver->clear_rts)
{
return pServerSerialDriver->clear_rts(pComm);
}
break;
}
case IOCTL_SERIAL_GET_MODEMSTATUS:
{
if (pServerSerialDriver->get_modemstatus)
{
ULONG *pRegister = (ULONG*)lpOutBuffer;
assert(nOutBufferSize >= sizeof(ULONG));
if (nOutBufferSize < sizeof(ULONG))
{
SetLastError(ERROR_INSUFFICIENT_BUFFER);
return FALSE;
}
if (!pServerSerialDriver->get_modemstatus(pComm, pRegister))
return FALSE;
*lpBytesReturned = sizeof(ULONG);
return TRUE;
}
break;
}
case IOCTL_SERIAL_SET_WAIT_MASK:
{
if (pServerSerialDriver->set_wait_mask)
{
ULONG *pWaitMask = (ULONG*)lpInBuffer;
assert(nInBufferSize >= sizeof(ULONG));
if (nInBufferSize < sizeof(ULONG))
{
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
return pServerSerialDriver->set_wait_mask(pComm, pWaitMask);
}
break;
}
case IOCTL_SERIAL_GET_WAIT_MASK:
{
if (pServerSerialDriver->get_wait_mask)
{
ULONG *pWaitMask = (ULONG*)lpOutBuffer;
assert(nOutBufferSize >= sizeof(ULONG));
if (nOutBufferSize < sizeof(ULONG))
{
SetLastError(ERROR_INSUFFICIENT_BUFFER);
return FALSE;
}
if (!pServerSerialDriver->get_wait_mask(pComm, pWaitMask))
return FALSE;
*lpBytesReturned = sizeof(ULONG);
return TRUE;
}
break;
}
case IOCTL_SERIAL_WAIT_ON_MASK:
{
if (pServerSerialDriver->wait_on_mask)
{
ULONG *pOutputMask = (ULONG*)lpOutBuffer;
assert(nOutBufferSize >= sizeof(ULONG));
if (nOutBufferSize < sizeof(ULONG))
{
SetLastError(ERROR_INSUFFICIENT_BUFFER);
return FALSE;
}
if (!pServerSerialDriver->wait_on_mask(pComm, pOutputMask))
{
*lpBytesReturned = sizeof(ULONG);
return FALSE;
}
*lpBytesReturned = sizeof(ULONG);
return TRUE;
}
break;
}
case IOCTL_SERIAL_SET_QUEUE_SIZE:
{
if (pServerSerialDriver->set_queue_size)
{
SERIAL_QUEUE_SIZE *pQueueSize = (SERIAL_QUEUE_SIZE*)lpInBuffer;
assert(nInBufferSize >= sizeof(SERIAL_QUEUE_SIZE));
if (nInBufferSize < sizeof(SERIAL_QUEUE_SIZE))
{
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
return pServerSerialDriver->set_queue_size(pComm, pQueueSize);
}
break;
}
case IOCTL_SERIAL_PURGE:
{
if (pServerSerialDriver->purge)
{
ULONG *pPurgeMask = (ULONG*)lpInBuffer;
assert(nInBufferSize >= sizeof(ULONG));
if (nInBufferSize < sizeof(ULONG))
{
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
return pServerSerialDriver->purge(pComm, pPurgeMask);
}
break;
}
case IOCTL_SERIAL_GET_COMMSTATUS:
{
if (pServerSerialDriver->get_commstatus)
{
SERIAL_STATUS *pCommstatus = (SERIAL_STATUS*)lpOutBuffer;
assert(nOutBufferSize >= sizeof(SERIAL_STATUS));
if (nOutBufferSize < sizeof(SERIAL_STATUS))
{
SetLastError(ERROR_INSUFFICIENT_BUFFER);
return FALSE;
}
if (!pServerSerialDriver->get_commstatus(pComm, pCommstatus))
return FALSE;
*lpBytesReturned = sizeof(SERIAL_STATUS);
return TRUE;
}
break;
}
case IOCTL_SERIAL_SET_BREAK_ON:
{
if (pServerSerialDriver->set_break_on)
{
return pServerSerialDriver->set_break_on(pComm);
}
break;
}
case IOCTL_SERIAL_SET_BREAK_OFF:
{
if (pServerSerialDriver->set_break_off)
{
return pServerSerialDriver->set_break_off(pComm);
}
break;
}
case IOCTL_SERIAL_SET_XOFF:
{
if (pServerSerialDriver->set_xoff)
{
return pServerSerialDriver->set_xoff(pComm);
}
break;
}
case IOCTL_SERIAL_SET_XON:
{
if (pServerSerialDriver->set_xon)
{
return pServerSerialDriver->set_xon(pComm);
}
break;
}
case IOCTL_SERIAL_GET_DTRRTS:
{
if (pServerSerialDriver->get_dtrrts)
{
ULONG *pMask = (ULONG*)lpOutBuffer;
assert(nOutBufferSize >= sizeof(ULONG));
if (nOutBufferSize < sizeof(ULONG))
{
SetLastError(ERROR_INSUFFICIENT_BUFFER);
return FALSE;
}
if (!pServerSerialDriver->get_dtrrts(pComm, pMask))
return FALSE;
*lpBytesReturned = sizeof(ULONG);
return TRUE;
}
break;
}
case IOCTL_SERIAL_CONFIG_SIZE:
{
if (pServerSerialDriver->config_size)
{
ULONG *pSize = (ULONG*)lpOutBuffer;
assert(nOutBufferSize >= sizeof(ULONG));
if (nOutBufferSize < sizeof(ULONG))
{
SetLastError(ERROR_INSUFFICIENT_BUFFER);
return FALSE;
}
if (!pServerSerialDriver->config_size(pComm, pSize))
return FALSE;
*lpBytesReturned = sizeof(ULONG);
return TRUE;
}
break;
}
case IOCTL_SERIAL_IMMEDIATE_CHAR:
{
if (pServerSerialDriver->immediate_char)
{
UCHAR *pChar = (UCHAR*)lpInBuffer;
assert(nInBufferSize >= sizeof(UCHAR));
if (nInBufferSize < sizeof(UCHAR))
{
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
return pServerSerialDriver->immediate_char(pComm, pChar);
}
break;
}
case IOCTL_SERIAL_RESET_DEVICE:
{
if (pServerSerialDriver->reset_device)
{
return pServerSerialDriver->reset_device(pComm);
}
break;
}
}
DEBUG_WARN(_T("unsupported IoControlCode=[0x%lX] %s (remote serial driver: %s)"),
dwIoControlCode, _comm_serial_ioctl_name(dwIoControlCode), pServerSerialDriver->name);
SetLastError(ERROR_CALL_NOT_IMPLEMENTED); /* => STATUS_NOT_IMPLEMENTED */
return FALSE;
}
/**
* FIXME: to be used through winpr-io's DeviceIoControl
*
* Any previous error as returned by GetLastError is cleared.
*
* ERRORS:
* ERROR_INVALID_HANDLE
* ERROR_INVALID_PARAMETER
* ERROR_NOT_SUPPORTED lpOverlapped is not supported
* ERROR_INSUFFICIENT_BUFFER
* ERROR_CALL_NOT_IMPLEMENTED unimplemented ioctl
*/
BOOL CommDeviceIoControl(HANDLE hDevice, DWORD dwIoControlCode, LPVOID lpInBuffer, DWORD nInBufferSize,
LPVOID lpOutBuffer, DWORD nOutBufferSize, LPDWORD lpBytesReturned, LPOVERLAPPED lpOverlapped)
{
WINPR_COMM* pComm = (WINPR_COMM*) hDevice;
BOOL result;
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;
}
result = _CommDeviceIoControl(hDevice, dwIoControlCode, lpInBuffer, nInBufferSize,
lpOutBuffer, nOutBufferSize, lpBytesReturned, lpOverlapped);
if (lpBytesReturned && *lpBytesReturned != nOutBufferSize)
{
/* This might be a hint for a bug, especially when result==TRUE */
DEBUG_WARN("lpBytesReturned=%ld and nOutBufferSize=%ld are different!", *lpBytesReturned, nOutBufferSize);
}
if (pComm->permissive)
{
if (!result)
{
DEBUG_WARN("[permissive]: whereas it failed, made to succeed IoControlCode=[0x%lX] %s, last-error: 0x%lX",
dwIoControlCode, _comm_serial_ioctl_name(dwIoControlCode), GetLastError());
}
return TRUE; /* always! */
}
return result;
}
int _comm_ioctl_tcsetattr(int fd, int optional_actions, const struct termios *termios_p)
{
int result;
struct termios currentState;
if ((result = tcsetattr(fd, optional_actions, termios_p)) < 0)
{
DEBUG_WARN("tcsetattr failure, errno: %d", errno);
return result;
}
/* NB: tcsetattr() can succeed even if not all changes have been applied. */
ZeroMemory(&currentState, sizeof(struct termios));
if ((result = tcgetattr(fd, &currentState)) < 0)
{
DEBUG_WARN("tcgetattr failure, errno: %d", errno);
return result;
}
if (memcmp(&currentState, &termios_p, sizeof(struct termios)) != 0)
{
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);
return result;
}
ZeroMemory(&currentState, sizeof(struct termios));
if ((result = tcgetattr(fd, &currentState)) < 0)
{
DEBUG_WARN("tcgetattr failure, errno: %d", errno);
return result;
}
if (memcmp(&currentState, termios_p, sizeof(struct termios)) != 0)
{
DEBUG_WARN("Failure: all termios parameters are still not set on a second attempt");
return -1;
}
}
return 0;
}
#endif /* _WIN32 */

View File

@ -0,0 +1,244 @@
/**
* WinPR: Windows Portable Runtime
* Serial Communication API
*
* Copyright 2011 O.S. Systems Software Ltda.
* Copyright 2011 Eduardo Fiss Beloni <beloni@ossystems.com.br>
* Copyright 2014 Hewlett-Packard Development Company, L.P.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef WINPR_COMM_IOCTL_H_
#define WINPR_COMM_IOCTL_H_
#ifndef _WIN32
#include <termios.h>
#include <winpr/io.h>
#include <winpr/tchar.h>
#include <winpr/wtypes.h>
#include "comm.h"
/* Serial I/O Request Interface: http://msdn.microsoft.com/en-us/library/dn265347%28v=vs.85%29.aspx
* Ntddser.h http://msdn.microsoft.com/en-us/cc308432.aspx
* Ntddpar.h http://msdn.microsoft.com/en-us/cc308431.aspx
*/
#ifdef __cplusplus
extern "C" {
#endif
/* TODO: defines and types below are very similar to those in comm.h, keep only
* those that differ more than the names */
#define STOP_BIT_1 0
#define STOP_BITS_1_5 1
#define STOP_BITS_2 2
#define NO_PARITY 0
#define ODD_PARITY 1
#define EVEN_PARITY 2
#define MARK_PARITY 3
#define SPACE_PARITY 4
typedef struct _SERIAL_BAUD_RATE
{
ULONG BaudRate;
} SERIAL_BAUD_RATE, *PSERIAL_BAUD_RATE;
typedef struct _SERIAL_CHARS
{
UCHAR EofChar;
UCHAR ErrorChar;
UCHAR BreakChar;
UCHAR EventChar;
UCHAR XonChar;
UCHAR XoffChar;
} SERIAL_CHARS, *PSERIAL_CHARS;
typedef struct _SERIAL_LINE_CONTROL
{
UCHAR StopBits;
UCHAR Parity;
UCHAR WordLength;
} SERIAL_LINE_CONTROL, *PSERIAL_LINE_CONTROL;
typedef struct _SERIAL_HANDFLOW
{
ULONG ControlHandShake;
ULONG FlowReplace;
LONG XonLimit;
LONG XoffLimit;
} SERIAL_HANDFLOW, *PSERIAL_HANDFLOW;
#define SERIAL_DTR_MASK ((ULONG)0x03)
#define SERIAL_DTR_CONTROL ((ULONG)0x01)
#define SERIAL_DTR_HANDSHAKE ((ULONG)0x02)
#define SERIAL_CTS_HANDSHAKE ((ULONG)0x08)
#define SERIAL_DSR_HANDSHAKE ((ULONG)0x10)
#define SERIAL_DCD_HANDSHAKE ((ULONG)0x20)
#define SERIAL_OUT_HANDSHAKEMASK ((ULONG)0x38)
#define SERIAL_DSR_SENSITIVITY ((ULONG)0x40)
#define SERIAL_ERROR_ABORT ((ULONG)0x80000000)
#define SERIAL_CONTROL_INVALID ((ULONG)0x7fffff84)
#define SERIAL_AUTO_TRANSMIT ((ULONG)0x01)
#define SERIAL_AUTO_RECEIVE ((ULONG)0x02)
#define SERIAL_ERROR_CHAR ((ULONG)0x04)
#define SERIAL_NULL_STRIPPING ((ULONG)0x08)
#define SERIAL_BREAK_CHAR ((ULONG)0x10)
#define SERIAL_RTS_MASK ((ULONG)0xc0)
#define SERIAL_RTS_CONTROL ((ULONG)0x40)
#define SERIAL_RTS_HANDSHAKE ((ULONG)0x80)
#define SERIAL_TRANSMIT_TOGGLE ((ULONG)0xc0)
#define SERIAL_XOFF_CONTINUE ((ULONG)0x80000000)
#define SERIAL_FLOW_INVALID ((ULONG)0x7fffff20)
#define SERIAL_SP_SERIALCOMM ((ULONG)0x00000001)
#define SERIAL_SP_UNSPECIFIED ((ULONG)0x00000000)
#define SERIAL_SP_RS232 ((ULONG)0x00000001)
#define SERIAL_SP_PARALLEL ((ULONG)0x00000002)
#define SERIAL_SP_RS422 ((ULONG)0x00000003)
#define SERIAL_SP_RS423 ((ULONG)0x00000004)
#define SERIAL_SP_RS449 ((ULONG)0x00000005)
#define SERIAL_SP_MODEM ((ULONG)0X00000006)
#define SERIAL_SP_FAX ((ULONG)0x00000021)
#define SERIAL_SP_SCANNER ((ULONG)0x00000022)
#define SERIAL_SP_BRIDGE ((ULONG)0x00000100)
#define SERIAL_SP_LAT ((ULONG)0x00000101)
#define SERIAL_SP_TELNET ((ULONG)0x00000102)
#define SERIAL_SP_X25 ((ULONG)0x00000103)
typedef struct _SERIAL_TIMEOUTS
{
ULONG ReadIntervalTimeout;
ULONG ReadTotalTimeoutMultiplier;
ULONG ReadTotalTimeoutConstant;
ULONG WriteTotalTimeoutMultiplier;
ULONG WriteTotalTimeoutConstant;
} SERIAL_TIMEOUTS,*PSERIAL_TIMEOUTS;
#define SERIAL_MSR_DCTS 0x01
#define SERIAL_MSR_DDSR 0x02
#define SERIAL_MSR_TERI 0x04
#define SERIAL_MSR_DDCD 0x08
#define SERIAL_MSR_CTS 0x10
#define SERIAL_MSR_DSR 0x20
#define SERIAL_MSR_RI 0x40
#define SERIAL_MSR_DCD 0x80
typedef struct _SERIAL_QUEUE_SIZE
{
ULONG InSize;
ULONG OutSize;
} SERIAL_QUEUE_SIZE, *PSERIAL_QUEUE_SIZE;
#define SERIAL_PURGE_TXABORT 0x00000001
#define SERIAL_PURGE_RXABORT 0x00000002
#define SERIAL_PURGE_TXCLEAR 0x00000004
#define SERIAL_PURGE_RXCLEAR 0x00000008
typedef struct _SERIAL_STATUS
{
ULONG Errors;
ULONG HoldReasons;
ULONG AmountInInQueue;
ULONG AmountInOutQueue;
BOOLEAN EofReceived;
BOOLEAN WaitForImmediate;
} SERIAL_STATUS, *PSERIAL_STATUS;
#define SERIAL_TX_WAITING_FOR_CTS ((ULONG)0x00000001)
#define SERIAL_TX_WAITING_FOR_DSR ((ULONG)0x00000002)
#define SERIAL_TX_WAITING_FOR_DCD ((ULONG)0x00000004)
#define SERIAL_TX_WAITING_FOR_XON ((ULONG)0x00000008)
#define SERIAL_TX_WAITING_XOFF_SENT ((ULONG)0x00000010)
#define SERIAL_TX_WAITING_ON_BREAK ((ULONG)0x00000020)
#define SERIAL_RX_WAITING_FOR_DSR ((ULONG)0x00000040)
#define SERIAL_ERROR_BREAK ((ULONG)0x00000001)
#define SERIAL_ERROR_FRAMING ((ULONG)0x00000002)
#define SERIAL_ERROR_OVERRUN ((ULONG)0x00000004)
#define SERIAL_ERROR_QUEUEOVERRUN ((ULONG)0x00000008)
#define SERIAL_ERROR_PARITY ((ULONG)0x00000010)
#define SERIAL_DTR_STATE ((ULONG)0x00000001)
#define SERIAL_RTS_STATE ((ULONG)0x00000002)
#define SERIAL_CTS_STATE ((ULONG)0x00000010)
#define SERIAL_DSR_STATE ((ULONG)0x00000020)
#define SERIAL_RI_STATE ((ULONG)0x00000040)
#define SERIAL_DCD_STATE ((ULONG)0x00000080)
/**
* A function might be NULL if not supported by the underlying driver.
*
* FIXME: better have to use input and output buffers for all functions?
*/
typedef struct _SERIAL_DRIVER
{
SERIAL_DRIVER_ID id;
TCHAR *name;
BOOL (*set_baud_rate)(WINPR_COMM *pComm, const SERIAL_BAUD_RATE *pBaudRate);
BOOL (*get_baud_rate)(WINPR_COMM *pComm, SERIAL_BAUD_RATE *pBaudRate);
BOOL (*get_properties)(WINPR_COMM *pComm, COMMPROP *pProperties);
BOOL (*set_serial_chars)(WINPR_COMM *pComm, const SERIAL_CHARS *pSerialChars);
BOOL (*get_serial_chars)(WINPR_COMM *pComm, SERIAL_CHARS *pSerialChars);
BOOL (*set_line_control)(WINPR_COMM *pComm, const SERIAL_LINE_CONTROL *pLineControl);
BOOL (*get_line_control)(WINPR_COMM *pComm, SERIAL_LINE_CONTROL *pLineControl);
BOOL (*set_handflow)(WINPR_COMM *pComm, const SERIAL_HANDFLOW *pHandflow);
BOOL (*get_handflow)(WINPR_COMM *pComm, SERIAL_HANDFLOW *pHandflow);
BOOL (*set_timeouts)(WINPR_COMM *pComm, const SERIAL_TIMEOUTS *pTimeouts);
BOOL (*get_timeouts)(WINPR_COMM *pComm, SERIAL_TIMEOUTS *pTimeouts);
BOOL (*set_dtr)(WINPR_COMM *pComm);
BOOL (*clear_dtr)(WINPR_COMM *pComm);
BOOL (*set_rts)(WINPR_COMM *pComm);
BOOL (*clear_rts)(WINPR_COMM *pComm);
BOOL (*get_modemstatus)(WINPR_COMM *pComm, ULONG *pRegister);
BOOL (*set_wait_mask)(WINPR_COMM *pComm, const ULONG *pWaitMask);
BOOL (*get_wait_mask)(WINPR_COMM *pComm, ULONG *pWaitMask);
BOOL (*wait_on_mask)(WINPR_COMM *pComm, ULONG *pOutputMask);
BOOL (*set_queue_size)(WINPR_COMM *pComm, const SERIAL_QUEUE_SIZE *pQueueSize);
BOOL (*purge)(WINPR_COMM *pComm, const ULONG *pPurgeMask);
BOOL (*get_commstatus)(WINPR_COMM *pComm, SERIAL_STATUS *pCommstatus);
BOOL (*set_break_on)(WINPR_COMM *pComm);
BOOL (*set_break_off)(WINPR_COMM *pComm);
BOOL (*set_xoff)(WINPR_COMM *pComm);
BOOL (*set_xon)(WINPR_COMM *pComm);
BOOL (*get_dtrrts)(WINPR_COMM *pComm, ULONG *pMask);
BOOL (*config_size)(WINPR_COMM *pComm, ULONG *pSize);
BOOL (*immediate_char)(WINPR_COMM *pComm, const UCHAR *pChar);
BOOL (*reset_device)(WINPR_COMM *pComm);
} SERIAL_DRIVER;
int _comm_ioctl_tcsetattr(int fd, int optional_actions, const struct termios *termios_p);
#ifdef __cplusplus
}
#endif
#endif /* _WIN32 */
#endif /* WINPR_COMM_IOCTL_H_ */

View File

@ -0,0 +1,207 @@
/**
* WinPR: Windows Portable Runtime
* Serial Communication API
*
* Copyright 2011 O.S. Systems Software Ltda.
* Copyright 2011 Eduardo Fiss Beloni <beloni@ossystems.com.br>
* Copyright 2014 Marc-Andre Moreau <marcandre.moreau@gmail.com>
* Copyright 2014 Hewlett-Packard Development Company, L.P.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef _WIN32
#include <freerdp/utils/debug.h>
#include "comm_serial_sys.h"
#include "comm_sercx_sys.h"
#include "comm_sercx2_sys.h"
/* http://msdn.microsoft.com/en-us/library/dn265347%28v=vs.85%29.aspx
*
* SerCx2 does not support special characters. SerCx2 always completes
* an IOCTL_SERIAL_SET_CHARS request with a STATUS_SUCCESS status
* code, but does not set any special characters or perform any other
* operation in response to this request. For an
* IOCTL_SERIAL_GET_CHARS request, SerCx2 sets all the character
* values in the SERIAL_CHARS structure to null, and completes the
* request with a STATUS_SUCCESS status code.
*/
static BOOL _set_serial_chars(WINPR_COMM* pComm, const SERIAL_CHARS* pSerialChars)
{
return TRUE;
}
static BOOL _get_serial_chars(WINPR_COMM* pComm, SERIAL_CHARS* pSerialChars)
{
ZeroMemory(pSerialChars, sizeof(SERIAL_CHARS));
return TRUE;
}
/* http://msdn.microsoft.com/en-us/library/windows/hardware/hh439605%28v=vs.85%29.aspx */
/* FIXME: only using the Serial.sys' events, complete the support of the remaining events */
static const ULONG _SERCX2_SYS_SUPPORTED_EV_MASK =
SERIAL_EV_RXCHAR |
SERIAL_EV_RXFLAG |
SERIAL_EV_TXEMPTY |
SERIAL_EV_CTS |
SERIAL_EV_DSR |
SERIAL_EV_RLSD |
SERIAL_EV_BREAK |
SERIAL_EV_ERR |
SERIAL_EV_RING |
/* SERIAL_EV_PERR | */
SERIAL_EV_RX80FULL /*|
SERIAL_EV_EVENT1 |
SERIAL_EV_EVENT2*/;
/* use Serial.sys for basis (not SerCx.sys) */
static BOOL _set_wait_mask(WINPR_COMM *pComm, const ULONG *pWaitMask)
{
ULONG possibleMask;
SERIAL_DRIVER* pSerialSys = SerialSys_s();
possibleMask = *pWaitMask & _SERCX2_SYS_SUPPORTED_EV_MASK;
if (possibleMask != *pWaitMask)
{
DEBUG_WARN("Not all wait events supported (SerCx2.sys), requested events= 0X%lX, possible events= 0X%lX", *pWaitMask, possibleMask);
/* FIXME: shall we really set the possibleMask and return FALSE? */
pComm->WaitEventMask = possibleMask;
return FALSE;
}
/* NB: All events that are supported by SerCx.sys are supported by Serial.sys*/
return pSerialSys->set_wait_mask(pComm, pWaitMask);
}
static BOOL _purge(WINPR_COMM *pComm, const ULONG *pPurgeMask)
{
SERIAL_DRIVER* pSerialSys = SerialSys_s();
/* http://msdn.microsoft.com/en-us/library/windows/hardware/ff546655%28v=vs.85%29.aspx */
if ((*pPurgeMask & SERIAL_PURGE_RXCLEAR) && !(*pPurgeMask & SERIAL_PURGE_RXABORT))
{
DEBUG_WARN("Expecting SERIAL_PURGE_RXABORT since SERIAL_PURGE_RXCLEAR is set");
SetLastError(ERROR_INVALID_DEVICE_OBJECT_PARAMETER);
return FALSE;
}
if ((*pPurgeMask & SERIAL_PURGE_TXCLEAR) && !(*pPurgeMask & SERIAL_PURGE_TXABORT))
{
DEBUG_WARN("Expecting SERIAL_PURGE_TXABORT since SERIAL_PURGE_TXCLEAR is set");
SetLastError(ERROR_INVALID_DEVICE_OBJECT_PARAMETER);
return FALSE;
}
return pSerialSys->purge(pComm, pPurgeMask);
}
/* specific functions only */
static SERIAL_DRIVER _SerCx2Sys =
{
.id = SerialDriverSerCx2Sys,
.name = _T("SerCx2.sys"),
.set_baud_rate = NULL,
.get_baud_rate = NULL,
.get_properties = NULL,
.set_serial_chars = _set_serial_chars,
.get_serial_chars = _get_serial_chars,
.set_line_control = NULL,
.get_line_control = NULL,
.set_handflow = NULL,
.get_handflow = NULL,
.set_timeouts = NULL,
.get_timeouts = NULL,
.set_dtr = NULL,
.clear_dtr = NULL,
.set_rts = NULL,
.clear_rts = NULL,
.get_modemstatus = NULL,
.set_wait_mask = _set_wait_mask,
.get_wait_mask = NULL,
.wait_on_mask = NULL,
.set_queue_size = NULL,
.purge = _purge,
.get_commstatus = NULL,
.set_break_on = NULL,
.set_break_off = NULL,
.set_xoff = NULL, /* not supported by SerCx2.sys */
.set_xon = NULL, /* not supported by SerCx2.sys */
.get_dtrrts = NULL,
.config_size = NULL, /* not supported by SerCx2.sys */
.immediate_char = NULL, /* not supported by SerCx2.sys */
.reset_device = NULL, /* not supported by SerCx2.sys */
};
SERIAL_DRIVER* SerCx2Sys_s()
{
/* _SerCx2Sys completed with inherited functions from SerialSys or SerCxSys */
SERIAL_DRIVER* pSerialSys = SerialSys_s();
SERIAL_DRIVER* pSerCxSys = SerCxSys_s();
_SerCx2Sys.set_baud_rate = pSerCxSys->set_baud_rate;
_SerCx2Sys.get_baud_rate = pSerCxSys->get_baud_rate;
_SerCx2Sys.get_properties = pSerCxSys->get_properties;
_SerCx2Sys.set_line_control = pSerCxSys->set_line_control;
_SerCx2Sys.get_line_control = pSerCxSys->get_line_control;
/* Only SERIAL_CTS_HANDSHAKE, SERIAL_RTS_CONTROL and SERIAL_RTS_HANDSHAKE flags are really required by SerCx2.sys
* http://msdn.microsoft.com/en-us/library/jj680685%28v=vs.85%29.aspx
*/
_SerCx2Sys.set_handflow = pSerialSys->set_handflow;
_SerCx2Sys.get_handflow = pSerialSys->get_handflow;
_SerCx2Sys.set_timeouts = pSerialSys->set_timeouts;
_SerCx2Sys.get_timeouts = pSerialSys->get_timeouts;
_SerCx2Sys.set_dtr = pSerialSys->set_dtr;
_SerCx2Sys.clear_dtr = pSerialSys->clear_dtr;
_SerCx2Sys.set_rts = pSerialSys->set_rts;
_SerCx2Sys.clear_rts = pSerialSys->clear_rts;
_SerCx2Sys.get_modemstatus = pSerialSys->get_modemstatus;
_SerCx2Sys.set_wait_mask = pSerialSys->set_wait_mask;
_SerCx2Sys.get_wait_mask = pSerialSys->get_wait_mask;
_SerCx2Sys.wait_on_mask = pSerialSys->wait_on_mask;
_SerCx2Sys.set_queue_size = pSerialSys->set_queue_size;
_SerCx2Sys.get_commstatus = pSerialSys->get_commstatus;
_SerCx2Sys.set_break_on = pSerialSys->set_break_on;
_SerCx2Sys.set_break_off = pSerialSys->set_break_off;
_SerCx2Sys.get_dtrrts = pSerialSys->get_dtrrts;
return &_SerCx2Sys;
}
#endif /* _WIN32 */

View File

@ -0,0 +1,39 @@
/**
* WinPR: Windows Portable Runtime
* Serial Communication API
*
* Copyright 2014 Hewlett-Packard Development Company, L.P.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef COMM_SERCX2_SYS_H
#define COMM_SERCX2_SYS_H
#ifndef _WIN32
#include "comm_ioctl.h"
#ifdef __cplusplus
extern "C" {
#endif
SERIAL_DRIVER* SerCx2Sys_s();
#ifdef __cplusplus
}
#endif
#endif /* _WIN32 */
#endif /* COMM_SERCX2_SYS_H */

View File

@ -0,0 +1,455 @@
/**
* WinPR: Windows Portable Runtime
* Serial Communication API
*
* Copyright 2011 O.S. Systems Software Ltda.
* Copyright 2011 Eduardo Fiss Beloni <beloni@ossystems.com.br>
* Copyright 2014 Marc-Andre Moreau <marcandre.moreau@gmail.com>
* Copyright 2014 Hewlett-Packard Development Company, L.P.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef _WIN32
#include <assert.h>
#include <termios.h>
#include <freerdp/utils/debug.h>
#include "comm_serial_sys.h"
/* 0: B* (Linux termios)
* 1: CBR_* or actual baud rate
* 2: BAUD_* (similar to SERIAL_BAUD_*)
*/
static const speed_t _SERCX_SYS_BAUD_TABLE[][3] = {
#ifdef B0
{B0, 0, 0}, /* hang up */
#endif
#ifdef B50
{B50, 50, 0},
#endif
#ifdef B75
{B75, 75, BAUD_075},
#endif
#ifdef B110
{B110, CBR_110, BAUD_110},
#endif
#ifdef B134
{B134, 134, 0 /*BAUD_134_5*/},
#endif
#ifdef B150
{B150, 150, BAUD_150},
#endif
#ifdef B200
{B200, 200, 0},
#endif
#ifdef B300
{B300, CBR_300, BAUD_300},
#endif
#ifdef B600
{B600, CBR_600, BAUD_600},
#endif
#ifdef B1200
{B1200, CBR_1200, BAUD_1200},
#endif
#ifdef B1800
{B1800, 1800, BAUD_1800},
#endif
#ifdef B2400
{B2400, CBR_2400, BAUD_2400},
#endif
#ifdef B4800
{B4800, CBR_4800, BAUD_4800},
#endif
/* {, ,BAUD_7200} */
#ifdef B9600
{B9600, CBR_9600, BAUD_9600},
#endif
/* {, CBR_14400, BAUD_14400}, /\* unsupported on Linux *\/ */
#ifdef B19200
{B19200, CBR_19200, BAUD_19200},
#endif
#ifdef B38400
{B38400, CBR_38400, BAUD_38400},
#endif
/* {, CBR_56000, BAUD_56K}, /\* unsupported on Linux *\/ */
#ifdef B57600
{B57600, CBR_57600, BAUD_57600},
#endif
#ifdef B115200
{B115200, CBR_115200, BAUD_115200},
#endif
/* {, CBR_128000, BAUD_128K}, /\* unsupported on Linux *\/ */
/* {, CBR_256000, BAUD_USER}, /\* unsupported on Linux *\/ */
#ifdef B230400
{B230400, 230400, BAUD_USER},
#endif
#ifdef B460800
{B460800, 460800, BAUD_USER},
#endif
#ifdef B500000
{B500000, 500000, BAUD_USER},
#endif
#ifdef B576000
{B576000, 576000, BAUD_USER},
#endif
#ifdef B921600
{B921600, 921600, BAUD_USER},
#endif
#ifdef B1000000
{B1000000, 1000000, BAUD_USER},
#endif
#ifdef B1152000
{B1152000, 1152000, BAUD_USER},
#endif
#ifdef B1500000
{B1500000, 1500000, BAUD_USER},
#endif
#ifdef B2000000
{B2000000, 2000000, BAUD_USER},
#endif
#ifdef B2500000
{B2500000, 2500000, BAUD_USER},
#endif
#ifdef B3000000
{B3000000, 3000000, BAUD_USER},
#endif
#ifdef B3500000
{B3500000, 3500000, BAUD_USER},
#endif
#ifdef B4000000
{B4000000, 4000000, BAUD_USER}, /* __MAX_BAUD */
#endif
};
static BOOL _get_properties(WINPR_COMM *pComm, COMMPROP *pProperties)
{
int i;
SERIAL_DRIVER* pSerialSys = SerialSys_s();
if (!pSerialSys->get_properties(pComm, pProperties))
{
return FALSE;
}
/* override some of the inherited properties from SerialSys ... */
pProperties->dwMaxBaud = BAUD_USER;
pProperties->dwSettableBaud = 0;
for (i=0; _SERCX_SYS_BAUD_TABLE[i][0]<=__MAX_BAUD; i++)
{
pProperties->dwSettableBaud |= _SERCX_SYS_BAUD_TABLE[i][2];
}
return TRUE;
}
static BOOL _set_baud_rate(WINPR_COMM *pComm, const SERIAL_BAUD_RATE *pBaudRate)
{
int i;
speed_t newSpeed;
struct termios futureState;
ZeroMemory(&futureState, sizeof(struct termios));
if (tcgetattr(pComm->fd, &futureState) < 0) /* NB: preserves current settings not directly handled by the Communication Functions */
{
SetLastError(ERROR_IO_DEVICE);
return FALSE;
}
for (i=0; _SERCX_SYS_BAUD_TABLE[i][0]<=__MAX_BAUD; i++)
{
if (_SERCX_SYS_BAUD_TABLE[i][1] == pBaudRate->BaudRate)
{
newSpeed = _SERCX_SYS_BAUD_TABLE[i][0];
if (cfsetspeed(&futureState, newSpeed) < 0)
{
DEBUG_WARN("failed to set speed 0x%x (%lu)", newSpeed, pBaudRate->BaudRate);
return FALSE;
}
assert(cfgetispeed(&futureState) == newSpeed);
if (_comm_ioctl_tcsetattr(pComm->fd, TCSANOW, &futureState) < 0)
{
DEBUG_WARN("_comm_ioctl_tcsetattr failure: last-error: 0x%lX", GetLastError());
return FALSE;
}
return TRUE;
}
}
DEBUG_WARN("could not find a matching speed for the baud rate %lu", pBaudRate->BaudRate);
SetLastError(ERROR_INVALID_DATA);
return FALSE;
}
static BOOL _get_baud_rate(WINPR_COMM *pComm, SERIAL_BAUD_RATE *pBaudRate)
{
int i;
speed_t currentSpeed;
struct termios currentState;
ZeroMemory(&currentState, sizeof(struct termios));
if (tcgetattr(pComm->fd, &currentState) < 0)
{
SetLastError(ERROR_IO_DEVICE);
return FALSE;
}
currentSpeed = cfgetispeed(&currentState);
for (i=0; _SERCX_SYS_BAUD_TABLE[i][0]<=__MAX_BAUD; i++)
{
if (_SERCX_SYS_BAUD_TABLE[i][0] == currentSpeed)
{
pBaudRate->BaudRate = _SERCX_SYS_BAUD_TABLE[i][1];
return TRUE;
}
}
DEBUG_WARN("could not find a matching baud rate for the speed 0x%x", currentSpeed);
SetLastError(ERROR_INVALID_DATA);
return FALSE;
}
static BOOL _set_handflow(WINPR_COMM *pComm, const SERIAL_HANDFLOW *pHandflow)
{
SERIAL_HANDFLOW SerCxHandflow;
BOOL result = TRUE;
SERIAL_DRIVER* pSerialSys = SerialSys_s();
memcpy(&SerCxHandflow, pHandflow, sizeof(SERIAL_HANDFLOW));
/* filter out unsupported bits by SerCx.sys
*
* http://msdn.microsoft.com/en-us/library/windows/hardware/jj680685%28v=vs.85%29.aspx
*/
SerCxHandflow.ControlHandShake = pHandflow->ControlHandShake & (SERIAL_DTR_CONTROL | SERIAL_DTR_HANDSHAKE | SERIAL_CTS_HANDSHAKE | SERIAL_DSR_HANDSHAKE);
SerCxHandflow.FlowReplace = pHandflow->FlowReplace & (SERIAL_RTS_CONTROL | SERIAL_RTS_HANDSHAKE);
if (SerCxHandflow.ControlHandShake != pHandflow->ControlHandShake)
{
if (pHandflow->ControlHandShake & SERIAL_DCD_HANDSHAKE)
{
DEBUG_WARN("SERIAL_DCD_HANDSHAKE not supposed to be implemented by SerCx.sys");
}
if (pHandflow->ControlHandShake & SERIAL_DSR_SENSITIVITY)
{
DEBUG_WARN("SERIAL_DSR_SENSITIVITY not supposed to be implemented by SerCx.sys");
}
if (pHandflow->ControlHandShake & SERIAL_ERROR_ABORT)
{
DEBUG_WARN("SERIAL_ERROR_ABORT not supposed to be implemented by SerCx.sys");
}
SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
result = FALSE;
}
if (SerCxHandflow.FlowReplace != pHandflow->FlowReplace)
{
if (pHandflow->ControlHandShake & SERIAL_AUTO_TRANSMIT)
{
DEBUG_WARN("SERIAL_AUTO_TRANSMIT not supposed to be implemented by SerCx.sys");
}
if (pHandflow->ControlHandShake & SERIAL_AUTO_RECEIVE)
{
DEBUG_WARN("SERIAL_AUTO_RECEIVE not supposed to be implemented by SerCx.sys");
}
if (pHandflow->ControlHandShake & SERIAL_ERROR_CHAR)
{
DEBUG_WARN("SERIAL_ERROR_CHAR not supposed to be implemented by SerCx.sys");
}
if (pHandflow->ControlHandShake & SERIAL_NULL_STRIPPING)
{
DEBUG_WARN("SERIAL_NULL_STRIPPING not supposed to be implemented by SerCx.sys");
}
if (pHandflow->ControlHandShake & SERIAL_BREAK_CHAR)
{
DEBUG_WARN("SERIAL_BREAK_CHAR not supposed to be implemented by SerCx.sys");
}
if (pHandflow->ControlHandShake & SERIAL_XOFF_CONTINUE)
{
DEBUG_WARN("SERIAL_XOFF_CONTINUE not supposed to be implemented by SerCx.sys");
}
SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
result = FALSE;
}
if (!pSerialSys->set_handflow(pComm, &SerCxHandflow))
return FALSE;
return result;
}
static BOOL _get_handflow(WINPR_COMM *pComm, SERIAL_HANDFLOW *pHandflow)
{
BOOL result;
SERIAL_DRIVER* pSerialSys = SerialSys_s();
result = pSerialSys->get_handflow(pComm, pHandflow);
/* filter out unsupported bits by SerCx.sys
*
* http://msdn.microsoft.com/en-us/library/windows/hardware/jj680685%28v=vs.85%29.aspx
*/
pHandflow->ControlHandShake = pHandflow->ControlHandShake & (SERIAL_DTR_CONTROL | SERIAL_DTR_HANDSHAKE | SERIAL_CTS_HANDSHAKE | SERIAL_DSR_HANDSHAKE);
pHandflow->FlowReplace = pHandflow->FlowReplace & (SERIAL_RTS_CONTROL | SERIAL_RTS_HANDSHAKE);
return result;
}
/* http://msdn.microsoft.com/en-us/library/windows/hardware/hh439605%28v=vs.85%29.aspx */
static const ULONG _SERCX_SYS_SUPPORTED_EV_MASK =
SERIAL_EV_RXCHAR |
/* SERIAL_EV_RXFLAG | */
SERIAL_EV_TXEMPTY |
SERIAL_EV_CTS |
SERIAL_EV_DSR |
SERIAL_EV_RLSD |
SERIAL_EV_BREAK |
SERIAL_EV_ERR |
SERIAL_EV_RING /* |
SERIAL_EV_PERR |
SERIAL_EV_RX80FULL |
SERIAL_EV_EVENT1 |
SERIAL_EV_EVENT2*/;
static BOOL _set_wait_mask(WINPR_COMM *pComm, const ULONG *pWaitMask)
{
ULONG possibleMask;
SERIAL_DRIVER* pSerialSys = SerialSys_s();
possibleMask = *pWaitMask & _SERCX_SYS_SUPPORTED_EV_MASK;
if (possibleMask != *pWaitMask)
{
DEBUG_WARN("Not all wait events supported (SerCx.sys), requested events= 0x%lX, possible events= 0x%lX", *pWaitMask, possibleMask);
/* FIXME: shall we really set the possibleMask and return FALSE? */
pComm->WaitEventMask = possibleMask;
return FALSE;
}
/* NB: All events that are supported by SerCx.sys are supported by Serial.sys*/
return pSerialSys->set_wait_mask(pComm, pWaitMask);
}
/* specific functions only */
static SERIAL_DRIVER _SerCxSys =
{
.id = SerialDriverSerCxSys,
.name = _T("SerCx.sys"),
.set_baud_rate = _set_baud_rate,
.get_baud_rate = _get_baud_rate,
.get_properties = _get_properties,
.set_serial_chars = NULL,
.get_serial_chars = NULL,
.set_line_control = NULL,
.get_line_control = NULL,
.set_handflow = _set_handflow,
.get_handflow = _get_handflow,
.set_timeouts = NULL,
.get_timeouts = NULL,
.set_dtr = NULL,
.clear_dtr = NULL,
.set_rts = NULL,
.clear_rts = NULL,
.get_modemstatus = NULL,
.set_wait_mask = _set_wait_mask,
.get_wait_mask = NULL,
.wait_on_mask = NULL,
.set_queue_size = NULL,
.purge = NULL,
.get_commstatus = NULL,
.set_break_on = NULL,
.set_break_off = NULL,
.set_xoff = NULL,
.set_xon = NULL,
.get_dtrrts = NULL,
.config_size = NULL, /* not supported by SerCx.sys */
.immediate_char = NULL,
.reset_device = NULL, /* not supported by SerCx.sys */
};
SERIAL_DRIVER* SerCxSys_s()
{
/* _SerCxSys completed with inherited functions from SerialSys */
SERIAL_DRIVER* pSerialSys = SerialSys_s();
_SerCxSys.set_serial_chars = pSerialSys->set_serial_chars;
_SerCxSys.get_serial_chars = pSerialSys->get_serial_chars;
_SerCxSys.set_line_control = pSerialSys->set_line_control;
_SerCxSys.get_line_control = pSerialSys->get_line_control;
_SerCxSys.set_timeouts = pSerialSys->set_timeouts;
_SerCxSys.get_timeouts = pSerialSys->get_timeouts;
_SerCxSys.set_dtr = pSerialSys->set_dtr;
_SerCxSys.clear_dtr = pSerialSys->clear_dtr;
_SerCxSys.set_rts = pSerialSys->set_rts;
_SerCxSys.clear_rts = pSerialSys->clear_rts;
_SerCxSys.get_modemstatus = pSerialSys->get_modemstatus;
_SerCxSys.set_wait_mask = pSerialSys->set_wait_mask;
_SerCxSys.get_wait_mask = pSerialSys->get_wait_mask;
_SerCxSys.wait_on_mask = pSerialSys->wait_on_mask;
_SerCxSys.set_queue_size = pSerialSys->set_queue_size;
_SerCxSys.purge = pSerialSys->purge;
_SerCxSys.get_commstatus = pSerialSys->get_commstatus;
_SerCxSys.set_break_on = pSerialSys->set_break_on;
_SerCxSys.set_break_off = pSerialSys->set_break_off;
_SerCxSys.set_xoff = pSerialSys->set_xoff;
_SerCxSys.set_xon = pSerialSys->set_xon;
_SerCxSys.get_dtrrts = pSerialSys->get_dtrrts;
_SerCxSys.immediate_char = pSerialSys->immediate_char;
return &_SerCxSys;
}
#endif /* _WIN32 */

View File

@ -0,0 +1,41 @@
/**
* WinPR: Windows Portable Runtime
* Serial Communication API
*
* Copyright 2014 Hewlett-Packard Development Company, L.P.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef COMM_SERCX_SYS_H
#define COMM_SERCX_SYS_H
#ifndef _WIN32
#include "comm_ioctl.h"
#ifdef __cplusplus
extern "C" {
#endif
SERIAL_DRIVER* SerCxSys_s();
#ifdef __cplusplus
}
#endif
#endif /* _WIN32 */
#endif /* COMM_SERCX_SYS_H */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,40 @@
/**
* WinPR: Windows Portable Runtime
* Serial Communication API
*
* Copyright 2014 Hewlett-Packard Development Company, L.P.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef COMM_SERIAL_SYS_H
#define COMM_SERIAL_SYS_H
#ifndef _WIN32
#include "comm_ioctl.h"
#ifdef __cplusplus
extern "C" {
#endif
SERIAL_DRIVER* SerialSys_s();
#ifdef __cplusplus
}
#endif
#endif /* _WIN32 */
#endif /* COMM_SERIAL_SYS_H */

View File

@ -0,0 +1,3 @@
LIBRARY "libwinpr-comm"
EXPORTS

2
winpr/libwinpr/comm/test/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
TestComm
TestComm.c

View File

@ -0,0 +1,39 @@
set(MODULE_NAME "TestComm")
set(MODULE_PREFIX "TEST_COMM")
set(${MODULE_PREFIX}_DRIVER ${MODULE_NAME}.c)
set(${MODULE_PREFIX}_TESTS
TestCommDevice.c
TestCommConfig.c
TestGetCommState.c
TestSetCommState.c
TestSerialChars.c
TestControlSettings.c
TestHandflow.c
TestTimeouts.c
TestCommMonitor.c)
create_test_sourcelist(${MODULE_PREFIX}_SRCS
${${MODULE_PREFIX}_DRIVER}
${${MODULE_PREFIX}_TESTS})
add_executable(${MODULE_NAME} ${${MODULE_PREFIX}_SRCS})
set_complex_link_libraries(VARIABLE ${MODULE_PREFIX}_LIBS
MONOLITHIC ${MONOLITHIC_BUILD}
MODULE winpr
MODULES winpr-comm winpr-crt winpr-file)
target_link_libraries(${MODULE_NAME} ${${MODULE_PREFIX}_LIBS})
set_target_properties(${MODULE_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${TESTING_OUTPUT_DIRECTORY}")
foreach(test ${${MODULE_PREFIX}_TESTS})
get_filename_component(TestName ${test} NAME_WE)
add_test(${TestName} ${TESTING_OUTPUT_DIRECTORY}/${MODULE_NAME} ${TestName})
endforeach()
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "WinPR/Test")

View File

@ -0,0 +1,141 @@
/**
* WinPR: Windows Portable Runtime
* Serial Communication API
*
* Copyright 2014 Marc-Andre Moreau <marcandre.moreau@gmail.com>
* Copyright 2014 Hewlett-Packard Development Company, L.P.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <winpr/crt.h>
#include <winpr/comm.h>
#include <winpr/file.h>
#include <winpr/synch.h>
#include <winpr/handle.h>
int TestCommConfig(int argc, char* argv[])
{
DCB dcb;
HANDLE hComm;
BOOL success;
LPCSTR lpFileName = "\\\\.\\COM1";
COMMPROP commProp;
hComm = CreateFileA(lpFileName,
GENERIC_READ | GENERIC_WRITE,
0, NULL, OPEN_EXISTING, 0, NULL);
if (hComm && (hComm != INVALID_HANDLE_VALUE))
{
fprintf(stderr, "CreateFileA failure: could create a handle on a not yet defined device: %s\n", lpFileName);
return EXIT_FAILURE;
}
// TMP: FIXME: check if we can proceed with tests on the actual device, skip and warn otherwise but don't fail
success = DefineCommDevice(lpFileName, "/dev/ttyS0");
if(!success)
{
fprintf(stderr, "DefineCommDevice failure: %s\n", lpFileName);
return EXIT_FAILURE;
}
hComm = CreateFileA(lpFileName,
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_WRITE, /* invalid parmaeter */
NULL,
CREATE_NEW, /* invalid parameter */
0,
(HANDLE)1234); /* invalid parmaeter */
if (hComm != INVALID_HANDLE_VALUE)
{
fprintf(stderr, "CreateFileA failure: could create a handle with some invalid parameters %s\n", lpFileName);
return EXIT_FAILURE;
}
hComm = CreateFileA(lpFileName,
GENERIC_READ | GENERIC_WRITE,
0, NULL, OPEN_EXISTING, 0, NULL);
if (!hComm || (hComm == INVALID_HANDLE_VALUE))
{
fprintf(stderr, "CreateFileA failure: %s GetLastError() = 0x%0.8x\n", lpFileName, GetLastError());
return EXIT_FAILURE;
}
/* TODO: a second call to CreateFileA should failed and
* GetLastError should return ERROR_SHARING_VIOLATION */
ZeroMemory(&dcb, sizeof(DCB));
dcb.DCBlength = sizeof(DCB);
success = GetCommState(hComm, &dcb);
if (!success)
{
fprintf(stderr, "GetCommState failure: GetLastError() = Ox%x\n", (int) GetLastError());
return EXIT_FAILURE;
}
fprintf(stderr, "BaudRate: %d ByteSize: %d Parity: %d StopBits: %d\n",
(int) dcb.BaudRate, (int) dcb.ByteSize, (int) dcb.Parity, (int) dcb.StopBits);
ZeroMemory(&commProp, sizeof(COMMPROP));
if (!GetCommProperties(hComm, &commProp))
{
fprintf(stderr, "GetCommProperties failure: GetLastError(): 0x0.8x\n", GetLastError());
return EXIT_FAILURE;
}
if ((commProp.dwSettableBaud & BAUD_57600) <= 0)
{
fprintf(stderr, "BAUD_57600 unsupported!\n");
return EXIT_FAILURE;
}
if ((commProp.dwSettableBaud & BAUD_14400) > 0)
{
fprintf(stderr, "BAUD_14400 supported!\n");
return EXIT_FAILURE;
}
dcb.BaudRate = CBR_57600;
dcb.ByteSize = 8;
dcb.Parity = NOPARITY;
dcb.StopBits = ONESTOPBIT;
success = SetCommState(hComm, &dcb);
if (!success)
{
fprintf(stderr, "SetCommState failure: GetLastError() = 0x%x\n", (int) GetLastError());
return EXIT_FAILURE;
}
success = GetCommState(hComm, &dcb);
if (!success)
{
fprintf(stderr, "GetCommState failure: GetLastError() = 0x%x\n", (int) GetLastError());
return 0;
}
if ((dcb.BaudRate != CBR_57600) || (dcb.ByteSize != 8) || (dcb.Parity != NOPARITY) || (dcb.StopBits != ONESTOPBIT))
{
fprintf(stderr, "Got an unexpeted value among: BaudRate: %d ByteSize: %d Parity: %d StopBits: %d\n",
(int) dcb.BaudRate, (int) dcb.ByteSize, (int) dcb.Parity, (int) dcb.StopBits);
}
CloseHandle(hComm);
return 0;
}

View File

@ -0,0 +1,115 @@
/**
* WinPR: Windows Portable Runtime
* Serial Communication API
*
* Copyright 2014 Hewlett-Packard Development Company, L.P.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <stdio.h>
#include <winpr/comm.h>
#include <winpr/tchar.h>
static int test_CommDevice(LPCTSTR lpDeviceName, BOOL expectedResult)
{
BOOL result;
TCHAR lpTargetPath[MAX_PATH];
DWORD tcslen;
result = DefineCommDevice(lpDeviceName, _T("/dev/test"));
if ((!expectedResult && result) || (expectedResult && !result)) /* logical XOR */
{
_tprintf(_T("DefineCommDevice failure: device name: %s, expected result: %s, result: %s\n"),
lpDeviceName,
(expectedResult ? "TRUE" : "FALSE"),
(result ? "TRUE" : "FALSE"));
return FALSE;
}
result = IsCommDevice(lpDeviceName);
if ((!expectedResult && result) || (expectedResult && !result)) /* logical XOR */
{
_tprintf(_T("IsCommDevice failure: device name: %s, expected result: %s, result: %s\n"),
lpDeviceName,
(expectedResult ? "TRUE" : "FALSE"),
(result ? "TRUE" : "FALSE"));
return FALSE;
}
tcslen = QueryCommDevice(lpDeviceName, lpTargetPath, MAX_PATH);
if (expectedResult)
{
if (tcslen <= _tcslen(lpDeviceName)) /* at least 2 more TCHAR are expected */
{
_tprintf(_T("QueryCommDevice failure: didn't found the device name: %s\n"), lpDeviceName);
return FALSE;
}
if (_tcscmp(_T("/dev/test"), lpTargetPath) != 0)
{
_tprintf(_T("QueryCommDevice failure: device name: %s, expected result: %s, result: %s\n"),
lpDeviceName, _T("/dev/test"), lpTargetPath);
return FALSE;
}
if (lpTargetPath[_tcslen(lpTargetPath) + 1] != 0)
{
_tprintf(_T("QueryCommDevice failure: device name: %s, the second NULL character is missing at the end of the buffer\n"), lpDeviceName);
return FALSE;
}
}
else
{
if (tcslen > 0)
{
_tprintf(_T("QueryCommDevice failure: device name: %s, expected result: <none>, result: %d %s\n"),
lpDeviceName, tcslen, lpTargetPath);
return FALSE;
}
}
return TRUE;
}
int TestCommDevice(int argc, char* argv[])
{
if (!test_CommDevice(_T("COM0"), FALSE))
return EXIT_FAILURE;
if (!test_CommDevice(_T("COM1"), TRUE))
return EXIT_FAILURE;
if (!test_CommDevice(_T("COM1"), TRUE))
return EXIT_FAILURE;
if (!test_CommDevice(_T("COM10"), FALSE))
return EXIT_FAILURE;
if (!test_CommDevice(_T("\\\\.\\COM5"), TRUE))
return EXIT_FAILURE;
if (!test_CommDevice(_T("\\\\.\\COM10"), TRUE))
return EXIT_FAILURE;
if (!test_CommDevice(_T("\\\\.COM10"), FALSE))
return EXIT_FAILURE;
return 0;
}

View File

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

View File

@ -0,0 +1,121 @@
/**
* WinPR: Windows Portable Runtime
* Serial Communication API
*
* Copyright 2014 Hewlett-Packard Development Company, L.P.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <stdio.h>
#include <winpr/comm.h>
#include <winpr/crt.h>
#include "../comm.h"
int TestControlSettings(int argc, char* argv[])
{
BOOL result;
HANDLE hComm;
DCB dcb;
// 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;
}
ZeroMemory(&dcb, sizeof(DCB));
dcb.DCBlength = sizeof(DCB);
if (!GetCommState(hComm, &dcb))
{
fprintf(stderr, "GetCommState failure; GetLastError(): %0.8x\n", GetLastError());
return FALSE;
}
/* Test 1 */
dcb.ByteSize = 5;
dcb.StopBits = ONESTOPBIT;
dcb.Parity = MARKPARITY;
if (!SetCommState(hComm, &dcb))
{
fprintf(stderr, "SetCommState failure; GetLastError(): %0.8x\n", GetLastError());
return FALSE;
}
ZeroMemory(&dcb, sizeof(DCB));
dcb.DCBlength = sizeof(DCB);
if (!GetCommState(hComm, &dcb))
{
fprintf(stderr, "GetCommState failure; GetLastError(): %0.8x\n", GetLastError());
return FALSE;
}
if ((dcb.ByteSize != 5) || (dcb.StopBits != ONESTOPBIT) || (dcb.Parity != MARKPARITY))
{
fprintf(stderr, "test1 failed.\n");
return FALSE;
}
/* Test 2 */
dcb.ByteSize = 8;
dcb.StopBits = ONESTOPBIT;
dcb.Parity = NOPARITY;
if (!SetCommState(hComm, &dcb))
{
fprintf(stderr, "SetCommState failure; GetLastError(): %0.8x\n", GetLastError());
return FALSE;
}
ZeroMemory(&dcb, sizeof(DCB));
dcb.DCBlength = sizeof(DCB);
if (!GetCommState(hComm, &dcb))
{
fprintf(stderr, "GetCommState failure; GetLastError(): %0.8x\n", GetLastError());
return FALSE;
}
if ((dcb.ByteSize != 8) || (dcb.StopBits != ONESTOPBIT) || (dcb.Parity != NOPARITY))
{
fprintf(stderr, "test2 failed.\n");
return FALSE;
}
if (!CloseHandle(hComm))
{
fprintf(stderr, "CloseHandle failure, GetLastError()=%0.8x\n", GetLastError());
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}

View File

@ -0,0 +1,129 @@
/**
* WinPR: Windows Portable Runtime
* Serial Communication API
*
* Copyright 2014 Hewlett-Packard Development Company, L.P.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <stdio.h>
#include <winpr/comm.h>
#include <winpr/crt.h>
#include "../comm.h"
static BOOL test_generic(HANDLE hComm)
{
DCB dcb, *pDcb;
BOOL result;
ZeroMemory(&dcb, sizeof(DCB));
result = GetCommState(hComm, &dcb);
if (result)
{
printf("GetCommState failure, should have returned false because dcb.DCBlength has been let uninitialized\n");
return FALSE;
}
ZeroMemory(&dcb, sizeof(DCB));
dcb.DCBlength = sizeof(DCB) / 2; /* improper value */
result = GetCommState(hComm, &dcb);
if (result)
{
printf("GetCommState failure, should have return false because dcb.DCBlength was not correctly initialized\n");
return FALSE;
}
ZeroMemory(&dcb, sizeof(DCB));
dcb.DCBlength = sizeof(DCB);
result = GetCommState(hComm, &dcb);
if (!result)
{
printf("GetCommState failure: Ox%x, with adjusted DCBlength\n", GetLastError());
return FALSE;
}
pDcb = (DCB*)calloc(1, sizeof(DCB) * 2);
pDcb->DCBlength = sizeof(DCB) * 2;
result = GetCommState(hComm, pDcb);
result = result && (pDcb->DCBlength == sizeof(DCB) * 2);
free(pDcb);
if (!result)
{
printf("GetCommState failure: 0x%x, with bigger DCBlength\n", GetLastError());
return FALSE;
}
return TRUE;
}
int TestGetCommState(int argc, char* argv[])
{
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)
{
printf("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)
{
printf("CreateFileA failure: 0x%x\n", GetLastError());
return EXIT_FAILURE;
}
if (!test_generic(hComm))
{
printf("test_generic failure (SerialDriverUnknown)\n");
return EXIT_FAILURE;
}
_comm_setServerSerialDriver(hComm, SerialDriverSerialSys);
if (!test_generic(hComm))
{
printf("test_generic failure (SerialDriverSerialSys)\n");
return EXIT_FAILURE;
}
_comm_setServerSerialDriver(hComm, SerialDriverSerCxSys);
if (!test_generic(hComm))
{
printf("test_generic failure (SerialDriverSerCxSys)\n");
return EXIT_FAILURE;
}
_comm_setServerSerialDriver(hComm, SerialDriverSerCx2Sys);
if (!test_generic(hComm))
{
printf("test_generic failure (SerialDriverSerCx2Sys)\n");
return EXIT_FAILURE;
}
if (!CloseHandle(hComm))
{
fprintf(stderr, "CloseHandle failure, GetLastError()=%0.8x\n", GetLastError());
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}

View File

@ -0,0 +1,86 @@
/**
* 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_SerialSys(HANDLE hComm)
{
// TMP: TODO:
return TRUE;
}
int TestHandflow(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_setServerSerialDriver(hComm, SerialDriverSerialSys);
if (!test_SerialSys(hComm))
{
fprintf(stderr, "test_SerCxSys failure\n");
return EXIT_FAILURE;
}
/* _comm_setServerSerialDriver(hComm, SerialDriverSerCxSys); */
/* if (!test_SerCxSys(hComm)) */
/* { */
/* fprintf(stderr, "test_SerCxSys failure\n"); */
/* return EXIT_FAILURE; */
/* } */
/* _comm_setServerSerialDriver(hComm, SerialDriverSerCx2Sys); */
/* if (!test_SerCx2Sys(hComm)) */
/* { */
/* fprintf(stderr, "test_SerCxSys failure\n"); */
/* return EXIT_FAILURE; */
/* } */
if (!CloseHandle(hComm))
{
fprintf(stderr, "CloseHandle failure, GetLastError()=%0.8x\n", GetLastError());
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}

View File

@ -0,0 +1,173 @@
/**
* 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_SerCxSys(HANDLE hComm)
{
DCB dcb;
UCHAR XonChar, XoffChar;
struct termios currentTermios;
ZeroMemory(&currentTermios, sizeof(struct termios));
if (tcgetattr(((WINPR_COMM*)hComm)->fd, &currentTermios) < 0)
{
fprintf(stderr, "tcgetattr failure.\n");
return FALSE;
}
ZeroMemory(&dcb, sizeof(DCB));
dcb.DCBlength = sizeof(DCB);
if (!GetCommState(hComm, &dcb))
{
fprintf(stderr, "GetCommState failure, GetLastError(): 0x%0.8x\n", GetLastError());
return FALSE;
}
if ((dcb.XonChar == '\0') || (dcb.XoffChar == '\0'))
{
fprintf(stderr, "test_SerCxSys failure, expected XonChar and XoffChar to be set\n");
return FALSE;
}
/* retrieve Xon/Xoff chars */
if ((dcb.XonChar != currentTermios.c_cc[VSTART]) || (dcb.XoffChar != currentTermios.c_cc[VSTOP]))
{
fprintf(stderr, "test_SerCxSys failure, could not retrieve XonChar and XoffChar\n");
return FALSE;
}
/* swap XonChar/XoffChar */
XonChar = dcb.XonChar;
XoffChar = dcb.XoffChar;
dcb.XonChar = XoffChar;
dcb.XoffChar = XonChar;
if (!SetCommState(hComm, &dcb))
{
fprintf(stderr, "SetCommState failure, GetLastError(): 0x%0.8x\n", GetLastError());
return FALSE;
}
ZeroMemory(&dcb, sizeof(DCB));
dcb.DCBlength = sizeof(DCB);
if (!GetCommState(hComm, &dcb))
{
fprintf(stderr, "GetCommState failure, GetLastError(): 0x%0.8x\n", GetLastError());
return FALSE;
}
if ((dcb.XonChar != XoffChar) || (dcb.XoffChar != XonChar))
{
fprintf(stderr, "test_SerCxSys, expected XonChar and XoffChar to be swapped\n");
return FALSE;
}
/* same XonChar / XoffChar */
dcb.XonChar = dcb.XoffChar;
if (SetCommState(hComm, &dcb))
{
fprintf(stderr, "test_SerCxSys failure, SetCommState() was supposed to failed because XonChar and XoffChar are the same\n");
return FALSE;
}
if (GetLastError() != ERROR_INVALID_PARAMETER)
{
fprintf(stderr, "test_SerCxSys failure, SetCommState() was supposed to failed with GetLastError()=ERROR_INVALID_PARAMETER\n");
return FALSE;
}
return TRUE;
}
static BOOL test_SerCx2Sys(HANDLE hComm)
{
DCB dcb;
ZeroMemory(&dcb, sizeof(DCB));
dcb.DCBlength = sizeof(DCB);
if (!GetCommState(hComm, &dcb))
{
fprintf(stderr, "GetCommState failure; GetLastError(): %0.8x\n", GetLastError());
return FALSE;
}
if ((dcb.ErrorChar != '\0') || (dcb.EofChar != '\0') || (dcb.EvtChar != '\0') || (dcb.XonChar != '\0') || (dcb.XoffChar != '\0'))
{
fprintf(stderr, "test_SerCx2Sys failure, expected all characters to be: '\\0'\n");
return FALSE;
}
return TRUE;
}
int TestSerialChars(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_setServerSerialDriver(hComm, SerialDriverSerCxSys);
if (!test_SerCxSys(hComm))
{
fprintf(stderr, "test_SerCxSys failure\n");
return EXIT_FAILURE;
}
_comm_setServerSerialDriver(hComm, SerialDriverSerCx2Sys);
if (!test_SerCx2Sys(hComm))
{
fprintf(stderr, "test_SerCxSys failure\n");
return EXIT_FAILURE;
}
if (!CloseHandle(hComm))
{
fprintf(stderr, "CloseHandle failure, GetLastError()=%0.8x\n", GetLastError());
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}

View File

@ -0,0 +1,390 @@
/**
* WinPR: Windows Portable Runtime
* Serial Communication API
*
* Copyright 2014 Hewlett-Packard Development Company, L.P.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <stdio.h>
#include <winpr/comm.h>
#include <winpr/crt.h>
#include "../comm.h"
static void init_empty_dcb(DCB *pDcb)
{
ZeroMemory(pDcb, sizeof(DCB));
pDcb->DCBlength = sizeof(DCB);
pDcb->XonChar = 1;
pDcb->XoffChar = 2;
}
static BOOL test_fParity(HANDLE hComm)
{
DCB dcb;
BOOL result;
init_empty_dcb(&dcb);
result = GetCommState(hComm, &dcb);
if (!result)
{
fprintf(stderr, "GetCommState failure: 0x%x\n", GetLastError());
return FALSE;
}
/* test 1 */
dcb.fParity = TRUE;
result = SetCommState(hComm, &dcb);
if (!result)
{
fprintf(stderr, "SetCommState failure: 0x%x\n", GetLastError());
return FALSE;
}
init_empty_dcb(&dcb);
result = GetCommState(hComm, &dcb);
if (!result)
{
fprintf(stderr, "GetCommState failure: 0x%x\n", GetLastError());
return FALSE;
}
if (!dcb.fParity)
{
fprintf(stderr, "unexpected fParity: %d instead of TRUE\n", dcb.fParity);
return FALSE;
}
/* test 2 */
dcb.fParity = FALSE;
result = SetCommState(hComm, &dcb);
if (!result)
{
fprintf(stderr, "SetCommState failure: 0x%x\n", GetLastError());
return FALSE;
}
init_empty_dcb(&dcb);
result = GetCommState(hComm, &dcb);
if (!result)
{
fprintf(stderr, "GetCommState failure: 0x%x\n", GetLastError());
return FALSE;
}
if (dcb.fParity)
{
fprintf(stderr, "unexpected fParity: %d instead of FALSE\n", dcb.fParity);
return FALSE;
}
/* test 3 (redo test 1) */
dcb.fParity = TRUE;
result = SetCommState(hComm, &dcb);
if (!result)
{
fprintf(stderr, "SetCommState failure: 0x%x\n", GetLastError());
return FALSE;
}
init_empty_dcb(&dcb);
result = GetCommState(hComm, &dcb);
if (!result)
{
fprintf(stderr, "GetCommState failure: 0x%x\n", GetLastError());
return FALSE;
}
if (!dcb.fParity)
{
fprintf(stderr, "unexpected fParity: %d instead of TRUE\n", dcb.fParity);
return FALSE;
}
return TRUE;
}
static BOOL test_SerialSys(HANDLE hComm)
{
DCB dcb;
BOOL result;
init_empty_dcb(&dcb);
result = GetCommState(hComm, &dcb);
if (!result)
{
fprintf(stderr, "GetCommState failure: 0x%x\n", GetLastError());
return FALSE;
}
/* Test 1 */
dcb.BaudRate = SERIAL_BAUD_115200;
result = SetCommState(hComm, &dcb);
if (!result)
{
fprintf(stderr, "SetCommState failure: 0x%0.8x\n", GetLastError());
return FALSE;
}
init_empty_dcb(&dcb);
result = GetCommState(hComm, &dcb);
if (!result)
{
fprintf(stderr, "GetCommState failure: 0x%x\n", GetLastError());
return FALSE;
}
if (dcb.BaudRate != SERIAL_BAUD_115200)
{
fprintf(stderr, "SetCommState failure: could not set BaudRate=%d (SERIAL_BAUD_115200)\n", SERIAL_BAUD_115200);
return FALSE;
}
/* Test 2 using a defferent baud rate */
dcb.BaudRate = SERIAL_BAUD_57600;
result = SetCommState(hComm, &dcb);
if (!result)
{
fprintf(stderr, "SetCommState failure: 0x%x\n", GetLastError());
return FALSE;
}
init_empty_dcb(&dcb);
result = GetCommState(hComm, &dcb);
if (!result)
{
fprintf(stderr, "GetCommState failure: 0x%x\n", GetLastError());
return FALSE;
}
if (dcb.BaudRate != SERIAL_BAUD_57600)
{
fprintf(stderr, "SetCommState failure: could not set BaudRate=%d (SERIAL_BAUD_57600)\n", SERIAL_BAUD_57600);
return FALSE;
}
/* Test 3 using an unsupported baud rate on Linux */
dcb.BaudRate = SERIAL_BAUD_128K;
result = SetCommState(hComm, &dcb);
if (result)
{
fprintf(stderr, "SetCommState failure: unexpected support of BaudRate=%d (SERIAL_BAUD_128K)\n", SERIAL_BAUD_128K);
return FALSE;
}
return TRUE;
}
static BOOL test_SerCxSys(HANDLE hComm)
{
DCB dcb;
BOOL result;
init_empty_dcb(&dcb);
result = GetCommState(hComm, &dcb);
if (!result)
{
fprintf(stderr, "GetCommState failure: 0x%x\n", GetLastError());
return FALSE;
}
/* Test 1 */
dcb.BaudRate = CBR_115200;
result = SetCommState(hComm, &dcb);
if (!result)
{
fprintf(stderr, "SetCommState failure: 0x%0.8x\n", GetLastError());
return FALSE;
}
init_empty_dcb(&dcb);
result = GetCommState(hComm, &dcb);
if (!result)
{
fprintf(stderr, "GetCommState failure: 0x%x\n", GetLastError());
return FALSE;
}
if (dcb.BaudRate != CBR_115200)
{
fprintf(stderr, "SetCommState failure: could not set BaudRate=%d (CBR_115200)\n", CBR_115200);
return FALSE;
}
/* Test 2 using a defferent baud rate */
dcb.BaudRate = CBR_57600;
result = SetCommState(hComm, &dcb);
if (!result)
{
fprintf(stderr, "SetCommState failure: 0x%x\n", GetLastError());
return FALSE;
}
init_empty_dcb(&dcb);
result = GetCommState(hComm, &dcb);
if (!result)
{
fprintf(stderr, "GetCommState failure: 0x%x\n", GetLastError());
return FALSE;
}
if (dcb.BaudRate != CBR_57600)
{
fprintf(stderr, "SetCommState failure: could not set BaudRate=%d (CBR_57600)\n", CBR_57600);
return FALSE;
}
/* Test 3 using an unsupported baud rate on Linux */
dcb.BaudRate = CBR_128000;
result = SetCommState(hComm, &dcb);
if (result)
{
fprintf(stderr, "SetCommState failure: unexpected support of BaudRate=%d (CBR_128000)\n", CBR_128000);
return FALSE;
}
return TRUE;
}
static BOOL test_SerCx2Sys(HANDLE hComm)
{
/* as of today there is no difference */
return test_SerCxSys(hComm);
}
static BOOL test_generic(HANDLE hComm)
{
DCB dcb, dcb2;
BOOL result;
init_empty_dcb(&dcb);
result = GetCommState(hComm, &dcb);
if (!result)
{
fprintf(stderr, "GetCommState failure: 0x%x\n", GetLastError());
return FALSE;
}
/* Checks whether we get the same information before and after SetCommState */
memcpy(&dcb2, &dcb, sizeof(DCB));
result = SetCommState(hComm, &dcb);
if (!result)
{
fprintf(stderr, "SetCommState failure: 0x%0.8x\n", GetLastError());
return FALSE;
}
result = GetCommState(hComm, &dcb);
if (!result)
{
fprintf(stderr, "GetCommState failure: 0x%x\n", GetLastError());
return FALSE;
}
if (memcmp(&dcb, &dcb2, sizeof(DCB)) != 0)
{
fprintf(stderr, "DCB is different after SetCommState() whereas it should have not changed\n");
return FALSE;
}
// TODO: a more complete and generic test using GetCommProperties()
/* TMP: TODO: fBinary tests */
/* fParity tests */
if (!test_fParity(hComm))
{
fprintf(stderr, "test_fParity failure\n");
return FALSE;
}
return TRUE;
}
int TestSetCommState(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;
}
if (!test_generic(hComm))
{
fprintf(stderr, "test_generic failure (SerialDriverUnknown)\n");
return EXIT_FAILURE;
}
_comm_setServerSerialDriver(hComm, SerialDriverSerialSys);
if (!test_generic(hComm))
{
fprintf(stderr, "test_generic failure (SerialDriverSerialSys)\n");
return EXIT_FAILURE;
}
if (!test_SerialSys(hComm))
{
fprintf(stderr, "test_SerialSys failure\n");
return EXIT_FAILURE;
}
_comm_setServerSerialDriver(hComm, SerialDriverSerCxSys);
if (!test_generic(hComm))
{
fprintf(stderr, "test_generic failure (SerialDriverSerCxSys)\n");
return EXIT_FAILURE;
}
if (!test_SerCxSys(hComm))
{
fprintf(stderr, "test_SerCxSys failure\n");
return EXIT_FAILURE;
}
_comm_setServerSerialDriver(hComm, SerialDriverSerCx2Sys);
if (!test_generic(hComm))
{
fprintf(stderr, "test_generic failure (SerialDriverSerCx2Sys)\n");
return EXIT_FAILURE;
}
if (!test_SerCx2Sys(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;
}

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_setServerSerialDriver(hComm, SerialDriverSerialSys);
if (!test_generic(hComm))
{
fprintf(stderr, "test_SerialSys failure\n");
return EXIT_FAILURE;
}
_comm_setServerSerialDriver(hComm, SerialDriverSerCxSys);
if (!test_generic(hComm))
{
fprintf(stderr, "test_SerCxSys failure\n");
return EXIT_FAILURE;
}
_comm_setServerSerialDriver(hComm, SerialDriverSerCx2Sys);
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;
}

View File

@ -3,6 +3,7 @@
* File Functions
*
* Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
* Copyright 2014 Hewlett-Packard Development Company, L.P.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -27,6 +28,7 @@
#include <winpr/error.h>
#include <winpr/handle.h>
#include <winpr/platform.h>
#include <winpr/collections.h>
#include <winpr/file.h>
@ -40,7 +42,7 @@
/**
* api-ms-win-core-file-l1-2-0.dll:
*
*
* CreateFileA
* CreateFileW
* CreateFile2
@ -148,8 +150,10 @@
#include <unistd.h>
#endif
#include <assert.h>
#include <time.h>
#include <errno.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@ -184,6 +188,64 @@
#include "../pipe/pipe.h"
/* TODO: FIXME: use of a wArrayList and split winpr-utils with
* winpr-collections to avoid a circular dependency
* _HandleCreators = ArrayList_New(TRUE);
*/
/* _HandleCreators is a NULL-terminated array with a maximun of HANDLE_CREATOR_MAX HANDLE_CREATOR */
#define HANDLE_CREATOR_MAX 128
static HANDLE_CREATOR **_HandleCreators = NULL;
static pthread_once_t _HandleCreatorsInitialized = PTHREAD_ONCE_INIT;
static void _HandleCreatorsInit()
{
/* NB: error management to be done outside of this function */
assert(_HandleCreators == NULL);
_HandleCreators = (HANDLE_CREATOR**)calloc(HANDLE_CREATOR_MAX+1, sizeof(HANDLE_CREATOR*));
assert(_HandleCreators != NULL);
}
/**
* Returns TRUE on success, FALSE otherwise.
*
* ERRORS:
* ERROR_DLL_INIT_FAILED
* ERROR_INSUFFICIENT_BUFFER _HandleCreators full
*/
BOOL RegisterHandleCreator(PHANDLE_CREATOR pHandleCreator)
{
int i;
if (pthread_once(&_HandleCreatorsInitialized, _HandleCreatorsInit) != 0)
{
SetLastError(ERROR_DLL_INIT_FAILED);
return FALSE;
}
if (_HandleCreators == NULL)
{
SetLastError(ERROR_DLL_INIT_FAILED);
return FALSE;
}
for (i=0; i<HANDLE_CREATOR_MAX; i++)
{
if (_HandleCreators[i] == NULL)
{
_HandleCreators[i] = pHandleCreator;
return TRUE;
}
}
SetLastError(ERROR_INSUFFICIENT_BUFFER);
return FALSE;
}
#ifdef HAVE_AIO_H
static BOOL g_AioSignalHandlerInstalled = FALSE;
@ -213,11 +275,12 @@ int InstallAioSignalHandler()
return 0;
}
#endif
#endif /* HAVE_AIO_H */
HANDLE CreateFileA(LPCSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode, LPSECURITY_ATTRIBUTES lpSecurityAttributes,
DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, HANDLE hTemplateFile)
{
int i;
char* name;
int status;
HANDLE hNamedPipe;
@ -227,6 +290,33 @@ HANDLE CreateFileA(LPCSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode,
if (!lpFileName)
return INVALID_HANDLE_VALUE;
if (pthread_once(&_HandleCreatorsInitialized, _HandleCreatorsInit) != 0)
{
SetLastError(ERROR_DLL_INIT_FAILED);
return INVALID_HANDLE_VALUE;
}
if (_HandleCreators == NULL)
{
SetLastError(ERROR_DLL_INIT_FAILED);
return INVALID_HANDLE_VALUE;
}
for (i=0; _HandleCreators[i] != NULL; i++)
{
HANDLE_CREATOR *creator = (HANDLE_CREATOR*)_HandleCreators[i];
if (creator && creator->IsHandled(lpFileName))
{
return creator->CreateFileA(lpFileName, dwDesiredAccess, dwShareMode, lpSecurityAttributes,
dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
}
}
/* TODO: use of a HANDLE_CREATOR for named pipes as well */
if (!IsNamedPipeFileNameA(lpFileName))
return INVALID_HANDLE_VALUE;
name = GetNamedPipeNameWithoutPrefixA(lpFileName);
if (!name)
@ -821,6 +911,14 @@ BOOL CreateDirectoryW(LPCWSTR lpPathName, LPSECURITY_ATTRIBUTES lpSecurityAttrib
#define NAMED_PIPE_PREFIX_PATH "\\\\.\\pipe\\"
BOOL IsNamedPipeFileNameA(LPCSTR lpName)
{
if (strncmp(lpName, NAMED_PIPE_PREFIX_PATH, sizeof(NAMED_PIPE_PREFIX_PATH) - 1) != 0)
return FALSE;
return TRUE;
}
char* GetNamedPipeNameWithoutPrefixA(LPCSTR lpName)
{
char* lpFileName;
@ -828,7 +926,7 @@ char* GetNamedPipeNameWithoutPrefixA(LPCSTR lpName)
if (!lpName)
return NULL;
if (strncmp(lpName, NAMED_PIPE_PREFIX_PATH, sizeof(NAMED_PIPE_PREFIX_PATH) - 1) != 0)
if (!IsNamedPipeFileNameA(lpName))
return NULL;
lpFileName = _strdup(&lpName[strlen(NAMED_PIPE_PREFIX_PATH)]);

View File

@ -25,9 +25,13 @@
#ifndef _WIN32
#include <assert.h>
#include <pthread.h>
#include "../synch/synch.h"
#include "../thread/thread.h"
#include "../pipe/pipe.h"
#include "../comm/comm.h"
#include "../security/security.h"
#ifdef HAVE_UNISTD_H
@ -38,14 +42,83 @@
#include "../handle/handle.h"
/* _HandleCreators is a NULL-terminated array with a maximun of HANDLE_CREATOR_MAX HANDLE_CREATOR */
#define HANDLE_CLOSE_CB_MAX 128
static HANDLE_CLOSE_CB **_HandleCloseCbs = NULL;
static pthread_once_t _HandleCloseCbsInitialized = PTHREAD_ONCE_INIT;
static void _HandleCloseCbsInit()
{
/* NB: error management to be done outside of this function */
assert(_HandleCloseCbs == NULL);
_HandleCloseCbs = (HANDLE_CLOSE_CB**)calloc(HANDLE_CLOSE_CB_MAX+1, sizeof(HANDLE_CLOSE_CB*));
assert(_HandleCloseCbs != NULL);
}
/**
* Returns TRUE on success, FALSE otherwise.
*/
BOOL RegisterHandleCloseCb(HANDLE_CLOSE_CB *pHandleCloseCb)
{
int i;
if (pthread_once(&_HandleCloseCbsInitialized, _HandleCloseCbsInit) != 0)
{
return FALSE;
}
if (_HandleCloseCbs == NULL)
{
return FALSE;
}
for (i=0; i<HANDLE_CLOSE_CB_MAX; i++)
{
if (_HandleCloseCbs[i] == NULL)
{
_HandleCloseCbs[i] = pHandleCloseCb;
return TRUE;
}
}
return FALSE;
}
BOOL CloseHandle(HANDLE hObject)
{
int i;
ULONG Type;
PVOID Object;
if (!winpr_Handle_GetInfo(hObject, &Type, &Object))
return FALSE;
if (pthread_once(&_HandleCloseCbsInitialized, _HandleCloseCbsInit) != 0)
{
return FALSE;
}
if (_HandleCloseCbs == NULL)
{
return FALSE;
}
for (i=0; _HandleCloseCbs[i] != NULL; i++)
{
HANDLE_CLOSE_CB *close_cb = (HANDLE_CLOSE_CB*)_HandleCloseCbs[i];
if (close_cb && close_cb->IsHandled(hObject))
{
return close_cb->CloseHandle(hObject);
}
}
if (Type == HANDLE_TYPE_THREAD)
{
WINPR_THREAD* thread;
@ -199,6 +272,8 @@ BOOL CloseHandle(HANDLE hObject)
free(token->Domain);
free(token);
return TRUE;
}
return FALSE;

View File

@ -36,6 +36,7 @@
#define HANDLE_TYPE_FILE 10
#define HANDLE_TYPE_TIMER_QUEUE 11
#define HANDLE_TYPE_TIMER_QUEUE_TIMER 12
#define HANDLE_TYPE_COMM 13
#define WINPR_HANDLE_DEF() \
ULONG Type
@ -64,4 +65,16 @@ static inline BOOL winpr_Handle_GetInfo(HANDLE handle, ULONG* pType, PVOID* pObj
return TRUE;
}
typedef BOOL (*pcIsHandled)(HANDLE handle);
typedef BOOL (*pcCloseHandle)(HANDLE handle);
typedef struct _HANDLE_CLOSE_CB
{
pcIsHandled IsHandled;
pcCloseHandle CloseHandle;
} HANDLE_CLOSE_CB;
BOOL RegisterHandleCloseCb(HANDLE_CLOSE_CB *pHandleClose);
#endif /* WINPR_HANDLE_PRIVATE_H */