Merge pull request #10510 from akallabeth/serial-cleanup

Serial cleanup
This commit is contained in:
akallabeth 2024-08-26 13:47:10 +02:00 committed by GitHub
commit 00eb86970d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 595 additions and 531 deletions

View File

@ -6,11 +6,19 @@ set(OPTION_SERVER_DEFAULT OFF)
if(WIN32)
set(OPTION_CLIENT_DEFAULT OFF)
set(OPTION_SERVER_DEFAULT OFF)
message("Serial redirection not supported on windows")
endif()
if(APPLE)
set(OPTION_CLIENT_DEFAULT OFF)
set(OPTION_SERVER_DEFAULT OFF)
message("Serial redirection not supported on apple")
endif()
if(ANDROID)
set(OPTION_CLIENT_DEFAULT OFF)
set(OPTION_SERVER_DEFAULT OFF)
message("Serial redirection not supported on android")
endif()
define_channel_options(NAME "parallel" TYPE "device"

View File

@ -53,6 +53,7 @@
#include <freerdp/constants.h>
#include <freerdp/channels/rdpdr.h>
#include <freerdp/channels/log.h>
#include <freerdp/utils/rdpdr_utils.h>
#define TAG CHANNELS_TAG("drive.client")
@ -67,6 +68,7 @@ typedef struct
HANDLE thread;
wMessageQueue* queue;
rdpContext* rdpcontext;
wLog* log;
} PARALLEL_DEVICE;
/**
@ -166,7 +168,7 @@ static UINT parallel_process_irp_read(PARALLEL_DEVICE* parallel, IRP* irp)
if (!buffer)
{
WLog_ERR(TAG, "malloc failed!");
WLog_Print(parallel->log, WLOG_ERROR, "malloc failed!");
return CHANNEL_RC_NO_MEMORY;
}
@ -190,7 +192,7 @@ static UINT parallel_process_irp_read(PARALLEL_DEVICE* parallel, IRP* irp)
{
if (!Stream_EnsureRemainingCapacity(irp->output, Length))
{
WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!");
WLog_Print(parallel->log, WLOG_ERROR, "Stream_EnsureRemainingCapacity failed!");
free(buffer);
return CHANNEL_RC_NO_MEMORY;
}
@ -271,7 +273,7 @@ static UINT parallel_process_irp_device_control(PARALLEL_DEVICE* parallel, IRP*
*/
static UINT parallel_process_irp(PARALLEL_DEVICE* parallel, IRP* irp)
{
UINT error = 0;
UINT error = ERROR_INTERNAL_ERROR;
WINPR_ASSERT(parallel);
WINPR_ASSERT(irp);
@ -279,57 +281,42 @@ static UINT parallel_process_irp(PARALLEL_DEVICE* parallel, IRP* irp)
switch (irp->MajorFunction)
{
case IRP_MJ_CREATE:
if ((error = parallel_process_irp_create(parallel, irp)))
{
WLog_ERR(TAG, "parallel_process_irp_create failed with error %" PRIu32 "!", error);
return error;
}
error = parallel_process_irp_create(parallel, irp);
break;
case IRP_MJ_CLOSE:
if ((error = parallel_process_irp_close(parallel, irp)))
{
WLog_ERR(TAG, "parallel_process_irp_close failed with error %" PRIu32 "!", error);
return error;
}
error = parallel_process_irp_close(parallel, irp);
break;
case IRP_MJ_READ:
if ((error = parallel_process_irp_read(parallel, irp)))
{
WLog_ERR(TAG, "parallel_process_irp_read failed with error %" PRIu32 "!", error);
return error;
}
error = parallel_process_irp_read(parallel, irp);
break;
case IRP_MJ_WRITE:
if ((error = parallel_process_irp_write(parallel, irp)))
{
WLog_ERR(TAG, "parallel_process_irp_write failed with error %" PRIu32 "!", error);
return error;
}
error = parallel_process_irp_write(parallel, irp);
break;
case IRP_MJ_DEVICE_CONTROL:
if ((error = parallel_process_irp_device_control(parallel, irp)))
{
WLog_ERR(TAG, "parallel_process_irp_device_control failed with error %" PRIu32 "!",
error);
return error;
}
error = parallel_process_irp_device_control(parallel, irp);
break;
default:
irp->IoStatus = STATUS_NOT_SUPPORTED;
return irp->Complete(irp);
error = irp->Complete(irp);
break;
}
return CHANNEL_RC_OK;
DWORD level = WLOG_TRACE;
if (error)
level = WLOG_WARN;
WLog_Print(parallel->log, level,
"[%s|0x%08" PRIx32 "] completed with %s [0x%08" PRIx32 "] (IoStatus %s [0x%08" PRIx32
"])",
rdpdr_irp_string(irp->MajorFunction), irp->MajorFunction, WTSErrorToString(error),
error, NtStatus2Tag(irp->IoStatus), irp->IoStatus);
return error;
}
static DWORD WINAPI parallel_thread_func(LPVOID arg)
@ -342,7 +329,7 @@ static DWORD WINAPI parallel_thread_func(LPVOID arg)
{
if (!MessageQueue_Wait(parallel->queue))
{
WLog_ERR(TAG, "MessageQueue_Wait failed!");
WLog_Print(parallel->log, WLOG_ERROR, "MessageQueue_Wait failed!");
error = ERROR_INTERNAL_ERROR;
break;
}
@ -350,7 +337,7 @@ static DWORD WINAPI parallel_thread_func(LPVOID arg)
wMessage message = { 0 };
if (!MessageQueue_Peek(parallel->queue, &message, TRUE))
{
WLog_ERR(TAG, "MessageQueue_Peek failed!");
WLog_Print(parallel->log, WLOG_ERROR, "MessageQueue_Peek failed!");
error = ERROR_INTERNAL_ERROR;
break;
}
@ -360,9 +347,11 @@ static DWORD WINAPI parallel_thread_func(LPVOID arg)
IRP* irp = (IRP*)message.wParam;
if ((error = parallel_process_irp(parallel, irp)))
error = parallel_process_irp(parallel, irp);
if (error)
{
WLog_ERR(TAG, "parallel_process_irp failed with error %" PRIu32 "!", error);
WLog_Print(parallel->log, WLOG_ERROR,
"parallel_process_irp failed with error %" PRIu32 "!", error);
break;
}
}
@ -383,9 +372,11 @@ static UINT parallel_irp_request(DEVICE* device, IRP* irp)
{
PARALLEL_DEVICE* parallel = (PARALLEL_DEVICE*)device;
WINPR_ASSERT(parallel);
if (!MessageQueue_Post(parallel->queue, NULL, 0, (void*)irp, NULL))
{
WLog_ERR(TAG, "MessageQueue_Post failed!");
WLog_Print(parallel->log, WLOG_ERROR, "MessageQueue_Post failed!");
return ERROR_INTERNAL_ERROR;
}
@ -399,21 +390,24 @@ static UINT parallel_irp_request(DEVICE* device, IRP* irp)
*/
static UINT parallel_free(DEVICE* device)
{
UINT error = 0;
PARALLEL_DEVICE* parallel = (PARALLEL_DEVICE*)device;
if (!MessageQueue_PostQuit(parallel->queue, 0) ||
(WaitForSingleObject(parallel->thread, INFINITE) == WAIT_FAILED))
if (parallel)
{
error = GetLastError();
WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "!", error);
return error;
}
if (!MessageQueue_PostQuit(parallel->queue, 0) ||
(WaitForSingleObject(parallel->thread, INFINITE) == WAIT_FAILED))
{
const UINT error = GetLastError();
WLog_Print(parallel->log, WLOG_ERROR,
"WaitForSingleObject failed with error %" PRIu32 "!", error);
return error;
}
CloseHandle(parallel->thread);
Stream_Free(parallel->device.data, TRUE);
MessageQueue_Free(parallel->queue);
free(parallel);
CloseHandle(parallel->thread);
Stream_Free(parallel->device.data, TRUE);
MessageQueue_Free(parallel->queue);
free(parallel);
}
return CHANNEL_RC_OK;
}
@ -439,24 +433,24 @@ static void parallel_message_free(void* obj)
*/
FREERDP_ENTRY_POINT(UINT parallel_DeviceServiceEntry(PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints))
{
char* name = NULL;
char* path = NULL;
size_t length = 0;
RDPDR_PARALLEL* device = NULL;
PARALLEL_DEVICE* parallel = NULL;
UINT error = 0;
WINPR_ASSERT(pEntryPoints);
device = (RDPDR_PARALLEL*)pEntryPoints->device;
RDPDR_PARALLEL* device = (RDPDR_PARALLEL*)pEntryPoints->device;
WINPR_ASSERT(device);
name = device->device.Name;
path = device->Path;
wLog* log = WLog_Get(TAG);
WINPR_ASSERT(log);
char* name = device->device.Name;
char* path = device->Path;
if (!name || (name[0] == '*') || !path)
{
/* TODO: implement auto detection of parallel ports */
WLog_Print(log, WLOG_WARN, "Autodetection not implemented, no ports will be redirected");
return CHANNEL_RC_INITIALIZATION_ERROR;
}
@ -466,21 +460,22 @@ FREERDP_ENTRY_POINT(UINT parallel_DeviceServiceEntry(PDEVICE_SERVICE_ENTRY_POINT
if (!parallel)
{
WLog_ERR(TAG, "calloc failed!");
WLog_Print(log, WLOG_ERROR, "calloc failed!");
return CHANNEL_RC_NO_MEMORY;
}
parallel->log = log;
parallel->device.type = RDPDR_DTYP_PARALLEL;
parallel->device.name = name;
parallel->device.IRPRequest = parallel_irp_request;
parallel->device.Free = parallel_free;
parallel->rdpcontext = pEntryPoints->rdpcontext;
length = strlen(name);
const size_t length = strlen(name);
parallel->device.data = Stream_New(NULL, length + 1);
if (!parallel->device.data)
{
WLog_ERR(TAG, "Stream_New failed!");
WLog_Print(parallel->log, WLOG_ERROR, "Stream_New failed!");
error = CHANNEL_RC_NO_MEMORY;
goto error_out;
}
@ -493,7 +488,7 @@ FREERDP_ENTRY_POINT(UINT parallel_DeviceServiceEntry(PDEVICE_SERVICE_ENTRY_POINT
if (!parallel->queue)
{
WLog_ERR(TAG, "MessageQueue_New failed!");
WLog_Print(parallel->log, WLOG_ERROR, "MessageQueue_New failed!");
error = CHANNEL_RC_NO_MEMORY;
goto error_out;
}
@ -502,16 +497,18 @@ FREERDP_ENTRY_POINT(UINT parallel_DeviceServiceEntry(PDEVICE_SERVICE_ENTRY_POINT
WINPR_ASSERT(obj);
obj->fnObjectFree = parallel_message_free;
if ((error = pEntryPoints->RegisterDevice(pEntryPoints->devman, (DEVICE*)parallel)))
error = pEntryPoints->RegisterDevice(pEntryPoints->devman, &parallel->device);
if (error)
{
WLog_ERR(TAG, "RegisterDevice failed with error %" PRIu32 "!", error);
WLog_Print(parallel->log, WLOG_ERROR, "RegisterDevice failed with error %" PRIu32 "!",
error);
goto error_out;
}
if (!(parallel->thread =
CreateThread(NULL, 0, parallel_thread_func, (void*)parallel, 0, NULL)))
parallel->thread = CreateThread(NULL, 0, parallel_thread_func, parallel, 0, NULL);
if (!parallel->thread)
{
WLog_ERR(TAG, "CreateThread failed!");
WLog_Print(parallel->log, WLOG_ERROR, "CreateThread failed!");
error = ERROR_INTERNAL_ERROR;
goto error_out;
}
@ -519,8 +516,7 @@ FREERDP_ENTRY_POINT(UINT parallel_DeviceServiceEntry(PDEVICE_SERVICE_ENTRY_POINT
return CHANNEL_RC_OK;
error_out:
MessageQueue_Free(parallel->queue);
Stream_Free(parallel->device.data, TRUE);
free(parallel);
if (parallel)
parallel_free(&parallel->device);
return error;
}

View File

@ -6,11 +6,19 @@ set(OPTION_SERVER_DEFAULT OFF)
if(WIN32)
set(OPTION_CLIENT_DEFAULT OFF)
set(OPTION_SERVER_DEFAULT OFF)
message("Serial redirection not supported on windows")
endif()
if(ANDROID)
set(OPTION_CLIENT_DEFAULT OFF)
set(OPTION_SERVER_DEFAULT OFF)
message("Serial redirection not supported on android")
endif()
if (APPLE)
set(OPTION_CLIENT_DEFAULT OFF)
set(OPTION_SERVER_DEFAULT OFF)
message("Serial redirection not supported on apple")
endif()
define_channel_options(NAME "serial" TYPE "device"

View File

@ -24,4 +24,8 @@ set(${MODULE_PREFIX}_SRCS
set(${MODULE_PREFIX}_LIBS
winpr freerdp
)
add_channel_client_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} TRUE "DeviceServiceEntry")
# Serial implementation is currently linux only. BSD* might also work but untested
if (UNIX AND NOT APPLE AND NOT ANDROID)
add_channel_client_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} TRUE "DeviceServiceEntry")
endif()

View File

@ -35,6 +35,7 @@
#include <winpr/synch.h>
#include <winpr/thread.h>
#include <winpr/wlog.h>
#include <winpr/assert.h>
#include <freerdp/freerdp.h>
#include <freerdp/channels/rdpdr.h>
@ -43,11 +44,6 @@
#define TAG CHANNELS_TAG("serial.client")
/* TODO: all #ifdef __linux__ could be removed once only some generic
* functions will be used. Replace CommReadFile by ReadFile,
* CommWriteFile by WriteFile etc.. */
#if defined __linux__ && !defined ANDROID
#define MAX_IRP_THREADS 5
typedef struct
@ -63,7 +59,6 @@ typedef struct
/* one thread per pending IRP and indexed according their CompletionId */
wListDictionary* IrpThreads;
UINT32 IrpThreadToBeTerminatedCount;
CRITICAL_SECTION TerminatingIrpThreadsLock;
rdpContext* rdpcontext;
} SERIAL_DEVICE;
@ -74,7 +69,8 @@ typedef struct
IRP* irp;
} IRP_THREAD_DATA;
static UINT32 _GetLastErrorToIoStatus(SERIAL_DEVICE* serial)
static void close_terminated_irp_thread_handles(SERIAL_DEVICE* serial, BOOL forceClose);
static UINT32 GetLastErrorToIoStatus(SERIAL_DEVICE* serial)
{
/* http://msdn.microsoft.com/en-us/library/ff547466%28v=vs.85%29.aspx#generic_status_values_for_serial_device_control_requests
*/
@ -126,7 +122,10 @@ static UINT serial_process_irp_create(SERIAL_DEVICE* serial, IRP* irp)
DWORD CreateDisposition = 0;
UINT32 PathLength = 0;
if (!Stream_CheckAndLogRequiredLength(TAG, irp->input, 32))
WINPR_ASSERT(serial);
WINPR_ASSERT(irp);
if (!Stream_CheckAndLogRequiredLengthWLog(serial->log, irp->input, 32))
return ERROR_INVALID_DATA;
Stream_Read_UINT32(irp->input, DesiredAccess); /* DesiredAccess (4 bytes) */
@ -202,11 +201,16 @@ error_handle:
static UINT serial_process_irp_close(SERIAL_DEVICE* serial, IRP* irp)
{
if (!Stream_CheckAndLogRequiredLength(TAG, irp->input, 32))
WINPR_ASSERT(serial);
WINPR_ASSERT(irp);
if (!Stream_CheckAndLogRequiredLengthWLog(serial->log, irp->input, 32))
return ERROR_INVALID_DATA;
Stream_Seek(irp->input, 32); /* Padding (32 bytes) */
close_terminated_irp_thread_handles(serial, TRUE);
if (!CloseHandle(serial->hComm))
{
WLog_Print(serial->log, WLOG_WARN, "CloseHandle failure: %s (%" PRIu32 ") closed.",
@ -217,9 +221,9 @@ static UINT serial_process_irp_close(SERIAL_DEVICE* serial, IRP* irp)
WLog_Print(serial->log, WLOG_DEBUG, "%s (DeviceId: %" PRIu32 ", FileId: %" PRIu32 ") closed.",
serial->device.name, irp->device->id, irp->FileId);
serial->hComm = NULL;
irp->IoStatus = STATUS_SUCCESS;
error_handle:
serial->hComm = NULL;
Stream_Zero(irp->output, 5); /* Padding (5 bytes) */
return CHANNEL_RC_OK;
}
@ -236,7 +240,10 @@ static UINT serial_process_irp_read(SERIAL_DEVICE* serial, IRP* irp)
BYTE* buffer = NULL;
DWORD nbRead = 0;
if (!Stream_CheckAndLogRequiredLength(TAG, irp->input, 32))
WINPR_ASSERT(serial);
WINPR_ASSERT(irp);
if (!Stream_CheckAndLogRequiredLengthWLog(serial->log, irp->input, 32))
return ERROR_INVALID_DATA;
Stream_Read_UINT32(irp->input, Length); /* Length (4 bytes) */
@ -268,7 +275,7 @@ static UINT serial_process_irp_read(SERIAL_DEVICE* serial, IRP* irp)
WLog_Print(serial->log, WLOG_DEBUG,
"read failure to %s, nbRead=%" PRIu32 ", last-error: 0x%08" PRIX32 "",
serial->device.name, nbRead, GetLastError());
irp->IoStatus = _GetLastErrorToIoStatus(serial);
irp->IoStatus = GetLastErrorToIoStatus(serial);
}
WLog_Print(serial->log, WLOG_DEBUG, "%" PRIu32 " bytes read from %s", nbRead,
@ -280,7 +287,7 @@ error_handle:
{
if (!Stream_EnsureRemainingCapacity(irp->output, nbRead))
{
WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!");
WLog_Print(serial->log, WLOG_ERROR, "Stream_EnsureRemainingCapacity failed!");
free(buffer);
return CHANNEL_RC_NO_MEMORY;
}
@ -298,7 +305,10 @@ static UINT serial_process_irp_write(SERIAL_DEVICE* serial, IRP* irp)
UINT64 Offset = 0;
DWORD nbWritten = 0;
if (!Stream_CheckAndLogRequiredLength(TAG, irp->input, 32))
WINPR_ASSERT(serial);
WINPR_ASSERT(irp);
if (!Stream_CheckAndLogRequiredLengthWLog(serial->log, irp->input, 32))
return ERROR_INVALID_DATA;
Stream_Read_UINT32(irp->input, Length); /* Length (4 bytes) */
@ -330,7 +340,7 @@ static UINT serial_process_irp_write(SERIAL_DEVICE* serial, IRP* irp)
WLog_Print(serial->log, WLOG_DEBUG,
"write failure to %s, nbWritten=%" PRIu32 ", last-error: 0x%08" PRIX32 "",
serial->device.name, nbWritten, GetLastError());
irp->IoStatus = _GetLastErrorToIoStatus(serial);
irp->IoStatus = GetLastErrorToIoStatus(serial);
}
WLog_Print(serial->log, WLOG_DEBUG, "%" PRIu32 " bytes written to %s", nbWritten,
@ -354,7 +364,10 @@ static UINT serial_process_irp_device_control(SERIAL_DEVICE* serial, IRP* irp)
BYTE* OutputBuffer = NULL;
DWORD BytesReturned = 0;
if (!Stream_CheckAndLogRequiredLength(TAG, irp->input, 32))
WINPR_ASSERT(serial);
WINPR_ASSERT(irp);
if (!Stream_CheckAndLogRequiredLengthWLog(serial->log, irp->input, 32))
return ERROR_INVALID_DATA;
Stream_Read_UINT32(irp->input, OutputBufferLength); /* OutputBufferLength (4 bytes) */
@ -362,7 +375,7 @@ static UINT serial_process_irp_device_control(SERIAL_DEVICE* serial, IRP* irp)
Stream_Read_UINT32(irp->input, IoControlCode); /* IoControlCode (4 bytes) */
Stream_Seek(irp->input, 20); /* Padding (20 bytes) */
if (!Stream_CheckAndLogRequiredLength(TAG, irp->input, InputBufferLength))
if (!Stream_CheckAndLogRequiredLengthWLog(serial->log, irp->input, InputBufferLength))
return ERROR_INVALID_DATA;
OutputBuffer = (BYTE*)calloc(OutputBufferLength, sizeof(BYTE));
@ -401,7 +414,7 @@ static UINT serial_process_irp_device_control(SERIAL_DEVICE* serial, IRP* irp)
"CommDeviceIoControl failure: IoControlCode=[0x%" PRIX32
"] %s, last-error: 0x%08" PRIX32 "",
IoControlCode, _comm_serial_ioctl_name(IoControlCode), GetLastError());
irp->IoStatus = _GetLastErrorToIoStatus(serial);
irp->IoStatus = GetLastErrorToIoStatus(serial);
}
error_handle:
@ -415,7 +428,7 @@ error_handle:
{
if (!Stream_EnsureRemainingCapacity(irp->output, BytesReturned))
{
WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!");
WLog_Print(serial->log, WLOG_ERROR, "Stream_EnsureRemainingCapacity failed!");
free(InputBuffer);
free(OutputBuffer);
return CHANNEL_RC_NO_MEMORY;
@ -445,7 +458,11 @@ error_handle:
static UINT serial_process_irp(SERIAL_DEVICE* serial, IRP* irp)
{
UINT error = CHANNEL_RC_OK;
WLog_Print(serial->log, WLOG_DEBUG, "IRP MajorFunction: s, MinorFunction: 0x%08" PRIX32 "\n",
WINPR_ASSERT(serial);
WINPR_ASSERT(irp);
WLog_Print(serial->log, WLOG_DEBUG, "IRP MajorFunction: %s, MinorFunction: 0x%08" PRIX32 "\n",
rdpdr_irp_string(irp->MajorFunction), irp->MinorFunction);
switch (irp->MajorFunction)
@ -459,9 +476,7 @@ static UINT serial_process_irp(SERIAL_DEVICE* serial, IRP* irp)
break;
case IRP_MJ_READ:
if ((error = serial_process_irp_read(serial, irp)))
WLog_ERR(TAG, "serial_process_irp_read failed with error %" PRIu32 "!", error);
error = serial_process_irp_read(serial, irp);
break;
case IRP_MJ_WRITE:
@ -469,10 +484,7 @@ static UINT serial_process_irp(SERIAL_DEVICE* serial, IRP* irp)
break;
case IRP_MJ_DEVICE_CONTROL:
if ((error = serial_process_irp_device_control(serial, irp)))
WLog_ERR(TAG, "serial_process_irp_device_control failed with error %" PRIu32 "!",
error);
error = serial_process_irp_device_control(serial, irp);
break;
default:
@ -480,6 +492,16 @@ static UINT serial_process_irp(SERIAL_DEVICE* serial, IRP* irp)
break;
}
DWORD level = WLOG_TRACE;
if (error)
level = WLOG_WARN;
WLog_Print(serial->log, level,
"[%s|0x%08" PRIx32 "] completed with %s [0x%08" PRIx32 "] (IoStatus %s [0x%08" PRIx32
"])",
rdpdr_irp_string(irp->MajorFunction), irp->MajorFunction, WTSErrorToString(error),
error, NtStatus2Tag(irp->IoStatus), irp->IoStatus);
return error;
}
@ -488,15 +510,19 @@ static DWORD WINAPI irp_thread_func(LPVOID arg)
IRP_THREAD_DATA* data = (IRP_THREAD_DATA*)arg;
UINT error = 0;
WINPR_ASSERT(data);
WINPR_ASSERT(data->serial);
WINPR_ASSERT(data->irp);
/* blocks until the end of the request */
if ((error = serial_process_irp(data->serial, data->irp)))
{
WLog_ERR(TAG, "serial_process_irp failed with error %" PRIu32 "", error);
WLog_Print(data->serial->log, WLOG_ERROR,
"serial_process_irp failed with error %" PRIu32 "", error);
goto error_out;
}
EnterCriticalSection(&data->serial->TerminatingIrpThreadsLock);
data->serial->IrpThreadToBeTerminatedCount++;
error = data->irp->Complete(data->irp);
LeaveCriticalSection(&data->serial->TerminatingIrpThreadsLock);
error_out:
@ -512,74 +538,71 @@ error_out:
return error;
}
static void close_unterminated_irp_thread(wListDictionary* list, wLog* log, ULONG_PTR id)
{
WINPR_ASSERT(list);
HANDLE self = _GetCurrentThread();
HANDLE cirpThread = ListDictionary_GetItemValue(list, (void*)id);
if (self == cirpThread)
WLog_Print(log, WLOG_DEBUG, "Skipping termination of own IRP thread");
else
ListDictionary_Remove(list, (void*)id);
}
static void close_terminated_irp_thread(wListDictionary* list, wLog* log, ULONG_PTR id)
{
WINPR_ASSERT(list);
HANDLE cirpThread = ListDictionary_GetItemValue(list, (void*)id);
/* FIXME: not quite sure a zero timeout is a good thing to check whether a thread is
* stil alived or not */
const DWORD waitResult = WaitForSingleObject(cirpThread, 0);
if (waitResult == WAIT_OBJECT_0)
ListDictionary_Remove(list, (void*)id);
else if (waitResult != WAIT_TIMEOUT)
{
/* unexpected thread state */
WLog_Print(log, WLOG_WARN, "WaitForSingleObject, got an unexpected result=0x%" PRIX32 "\n",
waitResult);
}
}
void close_terminated_irp_thread_handles(SERIAL_DEVICE* serial, BOOL forceClose)
{
WINPR_ASSERT(serial);
EnterCriticalSection(&serial->TerminatingIrpThreadsLock);
ULONG_PTR* ids = NULL;
const size_t nbIds = ListDictionary_GetKeys(serial->IrpThreads, &ids);
for (size_t i = 0; i < nbIds; i++)
{
ULONG_PTR id = ids[i];
if (forceClose)
close_unterminated_irp_thread(serial->IrpThreads, serial->log, id);
else
close_terminated_irp_thread(serial->IrpThreads, serial->log, id);
}
free(ids);
LeaveCriticalSection(&serial->TerminatingIrpThreadsLock);
}
static void create_irp_thread(SERIAL_DEVICE* serial, IRP* irp)
{
IRP_THREAD_DATA* data = NULL;
HANDLE irpThread = NULL;
HANDLE previousIrpThread = NULL;
uintptr_t key = 0;
/* 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 cirpThread = NULL;
ULONG_PTR* ids = NULL;
const size_t nbIds = ListDictionary_GetKeys(serial->IrpThreads, &ids);
WINPR_ASSERT(serial);
WINPR_ASSERT(irp);
for (size_t i = 0; i < nbIds; i++)
{
/* Checking if ids[i] is terminating or pending */
DWORD waitResult = 0;
ULONG_PTR id = ids[i];
cirpThread = 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(cirpThread, 0);
close_terminated_irp_thread_handles(serial, FALSE);
if (waitResult == WAIT_OBJECT_0)
{
/* terminating thread */
/* WLog_Print(serial->log, WLOG_DEBUG, "IRP thread with CompletionId=%"PRIuz"
* naturally died", id); */
CloseHandle(cirpThread);
ListDictionary_Remove(serial->IrpThreads, (void*)id);
serial->IrpThreadToBeTerminatedCount--;
}
else if (waitResult != WAIT_TIMEOUT)
{
/* unexpected thread state */
WLog_Print(serial->log, WLOG_WARN,
"WaitForSingleObject, got an unexpected result=0x%" PRIX32 "\n",
waitResult);
WINPR_ASSERT(FALSE);
}
/* pending thread (but not yet terminating thread) if waitResult == WAIT_TIMEOUT */
}
if (serial->IrpThreadToBeTerminatedCount > 0)
{
WLog_Print(serial->log, WLOG_DEBUG, "%" PRIu32 " IRP thread(s) not yet terminated",
serial->IrpThreadToBeTerminatedCount);
Sleep(1); /* 1 ms */
}
free(ids);
}
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
@ -642,7 +665,7 @@ static void create_irp_thread(SERIAL_DEVICE* serial, IRP* irp)
data->serial = serial;
data->irp = irp;
/* data freed by irp_thread_func */
irpThread = CreateThread(NULL, 0, irp_thread_func, (void*)data, 0, NULL);
irpThread = CreateThread(NULL, 0, irp_thread_func, (void*)data, CREATE_SUSPENDED, NULL);
if (irpThread == INVALID_HANDLE_VALUE)
{
@ -654,45 +677,21 @@ static void create_irp_thread(SERIAL_DEVICE* serial, IRP* irp)
if (!ListDictionary_Add(serial->IrpThreads, (void*)key, irpThread))
{
WLog_ERR(TAG, "ListDictionary_Add failed!");
WLog_Print(serial->log, WLOG_ERROR, "ListDictionary_Add failed!");
goto error_handle;
}
ResumeThread(irpThread);
return;
error_handle:
if (irpThread)
CloseHandle(irpThread);
irp->IoStatus = STATUS_NO_MEMORY;
irp->Complete(irp);
free(data);
}
static void terminate_pending_irp_threads(SERIAL_DEVICE* serial)
{
WINPR_ASSERT(serial);
ULONG_PTR* ids = NULL;
const size_t nbIds = ListDictionary_GetKeys(serial->IrpThreads, &ids);
WLog_Print(serial->log, WLOG_DEBUG, "Terminating %" PRIuz " IRP thread(s)", nbIds);
for (size_t i = 0; i < nbIds; i++)
{
HANDLE irpThread = NULL;
ULONG_PTR id = ids[i];
irpThread = ListDictionary_GetItemValue(serial->IrpThreads, (void*)id);
TerminateThread(irpThread, 0);
if (WaitForSingleObject(irpThread, INFINITE) == WAIT_FAILED)
{
WLog_ERR(TAG, "WaitForSingleObject failed!");
continue;
}
CloseHandle(irpThread);
WLog_Print(serial->log, WLOG_DEBUG, "IRP thread terminated, CompletionId %p", (void*)id);
}
ListDictionary_Clear(serial->IrpThreads);
free(ids);
}
static DWORD WINAPI serial_thread_func(LPVOID arg)
{
IRP* irp = NULL;
@ -700,27 +699,26 @@ static DWORD WINAPI serial_thread_func(LPVOID arg)
SERIAL_DEVICE* serial = (SERIAL_DEVICE*)arg;
UINT error = CHANNEL_RC_OK;
WINPR_ASSERT(serial);
while (1)
{
if (!MessageQueue_Wait(serial->MainIrpQueue))
{
WLog_ERR(TAG, "MessageQueue_Wait failed!");
WLog_Print(serial->log, WLOG_ERROR, "MessageQueue_Wait failed!");
error = ERROR_INTERNAL_ERROR;
break;
}
if (!MessageQueue_Peek(serial->MainIrpQueue, &message, TRUE))
{
WLog_ERR(TAG, "MessageQueue_Peek failed!");
WLog_Print(serial->log, WLOG_ERROR, "MessageQueue_Peek failed!");
error = ERROR_INTERNAL_ERROR;
break;
}
if (message.id == WMQ_QUIT)
{
terminate_pending_irp_threads(serial);
break;
}
irp = (IRP*)message.wParam;
@ -728,6 +726,7 @@ static DWORD WINAPI serial_thread_func(LPVOID arg)
create_irp_thread(serial, irp);
}
ListDictionary_Clear(serial->IrpThreads);
if (error && serial->rdpcontext)
setChannelError(serial->rdpcontext, error, "serial_thread_func reported an error");
@ -744,6 +743,7 @@ static UINT serial_irp_request(DEVICE* device, IRP* irp)
{
SERIAL_DEVICE* serial = (SERIAL_DEVICE*)device;
WINPR_ASSERT(irp != NULL);
WINPR_ASSERT(serial);
if (irp == NULL)
return CHANNEL_RC_OK;
@ -755,7 +755,7 @@ static UINT serial_irp_request(DEVICE* device, IRP* irp)
if (!MessageQueue_Post(serial->MainIrpQueue, NULL, 0, (void*)irp, NULL))
{
WLog_ERR(TAG, "MessageQueue_Post failed!");
WLog_Print(serial->log, WLOG_ERROR, "MessageQueue_Post failed!");
return ERROR_INTERNAL_ERROR;
}
@ -771,18 +771,24 @@ static UINT serial_free(DEVICE* device)
{
UINT error = 0;
SERIAL_DEVICE* serial = (SERIAL_DEVICE*)device;
if (!serial)
return CHANNEL_RC_OK;
WLog_Print(serial->log, WLOG_DEBUG, "freeing");
MessageQueue_PostQuit(serial->MainIrpQueue, 0);
if (serial->MainIrpQueue)
MessageQueue_PostQuit(serial->MainIrpQueue, 0);
if (WaitForSingleObject(serial->MainThread, INFINITE) == WAIT_FAILED)
if (serial->MainThread)
{
error = GetLastError();
WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "!", error);
return error;
if (WaitForSingleObject(serial->MainThread, INFINITE) == WAIT_FAILED)
{
error = GetLastError();
WLog_Print(serial->log, WLOG_ERROR,
"WaitForSingleObject failed with error %" PRIu32 "!", error);
}
CloseHandle(serial->MainThread);
}
CloseHandle(serial->MainThread);
if (serial->hComm)
CloseHandle(serial->hComm);
@ -795,8 +801,6 @@ static UINT serial_free(DEVICE* device)
return CHANNEL_RC_OK;
}
#endif /* __linux__ */
static void serial_message_free(void* obj)
{
wMessage* msg = obj;
@ -812,6 +816,23 @@ static void serial_message_free(void* obj)
irp->Discard(irp);
}
static void irp_thread_close(void* arg)
{
HANDLE hdl = arg;
if (hdl)
{
HANDLE thz = _GetCurrentThread();
if (thz == hdl)
WLog_WARN(TAG, "closing self, ignoring...");
else
{
TerminateThread(hdl, 0);
WaitForSingleObject(hdl, INFINITE);
CloseHandle(hdl);
}
}
}
/**
* Function description
*
@ -819,46 +840,36 @@ static void serial_message_free(void* obj)
*/
FREERDP_ENTRY_POINT(UINT serial_DeviceServiceEntry(PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints))
{
char* name = NULL;
char* path = NULL;
char* driver = NULL;
RDPDR_SERIAL* device = NULL;
#if defined __linux__ && !defined ANDROID
size_t len = 0;
SERIAL_DEVICE* serial = NULL;
#endif /* __linux__ */
UINT error = CHANNEL_RC_OK;
WINPR_ASSERT(pEntryPoints);
device = (RDPDR_SERIAL*)pEntryPoints->device;
RDPDR_SERIAL* device = (RDPDR_SERIAL*)pEntryPoints->device;
WINPR_ASSERT(device);
name = device->device.Name;
path = device->Path;
driver = device->Driver;
wLog* log = WLog_Get(TAG);
const char* name = device->device.Name;
const char* path = device->Path;
const char* driver = device->Driver;
if (!name || (name[0] == '*'))
{
/* TODO: implement auto detection of serial ports */
WLog_Print(log, WLOG_WARN,
"Serial port autodetection not implemented, nothing will be redirected!");
return CHANNEL_RC_OK;
}
if ((name && name[0]) && (path && path[0]))
{
wLog* log = NULL;
log = WLog_Get("com.freerdp.channel.serial.client");
WLog_Print(log, WLOG_DEBUG, "initializing");
#ifndef __linux__ /* to be removed */
WLog_Print(log, WLOG_WARN, "Serial ports redirection not supported on this platform.");
return CHANNEL_RC_INITIALIZATION_ERROR;
#else /* __linux __ */
WLog_Print(log, WLOG_DEBUG, "Defining %s as %s", name, path);
if (!DefineCommDevice(name /* eg: COM1 */, path /* eg: /dev/ttyS0 */))
{
DWORD status = GetLastError();
WLog_ERR(TAG, "DefineCommDevice failed with %08" PRIx32, status);
WLog_Print(log, WLOG_ERROR, "DefineCommDevice failed with %08" PRIx32, status);
return ERROR_INTERNAL_ERROR;
}
@ -866,7 +877,7 @@ FREERDP_ENTRY_POINT(UINT serial_DeviceServiceEntry(PDEVICE_SERVICE_ENTRY_POINTS
if (!serial)
{
WLog_ERR(TAG, "calloc failed!");
WLog_Print(log, WLOG_ERROR, "calloc failed!");
return CHANNEL_RC_NO_MEMORY;
}
@ -881,7 +892,7 @@ FREERDP_ENTRY_POINT(UINT serial_DeviceServiceEntry(PDEVICE_SERVICE_ENTRY_POINTS
if (!serial->device.data)
{
WLog_ERR(TAG, "calloc failed!");
WLog_Print(serial->log, WLOG_ERROR, "calloc failed!");
error = CHANNEL_RC_NO_MEMORY;
goto error_out;
}
@ -899,10 +910,10 @@ FREERDP_ENTRY_POINT(UINT serial_DeviceServiceEntry(PDEVICE_SERVICE_ENTRY_POINTS
serial->ServerSerialDriverId = SerialDriverSerCx2Sys;
else
{
WINPR_ASSERT(FALSE);
WLog_Print(serial->log, WLOG_DEBUG,
"Unknown server's serial driver: %s. SerCx2 will be used", driver);
serial->ServerSerialDriverId = SerialDriverSerialSys;
WLog_Print(serial->log, WLOG_WARN, "Unknown server's serial driver: %s.", driver);
WLog_Print(serial->log, WLOG_WARN,
"Valid options are: 'Serial' (default), 'SerCx' and 'SerCx2'");
goto error_out;
}
}
else
@ -919,64 +930,67 @@ FREERDP_ENTRY_POINT(UINT serial_DeviceServiceEntry(PDEVICE_SERVICE_ENTRY_POINTS
}
else
{
WLog_Print(serial->log, WLOG_DEBUG, "Unknown flag: %s", device->Permissive);
WINPR_ASSERT(FALSE);
WLog_Print(serial->log, WLOG_WARN, "Unknown flag: %s", device->Permissive);
goto error_out;
}
}
WLog_Print(serial->log, WLOG_DEBUG, "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);
if (!serial->MainIrpQueue)
{
WLog_ERR(TAG, "MessageQueue_New failed!");
WLog_Print(serial->log, WLOG_ERROR, "MessageQueue_New failed!");
error = CHANNEL_RC_NO_MEMORY;
goto error_out;
}
wObject* obj = MessageQueue_Object(serial->MainIrpQueue);
WINPR_ASSERT(obj);
obj->fnObjectFree = serial_message_free;
{
wObject* obj = MessageQueue_Object(serial->MainIrpQueue);
WINPR_ASSERT(obj);
obj->fnObjectFree = serial_message_free;
}
/* IrpThreads content only modified by create_irp_thread() */
serial->IrpThreads = ListDictionary_New(FALSE);
if (!serial->IrpThreads)
{
WLog_ERR(TAG, "ListDictionary_New failed!");
WLog_Print(serial->log, WLOG_ERROR, "ListDictionary_New failed!");
error = CHANNEL_RC_NO_MEMORY;
goto error_out;
}
serial->IrpThreadToBeTerminatedCount = 0;
{
wObject* obj = ListDictionary_ValueObject(serial->IrpThreads);
WINPR_ASSERT(obj);
obj->fnObjectFree = irp_thread_close;
}
InitializeCriticalSection(&serial->TerminatingIrpThreadsLock);
if ((error = pEntryPoints->RegisterDevice(pEntryPoints->devman, (DEVICE*)serial)))
error = pEntryPoints->RegisterDevice(pEntryPoints->devman, &serial->device);
if (error != CHANNEL_RC_OK)
{
WLog_ERR(TAG, "EntryPoints->RegisterDevice failed with error %" PRIu32 "!", error);
WLog_Print(serial->log, WLOG_ERROR,
"EntryPoints->RegisterDevice failed with error %" PRIu32 "!", error);
goto error_out;
}
if (!(serial->MainThread =
CreateThread(NULL, 0, serial_thread_func, (void*)serial, 0, NULL)))
serial->MainThread = CreateThread(NULL, 0, serial_thread_func, serial, 0, NULL);
if (!serial->MainThread)
{
WLog_ERR(TAG, "CreateThread failed!");
WLog_Print(serial->log, WLOG_ERROR, "CreateThread failed!");
error = ERROR_INTERNAL_ERROR;
goto error_out;
}
#endif /* __linux __ */
}
return error;
error_out:
#ifdef __linux__ /* to be removed */
ListDictionary_Free(serial->IrpThreads);
MessageQueue_Free(serial->MainIrpQueue);
Stream_Free(serial->device.data, TRUE);
free(serial);
#endif /* __linux __ */
if (serial)
serial_free(&serial->device);
return error;
}

View File

@ -626,9 +626,13 @@ BOOL freerdp_client_print_command_line_help_ex(int argc, char** argv,
printf("Smartcard Redirection: /smartcard:<device>\n");
printf("Smartcard logon with Kerberos authentication: /smartcard-logon /sec:nla\n");
#if defined(CHANNEL_SERIAL_CLIENT)
printf("Serial Port Redirection: /serial:<name>,<device>,[SerCx2|SerCx|Serial],[permissive]\n");
printf("Serial Port Redirection: /serial:COM1,/dev/ttyS0\n");
#endif
#if defined(CHANNEL_PARALLEL_CLIENT)
printf("Parallel Port Redirection: /parallel:<name>,<device>\n");
#endif
printf("Printer Redirection: /printer:<device>,<driver>,[default]\n");
printf("TCP redirection: /rdp2tcp:/usr/bin/rdp2tcp\n");
printf("\n");
@ -787,6 +791,7 @@ BOOL freerdp_client_add_device_channel(rdpSettings* settings, size_t count, cons
return TRUE;
}
#if defined(CHANNEL_SERIAL_CLIENT)
else if (option_equals(params[0], "serial"))
{
RDPDR_DEVICE* serial = NULL;
@ -812,6 +817,7 @@ BOOL freerdp_client_add_device_channel(rdpSettings* settings, size_t count, cons
return TRUE;
}
#endif
else if (option_equals(params[0], "parallel"))
{
RDPDR_DEVICE* parallel = NULL;
@ -1076,6 +1082,7 @@ static int freerdp_client_command_line_post_filter_int(void* context, COMMAND_LI
if (status)
return fail_at(arg, status);
}
#if defined(CHANNEL_SERIAL_CLIENT)
CommandLineSwitchCase(arg, "serial")
{
size_t count = 0;
@ -1086,6 +1093,8 @@ static int freerdp_client_command_line_post_filter_int(void* context, COMMAND_LI
if (status)
return fail_at(arg, status);
}
#endif
#if defined(CHANNEL_PARALLEL_CLIENT)
CommandLineSwitchCase(arg, "parallel")
{
size_t count = 0;
@ -1096,6 +1105,7 @@ static int freerdp_client_command_line_post_filter_int(void* context, COMMAND_LI
if (status)
return fail_at(arg, status);
}
#endif
CommandLineSwitchCase(arg, "smartcard")
{
size_t count = 0;

View File

@ -355,8 +355,10 @@ static const COMMAND_LINE_ARGUMENT_A global_cmd_args[] = {
{ "old-license", COMMAND_LINE_VALUE_BOOL, NULL, BoolValueFalse, NULL, -1, NULL,
"Use the old license workflow (no CAL and hwId set to 0)" },
{ "p", COMMAND_LINE_VALUE_REQUIRED, "<password>", NULL, NULL, -1, NULL, "Password" },
#if defined(CHANNEL_PARALLEL_CLIENT)
{ "parallel", COMMAND_LINE_VALUE_OPTIONAL, "<name>[,<path>]", NULL, NULL, -1, NULL,
"Redirect parallel device" },
#endif
{ "parent-window", COMMAND_LINE_VALUE_REQUIRED, "<window-id>", NULL, NULL, -1, NULL,
"Parent window id" },
{ "pcb", COMMAND_LINE_VALUE_REQUIRED, "<blob>", NULL, NULL, -1, NULL, "Preconnection Blob" },
@ -415,8 +417,10 @@ static const COMMAND_LINE_ARGUMENT_A global_cmd_args[] = {
{ "sec-tls", COMMAND_LINE_VALUE_BOOL, NULL, BoolValueTrue, NULL, -1, NULL,
"[DEPRECATED, use /sec:tls] TLS protocol security" },
#endif
#if defined(CHANNEL_SERIAL_CLIENT)
{ "serial", COMMAND_LINE_VALUE_OPTIONAL, "<name>[,<path>[,<driver>[,permissive]]]", NULL, NULL,
-1, "tty", "Redirect serial device" },
#endif
{ "server-name", COMMAND_LINE_VALUE_REQUIRED, "<name>", NULL, NULL, -1, NULL,
"User-specified server name to use for validation (TLS, Kerberos)" },
{ "shell", COMMAND_LINE_VALUE_REQUIRED, "<shell>", NULL, NULL, -1, NULL, "Alternate shell" },

View File

@ -18,7 +18,7 @@
set(MODULE_NAME "winpr-comm")
set(MODULE_PREFIX "WINPR_COMM")
if(UNIX AND NOT WIN32 AND NOT APPLE)
if(UNIX AND NOT ANDROID AND NOT APPLE)
set(${MODULE_PREFIX}_SRCS
comm.c
comm.h

View File

@ -22,8 +22,6 @@
#include <winpr/config.h>
#if defined __linux__ && !defined ANDROID
#include <winpr/assert.h>
#include <errno.h>
#include <fcntl.h>
@ -1378,19 +1376,6 @@ BOOL CommCloseHandle(HANDLE handle)
if (!CommIsHandled(handle))
return FALSE;
if (pComm->PendingEvents & SERIAL_EV_WINPR_WAITING)
{
ULONG WaitMask = 0;
DWORD BytesReturned = 0;
/* ensures to gracefully stop the WAIT_ON_MASK's loop */
if (!CommDeviceIoControl(handle, IOCTL_SERIAL_SET_WAIT_MASK, &WaitMask, sizeof(ULONG), NULL,
0, &BytesReturned, NULL))
{
CommLog_Print(WLOG_WARN, "failure to WAIT_ON_MASK's loop!");
}
}
DeleteCriticalSection(&pComm->ReadLock);
DeleteCriticalSection(&pComm->WriteLock);
DeleteCriticalSection(&pComm->EventsLock);
@ -1425,5 +1410,3 @@ int eventfd_write(int fd, eventfd_t value)
return (write(fd, &value, sizeof(value)) == sizeof(value)) ? 0 : -1;
}
#endif
#endif /* __linux__ */

View File

@ -19,8 +19,6 @@
#include <winpr/config.h>
#if defined __linux__ && !defined ANDROID
#include <winpr/assert.h>
#include <errno.h>
#include <termios.h>
@ -545,5 +543,3 @@ return_false:
LeaveCriticalSection(&pComm->WriteLock);
return FALSE;
}
#endif /* __linux__ */

View File

@ -20,8 +20,7 @@
* limitations under the License.
*/
#if defined __linux__ && !defined ANDROID
#include <winpr/assert.h>
#include <winpr/wlog.h>
#include "comm_serial_sys.h"
@ -40,14 +39,19 @@
* request with a STATUS_SUCCESS status code.
*/
static BOOL _set_serial_chars(WINPR_COMM* pComm, const SERIAL_CHARS* pSerialChars)
static BOOL set_serial_chars(WINPR_COMM* pComm, const SERIAL_CHARS* pSerialChars)
{
WINPR_ASSERT(pComm);
WINPR_ASSERT(pSerialChars);
return TRUE;
}
static BOOL _get_serial_chars(WINPR_COMM* pComm, SERIAL_CHARS* pSerialChars)
static BOOL get_serial_chars(WINPR_COMM* pComm, SERIAL_CHARS* pSerialChars)
{
WINPR_ASSERT(pComm);
WINPR_ASSERT(pSerialChars);
ZeroMemory(pSerialChars, sizeof(SERIAL_CHARS));
return TRUE;
}
@ -64,11 +68,15 @@ static const ULONG _SERCX2_SYS_SUPPORTED_EV_MASK =
;
/* use Serial.sys for basis (not SerCx.sys) */
static BOOL _set_wait_mask(WINPR_COMM* pComm, const ULONG* pWaitMask)
static BOOL set_wait_mask(WINPR_COMM* pComm, const ULONG* pWaitMask)
{
ULONG possibleMask = 0;
SERIAL_DRIVER* pSerialSys = SerialSys_s();
WINPR_ASSERT(pComm);
WINPR_ASSERT(pWaitMask);
WINPR_ASSERT(pSerialSys);
possibleMask = *pWaitMask & _SERCX2_SYS_SUPPORTED_EV_MASK;
if (possibleMask != *pWaitMask)
@ -87,10 +95,14 @@ static BOOL _set_wait_mask(WINPR_COMM* pComm, const ULONG* pWaitMask)
return pSerialSys->set_wait_mask(pComm, pWaitMask);
}
static BOOL _purge(WINPR_COMM* pComm, const ULONG* pPurgeMask)
static BOOL purge(WINPR_COMM* pComm, const ULONG* pPurgeMask)
{
SERIAL_DRIVER* pSerialSys = SerialSys_s();
WINPR_ASSERT(pComm);
WINPR_ASSERT(pPurgeMask);
WINPR_ASSERT(pSerialSys);
/* http://msdn.microsoft.com/en-us/library/windows/hardware/ff546655%28v=vs.85%29.aspx */
if ((*pPurgeMask & SERIAL_PURGE_RXCLEAR) && !(*pPurgeMask & SERIAL_PURGE_RXABORT))
@ -113,14 +125,14 @@ static BOOL _purge(WINPR_COMM* pComm, const ULONG* pPurgeMask)
}
/* specific functions only */
static SERIAL_DRIVER _SerCx2Sys = {
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_serial_chars = set_serial_chars,
.get_serial_chars = get_serial_chars,
.set_line_control = NULL,
.get_line_control = NULL,
.set_handflow = NULL,
@ -132,11 +144,11 @@ static SERIAL_DRIVER _SerCx2Sys = {
.set_rts = NULL,
.clear_rts = NULL,
.get_modemstatus = NULL,
.set_wait_mask = _set_wait_mask,
.set_wait_mask = set_wait_mask,
.get_wait_mask = NULL,
.wait_on_mask = NULL,
.set_queue_size = NULL,
.purge = _purge,
.purge = purge,
.get_commstatus = NULL,
.set_break_on = NULL,
.set_break_off = NULL,
@ -150,51 +162,49 @@ static SERIAL_DRIVER _SerCx2Sys = {
SERIAL_DRIVER* SerCx2Sys_s(void)
{
/* _SerCx2Sys completed with inherited functions from SerialSys or SerCxSys */
/* SerCx2Sys completed with inherited functions from SerialSys or SerCxSys */
SERIAL_DRIVER* pSerialSys = SerialSys_s();
SERIAL_DRIVER* pSerCxSys = SerCxSys_s();
if (!pSerialSys || !pSerCxSys)
return NULL;
_SerCx2Sys.set_baud_rate = pSerialSys->set_baud_rate;
_SerCx2Sys.get_baud_rate = pSerialSys->get_baud_rate;
SerCx2Sys.set_baud_rate = pSerialSys->set_baud_rate;
SerCx2Sys.get_baud_rate = pSerialSys->get_baud_rate;
_SerCx2Sys.get_properties = pSerialSys->get_properties;
SerCx2Sys.get_properties = pSerialSys->get_properties;
_SerCx2Sys.set_line_control = pSerCxSys->set_line_control;
_SerCx2Sys.get_line_control = pSerCxSys->get_line_control;
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_handflow = pSerialSys->set_handflow;
SerCx2Sys.get_handflow = pSerialSys->get_handflow;
_SerCx2Sys.set_timeouts = pSerialSys->set_timeouts;
_SerCx2Sys.get_timeouts = pSerialSys->get_timeouts;
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_dtr = pSerialSys->set_dtr;
SerCx2Sys.clear_dtr = pSerialSys->clear_dtr;
_SerCx2Sys.set_rts = pSerialSys->set_rts;
_SerCx2Sys.clear_rts = pSerialSys->clear_rts;
SerCx2Sys.set_rts = pSerialSys->set_rts;
SerCx2Sys.clear_rts = pSerialSys->clear_rts;
_SerCx2Sys.get_modemstatus = pSerialSys->get_modemstatus;
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_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.set_queue_size = pSerialSys->set_queue_size;
_SerCx2Sys.get_commstatus = pSerialSys->get_commstatus;
SerCx2Sys.get_commstatus = pSerialSys->get_commstatus;
_SerCx2Sys.set_break_on = pSerialSys->set_break_on;
_SerCx2Sys.set_break_off = pSerialSys->set_break_off;
SerCx2Sys.set_break_on = pSerialSys->set_break_on;
SerCx2Sys.set_break_off = pSerialSys->set_break_off;
_SerCx2Sys.get_dtrrts = pSerialSys->get_dtrrts;
SerCx2Sys.get_dtrrts = pSerialSys->get_dtrrts;
return &_SerCx2Sys;
return &SerCx2Sys;
}
#endif /* __linux__ */

View File

@ -20,8 +20,6 @@
* limitations under the License.
*/
#if defined __linux__ && !defined ANDROID
#include <winpr/assert.h>
#include <errno.h>
#include <fcntl.h>
@ -156,8 +154,11 @@ static const speed_t _BAUD_TABLE[][3] = {
{ BAUD_TABLE_END, 0, 0 }
};
static BOOL _get_properties(WINPR_COMM* pComm, COMMPROP* pProperties)
static BOOL commstatus_error(WINPR_COMM* pComm, const char* ctrl);
static BOOL get_properties(WINPR_COMM* pComm, COMMPROP* pProperties)
{
WINPR_ASSERT(pComm);
/* http://msdn.microsoft.com/en-us/library/windows/hardware/jj680684%28v=vs.85%29.aspx
* http://msdn.microsoft.com/en-us/library/windows/desktop/aa363189%28v=vs.85%29.aspx
*/
@ -220,12 +221,14 @@ static BOOL _get_properties(WINPR_COMM* pComm, COMMPROP* pProperties)
return TRUE;
}
static BOOL _set_baud_rate(WINPR_COMM* pComm, const SERIAL_BAUD_RATE* pBaudRate)
static BOOL set_baud_rate(WINPR_COMM* pComm, const SERIAL_BAUD_RATE* pBaudRate)
{
speed_t newSpeed = 0;
struct termios futureState;
struct termios futureState = { 0 };
WINPR_ASSERT(pComm);
WINPR_ASSERT(pBaudRate);
ZeroMemory(&futureState, sizeof(struct termios));
if (tcgetattr(pComm->fd, &futureState) <
0) /* NB: preserves current settings not directly handled by the Communication Functions */
{
@ -264,12 +267,14 @@ static BOOL _set_baud_rate(WINPR_COMM* pComm, const SERIAL_BAUD_RATE* pBaudRate)
return FALSE;
}
static BOOL _get_baud_rate(WINPR_COMM* pComm, SERIAL_BAUD_RATE* pBaudRate)
static BOOL get_baud_rate(WINPR_COMM* pComm, SERIAL_BAUD_RATE* pBaudRate)
{
speed_t currentSpeed = 0;
struct termios currentState;
struct termios currentState = { 0 };
WINPR_ASSERT(pComm);
WINPR_ASSERT(pBaudRate);
ZeroMemory(&currentState, sizeof(struct termios));
if (tcgetattr(pComm->fd, &currentState) < 0)
{
SetLastError(ERROR_IO_DEVICE);
@ -302,25 +307,20 @@ static BOOL _get_baud_rate(WINPR_COMM* pComm, SERIAL_BAUD_RATE* pBaudRate)
* ERROR_INVALID_PARAMETER when Xon and Xoff chars are the same;
* ERROR_NOT_SUPPORTED
*/
static BOOL _set_serial_chars(WINPR_COMM* pComm, const SERIAL_CHARS* pSerialChars)
static BOOL set_serial_chars(WINPR_COMM* pComm, const SERIAL_CHARS* pSerialChars)
{
BOOL result = TRUE;
struct termios upcomingTermios;
struct termios upcomingTermios = { 0 };
WINPR_ASSERT(pComm);
WINPR_ASSERT(pSerialChars);
ZeroMemory(&upcomingTermios, sizeof(struct termios));
if (tcgetattr(pComm->fd, &upcomingTermios) < 0)
{
SetLastError(ERROR_IO_DEVICE);
return FALSE;
}
if (pSerialChars->XonChar == pSerialChars->XoffChar)
{
/* https://msdn.microsoft.com/en-us/library/windows/hardware/ff546688?v=vs.85.aspx */
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
/* termios(3): (..) above symbolic subscript values are all
* different, except that VTIME, VMIN may have the same value
* as VEOL, VEOF, respectively. In noncanonical mode the
@ -344,7 +344,7 @@ static BOOL _set_serial_chars(WINPR_COMM* pComm, const SERIAL_CHARS* pSerialChar
* get the prefix the prefix \377 \0
*/
/* FIXME: see also: _set_handflow() */
/* FIXME: see also: set_handflow() */
if (pSerialChars->ErrorChar != '\0')
{
CommLog_Print(WLOG_WARN, "ErrorChar 0x%02" PRIX8 " ('%c') cannot be set (unsupported).\n",
@ -353,7 +353,7 @@ static BOOL _set_serial_chars(WINPR_COMM* pComm, const SERIAL_CHARS* pSerialChar
result = FALSE; /* but keep on */
}
/* FIXME: see also: _set_handflow() */
/* FIXME: see also: set_handflow() */
if (pSerialChars->BreakChar != '\0')
{
CommLog_Print(WLOG_WARN, "BreakChar 0x%02" PRIX8 " ('%c') cannot be set (unsupported).\n",
@ -381,11 +381,13 @@ static BOOL _set_serial_chars(WINPR_COMM* pComm, const SERIAL_CHARS* pSerialChar
return result;
}
static BOOL _get_serial_chars(WINPR_COMM* pComm, SERIAL_CHARS* pSerialChars)
static BOOL get_serial_chars(WINPR_COMM* pComm, SERIAL_CHARS* pSerialChars)
{
struct termios currentTermios;
struct termios currentTermios = { 0 };
WINPR_ASSERT(pComm);
WINPR_ASSERT(pSerialChars);
ZeroMemory(&currentTermios, sizeof(struct termios));
if (tcgetattr(pComm->fd, &currentTermios) < 0)
{
SetLastError(ERROR_IO_DEVICE);
@ -400,7 +402,7 @@ static BOOL _get_serial_chars(WINPR_COMM* pComm, SERIAL_CHARS* pSerialChars)
/* BreakChar unsupported */
/* FIXME: see also: _set_serial_chars() */
/* FIXME: see also: set_serial_chars() */
/* EventChar */
pSerialChars->XonChar = currentTermios.c_cc[VSTART];
@ -410,10 +412,13 @@ static BOOL _get_serial_chars(WINPR_COMM* pComm, SERIAL_CHARS* pSerialChars)
return TRUE;
}
static BOOL _set_line_control(WINPR_COMM* pComm, const SERIAL_LINE_CONTROL* pLineControl)
static BOOL set_line_control(WINPR_COMM* pComm, const SERIAL_LINE_CONTROL* pLineControl)
{
BOOL result = TRUE;
struct termios upcomingTermios;
struct termios upcomingTermios = { 0 };
WINPR_ASSERT(pComm);
WINPR_ASSERT(pLineControl);
/* http://msdn.microsoft.com/en-us/library/windows/desktop/aa363214%28v=vs.85%29.aspx
*
@ -424,7 +429,6 @@ static BOOL _set_line_control(WINPR_COMM* pComm, const SERIAL_LINE_CONTROL* pLin
* this issue. At least produce a warning message?
*/
ZeroMemory(&upcomingTermios, sizeof(struct termios));
if (tcgetattr(pComm->fd, &upcomingTermios) < 0)
{
SetLastError(ERROR_IO_DEVICE);
@ -525,11 +529,13 @@ static BOOL _set_line_control(WINPR_COMM* pComm, const SERIAL_LINE_CONTROL* pLin
return result;
}
static BOOL _get_line_control(WINPR_COMM* pComm, SERIAL_LINE_CONTROL* pLineControl)
static BOOL get_line_control(WINPR_COMM* pComm, SERIAL_LINE_CONTROL* pLineControl)
{
struct termios currentTermios;
struct termios currentTermios = { 0 };
WINPR_ASSERT(pComm);
WINPR_ASSERT(pLineControl);
ZeroMemory(&currentTermios, sizeof(struct termios));
if (tcgetattr(pComm->fd, &currentTermios) < 0)
{
SetLastError(ERROR_IO_DEVICE);
@ -571,12 +577,14 @@ static BOOL _get_line_control(WINPR_COMM* pComm, SERIAL_LINE_CONTROL* pLineContr
return TRUE;
}
static BOOL _set_handflow(WINPR_COMM* pComm, const SERIAL_HANDFLOW* pHandflow)
static BOOL set_handflow(WINPR_COMM* pComm, const SERIAL_HANDFLOW* pHandflow)
{
BOOL result = TRUE;
struct termios upcomingTermios;
struct termios upcomingTermios = { 0 };
WINPR_ASSERT(pComm);
WINPR_ASSERT(pHandflow);
ZeroMemory(&upcomingTermios, sizeof(struct termios));
if (tcgetattr(pComm->fd, &upcomingTermios) < 0)
{
SetLastError(ERROR_IO_DEVICE);
@ -771,11 +779,13 @@ static BOOL _set_handflow(WINPR_COMM* pComm, const SERIAL_HANDFLOW* pHandflow)
return result;
}
static BOOL _get_handflow(WINPR_COMM* pComm, SERIAL_HANDFLOW* pHandflow)
static BOOL get_handflow(WINPR_COMM* pComm, SERIAL_HANDFLOW* pHandflow)
{
struct termios currentTermios;
struct termios currentTermios = { 0 };
WINPR_ASSERT(pComm);
WINPR_ASSERT(pHandflow);
ZeroMemory(&currentTermios, sizeof(struct termios));
if (tcgetattr(pComm->fd, &currentTermios) < 0)
{
SetLastError(ERROR_IO_DEVICE);
@ -839,8 +849,11 @@ static BOOL _get_handflow(WINPR_COMM* pComm, SERIAL_HANDFLOW* pHandflow)
return TRUE;
}
static BOOL _set_timeouts(WINPR_COMM* pComm, const SERIAL_TIMEOUTS* pTimeouts)
static BOOL set_timeouts(WINPR_COMM* pComm, const SERIAL_TIMEOUTS* pTimeouts)
{
WINPR_ASSERT(pComm);
WINPR_ASSERT(pTimeouts);
/* NB: timeouts are applied on system during read/write I/O */
/* http://msdn.microsoft.com/en-us/library/windows/hardware/hh439614%28v=vs.85%29.aspx */
@ -874,8 +887,11 @@ static BOOL _set_timeouts(WINPR_COMM* pComm, const SERIAL_TIMEOUTS* pTimeouts)
return TRUE;
}
static BOOL _get_timeouts(WINPR_COMM* pComm, SERIAL_TIMEOUTS* pTimeouts)
static BOOL get_timeouts(WINPR_COMM* pComm, SERIAL_TIMEOUTS* pTimeouts)
{
WINPR_ASSERT(pComm);
WINPR_ASSERT(pTimeouts);
pTimeouts->ReadIntervalTimeout = pComm->timeouts.ReadIntervalTimeout;
pTimeouts->ReadTotalTimeoutMultiplier = pComm->timeouts.ReadTotalTimeoutMultiplier;
pTimeouts->ReadTotalTimeoutConstant = pComm->timeouts.ReadTotalTimeoutConstant;
@ -885,8 +901,10 @@ static BOOL _get_timeouts(WINPR_COMM* pComm, SERIAL_TIMEOUTS* pTimeouts)
return TRUE;
}
static BOOL _set_lines(WINPR_COMM* pComm, UINT32 lines)
static BOOL set_lines(WINPR_COMM* pComm, UINT32 lines)
{
WINPR_ASSERT(pComm);
if (ioctl(pComm->fd, TIOCMBIS, &lines) < 0)
{
char ebuffer[256] = { 0 };
@ -899,8 +917,10 @@ static BOOL _set_lines(WINPR_COMM* pComm, UINT32 lines)
return TRUE;
}
static BOOL _clear_lines(WINPR_COMM* pComm, UINT32 lines)
static BOOL clear_lines(WINPR_COMM* pComm, UINT32 lines)
{
WINPR_ASSERT(pComm);
if (ioctl(pComm->fd, TIOCMBIC, &lines) < 0)
{
char ebuffer[256] = { 0 };
@ -913,10 +933,12 @@ static BOOL _clear_lines(WINPR_COMM* pComm, UINT32 lines)
return TRUE;
}
static BOOL _set_dtr(WINPR_COMM* pComm)
static BOOL set_dtr(WINPR_COMM* pComm)
{
SERIAL_HANDFLOW handflow;
if (!_get_handflow(pComm, &handflow))
SERIAL_HANDFLOW handflow = { 0 };
WINPR_ASSERT(pComm);
if (!get_handflow(pComm, &handflow))
return FALSE;
/* SERIAL_DTR_HANDSHAKE not supported as of today */
@ -928,13 +950,15 @@ static BOOL _set_dtr(WINPR_COMM* pComm)
return FALSE;
}
return _set_lines(pComm, TIOCM_DTR);
return set_lines(pComm, TIOCM_DTR);
}
static BOOL _clear_dtr(WINPR_COMM* pComm)
static BOOL clear_dtr(WINPR_COMM* pComm)
{
SERIAL_HANDFLOW handflow;
if (!_get_handflow(pComm, &handflow))
SERIAL_HANDFLOW handflow = { 0 };
WINPR_ASSERT(pComm);
if (!get_handflow(pComm, &handflow))
return FALSE;
/* SERIAL_DTR_HANDSHAKE not supported as of today */
@ -946,13 +970,15 @@ static BOOL _clear_dtr(WINPR_COMM* pComm)
return FALSE;
}
return _clear_lines(pComm, TIOCM_DTR);
return clear_lines(pComm, TIOCM_DTR);
}
static BOOL _set_rts(WINPR_COMM* pComm)
static BOOL set_rts(WINPR_COMM* pComm)
{
SERIAL_HANDFLOW handflow;
if (!_get_handflow(pComm, &handflow))
SERIAL_HANDFLOW handflow = { 0 };
WINPR_ASSERT(pComm);
if (!get_handflow(pComm, &handflow))
return FALSE;
if (handflow.FlowReplace & SERIAL_RTS_HANDSHAKE)
@ -961,13 +987,14 @@ static BOOL _set_rts(WINPR_COMM* pComm)
return FALSE;
}
return _set_lines(pComm, TIOCM_RTS);
return set_lines(pComm, TIOCM_RTS);
}
static BOOL _clear_rts(WINPR_COMM* pComm)
static BOOL clear_rts(WINPR_COMM* pComm)
{
SERIAL_HANDFLOW handflow;
if (!_get_handflow(pComm, &handflow))
SERIAL_HANDFLOW handflow = { 0 };
WINPR_ASSERT(pComm);
if (!get_handflow(pComm, &handflow))
return FALSE;
if (handflow.FlowReplace & SERIAL_RTS_HANDSHAKE)
@ -976,35 +1003,23 @@ static BOOL _clear_rts(WINPR_COMM* pComm)
return FALSE;
}
return _clear_lines(pComm, TIOCM_RTS);
return clear_lines(pComm, TIOCM_RTS);
}
static BOOL _get_modemstatus(WINPR_COMM* pComm, ULONG* pRegister)
static BOOL get_modemstatus(WINPR_COMM* pComm, ULONG* pRegister)
{
UINT32 lines = 0;
WINPR_ASSERT(pComm);
WINPR_ASSERT(pRegister);
*pRegister = 0;
if (ioctl(pComm->fd, TIOCMGET, &lines) < 0)
{
char ebuffer[256] = { 0 };
CommLog_Print(WLOG_WARN, "TIOCMGET ioctl failed, errno=[%d] %s", errno,
winpr_strerror(errno, ebuffer, sizeof(ebuffer)));
SetLastError(ERROR_IO_DEVICE);
return FALSE;
if (!commstatus_error(pComm, "TIOCMGET"))
return FALSE;
}
ZeroMemory(pRegister, sizeof(ULONG));
/* FIXME: Is the last read of the MSR register available or
* cached somewhere? Not quite sure we need to return the 4
* LSBits anyway. A direct access to the register -- which
* would reset the register -- is likely not expected from
* this function.
*/
/* #define SERIAL_MSR_DCTS 0x01 */
/* #define SERIAL_MSR_DDSR 0x02 */
/* #define SERIAL_MSR_TERI 0x04 */
/* #define SERIAL_MSR_DDCD 0x08 */
if (lines & TIOCM_CTS)
*pRegister |= SERIAL_MSR_CTS;
if (lines & TIOCM_DSR)
@ -1018,7 +1033,7 @@ static BOOL _get_modemstatus(WINPR_COMM* pComm, ULONG* pRegister)
}
/* http://msdn.microsoft.com/en-us/library/windows/hardware/hh439605%28v=vs.85%29.aspx */
static const ULONG _SERIAL_SYS_SUPPORTED_EV_MASK =
static const ULONG SERIAL_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 | */
@ -1037,7 +1052,7 @@ static BOOL is_wait_set(WINPR_COMM* pComm)
return isWaiting;
}
static BOOL _set_wait_mask(WINPR_COMM* pComm, const ULONG* pWaitMask)
static BOOL set_wait_mask(WINPR_COMM* pComm, const ULONG* pWaitMask)
{
ULONG possibleMask = 0;
@ -1055,9 +1070,13 @@ static BOOL _set_wait_mask(WINPR_COMM* pComm, const ULONG* pWaitMask)
pComm->PendingEvents |= SERIAL_EV_WINPR_STOP;
LeaveCriticalSection(&pComm->EventsLock);
/* waiting the end of the pending _wait_on_mask() */
/* waiting the end of the pending wait_on_mask() */
while (is_wait_set(pComm))
Sleep(10); /* 10ms */
EnterCriticalSection(&pComm->EventsLock);
pComm->PendingEvents &= ~SERIAL_EV_WINPR_STOP;
LeaveCriticalSection(&pComm->EventsLock);
}
/* NB: ensure to leave the critical section before to return */
@ -1069,27 +1088,18 @@ static BOOL _set_wait_mask(WINPR_COMM* pComm, const ULONG* pWaitMask)
if (ioctl(pComm->fd, TIOCGICOUNT, &(pComm->counters)) < 0)
{
char ebuffer[256] = { 0 };
CommLog_Print(WLOG_WARN, "TIOCGICOUNT ioctl failed, errno=[%d] %s.", errno,
winpr_strerror(errno, ebuffer, sizeof(ebuffer)));
if (pComm->permissive)
if (!commstatus_error(pComm, "TIOCGICOUNT"))
{
/* counters could not be reset but keep on */
ZeroMemory(&(pComm->counters), sizeof(struct serial_icounter_struct));
}
else
{
SetLastError(ERROR_IO_DEVICE);
LeaveCriticalSection(&pComm->EventsLock);
return FALSE;
}
ZeroMemory(&(pComm->counters), sizeof(struct serial_icounter_struct));
}
pComm->PendingEvents = 0;
}
possibleMask = *pWaitMask & _SERIAL_SYS_SUPPORTED_EV_MASK;
possibleMask = *pWaitMask & SERIAL_SYS_SUPPORTED_EV_MASK;
if (possibleMask != *pWaitMask)
{
@ -1111,14 +1121,20 @@ static BOOL _set_wait_mask(WINPR_COMM* pComm, const ULONG* pWaitMask)
return TRUE;
}
static BOOL _get_wait_mask(WINPR_COMM* pComm, ULONG* pWaitMask)
static BOOL get_wait_mask(WINPR_COMM* pComm, ULONG* pWaitMask)
{
WINPR_ASSERT(pComm);
WINPR_ASSERT(pWaitMask);
*pWaitMask = pComm->WaitEventMask;
return TRUE;
}
static BOOL _set_queue_size(WINPR_COMM* pComm, const SERIAL_QUEUE_SIZE* pQueueSize)
static BOOL set_queue_size(WINPR_COMM* pComm, const SERIAL_QUEUE_SIZE* pQueueSize)
{
WINPR_ASSERT(pComm);
WINPR_ASSERT(pQueueSize);
if ((pQueueSize->InSize <= N_TTY_BUF_SIZE) && (pQueueSize->OutSize <= N_TTY_BUF_SIZE))
return TRUE; /* nothing to do */
@ -1140,8 +1156,11 @@ static BOOL _set_queue_size(WINPR_COMM* pComm, const SERIAL_QUEUE_SIZE* pQueueSi
return FALSE;
}
static BOOL _purge(WINPR_COMM* pComm, const ULONG* pPurgeMask)
static BOOL purge(WINPR_COMM* pComm, const ULONG* pPurgeMask)
{
WINPR_ASSERT(pComm);
WINPR_ASSERT(pPurgeMask);
if ((*pPurgeMask & ~(SERIAL_PURGE_TXABORT | SERIAL_PURGE_RXABORT | SERIAL_PURGE_TXCLEAR |
SERIAL_PURGE_RXCLEAR)) > 0)
{
@ -1220,42 +1239,56 @@ static BOOL _purge(WINPR_COMM* pComm, const ULONG* pPurgeMask)
return TRUE;
}
/* NB: _get_commstatus also produces most of the events consumed by _wait_on_mask(). Exceptions:
BOOL commstatus_error(WINPR_COMM* pComm, const char* ctrl)
{
char ebuffer[256] = { 0 };
CommLog_Print(WLOG_WARN, "%s ioctl failed, errno=[%d] %s.", ctrl, errno,
winpr_strerror(errno, ebuffer, sizeof(ebuffer)));
if (!pComm->permissive)
{
SetLastError(ERROR_IO_DEVICE);
return FALSE;
}
return TRUE;
}
/* NB: get_commstatus also produces most of the events consumed by wait_on_mask(). Exceptions:
* - SERIAL_EV_RXFLAG: FIXME: once EventChar supported
*
*/
static BOOL _get_commstatus(WINPR_COMM* pComm, SERIAL_STATUS* pCommstatus)
static BOOL get_commstatus(WINPR_COMM* pComm, SERIAL_STATUS* pCommstatus)
{
BOOL rc = FALSE;
/* http://msdn.microsoft.com/en-us/library/jj673022%28v=vs.85%29.aspx */
struct serial_icounter_struct currentCounters;
struct serial_icounter_struct currentCounters = { 0 };
WINPR_ASSERT(pComm);
WINPR_ASSERT(pCommstatus);
/* NB: ensure to leave the critical section before to return */
EnterCriticalSection(&pComm->EventsLock);
ZeroMemory(pCommstatus, sizeof(SERIAL_STATUS));
ZeroMemory(&currentCounters, sizeof(struct serial_icounter_struct));
ULONG status = 0;
if (!get_modemstatus(pComm, &status))
{
if (!commstatus_error(pComm, "TIOCGICOUNT"))
goto fail;
/* Errors and events based on counters could not be
* detected but keep on.
*/
SetLastError(0);
status = 0;
}
if (ioctl(pComm->fd, TIOCGICOUNT, &currentCounters) < 0)
{
char ebuffer[256] = { 0 };
CommLog_Print(WLOG_WARN, "TIOCGICOUNT ioctl failed, errno=[%d] %s.", errno,
winpr_strerror(errno, ebuffer, sizeof(ebuffer)));
CommLog_Print(WLOG_WARN, " could not read counters.");
if (pComm->permissive)
{
/* Errors and events based on counters could not be
* detected but keep on.
*/
ZeroMemory(&currentCounters, sizeof(struct serial_icounter_struct));
}
else
{
SetLastError(ERROR_IO_DEVICE);
LeaveCriticalSection(&pComm->EventsLock);
return FALSE;
}
if (!commstatus_error(pComm, "TIOCGICOUNT"))
goto fail;
ZeroMemory(&currentCounters, sizeof(struct serial_icounter_struct));
}
/* NB: preferred below (currentCounters.* != pComm->counters.*) over (currentCounters.* >
@ -1310,26 +1343,16 @@ static BOOL _get_commstatus(WINPR_COMM* pComm, SERIAL_STATUS* pCommstatus)
if (ioctl(pComm->fd, TIOCINQ, &(pCommstatus->AmountInInQueue)) < 0)
{
char ebuffer[256] = { 0 };
CommLog_Print(WLOG_WARN, "TIOCINQ ioctl failed, errno=[%d] %s", errno,
winpr_strerror(errno, ebuffer, sizeof(ebuffer)));
SetLastError(ERROR_IO_DEVICE);
LeaveCriticalSection(&pComm->EventsLock);
return FALSE;
if (!commstatus_error(pComm, "TIOCINQ"))
goto fail;
}
/* AmountInOutQueue */
if (ioctl(pComm->fd, TIOCOUTQ, &(pCommstatus->AmountInOutQueue)) < 0)
{
char ebuffer[256] = { 0 };
CommLog_Print(WLOG_WARN, "TIOCOUTQ ioctl failed, errno=[%d] %s", errno,
winpr_strerror(errno, ebuffer, sizeof(ebuffer)));
SetLastError(ERROR_IO_DEVICE);
LeaveCriticalSection(&pComm->EventsLock);
return FALSE;
if (!commstatus_error(pComm, "TIOCOUTQ"))
goto fail;
}
/* BOOLEAN EofReceived; FIXME: once EofChar supported */
@ -1388,17 +1411,20 @@ static BOOL _get_commstatus(WINPR_COMM* pComm, SERIAL_STATUS* pCommstatus)
pComm->counters = currentCounters;
rc = TRUE;
fail:
LeaveCriticalSection(&pComm->EventsLock);
return TRUE;
return rc;
}
static BOOL _refresh_PendingEvents(WINPR_COMM* pComm)
static BOOL refresh_PendingEvents(WINPR_COMM* pComm)
{
SERIAL_STATUS serialStatus;
SERIAL_STATUS serialStatus = { 0 };
WINPR_ASSERT(pComm);
/* NB: also ensures PendingEvents to be up to date */
ZeroMemory(&serialStatus, sizeof(SERIAL_STATUS));
if (!_get_commstatus(pComm, &serialStatus))
if (!get_commstatus(pComm, &serialStatus))
{
return FALSE;
}
@ -1406,8 +1432,11 @@ static BOOL _refresh_PendingEvents(WINPR_COMM* pComm)
return TRUE;
}
static void _consume_event(WINPR_COMM* pComm, ULONG* pOutputMask, ULONG event)
static void consume_event(WINPR_COMM* pComm, ULONG* pOutputMask, ULONG event)
{
WINPR_ASSERT(pComm);
WINPR_ASSERT(pOutputMask);
if ((pComm->WaitEventMask & event) && (pComm->PendingEvents & event))
{
pComm->PendingEvents &= ~event; /* consumed */
@ -1415,11 +1444,20 @@ static void _consume_event(WINPR_COMM* pComm, ULONG* pOutputMask, ULONG event)
}
}
/*
* NB: see also: _set_wait_mask()
*/
static BOOL _wait_on_mask(WINPR_COMM* pComm, ULONG* pOutputMask)
static BOOL unlock_return(WINPR_COMM* pComm, BOOL res)
{
EnterCriticalSection(&pComm->EventsLock);
pComm->PendingEvents &= ~SERIAL_EV_WINPR_WAITING;
LeaveCriticalSection(&pComm->EventsLock);
return res;
}
/*
* NB: see also: set_wait_mask()
*/
static BOOL wait_on_mask(WINPR_COMM* pComm, ULONG* pOutputMask)
{
WINPR_ASSERT(pComm);
WINPR_ASSERT(*pOutputMask == 0);
EnterCriticalSection(&pComm->EventsLock);
@ -1428,22 +1466,15 @@ static BOOL _wait_on_mask(WINPR_COMM* pComm, ULONG* pOutputMask)
while (TRUE)
{
/* NB: EventsLock also used by _refresh_PendingEvents() */
if (!_refresh_PendingEvents(pComm))
{
EnterCriticalSection(&pComm->EventsLock);
pComm->PendingEvents &= ~SERIAL_EV_WINPR_WAITING;
LeaveCriticalSection(&pComm->EventsLock);
return FALSE;
}
/* NB: EventsLock also used by refresh_PendingEvents() */
if (!refresh_PendingEvents(pComm))
return unlock_return(pComm, FALSE);
/* NB: ensure to leave the critical section before to return */
EnterCriticalSection(&pComm->EventsLock);
if (pComm->PendingEvents & SERIAL_EV_WINPR_STOP)
{
pComm->PendingEvents &= ~SERIAL_EV_WINPR_STOP;
/* pOutputMask must remain empty but should
* not have been modified.
*
@ -1451,21 +1482,20 @@ static BOOL _wait_on_mask(WINPR_COMM* pComm, ULONG* pOutputMask)
*/
WINPR_ASSERT(*pOutputMask == 0);
pComm->PendingEvents &= ~SERIAL_EV_WINPR_WAITING;
LeaveCriticalSection(&pComm->EventsLock);
return TRUE;
return unlock_return(pComm, TRUE);
}
_consume_event(pComm, pOutputMask, SERIAL_EV_RXCHAR);
_consume_event(pComm, pOutputMask, SERIAL_EV_RXFLAG);
_consume_event(pComm, pOutputMask, SERIAL_EV_TXEMPTY);
_consume_event(pComm, pOutputMask, SERIAL_EV_CTS);
_consume_event(pComm, pOutputMask, SERIAL_EV_DSR);
_consume_event(pComm, pOutputMask, SERIAL_EV_RLSD);
_consume_event(pComm, pOutputMask, SERIAL_EV_BREAK);
_consume_event(pComm, pOutputMask, SERIAL_EV_ERR);
_consume_event(pComm, pOutputMask, SERIAL_EV_RING);
_consume_event(pComm, pOutputMask, SERIAL_EV_RX80FULL);
consume_event(pComm, pOutputMask, SERIAL_EV_RXCHAR);
consume_event(pComm, pOutputMask, SERIAL_EV_RXFLAG);
consume_event(pComm, pOutputMask, SERIAL_EV_TXEMPTY);
consume_event(pComm, pOutputMask, SERIAL_EV_CTS);
consume_event(pComm, pOutputMask, SERIAL_EV_DSR);
consume_event(pComm, pOutputMask, SERIAL_EV_RLSD);
consume_event(pComm, pOutputMask, SERIAL_EV_BREAK);
consume_event(pComm, pOutputMask, SERIAL_EV_ERR);
consume_event(pComm, pOutputMask, SERIAL_EV_RING);
consume_event(pComm, pOutputMask, SERIAL_EV_RX80FULL);
LeaveCriticalSection(&pComm->EventsLock);
@ -1473,14 +1503,7 @@ static BOOL _wait_on_mask(WINPR_COMM* pComm, ULONG* pOutputMask)
* not pOutputMask */
if (*pOutputMask != 0)
{
/* at least an event occurred */
EnterCriticalSection(&pComm->EventsLock);
pComm->PendingEvents &= ~SERIAL_EV_WINPR_WAITING;
LeaveCriticalSection(&pComm->EventsLock);
return TRUE;
}
return unlock_return(pComm, TRUE);
/* waiting for a modification of PendingEvents.
*
@ -1492,10 +1515,13 @@ static BOOL _wait_on_mask(WINPR_COMM* pComm, ULONG* pOutputMask)
Sleep(100); /* 100 ms */
}
return unlock_return(pComm, FALSE);
}
static BOOL _set_break_on(WINPR_COMM* pComm)
static BOOL set_break_on(WINPR_COMM* pComm)
{
WINPR_ASSERT(pComm);
if (ioctl(pComm->fd, TIOCSBRK, NULL) < 0)
{
char ebuffer[256] = { 0 };
@ -1508,8 +1534,9 @@ static BOOL _set_break_on(WINPR_COMM* pComm)
return TRUE;
}
static BOOL _set_break_off(WINPR_COMM* pComm)
static BOOL set_break_off(WINPR_COMM* pComm)
{
WINPR_ASSERT(pComm);
if (ioctl(pComm->fd, TIOCCBRK, NULL) < 0)
{
char ebuffer[256] = { 0 };
@ -1522,8 +1549,9 @@ static BOOL _set_break_off(WINPR_COMM* pComm)
return TRUE;
}
static BOOL _set_xoff(WINPR_COMM* pComm)
static BOOL set_xoff(WINPR_COMM* pComm)
{
WINPR_ASSERT(pComm);
if (tcflow(pComm->fd, TCIOFF) < 0)
{
char ebuffer[256] = { 0 };
@ -1536,8 +1564,9 @@ static BOOL _set_xoff(WINPR_COMM* pComm)
return TRUE;
}
static BOOL _set_xon(WINPR_COMM* pComm)
static BOOL set_xon(WINPR_COMM* pComm)
{
WINPR_ASSERT(pComm);
if (tcflow(pComm->fd, TCION) < 0)
{
char ebuffer[256] = { 0 };
@ -1550,17 +1579,15 @@ static BOOL _set_xon(WINPR_COMM* pComm)
return TRUE;
}
static BOOL _get_dtrrts(WINPR_COMM* pComm, ULONG* pMask)
static BOOL get_dtrrts(WINPR_COMM* pComm, ULONG* pMask)
{
UINT32 lines = 0;
if (ioctl(pComm->fd, TIOCMGET, &lines) < 0)
{
char ebuffer[256] = { 0 };
CommLog_Print(WLOG_WARN, "TIOCMGET ioctl failed, errno=[%d] %s", errno,
winpr_strerror(errno, ebuffer, sizeof(ebuffer)));
SetLastError(ERROR_IO_DEVICE);
WINPR_ASSERT(pComm);
WINPR_ASSERT(pMask);
if (!get_modemstatus(pComm, &lines))
return FALSE;
}
*pMask = 0;
@ -1572,8 +1599,11 @@ static BOOL _get_dtrrts(WINPR_COMM* pComm, ULONG* pMask)
return TRUE;
}
static BOOL _config_size(WINPR_COMM* pComm, ULONG* pSize)
static BOOL config_size(WINPR_COMM* pComm, ULONG* pSize)
{
WINPR_ASSERT(pComm);
WINPR_ASSERT(pSize);
/* http://msdn.microsoft.com/en-us/library/ff546548%28v=vs.85%29.aspx */
if (!pSize)
return FALSE;
@ -1582,15 +1612,18 @@ static BOOL _config_size(WINPR_COMM* pComm, ULONG* pSize)
return TRUE;
}
static BOOL _immediate_char(WINPR_COMM* pComm, const UCHAR* pChar)
static BOOL immediate_char(WINPR_COMM* pComm, const UCHAR* pChar)
{
BOOL result = 0;
DWORD nbBytesWritten = -1;
WINPR_ASSERT(pComm);
WINPR_ASSERT(pChar);
/* FIXME: CommWriteFile uses a critical section, shall it be
* interrupted?
*
* FIXME: see also _get_commstatus()'s WaitForImmediate boolean
* FIXME: see also get_commstatus()'s WaitForImmediate boolean
*/
result = CommWriteFile(pComm, pChar, 1, &nbBytesWritten, NULL);
@ -1600,50 +1633,48 @@ static BOOL _immediate_char(WINPR_COMM* pComm, const UCHAR* pChar)
return result;
}
static BOOL _reset_device(WINPR_COMM* pComm)
static BOOL reset_device(WINPR_COMM* pComm)
{
/* http://msdn.microsoft.com/en-us/library/dn265347%28v=vs.85%29.aspx */
return TRUE;
}
static SERIAL_DRIVER _SerialSys = {
static SERIAL_DRIVER SerialSys = {
.id = SerialDriverSerialSys,
.name = _T("Serial.sys"),
.set_baud_rate = _set_baud_rate,
.get_baud_rate = _get_baud_rate,
.get_properties = _get_properties,
.set_serial_chars = _set_serial_chars,
.get_serial_chars = _get_serial_chars,
.set_line_control = _set_line_control,
.get_line_control = _get_line_control,
.set_handflow = _set_handflow,
.get_handflow = _get_handflow,
.set_timeouts = _set_timeouts,
.get_timeouts = _get_timeouts,
.set_dtr = _set_dtr,
.clear_dtr = _clear_dtr,
.set_rts = _set_rts,
.clear_rts = _clear_rts,
.get_modemstatus = _get_modemstatus,
.set_wait_mask = _set_wait_mask,
.get_wait_mask = _get_wait_mask,
.wait_on_mask = _wait_on_mask,
.set_queue_size = _set_queue_size,
.purge = _purge,
.get_commstatus = _get_commstatus,
.set_break_on = _set_break_on,
.set_break_off = _set_break_off,
.set_xoff = _set_xoff,
.set_xon = _set_xon,
.get_dtrrts = _get_dtrrts,
.config_size = _config_size,
.immediate_char = _immediate_char,
.reset_device = _reset_device,
.set_baud_rate = set_baud_rate,
.get_baud_rate = get_baud_rate,
.get_properties = get_properties,
.set_serial_chars = set_serial_chars,
.get_serial_chars = get_serial_chars,
.set_line_control = set_line_control,
.get_line_control = get_line_control,
.set_handflow = set_handflow,
.get_handflow = get_handflow,
.set_timeouts = set_timeouts,
.get_timeouts = get_timeouts,
.set_dtr = set_dtr,
.clear_dtr = clear_dtr,
.set_rts = set_rts,
.clear_rts = clear_rts,
.get_modemstatus = get_modemstatus,
.set_wait_mask = set_wait_mask,
.get_wait_mask = get_wait_mask,
.wait_on_mask = wait_on_mask,
.set_queue_size = set_queue_size,
.purge = purge,
.get_commstatus = get_commstatus,
.set_break_on = set_break_on,
.set_break_off = set_break_off,
.set_xoff = set_xoff,
.set_xon = set_xon,
.get_dtrrts = get_dtrrts,
.config_size = config_size,
.immediate_char = immediate_char,
.reset_device = reset_device,
};
SERIAL_DRIVER* SerialSys_s(void)
{
return &_SerialSys;
return &SerialSys;
}
#endif /* __linux__ */