2011-10-04 22:15:15 +04:00
/**
2012-10-09 07:02:04 +04:00
* FreeRDP : A Remote Desktop Protocol Implementation
2011-10-04 22:15:15 +04:00
* Serial Port Device Service Virtual Channel
*
* Copyright 2011 O . S . Systems Software Ltda .
* Copyright 2011 Eduardo Fiss Beloni < beloni @ ossystems . com . br >
2014-04-28 21:57:17 +04:00
* Copyright 2014 Hewlett - Packard Development Company , L . P .
2011-10-04 22:15:15 +04:00
*
* 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 .
*/
2012-08-15 01:09:01 +04:00
# ifdef HAVE_CONFIG_H
# include "config.h"
# endif
2013-09-05 16:27:34 +04:00
# include <assert.h>
2011-10-04 22:15:15 +04:00
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# include <errno.h>
# ifdef HAVE_SYS_MODEM_H
# include <sys/modem.h>
# endif
# ifdef HAVE_SYS_FILIO_H
# include <sys/filio.h>
# endif
# ifdef HAVE_SYS_STRTIO_H
# include <sys/strtio.h>
# endif
2011-10-27 21:41:39 +04:00
# ifdef HAVE_UNISTD_H
# include <unistd.h>
# endif
2011-10-04 22:15:15 +04:00
2014-04-27 21:41:25 +04:00
# include <winpr/collections.h>
# include <winpr/comm.h>
2012-12-17 08:34:07 +04:00
# include <winpr/crt.h>
2014-04-27 21:41:25 +04:00
# include <winpr/stream.h>
2013-03-21 05:22:47 +04:00
# include <winpr/synch.h>
# include <winpr/thread.h>
2014-04-27 21:41:25 +04:00
# include <winpr/wlog.h>
2012-12-17 08:34:07 +04:00
2012-10-09 05:00:07 +04:00
# include <freerdp/freerdp.h>
2013-03-21 05:22:47 +04:00
# include <freerdp/utils/list.h>
2012-10-09 05:00:07 +04:00
# include <freerdp/channels/rdpdr.h>
2013-10-17 23:42:51 +04:00
# include <freerdp/utils/svc_plugin.h>
2011-10-04 22:15:15 +04:00
2014-05-21 12:36:55 +04:00
# define MAX_IRP_THREADS 5
2011-10-04 22:15:15 +04:00
typedef struct _SERIAL_DEVICE SERIAL_DEVICE ;
2012-10-09 05:00:07 +04:00
2011-10-04 22:15:15 +04:00
struct _SERIAL_DEVICE
{
DEVICE device ;
2014-04-27 21:41:25 +04:00
HANDLE * hComm ;
2011-10-04 22:15:15 +04:00
2014-05-12 19:33:56 +04:00
// TMP: use of log
2014-03-27 01:11:15 +04:00
wLog * log ;
2014-05-13 16:55:30 +04:00
HANDLE MainThread ;
wMessageQueue * MainIrpQueue ;
2014-05-21 12:36:55 +04:00
/* one thread per pending IRP and indexed according their CompletionId */
wListDictionary * IrpThreads ;
2014-05-23 14:27:09 +04:00
UINT32 IrpThreadToTerminateCount ;
CRITICAL_SECTION TerminatingIrpThreadsLock ;
2014-05-21 12:36:55 +04:00
} ;
typedef struct _IRP_THREAD_DATA IRP_THREAD_DATA ;
struct _IRP_THREAD_DATA
{
SERIAL_DEVICE * serial ;
IRP * irp ;
2011-10-04 22:15:15 +04:00
} ;
2014-05-21 12:36:55 +04:00
2011-10-04 22:15:15 +04:00
static void serial_process_irp_create ( SERIAL_DEVICE * serial , IRP * irp )
{
2014-04-27 21:41:25 +04:00
DWORD DesiredAccess ;
DWORD SharedAccess ;
DWORD CreateDisposition ;
2014-03-26 23:21:44 +04:00
UINT32 PathLength ;
2014-05-12 19:33:56 +04:00
2014-04-27 21:41:25 +04:00
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) */
assert ( PathLength = = 0 ) ; /* MS-RDPESP 2.2.2.2 */
2014-05-12 19:33:56 +04:00
# 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
*
2014-05-12 21:21:06 +04:00
* then Windows 2012 sends :
2014-05-12 19:33:56 +04:00
* DesiredAccess = 0x00120089 : SYNCHRONIZE | READ_CONTROL | FILE_READ_ATTRIBUTES | FILE_READ_EA | FILE_READ_DATA
* SharedAccess = 0x00000007 : FILE_SHARE_DELETE | FILE_SHARE_WRITE | FILE_SHARE_READ
* CreateDisposition = 0x00000001 : CREATE_NEW
*
* assert ( DesiredAccess = = ( GENERIC_READ | GENERIC_WRITE ) ) ;
* assert ( SharedAccess = = 0 ) ;
* assert ( CreateDisposition = = OPEN_EXISTING ) ;
*
*/
DEBUG_SVC ( " DesiredAccess: 0x%0.8x, SharedAccess: 0x%0.8x, CreateDisposition: 0x%0.8x " , DesiredAccess , SharedAccess , CreateDisposition ) ;
/* FIXME: As of today only the flags below are supported by CommCreateFileA: */
DesiredAccess = GENERIC_READ | GENERIC_WRITE ;
SharedAccess = 0 ;
CreateDisposition = OPEN_EXISTING ;
# endif
2014-04-27 21:41:25 +04:00
serial - > hComm = CreateFile ( serial - > device . name ,
2014-05-12 19:33:56 +04:00
DesiredAccess ,
SharedAccess ,
2014-04-27 21:41:25 +04:00
NULL , /* SecurityAttributes */
2014-05-12 19:33:56 +04:00
CreateDisposition ,
2014-04-27 21:41:25 +04:00
0 , /* FlagsAndAttributes */
NULL ) ; /* TemplateFile */
if ( ! serial - > hComm | | ( serial - > hComm = = INVALID_HANDLE_VALUE ) )
2011-10-04 22:15:15 +04:00
{
2014-05-19 18:53:57 +04:00
DEBUG_WARN ( " CreateFile failure: %s last-error: Ox%lX \n " , serial - > device . name , GetLastError ( ) ) ;
2014-04-27 21:41:25 +04:00
2011-10-04 22:15:15 +04:00
irp - > IoStatus = STATUS_UNSUCCESSFUL ;
2014-04-27 21:41:25 +04:00
goto error_handle ;
2011-10-04 22:15:15 +04:00
}
2014-05-19 18:53:57 +04:00
/* 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); */
2014-05-14 23:21:31 +04:00
/* FIXME: this stinks, see also IOCTL_SERIAL_PURGE */
2014-05-21 12:36:55 +04:00
// TMP: to be removed
//_comm_set_ReadIrpQueue(serial->hComm, serial->ReadIrpQueue);
2014-05-14 23:21:31 +04:00
2014-05-12 19:33:56 +04:00
/* NOTE: binary mode/raw mode required for the redirection. On
* Linux , CommCreateFileA forces this setting .
*/
/* ZeroMemory(&dcb, sizeof(DCB)); */
/* dcb.DCBlength = sizeof(DCB); */
/* GetCommState(serial->hComm, &dcb); */
/* dcb.fBinary = TRUE; */
/* SetCommState(serial->hComm, &dcb); */
// TMP:
COMMTIMEOUTS timeouts ;
timeouts . ReadIntervalTimeout = MAXULONG ; /* ensures a non blocking state */
timeouts . ReadTotalTimeoutMultiplier = 0 ;
timeouts . ReadTotalTimeoutConstant = 0 ;
timeouts . WriteTotalTimeoutMultiplier = 0 ;
timeouts . WriteTotalTimeoutConstant = 0 ;
SetCommTimeouts ( serial - > hComm , & timeouts ) ;
assert ( irp - > FileId = = 0 ) ;
irp - > FileId = irp - > devman - > id_sequence + + ; /* FIXME: why not ((WINPR_COMM*)hComm)->fd? */
2014-04-27 21:41:25 +04:00
irp - > IoStatus = STATUS_SUCCESS ;
2014-05-12 19:33:56 +04:00
DEBUG_SVC ( " %s (DeviceId: %d, FileId: %d) created. " , serial - > device . name , irp - > device - > id , irp - > FileId ) ;
2011-10-04 22:15:15 +04:00
2014-04-27 21:41:25 +04:00
error_handle :
2014-05-12 19:33:56 +04:00
Stream_Write_UINT32 ( irp - > output , irp - > FileId ) ; /* FileId (4 bytes) */
2014-04-27 21:41:25 +04:00
Stream_Write_UINT8 ( irp - > output , 0 ) ; /* Information (1 byte) */
2011-10-04 22:15:15 +04:00
}
static void serial_process_irp_close ( SERIAL_DEVICE * serial , IRP * irp )
{
2014-03-27 01:11:15 +04:00
Stream_Seek ( irp - > input , 32 ) ; /* Padding (32 bytes) */
2012-09-24 02:41:07 +04:00
2014-04-27 21:41:25 +04:00
if ( ! CloseHandle ( serial - > hComm ) )
2011-10-04 22:15:15 +04:00
{
2014-05-12 19:33:56 +04:00
DEBUG_WARN ( " CloseHandle failure: %s (%d) closed. " , serial - > device . name , irp - > device - > id ) ;
2011-10-04 22:15:15 +04:00
irp - > IoStatus = STATUS_UNSUCCESSFUL ;
2014-04-27 21:41:25 +04:00
goto error_handle ;
2011-10-04 22:15:15 +04:00
}
2014-05-12 19:33:56 +04:00
DEBUG_SVC ( " %s (DeviceId: %d, FileId: %d) closed. " , serial - > device . name , irp - > device - > id , irp - > FileId ) ;
2014-04-27 21:41:25 +04:00
serial - > hComm = NULL ;
irp - > IoStatus = STATUS_SUCCESS ;
2011-10-04 22:15:15 +04:00
2014-04-27 21:41:25 +04:00
error_handle :
2014-03-26 23:21:44 +04:00
Stream_Zero ( irp - > output , 5 ) ; /* Padding (5 bytes) */
2011-10-04 22:15:15 +04:00
}
static void serial_process_irp_read ( SERIAL_DEVICE * serial , IRP * irp )
{
2012-10-09 11:26:39 +04:00
UINT32 Length ;
UINT64 Offset ;
2012-10-09 11:01:37 +04:00
BYTE * buffer = NULL ;
2014-05-12 19:33:56 +04:00
DWORD nbRead = 0 ;
2011-10-04 22:15:15 +04:00
2014-03-27 01:11:15 +04:00
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) */
2012-09-24 02:41:07 +04:00
2014-05-12 19:33:56 +04:00
buffer = ( BYTE * ) calloc ( Length , sizeof ( BYTE ) ) ;
if ( buffer = = NULL )
2011-10-04 22:15:15 +04:00
{
2014-05-12 19:33:56 +04:00
irp - > IoStatus = STATUS_NO_MEMORY ;
goto error_handle ;
}
2014-05-19 18:53:57 +04:00
/* MS-RDPESP 3.2.5.1.4: If the Offset field is not set to 0, the value MUST be ignored
2014-05-12 19:33:56 +04:00
* assert ( Offset = = 0 ) ;
*/
DEBUG_SVC ( " reading %lu bytes from %s " , Length , serial - > device . name ) ;
2011-10-04 22:15:15 +04:00
2014-05-12 19:33:56 +04:00
/* FIXME: CommReadFile to be replaced by ReadFile */
if ( CommReadFile ( serial - > hComm , buffer , Length , & nbRead , NULL ) )
{
irp - > IoStatus = STATUS_SUCCESS ;
2011-10-04 22:15:15 +04:00
}
else
{
2014-05-12 19:33:56 +04:00
DEBUG_SVC ( " read failure to %s, nbRead=%d, last-error: 0x%0.8x " , serial - > device . name , nbRead , GetLastError ( ) ) ;
2012-10-03 07:01:16 +04:00
2014-05-12 19:33:56 +04:00
switch ( GetLastError ( ) )
2011-10-04 22:15:15 +04:00
{
2014-05-12 19:33:56 +04:00
case ERROR_INVALID_HANDLE :
irp - > IoStatus = STATUS_INVALID_DEVICE_REQUEST ;
break ;
2011-10-04 22:15:15 +04:00
2014-05-12 19:33:56 +04:00
case ERROR_NOT_SUPPORTED :
irp - > IoStatus = STATUS_NOT_SUPPORTED ;
break ;
case ERROR_INVALID_PARAMETER :
irp - > IoStatus = STATUS_INVALID_PARAMETER ;
break ;
case ERROR_IO_DEVICE :
irp - > IoStatus = STATUS_IO_DEVICE_ERROR ;
break ;
case ERROR_TIMEOUT :
irp - > IoStatus = STATUS_TIMEOUT ;
break ;
case ERROR_BAD_DEVICE :
irp - > IoStatus = STATUS_INVALID_DEVICE_REQUEST ;
break ;
2014-05-14 23:21:31 +04:00
2014-05-12 19:33:56 +04:00
default :
DEBUG_SVC ( " unexpected last-error: 0x%x " , GetLastError ( ) ) ;
irp - > IoStatus = STATUS_UNSUCCESSFUL ;
break ;
2011-10-04 22:15:15 +04:00
}
}
2014-05-19 18:53:57 +04:00
DEBUG_SVC ( " %lu bytes read from %s " , nbRead , serial - > device . name ) ;
2014-05-12 19:33:56 +04:00
error_handle :
Stream_Write_UINT32 ( irp - > output , nbRead ) ; /* Length (4 bytes) */
2012-09-24 02:41:07 +04:00
2014-05-12 19:33:56 +04:00
if ( nbRead > 0 )
2011-10-04 22:15:15 +04:00
{
2014-05-12 19:33:56 +04:00
Stream_EnsureRemainingCapacity ( irp - > output , nbRead ) ;
Stream_Write ( irp - > output , buffer , nbRead ) ; /* ReadData */
2011-10-04 22:15:15 +04:00
}
2012-09-24 02:41:07 +04:00
2014-05-12 19:33:56 +04:00
if ( buffer )
free ( buffer ) ;
2011-10-04 22:15:15 +04:00
}
static void serial_process_irp_write ( SERIAL_DEVICE * serial , IRP * irp )
{
2012-10-09 11:26:39 +04:00
UINT32 Length ;
UINT64 Offset ;
2014-05-12 19:33:56 +04:00
DWORD nbWritten = 0 ;
2011-10-04 22:15:15 +04:00
2014-03-27 01:11:15 +04:00
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) */
2012-09-24 02:41:07 +04:00
2014-05-19 18:53:57 +04:00
/* 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 .
*/
2011-10-04 22:15:15 +04:00
2014-05-12 19:33:56 +04:00
DEBUG_SVC ( " writing %lu bytes to %s " , Length , serial - > device . name ) ;
2014-03-27 01:54:36 +04:00
2014-05-12 19:33:56 +04:00
/* FIXME: CommWriteFile to be replaced by WriteFile */
if ( CommWriteFile ( serial - > hComm , Stream_Pointer ( irp - > input ) , Length , & nbWritten , NULL ) )
{
irp - > IoStatus = STATUS_SUCCESS ;
2011-10-04 22:15:15 +04:00
}
2014-05-12 19:33:56 +04:00
else
{
DEBUG_SVC ( " write failure to %s, nbWritten=%d, last-error: 0x%0.8x " , serial - > device . name , nbWritten , GetLastError ( ) ) ;
switch ( GetLastError ( ) )
{
case ERROR_INVALID_HANDLE :
irp - > IoStatus = STATUS_INVALID_DEVICE_REQUEST ;
break ;
2014-03-27 01:54:36 +04:00
2014-05-12 19:33:56 +04:00
case ERROR_NOT_SUPPORTED :
irp - > IoStatus = STATUS_NOT_SUPPORTED ;
break ;
case ERROR_INVALID_PARAMETER :
irp - > IoStatus = STATUS_INVALID_PARAMETER ;
break ;
case ERROR_IO_DEVICE :
irp - > IoStatus = STATUS_IO_DEVICE_ERROR ;
break ;
2011-10-04 22:15:15 +04:00
2014-05-12 19:33:56 +04:00
case ERROR_TIMEOUT :
irp - > IoStatus = STATUS_TIMEOUT ;
break ;
2014-03-27 01:54:36 +04:00
2014-05-12 19:33:56 +04:00
case ERROR_BAD_DEVICE :
irp - > IoStatus = STATUS_INVALID_DEVICE_REQUEST ;
break ;
default :
DEBUG_SVC ( " unexpected last-error: 0x%x " , GetLastError ( ) ) ;
irp - > IoStatus = STATUS_UNSUCCESSFUL ;
break ;
}
2011-10-04 22:15:15 +04:00
}
2014-05-13 16:55:30 +04:00
DEBUG_SVC ( " %lu bytes written to %s " , nbWritten , serial - > device . name ) ;
2014-05-12 19:33:56 +04:00
Stream_Write_UINT32 ( irp - > output , nbWritten ) ; /* Length (4 bytes) */
2014-03-27 01:11:15 +04:00
Stream_Write_UINT8 ( irp - > output , 0 ) ; /* Padding (1 byte) */
2011-10-04 22:15:15 +04:00
}
2014-05-19 18:53:57 +04:00
2011-10-04 22:15:15 +04:00
static void serial_process_irp_device_control ( SERIAL_DEVICE * serial , IRP * irp )
{
2012-10-09 11:26:39 +04:00
UINT32 IoControlCode ;
UINT32 InputBufferLength ;
2014-04-27 21:41:25 +04:00
BYTE * InputBuffer = NULL ;
2012-10-09 11:26:39 +04:00
UINT32 OutputBufferLength ;
2014-04-27 21:41:25 +04:00
BYTE * OutputBuffer = NULL ;
DWORD BytesReturned = 0 ;
2011-10-04 22:15:15 +04:00
2014-03-27 01:11:15 +04:00
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) */
2012-09-24 02:41:07 +04:00
2014-04-27 21:41:25 +04:00
OutputBuffer = ( BYTE * ) calloc ( OutputBufferLength , sizeof ( BYTE ) ) ;
if ( OutputBuffer = = NULL )
{
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 ;
}
2014-05-12 19:33:56 +04:00
Stream_Read ( irp - > input , InputBuffer , InputBufferLength ) ;
2014-05-23 14:27:09 +04:00
DEBUG_SVC ( " CommDeviceIoControl: CompletionId=%d, IoControlCode=[0x%X] %s " , irp - > CompletionId , IoControlCode , _comm_serial_ioctl_name ( IoControlCode ) ) ;
2014-04-27 21:41:25 +04:00
2014-05-12 19:33:56 +04:00
/* FIXME: CommDeviceIoControl to be replaced by DeviceIoControl() */
2014-04-27 21:41:25 +04:00
if ( CommDeviceIoControl ( serial - > hComm , IoControlCode , InputBuffer , InputBufferLength , OutputBuffer , OutputBufferLength , & BytesReturned , NULL ) )
{
2014-05-23 14:27:09 +04:00
/* DEBUG_SVC("CommDeviceIoControl: CompletionId=%d, IoControlCode=[0x%X] %s done", irp->CompletionId, IoControlCode, _comm_serial_ioctl_name(IoControlCode)); */
2014-04-27 21:41:25 +04:00
irp - > IoStatus = STATUS_SUCCESS ;
2011-10-04 22:15:15 +04:00
}
else
{
2014-05-14 18:29:10 +04:00
DEBUG_SVC ( " CommDeviceIoControl failure: IoControlCode=[0x%0.8x] %s, last-error: 0x%x " ,
IoControlCode , _comm_serial_ioctl_name ( IoControlCode ) , GetLastError ( ) ) ;
2014-05-14 19:30:29 +04:00
// TMP: TODO: Status codes to be reviewed according: http://msdn.microsoft.com/en-us/library/ff547466%28v=vs.85%29.aspx#generic_status_values_for_serial_device_control_requests
2014-04-27 21:41:25 +04:00
switch ( GetLastError ( ) )
{
case ERROR_INVALID_HANDLE :
irp - > IoStatus = STATUS_INVALID_DEVICE_REQUEST ;
break ;
case ERROR_NOT_SUPPORTED :
2014-05-06 18:08:58 +04:00
irp - > IoStatus = STATUS_NOT_SUPPORTED ;
2014-04-27 21:41:25 +04:00
break ;
case ERROR_INSUFFICIENT_BUFFER :
irp - > IoStatus = STATUS_BUFFER_TOO_SMALL ; /* TMP: better have STATUS_BUFFER_SIZE_TOO_SMALL? http://msdn.microsoft.com/en-us/library/windows/hardware/ff547466%28v=vs.85%29.aspx#generic_status_values_for_serial_device_control_requests */
break ;
2014-04-29 06:04:09 +04:00
case ERROR_INVALID_PARAMETER :
irp - > IoStatus = STATUS_INVALID_PARAMETER ;
break ;
2014-05-06 18:08:58 +04:00
case ERROR_CALL_NOT_IMPLEMENTED :
irp - > IoStatus = STATUS_NOT_IMPLEMENTED ;
break ;
2014-05-14 18:29:10 +04:00
case ERROR_IO_PENDING :
irp - > IoStatus = STATUS_PENDING ;
break ;
2014-05-14 23:21:31 +04:00
case ERROR_INVALID_DEVICE_OBJECT_PARAMETER : /* eg: SerCx2.sys' _purge() */
irp - > IoStatus = STATUS_INVALID_DEVICE_STATE ;
break ;
case ERROR_CANCELLED :
irp - > IoStatus = STATUS_CANCELLED ;
break ;
2014-04-27 21:41:25 +04:00
default :
DEBUG_SVC ( " unexpected last-error: 0x%x " , GetLastError ( ) ) ;
2014-05-14 23:21:31 +04:00
irp - > IoStatus = STATUS_UNSUCCESSFUL ;
2014-04-27 21:41:25 +04:00
break ;
}
2011-10-04 22:15:15 +04:00
}
2014-04-27 21:41:25 +04:00
error_handle :
2014-05-12 19:33:56 +04:00
2014-05-19 18:53:57 +04:00
assert ( OutputBufferLength = = BytesReturned ) ;
2014-05-12 19:33:56 +04:00
Stream_Write_UINT32 ( irp - > output , BytesReturned ) ; /* OutputBufferLength (4 bytes) */
if ( BytesReturned > 0 )
{
Stream_EnsureRemainingCapacity ( irp - > output , BytesReturned ) ;
Stream_Write ( irp - > output , OutputBuffer , BytesReturned ) ; /* OutputBuffer */
}
2014-05-23 14:27:09 +04:00
/* FIXME: Why at least Windows 2008R2 gets lost with this
2014-05-12 21:21:06 +04:00
* 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) *\/ */
/* } */
2014-05-12 19:33:56 +04:00
2014-04-27 21:41:25 +04:00
if ( InputBuffer ! = NULL )
free ( InputBuffer ) ;
if ( OutputBuffer ! = NULL )
free ( OutputBuffer ) ;
2011-10-04 22:15:15 +04:00
}
static void serial_process_irp ( SERIAL_DEVICE * serial , IRP * irp )
{
2014-03-27 01:11:15 +04:00
WLog_Print ( serial - > log , WLOG_DEBUG , " IRP MajorFunction: 0x%04X MinorFunction: 0x%04X \n " ,
irp - > MajorFunction , irp - > MinorFunction ) ;
2011-10-04 22:15:15 +04:00
switch ( irp - > MajorFunction )
{
case IRP_MJ_CREATE :
serial_process_irp_create ( serial , irp ) ;
break ;
case IRP_MJ_CLOSE :
serial_process_irp_close ( serial , irp ) ;
break ;
case IRP_MJ_READ :
2014-03-26 23:21:44 +04:00
serial_process_irp_read ( serial , irp ) ;
2011-10-04 22:15:15 +04:00
break ;
case IRP_MJ_WRITE :
2014-03-26 23:21:44 +04:00
serial_process_irp_write ( serial , irp ) ;
2011-10-04 22:15:15 +04:00
break ;
case IRP_MJ_DEVICE_CONTROL :
serial_process_irp_device_control ( serial , irp ) ;
break ;
default :
DEBUG_WARN ( " MajorFunction 0x%X not supported " , irp - > MajorFunction ) ;
irp - > IoStatus = STATUS_NOT_SUPPORTED ;
break ;
}
2013-08-16 17:51:44 +04:00
}
2014-05-23 14:27:09 +04:00
2014-05-21 12:36:55 +04:00
static void * irp_thread_func ( void * arg )
2014-05-13 16:55:30 +04:00
{
2014-05-21 12:36:55 +04:00
IRP_THREAD_DATA * data = ( IRP_THREAD_DATA * ) arg ;
2014-05-13 16:55:30 +04:00
2014-05-21 12:36:55 +04:00
/* blocks until the end of the request */
serial_process_irp ( data - > serial , data - > irp ) ;
2014-05-23 14:27:09 +04:00
EnterCriticalSection ( & data - > serial - > TerminatingIrpThreadsLock ) ;
data - > serial - > IrpThreadToTerminateCount + + ;
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 */
2014-05-21 12:36:55 +04:00
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 ;
2014-05-23 14:27:09 +04:00
/* uncomment the code below to get a single thread per IRP for
* a test / debug purpose . NB : two IRPs could not occur at the
* same time , typically two concurent Read / Write
* operations . */
/* serial_process_irp(serial, irp); */
/* irp->Complete(irp); */
/* return; */
2014-05-21 12:36:55 +04:00
2014-05-23 14:27:09 +04:00
EnterCriticalSection ( & serial - > TerminatingIrpThreadsLock ) ;
while ( serial - > IrpThreadToTerminateCount > 0 )
2014-05-13 16:55:30 +04:00
{
2014-05-23 14:27:09 +04:00
/* Cleaning up termitating and pending irp
* threads . See also : irp_thread_func ( ) */
2014-05-13 16:55:30 +04:00
2014-05-23 14:27:09 +04:00
HANDLE irpThread ;
ULONG_PTR * ids ;
int i , nbIds ;
2014-05-13 16:55:30 +04:00
2014-05-23 14:27:09 +04:00
nbIds = ListDictionary_GetKeys ( serial - > IrpThreads , & ids ) ;
for ( i = 0 ; i < nbIds ; i + + )
2014-05-21 12:36:55 +04:00
{
2014-05-23 14:27:09 +04:00
/* Checking if ids[i] is terminating or pending */
2014-05-13 16:55:30 +04:00
2014-05-23 14:27:09 +04:00
DWORD waitResult ;
ULONG_PTR id = ids [ i ] ;
2014-05-13 16:55:30 +04:00
2014-05-23 14:27:09 +04:00
irpThread = ListDictionary_GetItemValue ( serial - > IrpThreads , ( void * ) id ) ;
2014-05-21 12:36:55 +04:00
2014-05-23 14:27:09 +04:00
/* 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 - > IrpThreadToTerminateCount - - ;
}
else if ( waitResult ! = WAIT_TIMEOUT )
{
/* unexpected thread state */
DEBUG_WARN ( " WaitForSingleObject, got an unexpected result=0x%X \n " , waitResult ) ;
assert ( FALSE ) ;
}
/* pending thread (but not yet terminating thread) if waitResult == WAIT_TIMEOUT */
2014-05-21 12:36:55 +04:00
}
2014-05-23 14:27:09 +04:00
2014-05-21 12:36:55 +04:00
2014-05-23 14:27:09 +04:00
assert ( serial - > IrpThreadToTerminateCount = = 0 ) ; /* TMP: */
2014-05-21 12:36:55 +04:00
2014-05-23 14:27:09 +04:00
if ( serial - > IrpThreadToTerminateCount > 0 )
{
DEBUG_SVC ( " %d IRP thread(s) not yet terminated " , serial - > IrpThreadToTerminateCount ) ;
Sleep ( 1 ) ; /* 1 ms */
2014-05-13 16:55:30 +04:00
}
}
2014-05-23 14:27:09 +04:00
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 FIXME :
* behavior documented somewhere ? ) .
*/
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 ) ;
/* TMP: TODO: taking over the pending IRP or sending a kind of wake up signal to accelerate the pending request */
assert ( FALSE ) ;
/* FIXME: asserts that the previous thread's IRP is
* well the same request by checking more
* details . Need an access to the IRP object used by
* previousIrpThread */
irp - > Discard ( irp ) ;
return ;
}
2014-05-13 16:55:30 +04:00
2014-05-21 12:36:55 +04:00
if ( ListDictionary_Count ( serial - > IrpThreads ) > = MAX_IRP_THREADS )
{
DEBUG_WARN ( " Maximal number of IRP threads reached: %d " , ListDictionary_Count ( serial - > IrpThreads ) ) ;
assert ( FALSE ) ;
/* TODO: FIXME: WaitForMultipleObjects() not yet implemented for threads */
}
2014-05-23 14:27:09 +04:00
2014-05-21 12:36:55 +04:00
/* 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 ;
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 ; /* data freed by irp_thread_func */
error_handle :
irp - > IoStatus = STATUS_NO_MEMORY ;
irp - > Complete ( irp ) ;
if ( data )
free ( data ) ;
2014-05-13 16:55:30 +04:00
}
2014-05-21 12:36:55 +04:00
2011-10-04 22:15:15 +04:00
static void * serial_thread_func ( void * arg )
{
2013-03-21 05:22:47 +04:00
IRP * irp ;
2014-03-26 23:21:44 +04:00
wMessage message ;
2014-05-13 16:55:30 +04:00
SERIAL_DEVICE * serial = ( SERIAL_DEVICE * ) arg ;
2011-10-04 22:15:15 +04:00
while ( 1 )
{
2014-05-13 16:55:30 +04:00
if ( ! MessageQueue_Wait ( serial - > MainIrpQueue ) )
2013-02-22 06:09:42 +04:00
break ;
2013-08-16 14:40:06 +04:00
2014-05-13 16:55:30 +04:00
if ( ! MessageQueue_Peek ( serial - > MainIrpQueue , & message , TRUE ) )
2014-03-26 23:21:44 +04:00
break ;
2013-08-16 14:40:06 +04:00
2014-03-26 23:21:44 +04:00
if ( message . id = = WMQ_QUIT )
break ;
irp = ( IRP * ) message . wParam ;
2013-07-03 16:09:04 +04:00
2014-03-26 23:21:44 +04:00
if ( irp )
2014-05-21 12:36:55 +04:00
create_irp_thread ( serial , irp ) ;
2011-10-04 22:15:15 +04:00
}
2013-09-05 16:27:34 +04:00
ExitThread ( 0 ) ;
2011-10-04 22:15:15 +04:00
return NULL ;
}
2014-05-19 18:53:57 +04:00
2011-10-04 22:15:15 +04:00
static void serial_irp_request ( DEVICE * device , IRP * irp )
{
2013-02-08 19:59:37 +04:00
SERIAL_DEVICE * serial = ( SERIAL_DEVICE * ) device ;
2014-05-13 16:55:30 +04:00
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 .
*/
2014-05-21 12:36:55 +04:00
MessageQueue_Post ( serial - > MainIrpQueue , NULL , 0 , ( void * ) irp , NULL ) ;
2011-10-04 22:15:15 +04:00
}
static void serial_free ( DEVICE * device )
{
2012-10-03 07:01:16 +04:00
SERIAL_DEVICE * serial = ( SERIAL_DEVICE * ) device ;
2014-05-12 19:33:56 +04:00
2014-03-27 01:11:15 +04:00
WLog_Print ( serial - > log , WLOG_DEBUG , " freeing " ) ;
2011-10-04 22:15:15 +04:00
2014-05-13 16:55:30 +04:00
MessageQueue_PostQuit ( serial - > MainIrpQueue , 0 ) ;
2014-05-13 19:27:51 +04:00
WaitForSingleObject ( serial - > MainThread , INFINITE ) ; /* FIXME: might likely block on a pending Write or ioctl */
2014-05-13 16:55:30 +04:00
CloseHandle ( serial - > MainThread ) ;
2013-08-13 18:04:19 +04:00
2014-05-12 19:33:56 +04:00
if ( serial - > hComm )
CloseHandle ( serial - > hComm ) ;
2013-08-14 17:14:40 +04:00
2013-08-13 18:04:19 +04:00
/* Clean up resources */
Stream_Free ( serial - > device . data , TRUE ) ;
2014-05-13 16:55:30 +04:00
MessageQueue_Free ( serial - > MainIrpQueue ) ;
2014-05-21 12:36:55 +04:00
ListDictionary_Free ( serial - > IrpThreads ) ;
2014-05-23 14:27:09 +04:00
DeleteCriticalSection ( & serial - > TerminatingIrpThreadsLock ) ;
2014-03-26 19:05:12 +04:00
2012-10-09 07:21:26 +04:00
free ( serial ) ;
2011-10-04 22:15:15 +04:00
}
2012-11-09 04:01:52 +04:00
# ifdef STATIC_CHANNELS
# define DeviceServiceEntry serial_DeviceServiceEntry
# endif
int DeviceServiceEntry ( PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints )
{
int i , len ;
char * name ;
char * path ;
RDPDR_SERIAL * device ;
SERIAL_DEVICE * serial ;
device = ( RDPDR_SERIAL * ) pEntryPoints - > device ;
name = device - > Name ;
path = device - > Path ;
2014-03-26 19:05:12 +04:00
if ( ! name | | ( name [ 0 ] = = ' * ' ) )
{
/* TODO: implement auto detection of parallel ports */
return 0 ;
}
2013-07-03 16:09:04 +04:00
if ( ( name & & name [ 0 ] ) & & ( path & & path [ 0 ] ) )
2012-11-09 04:01:52 +04:00
{
2014-05-12 19:33:56 +04:00
DEBUG_SVC ( " Defining %s as %s " , name , path ) ;
2014-04-27 21:41:25 +04:00
if ( ! DefineCommDevice ( name /* eg: COM1 */ , path /* eg: /dev/ttyS0 */ ) )
{
2014-05-12 19:33:56 +04:00
DEBUG_SVC ( " Could not define %s as %s " , name , path ) ;
2014-04-27 21:41:25 +04:00
return - 1 ;
}
2014-03-26 19:05:12 +04:00
2014-04-27 21:41:25 +04:00
serial = ( SERIAL_DEVICE * ) calloc ( 1 , sizeof ( SERIAL_DEVICE ) ) ;
2014-03-26 19:05:12 +04:00
if ( ! serial )
return - 1 ;
2012-11-09 04:01:52 +04:00
serial - > device . type = RDPDR_DTYP_SERIAL ;
serial - > device . name = name ;
serial - > device . IRPRequest = serial_irp_request ;
serial - > device . Free = serial_free ;
len = strlen ( name ) ;
2013-05-09 01:48:30 +04:00
serial - > device . data = Stream_New ( NULL , len + 1 ) ;
2012-11-09 04:01:52 +04:00
for ( i = 0 ; i < = len ; i + + )
2013-05-09 00:09:16 +04:00
Stream_Write_UINT8 ( serial - > device . data , name [ i ] < 0 ? ' _ ' : name [ i ] ) ;
2012-11-09 04:01:52 +04:00
2014-05-13 16:55:30 +04:00
serial - > MainIrpQueue = MessageQueue_New ( NULL ) ;
2013-03-21 05:22:47 +04:00
2014-05-21 12:36:55 +04:00
serial - > IrpThreads = ListDictionary_New ( FALSE ) ; /* only handled in create_irp_thread() */
2014-05-23 14:27:09 +04:00
serial - > IrpThreadToTerminateCount = 0 ;
InitializeCriticalSection ( & serial - > TerminatingIrpThreadsLock ) ;
2014-03-27 01:11:15 +04:00
WLog_Init ( ) ;
serial - > log = WLog_Get ( " com.freerdp.channel.serial.client " ) ;
WLog_Print ( serial - > log , WLOG_DEBUG , " initializing " ) ;
2012-11-27 05:15:48 +04:00
pEntryPoints - > RegisterDevice ( pEntryPoints - > devman , ( DEVICE * ) serial ) ;
2012-11-09 04:01:52 +04:00
2014-05-13 16:55:30 +04:00
serial - > MainThread = CreateThread ( NULL ,
0 ,
( LPTHREAD_START_ROUTINE ) serial_thread_func ,
( void * ) serial ,
0 ,
NULL ) ;
2012-11-09 04:01:52 +04:00
}
return 0 ;
}