Merge branch 'ports'
Conflicts: channels/serial/client/serial_tty.c
This commit is contained in:
commit
9fc225ac5d
3
.gitignore
vendored
3
.gitignore
vendored
@ -122,3 +122,6 @@ default.log
|
||||
*.txt.user
|
||||
|
||||
*.autosave
|
||||
|
||||
# etags
|
||||
TAGS
|
||||
|
@ -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)
|
||||
|
@ -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
|
@ -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
@ -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 */
|
@ -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;
|
||||
|
@ -466,6 +466,7 @@ struct _RDPDR_SERIAL
|
||||
UINT32 Type;
|
||||
char* Name;
|
||||
char* Path;
|
||||
char* Driver;
|
||||
};
|
||||
typedef struct _RDPDR_SERIAL RDPDR_SERIAL;
|
||||
|
||||
|
@ -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
589
winpr/include/winpr/comm.h
Normal 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 */
|
||||
|
@ -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 */
|
||||
|
||||
|
@ -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
|
||||
|
61
winpr/libwinpr/comm/CMakeLists.txt
Normal file
61
winpr/libwinpr/comm/CMakeLists.txt
Normal 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()
|
||||
|
9
winpr/libwinpr/comm/ModuleOptions.cmake
Normal file
9
winpr/libwinpr/comm/ModuleOptions.cmake
Normal 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
1312
winpr/libwinpr/comm/comm.c
Normal file
File diff suppressed because it is too large
Load Diff
94
winpr/libwinpr/comm/comm.h
Normal file
94
winpr/libwinpr/comm/comm.h
Normal 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 */
|
551
winpr/libwinpr/comm/comm_io.c
Normal file
551
winpr/libwinpr/comm/comm_io.c
Normal 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, ¤tTermios) < 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, ¤tTermios) < 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 */
|
739
winpr/libwinpr/comm/comm_ioctl.c
Normal file
739
winpr/libwinpr/comm/comm_ioctl.c
Normal 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 2—Serial 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(¤tState, sizeof(struct termios));
|
||||
if ((result = tcgetattr(fd, ¤tState)) < 0)
|
||||
{
|
||||
DEBUG_WARN("tcgetattr failure, errno: %d", errno);
|
||||
return result;
|
||||
}
|
||||
|
||||
if (memcmp(¤tState, &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(¤tState, sizeof(struct termios));
|
||||
if ((result = tcgetattr(fd, ¤tState)) < 0)
|
||||
{
|
||||
DEBUG_WARN("tcgetattr failure, errno: %d", errno);
|
||||
return result;
|
||||
}
|
||||
|
||||
if (memcmp(¤tState, 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 */
|
244
winpr/libwinpr/comm/comm_ioctl.h
Normal file
244
winpr/libwinpr/comm/comm_ioctl.h
Normal 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_ */
|
207
winpr/libwinpr/comm/comm_sercx2_sys.c
Normal file
207
winpr/libwinpr/comm/comm_sercx2_sys.c
Normal 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 */
|
39
winpr/libwinpr/comm/comm_sercx2_sys.h
Normal file
39
winpr/libwinpr/comm/comm_sercx2_sys.h
Normal 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 */
|
455
winpr/libwinpr/comm/comm_sercx_sys.c
Normal file
455
winpr/libwinpr/comm/comm_sercx_sys.c
Normal 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(¤tState, sizeof(struct termios));
|
||||
if (tcgetattr(pComm->fd, ¤tState) < 0)
|
||||
{
|
||||
SetLastError(ERROR_IO_DEVICE);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
currentSpeed = cfgetispeed(¤tState);
|
||||
|
||||
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 */
|
41
winpr/libwinpr/comm/comm_sercx_sys.h
Normal file
41
winpr/libwinpr/comm/comm_sercx_sys.h
Normal 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 */
|
1549
winpr/libwinpr/comm/comm_serial_sys.c
Normal file
1549
winpr/libwinpr/comm/comm_serial_sys.c
Normal file
File diff suppressed because it is too large
Load Diff
40
winpr/libwinpr/comm/comm_serial_sys.h
Normal file
40
winpr/libwinpr/comm/comm_serial_sys.h
Normal 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 */
|
3
winpr/libwinpr/comm/module.def
Normal file
3
winpr/libwinpr/comm/module.def
Normal file
@ -0,0 +1,3 @@
|
||||
LIBRARY "libwinpr-comm"
|
||||
EXPORTS
|
||||
|
2
winpr/libwinpr/comm/test/.gitignore
vendored
Normal file
2
winpr/libwinpr/comm/test/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
TestComm
|
||||
TestComm.c
|
39
winpr/libwinpr/comm/test/CMakeLists.txt
Normal file
39
winpr/libwinpr/comm/test/CMakeLists.txt
Normal 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")
|
||||
|
141
winpr/libwinpr/comm/test/TestCommConfig.c
Normal file
141
winpr/libwinpr/comm/test/TestCommConfig.c
Normal 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;
|
||||
}
|
115
winpr/libwinpr/comm/test/TestCommDevice.c
Normal file
115
winpr/libwinpr/comm/test/TestCommDevice.c
Normal 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;
|
||||
}
|
69
winpr/libwinpr/comm/test/TestCommMonitor.c
Normal file
69
winpr/libwinpr/comm/test/TestCommMonitor.c
Normal 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;
|
||||
}
|
||||
|
121
winpr/libwinpr/comm/test/TestControlSettings.c
Normal file
121
winpr/libwinpr/comm/test/TestControlSettings.c
Normal 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;
|
||||
}
|
129
winpr/libwinpr/comm/test/TestGetCommState.c
Normal file
129
winpr/libwinpr/comm/test/TestGetCommState.c
Normal 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;
|
||||
}
|
86
winpr/libwinpr/comm/test/TestHandflow.c
Normal file
86
winpr/libwinpr/comm/test/TestHandflow.c
Normal 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;
|
||||
}
|
173
winpr/libwinpr/comm/test/TestSerialChars.c
Normal file
173
winpr/libwinpr/comm/test/TestSerialChars.c
Normal 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(¤tTermios, sizeof(struct termios));
|
||||
if (tcgetattr(((WINPR_COMM*)hComm)->fd, ¤tTermios) < 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;
|
||||
}
|
390
winpr/libwinpr/comm/test/TestSetCommState.c
Normal file
390
winpr/libwinpr/comm/test/TestSetCommState.c
Normal 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;
|
||||
}
|
126
winpr/libwinpr/comm/test/TestTimeouts.c
Normal file
126
winpr/libwinpr/comm/test/TestTimeouts.c
Normal file
@ -0,0 +1,126 @@
|
||||
/**
|
||||
* WinPR: Windows Portable Runtime
|
||||
* Serial Communication API
|
||||
*
|
||||
* Copyright 2014 Hewlett-Packard Development Company, L.P.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <termios.h>
|
||||
|
||||
#include <winpr/comm.h>
|
||||
#include <winpr/crt.h>
|
||||
|
||||
#include "../comm.h"
|
||||
|
||||
static BOOL test_generic(HANDLE hComm)
|
||||
{
|
||||
COMMTIMEOUTS timeouts, timeouts2;
|
||||
|
||||
timeouts.ReadIntervalTimeout = 1;
|
||||
timeouts.ReadTotalTimeoutMultiplier = 2;
|
||||
timeouts.ReadTotalTimeoutConstant = 3;
|
||||
timeouts.WriteTotalTimeoutMultiplier = 4;
|
||||
timeouts.WriteTotalTimeoutConstant = 5;
|
||||
|
||||
if (!SetCommTimeouts(hComm, &timeouts))
|
||||
{
|
||||
fprintf(stderr, "SetCommTimeouts failure, GetLastError: 0x%0.8x\n", GetLastError());
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
ZeroMemory(&timeouts2, sizeof(COMMTIMEOUTS));
|
||||
if (!GetCommTimeouts(hComm, &timeouts2))
|
||||
{
|
||||
fprintf(stderr, "GetCommTimeouts failure, GetLastError: 0x%0.8x\n", GetLastError());
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (memcmp(&timeouts, &timeouts2, sizeof(COMMTIMEOUTS)) != 0)
|
||||
{
|
||||
fprintf(stderr, "TestTimeouts failure, didn't get back the same timeouts.\n");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* not supported combination */
|
||||
timeouts.ReadIntervalTimeout = MAXULONG;
|
||||
timeouts.ReadTotalTimeoutConstant = MAXULONG;
|
||||
if (SetCommTimeouts(hComm, &timeouts))
|
||||
{
|
||||
fprintf(stderr, "SetCommTimeouts succeeded with ReadIntervalTimeout and ReadTotalTimeoutConstant set to MAXULONG. GetLastError: 0x%0.8x\n", GetLastError());
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (GetLastError() != ERROR_INVALID_PARAMETER)
|
||||
{
|
||||
fprintf(stderr, "SetCommTimeouts failure, expected GetLastError to return ERROR_INVALID_PARAMETER and got: 0x%0.8x\n", GetLastError());
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
int TestTimeouts(int argc, char* argv[])
|
||||
{
|
||||
BOOL result;
|
||||
HANDLE hComm;
|
||||
|
||||
// TMP: FIXME: check if we can proceed with tests on the actual device, skip and warn otherwise but don't fail
|
||||
result = DefineCommDevice("COM1", "/dev/ttyS0");
|
||||
if (!result)
|
||||
{
|
||||
fprintf(stderr, "DefineCommDevice failure: 0x%x\n", GetLastError());
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
hComm = CreateFile("COM1",
|
||||
GENERIC_READ | GENERIC_WRITE,
|
||||
0, NULL, OPEN_EXISTING, 0, NULL);
|
||||
if (hComm == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
fprintf(stderr, "CreateFileA failure: 0x%x\n", GetLastError());
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
_comm_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;
|
||||
}
|
@ -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)]);
|
||||
|
@ -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;
|
||||
|
@ -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 */
|
||||
|
Loading…
Reference in New Issue
Block a user