Merge branch 'master' of github.com:FreeRDP/FreeRDP

This commit is contained in:
Marc-André Moreau 2011-10-16 00:50:48 -04:00
commit fb5a7d73e4
21 changed files with 3842 additions and 0 deletions

View File

@ -107,6 +107,7 @@ if(NOT WIN32)
find_required_package(ZLIB)
find_suggested_package(ALSA)
find_optional_package(PulseAudio)
find_optional_package(PCSC)
find_suggested_package(Cups)
find_suggested_package(FFmpeg)
endif()
@ -127,6 +128,10 @@ include_directories(${CMAKE_SOURCE_DIR}/include)
# Configure files
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config.h.in ${CMAKE_CURRENT_BINARY_DIR}/config.h)
# Generate pkg-config
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/freerdp.pc.in ${CMAKE_CURRENT_BINARY_DIR}/freerdp.pc @ONLY)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/freerdp.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig)
# Mac OS X
if(APPLE)
include_directories(/opt/local/include)

View File

@ -40,3 +40,8 @@ install(TARGETS rdpdr DESTINATION ${FREERDP_PLUGIN_PATH})
add_subdirectory(disk)
add_subdirectory(printer)
add_subdirectory(parallel)
add_subdirectory(serial)
if(PCSC_FOUND)
add_subdirectory(smartcard)
endif()

View File

@ -24,6 +24,7 @@
#include "config.h"
#include <freerdp/utils/stream.h>
#include <freerdp/utils/list.h>
#include <freerdp/utils/svc_plugin.h>
typedef struct _DEVICE DEVICE;
typedef struct _IRP IRP;

View File

@ -0,0 +1,34 @@
# FreeRDP: A Remote Desktop Protocol Client
# FreeRDP cmake build script
#
# Copyright 2011 O.S. Systems Software Ltda.
# Copyright 2011 Otavio Salvador <otavio@ossystems.com.br>
# Copyright 2011 Marc-Andre Moreau <marcandre.moreau@gmail.com>
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
set(serial_SRCS
serial_tty.c
serial_tty.h
serial_constants.h
serial_main.c
)
include_directories(..)
add_library(serial ${serial_SRCS})
set_target_properties(serial PROPERTIES PREFIX "")
target_link_libraries(serial freerdp-utils)
install(TARGETS serial DESTINATION ${FREERDP_PLUGIN_PATH})

View File

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

View File

@ -0,0 +1,710 @@
/**
* FreeRDP: A Remote Desktop Protocol client.
* Serial Port Device Service Virtual Channel
*
* Copyright 2011 O.S. Systems Software Ltda.
* Copyright 2011 Eduardo Fiss Beloni <beloni@ossystems.com.br>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include "config.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
#include "rdpdr_types.h"
#include "rdpdr_constants.h"
#include "devman.h"
#include "serial_tty.h"
#include "serial_constants.h"
#include <freerdp/utils/stream.h>
#include <freerdp/utils/thread.h>
#include <freerdp/utils/unicode.h>
#include <freerdp/utils/wait_obj.h>
typedef struct _SERIAL_DEVICE SERIAL_DEVICE;
struct _SERIAL_DEVICE
{
DEVICE device;
char* path;
SERIAL_TTY* tty;
LIST* irp_list;
LIST* pending_irps;
freerdp_thread* thread;
struct wait_obj* in_event;
fd_set read_fds;
fd_set write_fds;
uint32 nfds;
struct timeval tv;
uint32 select_timeout;
uint32 timeout_id;
};
static void serial_abort_single_io(SERIAL_DEVICE* serial, uint32 file_id, uint32 abort_io, uint32 io_status);
static void serial_check_for_events(SERIAL_DEVICE* serial);
static void serial_handle_async_irp(SERIAL_DEVICE* serial, IRP* irp);
static boolean serial_check_fds(SERIAL_DEVICE* serial);
static void serial_process_irp_create(SERIAL_DEVICE* serial, IRP* irp)
{
SERIAL_TTY* tty;
uint32 PathLength;
uint32 FileId;
char* path;
UNICONV* uniconv;
stream_seek(irp->input, 28); /* DesiredAccess(4) AllocationSize(8), FileAttributes(4) */
/* SharedAccess(4) CreateDisposition(4), CreateOptions(4) */
stream_read_uint32(irp->input, PathLength);
uniconv = freerdp_uniconv_new();
path = freerdp_uniconv_in(uniconv, stream_get_tail(irp->input), PathLength);
freerdp_uniconv_free(uniconv);
FileId = irp->devman->id_sequence++;
tty = serial_tty_new(serial->path, FileId);
if (tty == NULL)
{
irp->IoStatus = STATUS_UNSUCCESSFUL;
FileId = 0;
DEBUG_WARN("failed to create %s", path);
}
else
{
serial->tty = tty;
DEBUG_SVC("%s(%d) created.", serial->path, FileId);
}
stream_write_uint32(irp->output, FileId);
stream_write_uint8(irp->output, 0);
xfree(path);
irp->Complete(irp);
}
static void serial_process_irp_close(SERIAL_DEVICE* serial, IRP* irp)
{
SERIAL_TTY* tty;
tty = serial->tty;
if (tty == NULL)
{
irp->IoStatus = STATUS_UNSUCCESSFUL;
DEBUG_WARN("tty not valid.");
}
else
{
DEBUG_SVC("%s(%d) closed.", serial->path, tty->id);
serial_tty_free(tty);
serial->tty = NULL;
}
stream_write_zero(irp->output, 5); /* Padding(5) */
irp->Complete(irp);
}
static void serial_process_irp_read(SERIAL_DEVICE* serial, IRP* irp)
{
SERIAL_TTY* tty;
uint32 Length;
uint64 Offset;
uint8* buffer = NULL;
stream_read_uint32(irp->input, Length);
stream_read_uint64(irp->input, Offset);
DEBUG_SVC("length %u offset %llu", Length, Offset);
tty = serial->tty;
if (tty == NULL)
{
irp->IoStatus = STATUS_UNSUCCESSFUL;
Length = 0;
DEBUG_WARN("tty not valid.");
}
else
{
buffer = (uint8*)xmalloc(Length);
if (!serial_tty_read(tty, buffer, &Length))
{
irp->IoStatus = STATUS_UNSUCCESSFUL;
xfree(buffer);
buffer = NULL;
Length = 0;
DEBUG_WARN("read %s(%d) failed.", serial->path, tty->id);
}
else
{
DEBUG_SVC("read %llu-%llu from %d", Offset, Offset + Length, tty->id);
}
}
stream_write_uint32(irp->output, Length);
if (Length > 0)
{
stream_check_size(irp->output, Length);
stream_write(irp->output, buffer, Length);
}
xfree(buffer);
irp->Complete(irp);
}
static void serial_process_irp_write(SERIAL_DEVICE* serial, IRP* irp)
{
SERIAL_TTY* tty;
uint32 Length;
uint64 Offset;
stream_read_uint32(irp->input, Length);
stream_read_uint64(irp->input, Offset);
stream_seek(irp->input, 20); /* Padding */
DEBUG_SVC("length %u offset %llu", Length, Offset);
tty = serial->tty;
if (tty == NULL)
{
irp->IoStatus = STATUS_UNSUCCESSFUL;
Length = 0;
DEBUG_WARN("tty not valid.");
}
else if (!serial_tty_write(tty, stream_get_tail(irp->input), Length))
{
irp->IoStatus = STATUS_UNSUCCESSFUL;
Length = 0;
DEBUG_WARN("write %s(%d) failed.", serial->path, tty->id);
}
else
{
DEBUG_SVC("write %llu-%llu to %s(%d).", Offset, Offset + Length, serial->path, tty->id);
}
stream_write_uint32(irp->output, Length);
stream_write_uint8(irp->output, 0); /* Padding */
irp->Complete(irp);
}
static void serial_process_irp_device_control(SERIAL_DEVICE* serial, IRP* irp)
{
SERIAL_TTY* tty;
uint32 IoControlCode;
uint32 InputBufferLength;
uint32 OutputBufferLength;
uint32 abort_io = SERIAL_ABORT_IO_NONE;
DEBUG_SVC("[in] pending size %d", list_size(serial->pending_irps));
stream_read_uint32(irp->input, InputBufferLength);
stream_read_uint32(irp->input, OutputBufferLength);
stream_read_uint32(irp->input, IoControlCode);
stream_seek(irp->input, 20); /* Padding */
tty = serial->tty;
if (tty == NULL)
{
irp->IoStatus = STATUS_UNSUCCESSFUL;
OutputBufferLength = 0;
DEBUG_WARN("tty not valid.");
}
else
{
irp->IoStatus = serial_tty_control(tty, IoControlCode, irp->input, irp->output, &abort_io);
}
if (abort_io & SERIAL_ABORT_IO_WRITE)
serial_abort_single_io(serial, tty->id, SERIAL_ABORT_IO_WRITE, STATUS_CANCELLED);
if (abort_io & SERIAL_ABORT_IO_READ)
serial_abort_single_io(serial, tty->id, SERIAL_ABORT_IO_READ, STATUS_CANCELLED);
if (irp->IoStatus == STATUS_PENDING)
list_enqueue(serial->pending_irps, irp);
else
irp->Complete(irp);
}
static void serial_process_irp(SERIAL_DEVICE* serial, IRP* irp)
{
DEBUG_SVC("MajorFunction %u", irp->MajorFunction);
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:
serial_handle_async_irp(serial, irp);
//serial_process_irp_read(serial, irp);
break;
case IRP_MJ_WRITE:
serial_handle_async_irp(serial, irp);
//serial_process_irp_write(serial, irp);
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;
irp->Complete(irp);
break;
}
serial_check_for_events(serial);
}
static void serial_process_irp_list(SERIAL_DEVICE* serial)
{
IRP* irp;
while (1)
{
if (freerdp_thread_is_stopped(serial->thread))
break;
freerdp_thread_lock(serial->thread);
irp = (IRP*)list_dequeue(serial->irp_list);
freerdp_thread_unlock(serial->thread);
if (irp == NULL)
break;
serial_process_irp(serial, irp);
}
}
static void* serial_thread_func(void* arg)
{
SERIAL_DEVICE* serial = (SERIAL_DEVICE*)arg;
while (1)
{
freerdp_thread_wait(serial->thread);
serial->nfds = 1;
FD_ZERO(&serial->read_fds);
FD_ZERO(&serial->write_fds);
serial->tv.tv_sec = 20;
serial->tv.tv_usec = 0;
serial->select_timeout = 0;
if (freerdp_thread_is_stopped(serial->thread))
break;
freerdp_thread_reset(serial->thread);
serial_process_irp_list(serial);
if (wait_obj_is_set(serial->in_event))
{
if (serial_check_fds(serial))
wait_obj_clear(serial->in_event);
}
}
freerdp_thread_quit(serial->thread);
return NULL;
}
static void serial_irp_request(DEVICE* device, IRP* irp)
{
SERIAL_DEVICE* serial = (SERIAL_DEVICE*)device;
freerdp_thread_lock(serial->thread);
list_enqueue(serial->irp_list, irp);
freerdp_thread_unlock(serial->thread);
freerdp_thread_signal(serial->thread);
}
static void serial_free(DEVICE* device)
{
SERIAL_DEVICE* serial = (SERIAL_DEVICE*)device;
IRP* irp;
DEBUG_SVC("freeing device");
freerdp_thread_stop(serial->thread);
freerdp_thread_free(serial->thread);
while ((irp = (IRP*)list_dequeue(serial->irp_list)) != NULL)
irp->Discard(irp);
list_free(serial->irp_list);
while ((irp = (IRP*)list_dequeue(serial->pending_irps)) != NULL)
irp->Discard(irp);
list_free(serial->pending_irps);
xfree(serial);
}
int DeviceServiceEntry(PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints)
{
SERIAL_DEVICE* serial;
char* name;
char* path;
int i, len;
name = (char*)pEntryPoints->plugin_data->data[1];
path = (char*)pEntryPoints->plugin_data->data[2];
if (name[0] && path[0])
{
serial = xnew(SERIAL_DEVICE);
serial->device.type = RDPDR_DTYP_SERIAL;
serial->device.name = name;
serial->device.IRPRequest = serial_irp_request;
serial->device.Free = serial_free;
len = strlen(name);
serial->device.data = stream_new(len + 1);
for (i = 0; i <= len; i++)
stream_write_uint8(serial->device.data, name[i] < 0 ? '_' : name[i]);
serial->path = path;
serial->irp_list = list_new();
serial->pending_irps = list_new();
serial->thread = freerdp_thread_new();
serial->in_event = wait_obj_new();
pEntryPoints->RegisterDevice(pEntryPoints->devman, (DEVICE*)serial);
freerdp_thread_start(serial->thread, serial_thread_func, serial);
}
return 0;
}
static void serial_abort_single_io(SERIAL_DEVICE* serial, uint32 file_id, uint32 abort_io, uint32 io_status)
{
IRP* irp = NULL;
uint32 major;
SERIAL_TTY* tty;
DEBUG_SVC("[in] pending size %d", list_size(serial->pending_irps));
tty = serial->tty;
switch (abort_io)
{
case SERIAL_ABORT_IO_NONE:
major = 0;
break;
case SERIAL_ABORT_IO_READ:
major = IRP_MJ_READ;
break;
case SERIAL_ABORT_IO_WRITE:
major = IRP_MJ_WRITE;
break;
default:
DEBUG_SVC("unexpected abort_io code %d", abort_io);
return;
}
irp = (IRP*)list_peek(serial->pending_irps);
while (irp)
{
if (irp->FileId != file_id || irp->MajorFunction != major)
{
irp = (IRP*)list_next(serial->pending_irps, irp);
continue;
}
/* Process a SINGLE FileId and MajorFunction */
list_remove(serial->pending_irps, irp);
irp->IoStatus = io_status;
irp->Complete(irp);
wait_obj_set(serial->in_event);
break;
}
DEBUG_SVC("[out] pending size %d", list_size(serial->pending_irps));
}
static void serial_check_for_events(SERIAL_DEVICE* serial)
{
IRP* irp = NULL;
IRP* prev;
uint32 result = 0;
SERIAL_TTY* tty;
tty = serial->tty;
DEBUG_SVC("[in] pending size %d", list_size(serial->pending_irps));
irp = (IRP*)list_peek(serial->pending_irps);
while (irp)
{
prev = NULL;
if (irp->MajorFunction == IRP_MJ_DEVICE_CONTROL)
{
if (serial_tty_get_event(tty, &result))
{
DEBUG_SVC("got event result %u", result);
irp->IoStatus = STATUS_SUCCESS;
stream_write_uint32(irp->output, result);
irp->Complete(irp);
prev = irp;
irp = (IRP*)list_next(serial->pending_irps, irp);
list_remove(serial->pending_irps, prev);
wait_obj_set(serial->in_event);
}
}
if (!prev)
irp = (IRP*)list_next(serial->pending_irps, irp);
}
DEBUG_SVC("[out] pending size %d", list_size(serial->pending_irps));
}
void serial_get_timeouts(SERIAL_DEVICE* serial, IRP* irp, uint32* timeout, uint32* interval_timeout)
{
SERIAL_TTY* tty;
uint32 Length;
uint32 pos;
pos = stream_get_pos(irp->input);
stream_read_uint32(irp->input, Length);
stream_set_pos(irp->input, pos);
DEBUG_SVC("length read %u", Length);
tty = serial->tty;
*timeout = (tty->read_total_timeout_multiplier * Length) +
tty->read_total_timeout_constant;
*interval_timeout = tty->read_interval_timeout;
DEBUG_SVC("timeouts %u %u", *timeout, *interval_timeout);
}
static void serial_handle_async_irp(SERIAL_DEVICE* serial, IRP* irp)
{
uint32 timeout = 0;
uint32 itv_timeout = 0;
SERIAL_TTY* tty;
tty = serial->tty;
switch (irp->MajorFunction)
{
case IRP_MJ_WRITE:
DEBUG_SVC("handling IRP_MJ_WRITE");
break;
case IRP_MJ_READ:
DEBUG_SVC("handling IRP_MJ_READ");
serial_get_timeouts(serial, irp, &timeout, &itv_timeout);
/* Check if io request timeout is smaller than current (but not 0). */
if (timeout && (serial->select_timeout == 0 || timeout < serial->select_timeout))
{
serial->select_timeout = timeout;
serial->tv.tv_sec = serial->select_timeout / 1000;
serial->tv.tv_usec = (serial->select_timeout % 1000) * 1000;
serial->timeout_id = tty->id;
}
if (itv_timeout && (serial->select_timeout == 0 || itv_timeout < serial->select_timeout))
{
serial->select_timeout = itv_timeout;
serial->tv.tv_sec = serial->select_timeout / 1000;
serial->tv.tv_usec = (serial->select_timeout % 1000) * 1000;
serial->timeout_id = tty->id;
}
DEBUG_SVC("select_timeout %u, tv_sec %lu tv_usec %lu, timeout_id %u",
serial->select_timeout, serial->tv.tv_sec, serial->tv.tv_usec, serial->timeout_id);
break;
default:
DEBUG_SVC("no need to handle %d", irp->MajorFunction);
return;
}
irp->IoStatus = STATUS_PENDING;
list_enqueue(serial->pending_irps, irp);
wait_obj_set(serial->in_event);
}
static void __serial_check_fds(SERIAL_DEVICE* serial)
{
IRP* irp;
IRP* prev;
SERIAL_TTY* tty;
uint32 result = 0;
memset(&serial->tv, 0, sizeof(struct timeval));
tty = serial->tty;
/* scan every pending */
irp = list_peek(serial->pending_irps);
while (irp)
{
DEBUG_SVC("MajorFunction %u", irp->MajorFunction);
switch (irp->MajorFunction)
{
case IRP_MJ_READ:
if (FD_ISSET(tty->fd, &serial->read_fds))
{
irp->IoStatus = STATUS_SUCCESS;
serial_process_irp_read(serial, irp);
}
break;
case IRP_MJ_WRITE:
if (FD_ISSET(tty->fd, &serial->write_fds))
{
irp->IoStatus = STATUS_SUCCESS;
serial_process_irp_write(serial, irp);
}
break;
case IRP_MJ_DEVICE_CONTROL:
if (serial_tty_get_event(tty, &result))
{
DEBUG_SVC("got event result %u", result);
irp->IoStatus = STATUS_SUCCESS;
stream_write_uint32(irp->output, result);
irp->Complete(irp);
}
break;
default:
DEBUG_SVC("no request found");
break;
}
prev = irp;
irp = (IRP*)list_next(serial->pending_irps, irp);
if (prev->IoStatus == STATUS_SUCCESS)
{
list_remove(serial->pending_irps, prev);
wait_obj_set(serial->in_event);
}
}
}
static void serial_set_fds(SERIAL_DEVICE* serial)
{
fd_set* fds;
IRP* irp;
SERIAL_TTY* tty;
DEBUG_SVC("[in] pending size %d", list_size(serial->pending_irps));
tty = serial->tty;
irp = (IRP*)list_peek(serial->pending_irps);
while (irp)
{
fds = NULL;
switch (irp->MajorFunction)
{
case IRP_MJ_WRITE:
fds = &serial->write_fds;
break;
case IRP_MJ_READ:
fds = &serial->read_fds;
break;
}
if (fds && (tty->fd >= 0))
{
FD_SET(tty->fd, fds);
serial->nfds = MAX(serial->nfds, tty->fd);
}
irp = (IRP*)list_next(serial->pending_irps, irp);
}
}
static boolean serial_check_fds(SERIAL_DEVICE* serial)
{
if (list_size(serial->pending_irps) == 0)
return 1;
serial_set_fds(serial);
DEBUG_SVC("waiting %lu %lu", serial->tv.tv_sec, serial->tv.tv_usec);
switch (select(serial->nfds + 1, &serial->read_fds, &serial->write_fds, NULL, &serial->tv))
{
case -1:
DEBUG_SVC("select has returned -1 with error: %s", strerror(errno));
return 0;
case 0:
if (serial->select_timeout)
{
serial_abort_single_io(serial, serial->timeout_id, SERIAL_ABORT_IO_NONE, STATUS_TIMEOUT);
serial_abort_single_io(serial, serial->timeout_id, SERIAL_ABORT_IO_READ, STATUS_TIMEOUT);
serial_abort_single_io(serial, serial->timeout_id, SERIAL_ABORT_IO_WRITE, STATUS_TIMEOUT);
}
DEBUG_SVC("select has timed out");
return 0;
default:
break;
}
__serial_check_fds(serial);
return 1;
}

View File

@ -0,0 +1,973 @@
/**
* FreeRDP: A Remote Desktop Protocol client.
* Serial Port Device Service Virtual Channel
*
* Copyright 2011 O.S. Systems Software Ltda.
* Copyright 2011 Eduardo Fiss Beloni <beloni@ossystems.com.br>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "config.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <freerdp/utils/memory.h>
#include <freerdp/utils/stream.h>
#include <freerdp/utils/unicode.h>
#include <freerdp/utils/list.h>
#include <freerdp/utils/thread.h>
#include <freerdp/utils/svc_plugin.h>
#include <freerdp/utils/hexdump.h>
#include <unistd.h>
#include <termios.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <sys/ioctl.h>
#include <errno.h>
#include "rdpdr_constants.h"
#include "rdpdr_types.h"
#include "serial_tty.h"
#include "serial_constants.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
#ifndef CRTSCTS
#define CRTSCTS 0
#endif
/* FIONREAD should really do the same thing as TIOCINQ, where it is
* not available */
#if !defined(TIOCINQ) && defined(FIONREAD)
#define TIOCINQ FIONREAD
#endif
#if !defined(TIOCOUTQ) && defined(FIONWRITE)
#define TIOCOUTQ FIONWRITE
#endif
static uint32 tty_write_data(SERIAL_TTY* tty, uint8* data, int len);
static void tty_set_termios(SERIAL_TTY* tty);
static boolean tty_get_termios(SERIAL_TTY* tty);
static int tty_get_error_status();
uint32 serial_tty_control(SERIAL_TTY* tty, uint32 IoControlCode, STREAM* input, STREAM* output, uint32* abort_io)
{
int purge_mask;
uint32 result;
uint32 modemstate;
uint8 immediate;
uint32 ret = STATUS_SUCCESS;
uint32 length = 0;
uint32 pos;
DEBUG_SVC("in");
stream_seek(output, sizeof(uint32));
switch (IoControlCode)
{
case IOCTL_SERIAL_SET_BAUD_RATE:
stream_read_uint32(input, tty->baud_rate);
tty_set_termios(tty);
DEBUG_SVC("SERIAL_SET_BAUD_RATE %d", tty->baud_rate);
break;
case IOCTL_SERIAL_GET_BAUD_RATE:
length = 4;
stream_write_uint32(output, tty->baud_rate);
DEBUG_SVC("SERIAL_GET_BAUD_RATE %d", tty->baud_rate);
break;
case IOCTL_SERIAL_SET_QUEUE_SIZE:
stream_read_uint32(input, tty->queue_in_size);
stream_read_uint32(input, tty->queue_out_size);
DEBUG_SVC("SERIAL_SET_QUEUE_SIZE in %d out %d", tty->queue_in_size, tty->queue_out_size);
break;
case IOCTL_SERIAL_SET_LINE_CONTROL:
stream_read_uint8(input, tty->stop_bits);
stream_read_uint8(input, tty->parity);
stream_read_uint8(input, tty->word_length);
tty_set_termios(tty);
DEBUG_SVC("SERIAL_SET_LINE_CONTROL stop %d parity %d word %d",
tty->stop_bits, tty->parity, tty->word_length);
break;
case IOCTL_SERIAL_GET_LINE_CONTROL:
DEBUG_SVC("SERIAL_GET_LINE_CONTROL");
length = 3;
stream_write_uint8(output, tty->stop_bits);
stream_write_uint8(output, tty->parity);
stream_write_uint8(output, tty->word_length);
break;
case IOCTL_SERIAL_IMMEDIATE_CHAR:
DEBUG_SVC("SERIAL_IMMEDIATE_CHAR");
stream_read_uint8(input, immediate);
tty_write_data(tty, &immediate, 1);
break;
case IOCTL_SERIAL_CONFIG_SIZE:
DEBUG_SVC("SERIAL_CONFIG_SIZE");
length = 4;
stream_write_uint32(output, 0);
break;
case IOCTL_SERIAL_GET_CHARS:
DEBUG_SVC("SERIAL_GET_CHARS");
length = 6;
stream_write(output, tty->chars, 6);
break;
case IOCTL_SERIAL_SET_CHARS:
DEBUG_SVC("SERIAL_SET_CHARS");
stream_read(input, tty->chars, 6);
tty_set_termios(tty);
break;
case IOCTL_SERIAL_GET_HANDFLOW:
length = 16;
tty_get_termios(tty);
stream_write_uint32(output, tty->control);
stream_write_uint32(output, tty->xonoff);
stream_write_uint32(output, tty->onlimit);
stream_write_uint32(output, tty->offlimit);
DEBUG_SVC("IOCTL_SERIAL_GET_HANDFLOW %X %X %X %X",
tty->control, tty->xonoff, tty->onlimit, tty->offlimit);
break;
case IOCTL_SERIAL_SET_HANDFLOW:
stream_read_uint32(input, tty->control);
stream_read_uint32(input, tty->xonoff);
stream_read_uint32(input, tty->onlimit);
stream_read_uint32(input, tty->offlimit);
DEBUG_SVC("IOCTL_SERIAL_SET_HANDFLOW %X %X %X %X",
tty->control, tty->xonoff, tty->onlimit, tty->offlimit);
tty_set_termios(tty);
break;
case IOCTL_SERIAL_SET_TIMEOUTS:
stream_read_uint32(input, tty->read_interval_timeout);
stream_read_uint32(input, tty->read_total_timeout_multiplier);
stream_read_uint32(input, tty->read_total_timeout_constant);
stream_read_uint32(input, tty->write_total_timeout_multiplier);
stream_read_uint32(input, tty->write_total_timeout_constant);
/* http://www.codeproject.com/KB/system/chaiyasit_t.aspx, see 'ReadIntervalTimeout' section
http://msdn.microsoft.com/en-us/library/ms885171.aspx */
if (tty->read_interval_timeout == SERIAL_TIMEOUT_MAX)
{
tty->read_interval_timeout = 0;
tty->read_total_timeout_multiplier = 0;
}
DEBUG_SVC("SERIAL_SET_TIMEOUTS read timeout %d %d %d",
tty->read_interval_timeout,
tty->read_total_timeout_multiplier,
tty->read_total_timeout_constant);
break;
case IOCTL_SERIAL_GET_TIMEOUTS:
DEBUG_SVC("SERIAL_GET_TIMEOUTS read timeout %d %d %d",
tty->read_interval_timeout,
tty->read_total_timeout_multiplier,
tty->read_total_timeout_constant);
length = 20;
stream_write_uint32(output, tty->read_interval_timeout);
stream_write_uint32(output, tty->read_total_timeout_multiplier);
stream_write_uint32(output, tty->read_total_timeout_constant);
stream_write_uint32(output, tty->write_total_timeout_multiplier);
stream_write_uint32(output, tty->write_total_timeout_constant);
break;
case IOCTL_SERIAL_GET_WAIT_MASK:
DEBUG_SVC("SERIAL_GET_WAIT_MASK %X", tty->wait_mask);
length = 4;
stream_write_uint32(output, tty->wait_mask);
break;
case IOCTL_SERIAL_SET_WAIT_MASK:
stream_read_uint32(input, tty->wait_mask);
DEBUG_SVC("SERIAL_SET_WAIT_MASK %X", tty->wait_mask);
break;
case IOCTL_SERIAL_SET_DTR:
DEBUG_SVC("SERIAL_SET_DTR");
ioctl(tty->fd, TIOCMGET, &result);
result |= TIOCM_DTR;
ioctl(tty->fd, TIOCMSET, &result);
tty->dtr = 1;
break;
case IOCTL_SERIAL_CLR_DTR:
DEBUG_SVC("SERIAL_CLR_DTR");
ioctl(tty->fd, TIOCMGET, &result);
result &= ~TIOCM_DTR;
ioctl(tty->fd, TIOCMSET, &result);
tty->dtr = 0;
break;
case IOCTL_SERIAL_SET_RTS:
DEBUG_SVC("SERIAL_SET_RTS");
ioctl(tty->fd, TIOCMGET, &result);
result |= TIOCM_RTS;
ioctl(tty->fd, TIOCMSET, &result);
tty->rts = 1;
break;
case IOCTL_SERIAL_CLR_RTS:
DEBUG_SVC("SERIAL_CLR_RTS");
ioctl(tty->fd, TIOCMGET, &result);
result &= ~TIOCM_RTS;
ioctl(tty->fd, TIOCMSET, &result);
tty->rts = 0;
break;
case IOCTL_SERIAL_GET_MODEMSTATUS:
modemstate = 0;
#ifdef TIOCMGET
ioctl(tty->fd, TIOCMGET, &result);
if (result & TIOCM_CTS)
modemstate |= SERIAL_MS_CTS;
if (result & TIOCM_DSR)
modemstate |= SERIAL_MS_DSR;
if (result & TIOCM_RNG)
modemstate |= SERIAL_MS_RNG;
if (result & TIOCM_CAR)
modemstate |= SERIAL_MS_CAR;
if (result & TIOCM_DTR)
modemstate |= SERIAL_MS_DTR;
if (result & TIOCM_RTS)
modemstate |= SERIAL_MS_RTS;
#endif
DEBUG_SVC("SERIAL_GET_MODEMSTATUS %X", modemstate);
length = 4;
stream_write_uint32(output, modemstate);
break;
case IOCTL_SERIAL_GET_COMMSTATUS:
length = 18;
stream_write_uint32(output, 0); /* Errors */
stream_write_uint32(output, 0); /* Hold reasons */
result = 0;
#ifdef TIOCINQ
ioctl(tty->fd, TIOCINQ, &result);
#endif
stream_write_uint32(output, result); /* Amount in in queue */
if (result)
DEBUG_SVC("SERIAL_GET_COMMSTATUS in queue %d", result);
result = 0;
#ifdef TIOCOUTQ
ioctl(tty->fd, TIOCOUTQ, &result);
#endif
stream_write_uint32(output, result); /* Amount in out queue */
DEBUG_SVC("SERIAL_GET_COMMSTATUS out queue %d", result);
stream_write_uint8(output, 0); /* EofReceived */
stream_write_uint8(output, 0); /* WaitForImmediate */
break;
case IOCTL_SERIAL_PURGE:
stream_read_uint32(input, purge_mask);
DEBUG_SVC("SERIAL_PURGE purge_mask %X", purge_mask);
if ((purge_mask & SERIAL_PURGE_TXCLEAR)
&& (purge_mask & SERIAL_PURGE_RXCLEAR))
tcflush(tty->fd, TCIOFLUSH);
else if (purge_mask & SERIAL_PURGE_TXCLEAR)
tcflush(tty->fd, TCOFLUSH);
else if (purge_mask & SERIAL_PURGE_RXCLEAR)
tcflush(tty->fd, TCIFLUSH);
if (purge_mask & SERIAL_PURGE_TXABORT)
*abort_io |= SERIAL_ABORT_IO_WRITE;
if (purge_mask & SERIAL_PURGE_RXABORT)
*abort_io |= SERIAL_ABORT_IO_READ;
break;
case IOCTL_SERIAL_WAIT_ON_MASK:
DEBUG_SVC("SERIAL_WAIT_ON_MASK %X", tty->wait_mask);
tty->event_pending = 1;
length = 4;
if (serial_tty_get_event(tty, &result))
{
DEBUG_SVC("WAIT end event = %X", result);
stream_write_uint32(output, result);
break;
}
ret = STATUS_PENDING;
break;
case IOCTL_SERIAL_SET_BREAK_ON:
DEBUG_SVC("SERIAL_SET_BREAK_ON");
tcsendbreak(tty->fd, 0);
break;
case IOCTL_SERIAL_RESET_DEVICE:
DEBUG_SVC("SERIAL_RESET_DEVICE");
break;
case IOCTL_SERIAL_SET_BREAK_OFF:
DEBUG_SVC("SERIAL_SET_BREAK_OFF");
break;
case IOCTL_SERIAL_SET_XOFF:
DEBUG_SVC("SERIAL_SET_XOFF");
break;
case IOCTL_SERIAL_SET_XON:
DEBUG_SVC("SERIAL_SET_XON");
tcflow(tty->fd, TCION);
break;
default:
DEBUG_SVC("NOT FOUND IoControlCode SERIAL IOCTL %d", IoControlCode);
return STATUS_INVALID_PARAMETER;
}
/* Write OutputBufferLength */
pos = stream_get_pos(output);
stream_set_pos(output, 16);
stream_write_uint32(output, length);
stream_set_pos(output, pos);
return ret;
}
boolean serial_tty_read(SERIAL_TTY* tty, uint8* buffer, uint32* Length)
{
long timeout = 90;
struct termios *ptermios;
ssize_t r;
DEBUG_SVC("in");
ptermios = tty->ptermios;
/* Set timeouts kind of like the windows serial timeout parameters. Multiply timeout
with requested read size */
if (tty->read_total_timeout_multiplier | tty->read_total_timeout_constant)
{
timeout =
(tty->read_total_timeout_multiplier * (*Length) +
tty->read_total_timeout_constant + 99) / 100;
}
else if (tty->read_interval_timeout)
{
timeout = (tty->read_interval_timeout * (*Length) + 99) / 100;
}
/* If a timeout is set, do a blocking read, which times out after some time.
It will make FreeRDP less responsive, but it will improve serial performance,
by not reading one character at a time. */
if (timeout == 0)
{
ptermios->c_cc[VTIME] = 0;
ptermios->c_cc[VMIN] = 0;
}
else
{
ptermios->c_cc[VTIME] = timeout;
ptermios->c_cc[VMIN] = 1;
}
tcsetattr(tty->fd, TCSANOW, ptermios);
memset(buffer, 0, *Length);
r = read(tty->fd, buffer, *Length);
if (r < 0)
return False;
tty->event_txempty = r;
*Length = r;
return True;
}
boolean serial_tty_write(SERIAL_TTY* tty, uint8* buffer, uint32 Length)
{
ssize_t r;
uint32 event_txempty = Length;
DEBUG_SVC("in");
while (Length > 0)
{
r = write(tty->fd, buffer, Length);
if (r < 0)
return False;
Length -= r;
buffer += r;
}
tty->event_txempty = event_txempty;
return True;
}
void serial_tty_free(SERIAL_TTY* tty)
{
DEBUG_SVC("in");
if (tty->fd >= 0)
{
tcsetattr(tty->fd, TCSANOW, tty->pold_termios);
close(tty->fd);
}
xfree(tty->ptermios);
xfree(tty->pold_termios);
xfree(tty);
}
SERIAL_TTY* serial_tty_new(const char* path, uint32 id)
{
SERIAL_TTY* tty;
tty = xnew(SERIAL_TTY);
tty->id = id;
tty->fd = open(path, O_RDWR | O_NOCTTY | O_NONBLOCK);
if (tty->fd < 0)
{
perror("open");
DEBUG_WARN("failed to open device %s", path);
return NULL;
}
else
DEBUG_SVC("tty fd %d successfully opened", tty->fd);
tty->ptermios = (struct termios*) malloc(sizeof(struct termios));
memset(tty->ptermios, 0, sizeof(struct termios));
tty->pold_termios = (struct termios*) malloc(sizeof(struct termios));
memset(tty->pold_termios, 0, sizeof(struct termios));
tcgetattr(tty->fd, tty->pold_termios);
if (!tty_get_termios(tty))
{
DEBUG_WARN("%s access denied", path);
fflush(stdout);
return NULL;
}
tty->ptermios->c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON);
tty->ptermios->c_iflag = IGNPAR | ICRNL;
tty->ptermios->c_oflag &= ~OPOST;
tty->ptermios->c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
tty->ptermios->c_cflag &= ~(CSIZE | PARENB);
tty->ptermios->c_cflag |= CLOCAL | CREAD | CS8;
tcsetattr(tty->fd, TCSANOW, tty->ptermios);
tty->event_txempty = 0;
tty->event_cts = 0;
tty->event_dsr = 0;
tty->event_rlsd = 0;
tty->event_pending = 0;
/* all read and writes should be non-blocking */
if (fcntl(tty->fd, F_SETFL, O_NONBLOCK) == -1)
{
DEBUG_WARN("%s fcntl", path);
perror("fcntl");
return NULL;
}
tty->read_total_timeout_constant = 5;
return tty;
}
boolean serial_tty_get_event(SERIAL_TTY* tty, uint32* result)
{
int bytes;
boolean ret = False;
DEBUG_SVC("in");
*result = 0;
#ifdef TIOCINQ
/* When wait_mask is set to zero we ought to cancel it all
For reference: http://msdn.microsoft.com/en-us/library/aa910487.aspx */
if (tty->wait_mask == 0)
{
tty->event_pending = 0;
return True;
}
ioctl(tty->fd, TIOCINQ, &bytes);
if (bytes > 0)
{
DEBUG_SVC("bytes %d", bytes);
if (bytes > tty->event_rlsd)
{
tty->event_rlsd = bytes;
if (tty->wait_mask & SERIAL_EV_RLSD)
{
DEBUG_SVC("SERIAL_EV_RLSD");
*result |= SERIAL_EV_RLSD;
ret = True;
}
}
if ((bytes > 1) && (tty->wait_mask & SERIAL_EV_RXFLAG))
{
DEBUG_SVC("SERIAL_EV_RXFLAG bytes %d", bytes);
*result |= SERIAL_EV_RXFLAG;
ret = True;
}
if ((tty->wait_mask & SERIAL_EV_RXCHAR))
{
DEBUG_SVC("SERIAL_EV_RXCHAR bytes %d", bytes);
*result |= SERIAL_EV_RXCHAR;
ret = True;
}
}
else
{
tty->event_rlsd = 0;
}
#endif
#ifdef TIOCOUTQ
ioctl(tty->fd, TIOCOUTQ, &bytes);
if ((bytes == 0)
&& (tty->event_txempty > 0) && (tty->wait_mask & SERIAL_EV_TXEMPTY))
{
DEBUG_SVC("SERIAL_EV_TXEMPTY");
*result |= SERIAL_EV_TXEMPTY;
ret = True;
}
tty->event_txempty = bytes;
#endif
ioctl(tty->fd, TIOCMGET, &bytes);
if ((bytes & TIOCM_DSR) != tty->event_dsr)
{
tty->event_dsr = bytes & TIOCM_DSR;
if (tty->wait_mask & SERIAL_EV_DSR)
{
DEBUG_SVC("SERIAL_EV_DSR %s", (bytes & TIOCM_DSR) ? "ON" : "OFF");
*result |= SERIAL_EV_DSR;
ret = True;
}
}
if ((bytes & TIOCM_CTS) != tty->event_cts)
{
tty->event_cts = bytes & TIOCM_CTS;
if (tty->wait_mask & SERIAL_EV_CTS)
{
DEBUG_SVC("SERIAL_EV_CTS %s", (bytes & TIOCM_CTS) ? "ON" : "OFF");
*result |= SERIAL_EV_CTS;
ret = True;
}
}
if (ret)
tty->event_pending = 0;
return ret;
}
static boolean tty_get_termios(SERIAL_TTY* tty)
{
speed_t speed;
struct termios *ptermios;
ptermios = tty->ptermios;
DEBUG_SVC("tcgetattr? %d", tcgetattr(tty->fd, ptermios) >= 0);
if (tcgetattr(tty->fd, ptermios) < 0)
return False;
speed = cfgetispeed(ptermios);
switch (speed)
{
#ifdef B75
case B75:
tty->baud_rate = 75;
break;
#endif
#ifdef B110
case B110:
tty->baud_rate = 110;
break;
#endif
#ifdef B134
case B134:
tty->baud_rate = 134;
break;
#endif
#ifdef B150
case B150:
tty->baud_rate = 150;
break;
#endif
#ifdef B300
case B300:
tty->baud_rate = 300;
break;
#endif
#ifdef B600
case B600:
tty->baud_rate = 600;
break;
#endif
#ifdef B1200
case B1200:
tty->baud_rate = 1200;
break;
#endif
#ifdef B1800
case B1800:
tty->baud_rate = 1800;
break;
#endif
#ifdef B2400
case B2400:
tty->baud_rate = 2400;
break;
#endif
#ifdef B4800
case B4800:
tty->baud_rate = 4800;
break;
#endif
#ifdef B9600
case B9600:
tty->baud_rate = 9600;
break;
#endif
#ifdef B19200
case B19200:
tty->baud_rate = 19200;
break;
#endif
#ifdef B38400
case B38400:
tty->baud_rate = 38400;
break;
#endif
#ifdef B57600
case B57600:
tty->baud_rate = 57600;
break;
#endif
#ifdef B115200
case B115200:
tty->baud_rate = 115200;
break;
#endif
#ifdef B230400
case B230400:
tty->baud_rate = 230400;
break;
#endif
#ifdef B460800
case B460800:
tty->baud_rate = 460800;
break;
#endif
default:
tty->baud_rate = 9600;
break;
}
speed = cfgetospeed(ptermios);
tty->dtr = (speed == B0) ? 0 : 1;
tty->stop_bits = (ptermios->c_cflag & CSTOPB) ? SERIAL_STOP_BITS_2 : SERIAL_STOP_BITS_1;
tty->parity =
(ptermios->c_cflag & PARENB) ? ((ptermios->c_cflag & PARODD) ? SERIAL_ODD_PARITY :
SERIAL_EVEN_PARITY) : SERIAL_NO_PARITY;
switch (ptermios->c_cflag & CSIZE)
{
case CS5:
tty->word_length = 5;
break;
case CS6:
tty->word_length = 6;
break;
case CS7:
tty->word_length = 7;
break;
default:
tty->word_length = 8;
break;
}
if (ptermios->c_cflag & CRTSCTS)
{
tty->control = SERIAL_DTR_CONTROL | SERIAL_CTS_HANDSHAKE | SERIAL_ERROR_ABORT;
}
else
{
tty->control = SERIAL_DTR_CONTROL | SERIAL_ERROR_ABORT;
}
tty->xonoff = SERIAL_DSR_SENSITIVITY;
if (ptermios->c_iflag & IXON)
tty->xonoff |= SERIAL_XON_HANDSHAKE;
if (ptermios->c_iflag & IXOFF)
tty->xonoff |= SERIAL_XOFF_HANDSHAKE;
tty->chars[SERIAL_CHAR_XON] = ptermios->c_cc[VSTART];
tty->chars[SERIAL_CHAR_XOFF] = ptermios->c_cc[VSTOP];
tty->chars[SERIAL_CHAR_EOF] = ptermios->c_cc[VEOF];
tty->chars[SERIAL_CHAR_BREAK] = ptermios->c_cc[VINTR];
tty->chars[SERIAL_CHAR_ERROR] = ptermios->c_cc[VKILL];
return True;
}
static void tty_set_termios(SERIAL_TTY* tty)
{
speed_t speed;
struct termios *ptermios;
DEBUG_SVC("in");
ptermios = tty->ptermios;
switch (tty->baud_rate)
{
#ifdef B75
case 75:
speed = B75;
break;
#endif
#ifdef B110
case 110:
speed = B110;
break;
#endif
#ifdef B134
case 134:
speed = B134;
break;
#endif
#ifdef B150
case 150:
speed = B150;
break;
#endif
#ifdef B300
case 300:
speed = B300;
break;
#endif
#ifdef B600
case 600:
speed = B600;
break;
#endif
#ifdef B1200
case 1200:
speed = B1200;
break;
#endif
#ifdef B1800
case 1800:
speed = B1800;
break;
#endif
#ifdef B2400
case 2400:
speed = B2400;
break;
#endif
#ifdef B4800
case 4800:
speed = B4800;
break;
#endif
#ifdef B9600
case 9600:
speed = B9600;
break;
#endif
#ifdef B19200
case 19200:
speed = B19200;
break;
#endif
#ifdef B38400
case 38400:
speed = B38400;
break;
#endif
#ifdef B57600
case 57600:
speed = B57600;
break;
#endif
#ifdef B115200
case 115200:
speed = B115200;
break;
#endif
#ifdef B230400
case 230400:
speed = B115200;
break;
#endif
#ifdef B460800
case 460800:
speed = B115200;
break;
#endif
default:
speed = B9600;
break;
}
#ifdef CBAUD
ptermios->c_cflag &= ~CBAUD;
ptermios->c_cflag |= speed;
#else
/* on systems with separate ispeed and ospeed, we can remember the speed
in ispeed while changing DTR with ospeed */
cfsetispeed(tty->ptermios, speed);
cfsetospeed(tty->ptermios, tty->dtr ? speed : 0);
#endif
ptermios->c_cflag &= ~(CSTOPB | PARENB | PARODD | CSIZE | CRTSCTS);
switch (tty->stop_bits)
{
case SERIAL_STOP_BITS_2:
ptermios->c_cflag |= CSTOPB;
break;
default:
ptermios->c_cflag &= ~CSTOPB;
break;
}
switch (tty->parity)
{
case SERIAL_EVEN_PARITY:
ptermios->c_cflag |= PARENB;
break;
case SERIAL_ODD_PARITY:
ptermios->c_cflag |= PARENB | PARODD;
break;
case SERIAL_NO_PARITY:
ptermios->c_cflag &= ~(PARENB | PARODD);
break;
}
switch (tty->word_length)
{
case 5:
ptermios->c_cflag |= CS5;
break;
case 6:
ptermios->c_cflag |= CS6;
break;
case 7:
ptermios->c_cflag |= CS7;
break;
default:
ptermios->c_cflag |= CS8;
break;
}
#if 0
if (tty->rts)
ptermios->c_cflag |= CRTSCTS;
else
ptermios->c_cflag &= ~CRTSCTS;
#endif
if (tty->control & SERIAL_CTS_HANDSHAKE)
{
ptermios->c_cflag |= CRTSCTS;
}
else
{
ptermios->c_cflag &= ~CRTSCTS;
}
if (tty->xonoff & SERIAL_XON_HANDSHAKE)
{
ptermios->c_iflag |= IXON | IMAXBEL;
}
if (tty->xonoff & SERIAL_XOFF_HANDSHAKE)
{
ptermios->c_iflag |= IXOFF | IMAXBEL;
}
if ((tty->xonoff & (SERIAL_XOFF_HANDSHAKE | SERIAL_XON_HANDSHAKE)) == 0)
{
ptermios->c_iflag &= ~IXON;
ptermios->c_iflag &= ~IXOFF;
}
ptermios->c_cc[VSTART] = tty->chars[SERIAL_CHAR_XON];
ptermios->c_cc[VSTOP] = tty->chars[SERIAL_CHAR_XOFF];
ptermios->c_cc[VEOF] = tty->chars[SERIAL_CHAR_EOF];
ptermios->c_cc[VINTR] = tty->chars[SERIAL_CHAR_BREAK];
ptermios->c_cc[VKILL] = tty->chars[SERIAL_CHAR_ERROR];
tcsetattr(tty->fd, TCSANOW, ptermios);
}
static uint32 tty_write_data(SERIAL_TTY* tty, uint8* data, int len)
{
ssize_t r;
DEBUG_SVC("in");
r = write(tty->fd, data, len);
if (r < 0)
return tty_get_error_status();
tty->event_txempty = r;
return STATUS_SUCCESS;
}
static int tty_get_error_status()
{
DEBUG_SVC("in errno %d", errno);
switch (errno)
{
case EACCES:
case ENOTDIR:
case ENFILE:
return STATUS_ACCESS_DENIED;
case EISDIR:
return STATUS_FILE_IS_A_DIRECTORY;
case EEXIST:
return STATUS_OBJECT_NAME_COLLISION;
case EBADF:
return STATUS_INVALID_HANDLE;
default:
return STATUS_NO_SUCH_FILE;
}
}

View File

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

View File

@ -0,0 +1,34 @@
# FreeRDP: A Remote Desktop Protocol Client
# FreeRDP cmake build script
#
# Copyright 2011 O.S. Systems Software Ltda.
# Copyright 2011 Otavio Salvador <otavio@ossystems.com.br>
# Copyright 2011 Marc-Andre Moreau <marcandre.moreau@gmail.com>
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
set(SCARD_SRCS
scard_main.c
scard_operations.c
)
include_directories(..)
include_directories(${PCSC_INCLUDE_DIRS})
add_library(scard ${SCARD_SRCS})
set_target_properties(scard PROPERTIES PREFIX "")
target_link_libraries(scard freerdp-utils)
target_link_libraries(scard ${PCSC_LIBRARIES})
install(TARGETS scard DESTINATION ${FREERDP_PLUGIN_PATH})

View File

@ -0,0 +1,204 @@
/*
FreeRDP: A Remote Desktop Protocol client.
Redirected Smart Card Device Service
Copyright 2011 O.S. Systems Software Ltda.
Copyright 2011 Eduardo Fiss Beloni <beloni@ossystems.com.br>
Copyright 2011 Anthony Tong <atong@trustedcs.com>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#include "config.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <freerdp/utils/list.h>
#include <freerdp/utils/thread.h>
#include <freerdp/utils/svc_plugin.h>
#include "rdpdr_types.h"
#include "rdpdr_constants.h"
#include "scard_main.h"
static void
scard_free(DEVICE* dev)
{
SCARD_DEVICE* scard = (SCARD_DEVICE*)dev;
IRP* irp;
freerdp_thread_stop(scard->thread);
freerdp_thread_free(scard->thread);
while ((irp = (IRP*)list_dequeue(scard->irp_list)) != NULL)
irp->Discard(irp);
list_free(scard->irp_list);
xfree(dev);
return;
}
static void
scard_process_irp(SCARD_DEVICE* scard, IRP* irp)
{
switch (irp->MajorFunction)
{
case IRP_MJ_DEVICE_CONTROL:
scard_device_control(scard, irp);
break;
default:
printf("MajorFunction 0x%X unexpected for smartcards.", irp->MajorFunction);
DEBUG_WARN("Smartcard MajorFunction 0x%X not supported.", irp->MajorFunction);
irp->IoStatus = STATUS_NOT_SUPPORTED;
irp->Complete(irp);
break;
}
}
static void
scard_process_irp_list(SCARD_DEVICE* scard)
{
IRP *irp;
while (!freerdp_thread_is_stopped(scard->thread))
{
freerdp_thread_lock(scard->thread);
irp = (IRP *) list_dequeue(scard->irp_list);
freerdp_thread_unlock(scard->thread);
if (irp == NULL)
break;
scard_process_irp(scard, irp);
}
}
struct scard_irp_thread_args {
SCARD_DEVICE* scard;
IRP* irp;
freerdp_thread* thread;
};
static void
scard_process_irp_thread_func(struct scard_irp_thread_args* args)
{
scard_process_irp(args->scard, args->irp);
freerdp_thread_free(args->thread);
xfree(args);
}
static void *
scard_thread_func(void* arg)
{
SCARD_DEVICE* scard = (SCARD_DEVICE*) arg;
while (1)
{
freerdp_thread_wait(scard->thread);
if (freerdp_thread_is_stopped(scard->thread))
break;
freerdp_thread_reset(scard->thread);
scard_process_irp_list(scard);
}
freerdp_thread_quit(scard->thread);
return NULL;
}
static void
scard_irp_request(DEVICE* device, IRP* irp)
{
SCARD_DEVICE* scard = (SCARD_DEVICE*)device;
if (irp->MajorFunction == IRP_MJ_DEVICE_CONTROL &&
scard_async_op(irp))
{
/*
* certain potentially long running operations
* get their own thread
* TODO: revise this mechanism.. maybe worker pool
*/
struct scard_irp_thread_args *args = xmalloc(sizeof(struct scard_irp_thread_args));
args->thread = freerdp_thread_new();
args->scard = scard;
args->irp = irp;
freerdp_thread_start(args->thread, scard_process_irp_thread_func, args);
return;
}
freerdp_thread_lock(scard->thread);
list_enqueue(scard->irp_list, irp);
freerdp_thread_unlock(scard->thread);
freerdp_thread_signal(scard->thread);
}
int
DeviceServiceEntry(PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints)
{
SCARD_DEVICE* scard;
char* name;
char* path;
int i, length;
name = (char *)pEntryPoints->plugin_data->data[1];
path = (char *)pEntryPoints->plugin_data->data[2];
if (name)
{
/* TODO: check if server supports sc redirect (version 5.1) */
scard = xnew(SCARD_DEVICE);
scard->device.type = RDPDR_DTYP_SMARTCARD;
scard->device.name = "SCARD";
scard->device.IRPRequest = scard_irp_request;
scard->device.Free = scard_free;
length = strlen(scard->device.name);
scard->device.data = stream_new(length + 1);
for (i = 0; i <= length; i++)
stream_write_uint8(scard->device.data, name[i] < 0 ? '_' : name[i]);
scard->path = path;
scard->irp_list = list_new();
scard->thread = freerdp_thread_new();
pEntryPoints->RegisterDevice(pEntryPoints->devman, (DEVICE *)scard);
freerdp_thread_start(scard->thread, scard_thread_func, scard);
}
return 0;
}

View File

@ -0,0 +1,52 @@
/*
FreeRDP: A Remote Desktop Protocol client.
Redirected Smart Card Device Service
Copyright 2011 O.S. Systems Software Ltda.
Copyright 2011 Eduardo Fiss Beloni <beloni@ossystems.com.br>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#ifndef __SCARD_MAIN_H
#define __SCARD_MAIN_H
#include <inttypes.h>
#include "devman.h"
#include "rdpdr_types.h"
#include <freerdp/utils/debug.h>
struct _SCARD_DEVICE
{
DEVICE device;
char * name;
char * path;
LIST* irp_list;
freerdp_thread* thread;
};
typedef struct _SCARD_DEVICE SCARD_DEVICE;
#ifdef WITH_DEBUG_SCARD
#define DEBUG_SCARD(fmt, ...) DEBUG_CLASS(SCARD, fmt, ## __VA_ARGS__)
#else
#define DEBUG_SCARD(fmt, ...) DEBUG_NULL(fmt, ## __VA_ARGS__)
#endif
boolean scard_async_op(IRP*);
void scard_device_control(SCARD_DEVICE*, IRP*);
#endif

File diff suppressed because it is too large Load Diff

View File

@ -308,6 +308,9 @@ void xf_gdi_bitmap_update(rdpUpdate* update, BITMAP_UPDATE* bitmap)
XPutImage(xfi->display, xfi->primary, xfi->gc, image, 0, 0, x, y, w, h);
XFree(image);
xfree(data);
if (xfi->remote_app != True)
XCopyArea(xfi->display, xfi->primary, xfi->drawable, xfi->gc, x, y, w, h, x, y);

3
cmake/FindPCSC.cmake Normal file
View File

@ -0,0 +1,3 @@
pkg_check_modules(PCSC libpcsclite)
# TODO: pcsc version

View File

@ -34,5 +34,6 @@
#cmakedefine WITH_DEBUG_X11
#cmakedefine WITH_DEBUG_RAIL
#cmakedefine WITH_DEBUG_XV
#cmakedefine WITH_DEBUG_SCARD
#endif

BIN
docs/FreeRDP.vsd Normal file

Binary file not shown.

13
freerdp.pc.in Normal file
View File

@ -0,0 +1,13 @@
prefix=@CMAKE_INSTALL_PREFIX@
exec_prefix=@CMAKE_INSTALL_PREFIX@
libdir=@CMAKE_INSTALL_PREFIX@/@CMAKE_INSTALL_LIBDIR@
includedir=@CMAKE_INSTALL_PREFIX@/include
Name: FreeRDP
Description: A free remote desktop protocol client
URL: http://www.freerdp.com/
Version: @FREERDP_VERSION_FULL@
Requires:
Libs: -L${libdir} -lfreerdp-core -lfreerdp-codec -lfreerdp-gdi -lfreerdp-kbd -lfreerdp-rail -lfreerdp-chanman -lfreerdp-utils
Cflags: -I${includedir}

View File

@ -43,6 +43,7 @@ struct rdp_offscreen_cache
cbSetSurface SetSurface;
uint16 currentSurface;
uint16 maxSize;
uint16 maxEntries;
rdpUpdate* update;

View File

@ -44,7 +44,9 @@ FREERDP_API void list_free(LIST* list);
FREERDP_API void list_enqueue(LIST* list, void* data);
FREERDP_API void* list_dequeue(LIST* list);
FREERDP_API void* list_peek(LIST* list);
FREERDP_API void* list_next(LIST* list, void* data);
#define list_add(_l, _d) list_enqueue(_l, _d)
FREERDP_API void* list_remove(LIST* list, void* data);
FREERDP_API int list_size(LIST* list);
#endif /* __LIST_UTILS_H */

View File

@ -45,6 +45,9 @@ void update_gdi_create_offscreen_bitmap(rdpUpdate* update, CREATE_OFFSCREEN_BITM
}
offscreen_cache_put(cache->offscreen, create_offscreen_bitmap->id, bitmap);
if(cache->offscreen->currentSurface == create_offscreen_bitmap->id)
IFCALL(cache->offscreen->SetSurface, update, bitmap, False);
}
void update_gdi_switch_surface(rdpUpdate* update, SWITCH_SURFACE_ORDER* switch_surface)
@ -61,6 +64,7 @@ void update_gdi_switch_surface(rdpUpdate* update, SWITCH_SURFACE_ORDER* switch_s
bitmap = offscreen_cache_get(cache->offscreen, switch_surface->bitmapId);
IFCALL(cache->offscreen->SetSurface, update, bitmap, False);
}
cache->offscreen->currentSurface = switch_surface->bitmapId;
}
rdpBitmap* offscreen_cache_get(rdpOffscreenCache* offscreen_cache, uint16 index)
@ -112,6 +116,7 @@ rdpOffscreenCache* offscreen_cache_new(rdpSettings* settings)
offscreen_cache->settings = settings;
offscreen_cache->update = ((freerdp*) settings->instance)->update;
offscreen_cache->currentSurface = SCREEN_BITMAP_SURFACE;
offscreen_cache->maxSize = 7680;
offscreen_cache->maxEntries = 100;

View File

@ -49,6 +49,7 @@ LIST* list_new(void)
LIST* list;
list = xnew(LIST);
list->count = 0;
return list;
}
@ -75,6 +76,7 @@ void list_enqueue(LIST* list, void* data)
list->tail->next = item;
list->tail = item;
}
list->count++;
}
void* list_dequeue(LIST* list)
@ -93,6 +95,7 @@ void* list_dequeue(LIST* list)
data = item->data;
xfree(item);
list->count--;
}
return data;
}
@ -105,6 +108,20 @@ void* list_peek(LIST* list)
return item ? item->data : NULL;
}
void* list_next(LIST* list, void* data)
{
LIST_ITEM* item;
item = list_item_find(list, data);
data = NULL;
if (item != NULL)
{
if (item->next != NULL)
data = item->next->data;
}
return data;
}
void* list_remove(LIST* list, void* data)
{
LIST_ITEM* item;
@ -121,8 +138,14 @@ void* list_remove(LIST* list, void* data)
if (list->tail == item)
list->tail = item->prev;
xfree(item);
list->count--;
}
else
data = NULL;
return data;
}
int list_size(LIST* list)
{
return list->count;
}