diff --git a/CMakeLists.txt b/CMakeLists.txt index c43c73981..e04aa5089 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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) diff --git a/channels/rdpdr/CMakeLists.txt b/channels/rdpdr/CMakeLists.txt index a0085ff52..a05b61893 100644 --- a/channels/rdpdr/CMakeLists.txt +++ b/channels/rdpdr/CMakeLists.txt @@ -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() diff --git a/channels/rdpdr/rdpdr_types.h b/channels/rdpdr/rdpdr_types.h index 4419edb49..a87dad289 100644 --- a/channels/rdpdr/rdpdr_types.h +++ b/channels/rdpdr/rdpdr_types.h @@ -24,6 +24,7 @@ #include "config.h" #include #include +#include typedef struct _DEVICE DEVICE; typedef struct _IRP IRP; diff --git a/channels/rdpdr/serial/CMakeLists.txt b/channels/rdpdr/serial/CMakeLists.txt new file mode 100644 index 000000000..40c962a58 --- /dev/null +++ b/channels/rdpdr/serial/CMakeLists.txt @@ -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 +# Copyright 2011 Marc-Andre Moreau +# +# 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}) diff --git a/channels/rdpdr/serial/serial_constants.h b/channels/rdpdr/serial/serial_constants.h new file mode 100644 index 000000000..ac16f9a7b --- /dev/null +++ b/channels/rdpdr/serial/serial_constants.h @@ -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 + * + * 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 diff --git a/channels/rdpdr/serial/serial_main.c b/channels/rdpdr/serial/serial_main.c new file mode 100644 index 000000000..784e4cc1e --- /dev/null +++ b/channels/rdpdr/serial/serial_main.c @@ -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 + * + * 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 +#include +#include +#include + +#include "config.h" + +#ifdef HAVE_SYS_MODEM_H +#include +#endif +#ifdef HAVE_SYS_FILIO_H +#include +#endif +#ifdef HAVE_SYS_STRTIO_H +#include +#endif + +#include "rdpdr_types.h" +#include "rdpdr_constants.h" +#include "devman.h" +#include "serial_tty.h" +#include "serial_constants.h" + +#include +#include +#include +#include + +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; +} diff --git a/channels/rdpdr/serial/serial_tty.c b/channels/rdpdr/serial/serial_tty.c new file mode 100644 index 000000000..d9b3ed474 --- /dev/null +++ b/channels/rdpdr/serial/serial_tty.c @@ -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 + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rdpdr_constants.h" +#include "rdpdr_types.h" +#include "serial_tty.h" +#include "serial_constants.h" + +#ifdef HAVE_SYS_MODEM_H +#include +#endif +#ifdef HAVE_SYS_FILIO_H +#include +#endif +#ifdef HAVE_SYS_STRTIO_H +#include +#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; + } +} diff --git a/channels/rdpdr/serial/serial_tty.h b/channels/rdpdr/serial/serial_tty.h new file mode 100644 index 000000000..a4b82f98f --- /dev/null +++ b/channels/rdpdr/serial/serial_tty.h @@ -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 + * + * 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 +#include +#include + +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 */ diff --git a/channels/rdpdr/smartcard/CMakeLists.txt b/channels/rdpdr/smartcard/CMakeLists.txt new file mode 100644 index 000000000..816fc09f4 --- /dev/null +++ b/channels/rdpdr/smartcard/CMakeLists.txt @@ -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 +# Copyright 2011 Marc-Andre Moreau +# +# 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}) diff --git a/channels/rdpdr/smartcard/scard_main.c b/channels/rdpdr/smartcard/scard_main.c new file mode 100644 index 000000000..5066bf2d0 --- /dev/null +++ b/channels/rdpdr/smartcard/scard_main.c @@ -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 + Copyright 2011 Anthony Tong + + 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 +#include +#include + +#include +#include +#include + +#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; +} diff --git a/channels/rdpdr/smartcard/scard_main.h b/channels/rdpdr/smartcard/scard_main.h new file mode 100644 index 000000000..7813f486c --- /dev/null +++ b/channels/rdpdr/smartcard/scard_main.h @@ -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 + + 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 + +#include "devman.h" +#include "rdpdr_types.h" +#include + +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 diff --git a/channels/rdpdr/smartcard/scard_operations.c b/channels/rdpdr/smartcard/scard_operations.c new file mode 100644 index 000000000..7bce705d5 --- /dev/null +++ b/channels/rdpdr/smartcard/scard_operations.c @@ -0,0 +1,1547 @@ +/* + FreeRDP: A Remote Desktop Protocol client. + Redirected Smart Card Device Service + + Copyright (C) Alexi Volkov 2006 + Copyright 2011 O.S. Systems Software Ltda. + Copyright 2011 Anthony Tong + + 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 +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "rdpdr_types.h" +#include "rdpdr_constants.h" + +#include "scard_main.h" + + +/* [MS-RDPESC] 3.1.4 */ +#define SCARD_IOCTL_ESTABLISH_CONTEXT 0x00090014 /* EstablishContext */ +#define SCARD_IOCTL_RELEASE_CONTEXT 0x00090018 /* ReleaseContext */ +#define SCARD_IOCTL_IS_VALID_CONTEXT 0x0009001C /* IsValidContext */ +#define SCARD_IOCTL_LIST_READER_GROUPS 0x00090020 /* ListReaderGroups */ +#define SCARD_IOCTL_LIST_READERS 0x00090028 /* ListReadersA */ +#define SCARD_IOCTL_INTRODUCE_READER_GROUP 0x00090050 /* IntroduceReaderGroup */ +#define SCARD_IOCTL_FORGET_READER_GROUP 0x00090058 /* ForgetReader */ +#define SCARD_IOCTL_INTRODUCE_READER 0x00090060 /* IntroduceReader */ +#define SCARD_IOCTL_FORGET_READER 0x00090068 /* IntroduceReader */ +#define SCARD_IOCTL_ADD_READER_TO_GROUP 0x00090070 /* AddReaderToGroup */ +#define SCARD_IOCTL_REMOVE_READER_FROM_GROUP 0x00090078 /* RemoveReaderFromGroup */ +#define SCARD_IOCTL_GET_STATUS_CHANGE 0x000900A0 /* GetStatusChangeA */ +#define SCARD_IOCTL_CANCEL 0x000900A8 /* Cancel */ +#define SCARD_IOCTL_CONNECT 0x000900AC /* ConnectA */ +#define SCARD_IOCTL_RECONNECT 0x000900B4 /* Reconnect */ +#define SCARD_IOCTL_DISCONNECT 0x000900B8 /* Disconnect */ +#define SCARD_IOCTL_BEGIN_TRANSACTION 0x000900BC /* BeginTransaction */ +#define SCARD_IOCTL_END_TRANSACTION 0x000900C0 /* EndTransaction */ +#define SCARD_IOCTL_STATE 0x000900C4 /* State */ +#define SCARD_IOCTL_STATUS 0x000900C8 /* StatusA */ +#define SCARD_IOCTL_TRANSMIT 0x000900D0 /* Transmit */ +#define SCARD_IOCTL_CONTROL 0x000900D4 /* Control */ +#define SCARD_IOCTL_GETATTRIB 0x000900D8 /* GetAttrib */ +#define SCARD_IOCTL_SETATTRIB 0x000900DC /* SetAttrib */ +#define SCARD_IOCTL_ACCESS_STARTED_EVENT 0x000900E0 /* SCardAccessStartedEvent */ +#define SCARD_IOCTL_LOCATE_CARDS_BY_ATR 0x000900E8 /* LocateCardsByATR */ + + +#define SCARD_INPUT_LINKED 0xFFFFFFFF + + +static uint32 +sc_output_string(IRP* irp, char *src, boolean wide) +{ + uint8* p; + uint32 len; + + p = stream_get_tail(irp->output); + len = strlen(src) + 1; + + if (wide) + { + // len = mbstowcs(p, src, len); // need to setlocale for 2byte wchar.. + int i; + for (i = 0; i < len; i++ ) + { + p[2 * i] = src[i] < 0 ? '?' : src[i]; + p[2 * i + 1] = '\0'; + } + + len *= 2; + } + else + { + memcpy(p, src, len); + } + + stream_seek(irp->output, len); + return len; +} + + +static void +sc_output_alignment(IRP *irp, uint32 seed) +{ + uint32 size = stream_get_length(irp->output) - 20; + uint32 add = (seed - (size % seed)) % seed; + + if (add > 0) + stream_write_zero(irp->output, add); +} + + +static void +sc_output_repos(IRP* irp, uint32 written) +{ + uint32 add = (4 - (written % 4)) % 4; + + if (add > 0) + stream_write_zero(irp->output, add); +} + + +static uint32 +sc_output_return(IRP* irp, uint32 rv) +{ + stream_write_zero(irp->output, 256); + return rv; +} + + +static void +sc_output_buffer_limit(IRP* irp, char *buffer, unsigned int length, unsigned int highLimit) +{ + int header = (length < 0) ? (0) : ((length > highLimit) ? (highLimit) : (length)); + + stream_write_uint32(irp->output, header); + + if (length <= 0) + { + stream_write_uint32(irp->output, 0); + } + else + { + if (header < length) + length = header; + + stream_write(irp->output, buffer, length); + sc_output_repos(irp, length); + } +} + + +static void +sc_output_buffer(IRP* irp, char *buffer, unsigned int length) +{ + sc_output_buffer_limit(irp, buffer, length, 0x7FFFFFFF); +} + + +static void +sc_output_buffer_start_limit(IRP *irp, int length, int highLimit) +{ + int header = (length < 0) ? (0) : ((length > highLimit) ? (highLimit) : (length)); + + stream_write_uint32(irp->output, header); + stream_write_uint32(irp->output, 0x00000001); /* Magic DWORD - any non zero */ +} + + +static void +sc_output_buffer_start(IRP *irp, int length) +{ + sc_output_buffer_start_limit(irp, length, 0x7FFFFFFF); +} + + +static uint32 +sc_input_string(IRP* irp, char **dest, uint32 dataLength, boolean wide) +{ + char *buffer; + int bufferSize; + + bufferSize = wide ? (2 * dataLength) : dataLength; + buffer = xmalloc(bufferSize + 2); /* reserve 2 bytes for the '\0' */ + + stream_read(irp->input, buffer, bufferSize); + if (wide) + { + int i; + for (i = 0; i < dataLength; i++) + { + if ((buffer[2 * i] < 0) || (buffer[2 * i + 1] != 0)) + buffer[i] = '?'; + else + buffer[i] = buffer[2 * i]; + } + } + + buffer[dataLength] = '\0'; + *dest = buffer; + + return bufferSize; +} + + +static void +sc_input_repos(IRP* irp, uint32 read) +{ + uint32 add = 4 - (read % 4); + + if (add < 4 && add > 0) + stream_seek(irp->input, add); +} + + +static void +sc_input_reader_name(IRP* irp, char **dest, boolean wide) +{ + uint32 dataLength; + + stream_seek(irp->input, 8); + stream_read_uint32(irp->input, dataLength); + + DEBUG_SCARD("datalength %d", dataLength); + sc_input_repos(irp, sc_input_string(irp, dest, dataLength, wide)); +} + + +static void +sc_input_skip_linked(IRP* irp) +{ + uint32 len; + stream_read_uint32(irp->input, len); + + if (len > 0) + { + stream_seek(irp->input, len); + sc_input_repos(irp, len); + } +} + + +static uint32 +sc_map_state(uint32 state) +{ + // is this mapping still needed? + if (state & SCARD_SPECIFIC) + state = 0x00000006; + else if (state & SCARD_NEGOTIABLE) + state = 0x00000005; + else if (state & SCARD_POWERED) + state = 0x00000004; + else if (state & SCARD_SWALLOWED) + state = 0x00000003; + else if (state & SCARD_PRESENT) + state = 0x00000002; + else if (state & SCARD_ABSENT) + state = 0x00000001; + else + state = 0x00000000; + + return state; +} + + +static uint32 +handle_EstablishContext(IRP* irp) +{ + uint32 len, rv; + uint32 scope; + SCARDCONTEXT hContext = -1; + + stream_seek(irp->input, 8); + stream_read_uint32(irp->input, len); + if (len != 8) + return SCARD_F_INTERNAL_ERROR; + + stream_seek_uint32(irp->input); + stream_read_uint32(irp->input, scope); + + rv = SCardEstablishContext(scope, NULL, NULL, &hContext); + + stream_write_uint32(irp->output, 4); // ? + stream_write_uint32(irp->output, -1); // ? + + stream_write_uint32(irp->output, 4); + stream_write_uint32(irp->output, hContext); + + // TODO: store hContext in allowed context list + + return SCARD_S_SUCCESS; +} + + +static uint32 +handle_ReleaseContext(IRP* irp) +{ + uint32 len, rv; + SCARDCONTEXT hContext = -1; + + stream_seek(irp->input, 8); + stream_read_uint32(irp->input, len); + // if (len != 16) return SCARD_F_INTERNAL_ERROR; + + stream_seek(irp->input, 0x10); + stream_read_uint32(irp->input, hContext); + + rv = SCardReleaseContext(hContext); + if (rv) + DEBUG_SCARD("%s (0x%08x)", pcsc_stringify_error(rv), (unsigned) rv); + else + DEBUG_SCARD("success 0x%08lx", hContext); + + return rv; +} + + +static uint32 +handle_IsValidContext(IRP* irp) +{ + uint32 rv; + SCARDCONTEXT hContext; + + stream_seek(irp->input, 0x1c); + stream_read_uint32(irp->input, hContext); + + rv = SCardIsValidContext(hContext); + + if (rv) + DEBUG_SCARD("Failure: %s (0x%08x)", pcsc_stringify_error(rv), (unsigned) rv); + else + DEBUG_SCARD("Success context: 0x%08x", (unsigned) hContext); + + stream_write_uint32(irp->output, rv); + + return rv; +} + + +static uint32 +handle_ListReaders(IRP* irp, boolean wide) +{ + uint32 len, rv; + SCARDCONTEXT hContext; + DWORD dwReaders; + char *readerList = NULL, *walker; + int elemLength, dataLength; + int pos, poslen1, poslen2; + + stream_seek(irp->input, 8); + stream_read_uint32(irp->input, len); + + stream_seek(irp->input, 0x1c); + stream_read_uint32(irp->input, len); + + if (len != 4) + return SCARD_F_INTERNAL_ERROR; + + stream_read_uint32(irp->input, hContext); + + /* ignore rest of [MS-RDPESC] 2.2.2.4 ListReaders_Call */ + + rv = SCARD_S_SUCCESS; +#ifdef SCARD_AUTOALLOCATE + dwReaders = SCARD_AUTOALLOCATE; + rv = SCardListReaders(hContext, NULL, (LPSTR) &readerList, &dwReaders); +#else + rv = SCardListReaders(hContext, NULL, NULL, &dwReaders); + + readerList = xmalloc(dwReaders); + rv = SCardListReaders(hContext, NULL, readerList, &dwReaders); +#endif + if (rv != SCARD_S_SUCCESS) + { + DEBUG_SCARD("Failure: %s (0x%08x)", pcsc_stringify_error(rv), (unsigned) rv); + return rv; + } + + DEBUG_SCARD("Success 0x%08x %d %d", (unsigned) hContext, (unsigned) cchReaders, (int) strlen(readerList)); + + poslen1 = stream_get_pos(irp->output); + stream_seek_uint32(irp->output); + + stream_write_uint32(irp->output, 0x01760650); + + poslen2 = stream_get_pos(irp->output); + stream_seek_uint32(irp->output); + + walker = readerList; + dataLength = 0; + while (1) + { + elemLength = strlen(walker); + if (elemLength == 0) + break; + + dataLength += sc_output_string(irp, walker, wide); + walker += elemLength + 1; + elemLength = strlen(walker); + } + + dataLength += sc_output_string(irp, "\0", wide); + + pos = stream_get_pos(irp->output); + + stream_set_pos(irp->output, poslen1); + stream_write_uint32(irp->output, dataLength); + stream_set_pos(irp->output, poslen2); + stream_write_uint32(irp->output, dataLength); + + stream_set_pos(irp->output, pos); + + sc_output_repos(irp, dataLength); + sc_output_alignment(irp, 8); + +#ifdef SCARD_AUTOALLOCATE + SCardFreeMemory(hContext, readerList); +#else + xfree(readerList); +#endif + + return rv; +} + + +static uint32 +handle_GetStatusChange(IRP* irp, boolean wide) +{ + LONG rv; + SCARDCONTEXT hContext; + DWORD dwTimeout = 0; + DWORD readerCount = 0; + SCARD_READERSTATE *readerStates, *cur; + int i; + + stream_seek(irp->input, 0x18); + stream_read_uint32(irp->input, dwTimeout); + stream_read_uint32(irp->input, readerCount); + + stream_seek(irp->input, 8); + + stream_read_uint32(irp->input, hContext); + + stream_seek(irp->input, 4); + + DEBUG_SCARD("context: 0x%08x, timeout: 0x%08x, count: %d", + (unsigned) hContext, (unsigned) dwTimeout, (int) readerCount); + if (readerCount > 0) + { + readerStates = xzalloc(readerCount * sizeof(SCARD_READERSTATE)); + if (!readerStates) + return sc_output_return(irp, SCARD_E_NO_MEMORY); + + + for (i = 0; i < readerCount; i++) + { + cur = &readerStates[i]; + + stream_seek(irp->input, 4); + + /* + * TODO: on-wire is little endian; need to either + * convert to host endian or fix the headers to + * request the order we want + */ + stream_read_uint32(irp->input, cur->dwCurrentState); + stream_read_uint32(irp->input, cur->dwEventState); + stream_read_uint32(irp->input, cur->cbAtr); + stream_read(irp->input, cur->rgbAtr, 32); + + stream_seek(irp->input, 4); + + /* reset high bytes? */ + cur->dwCurrentState &= 0x0000FFFF; + cur->dwEventState &= 0x0000FFFF; + cur->dwEventState = 0; + } + + for (i = 0; i < readerCount; i++) + { + cur = &readerStates[i]; + uint32 dataLength; + + stream_seek(irp->input, 8); + stream_read_uint32(irp->input, dataLength); + sc_input_repos(irp, sc_input_string(irp, (char **) &cur->szReader, dataLength, wide)); + + DEBUG_SCARD(" \"%s\"", cur->szReader ? cur->szReader : "NULL"); + DEBUG_SCARD(" user: 0x%08x, state: 0x%08x, event: 0x%08x", + (unsigned) cur->pvUserData, (unsigned) cur->dwCurrentState, + (unsigned) cur->dwEventState); + + if (strcmp(cur->szReader, "\\\\?PnP?\\Notification") == 0) + cur->dwCurrentState |= SCARD_STATE_IGNORE; + } + } + else + { + readerStates = NULL; + } + + rv = SCardGetStatusChange(hContext, (DWORD) dwTimeout, readerStates, (DWORD) readerCount); + + if (rv != SCARD_S_SUCCESS) + DEBUG_SCARD("Failure: %s (0x%08x)", pcsc_stringify_error(rv), (unsigned) rv); + else + DEBUG_SCARD("Success"); + + stream_write_uint32(irp->output, readerCount); + stream_write_uint32(irp->output, 0x00084dd8); + stream_write_uint32(irp->output, readerCount); + + for (i = 0; i < readerCount; i++) + { + cur = &readerStates[i]; + + DEBUG_SCARD(" \"%s\"", cur->szReader ? cur->szReader : "NULL"); + DEBUG_SCARD(" user: 0x%08x, state: 0x%08x, event: 0x%08x\n", + (unsigned) cur->pvUserData, (unsigned) cur->dwCurrentState, + (unsigned) cur->dwEventState); + + /* TODO: do byte conversions if necessary */ + stream_write_uint32(irp->output, cur->dwCurrentState); + stream_write_uint32(irp->output, cur->dwEventState); + stream_write_uint32(irp->output, cur->cbAtr); + stream_write(irp->output, cur->rgbAtr, 32); + + stream_write_zero(irp->output, 4); + + xfree((void *)cur->szReader); + } + + sc_output_alignment(irp, 8); + + xfree(readerStates); + return rv; +} + + +static uint32 +handle_Cancel(IRP *irp) // TESTME +{ + LONG rv; + SCARDCONTEXT hContext; + + stream_seek(irp->input, 0x1c); + stream_read_uint32(irp->input, hContext); + + rv = SCardCancel(hContext); + + if (rv != SCARD_S_SUCCESS) + DEBUG_SCARD("Failure: %s (0x%08x)\n", pcsc_stringify_error(rv), (unsigned) rv); + else + DEBUG_SCARD("Success context: 0x%08x %s\n", (unsigned) hContext, pcsc_stringify_error(rv)); + + sc_output_alignment(irp, 8); + return rv; +} + + +static uint32 +handle_Connect(IRP* irp, boolean wide) +{ + LONG rv; + SCARDCONTEXT hContext; + char *readerName = NULL; + DWORD dwShareMode = 0; + DWORD dwPreferredProtocol = 0; + DWORD dwActiveProtocol = 0; + SCARDHANDLE hCard; + + stream_seek(irp->input, 0x1c); + stream_read_uint32(irp->input, dwShareMode); + stream_read_uint32(irp->input, dwPreferredProtocol); + + sc_input_reader_name(irp, &readerName, wide); + + stream_seek(irp->input, 4); + stream_read_uint32(irp->input, hContext); + + DEBUG_SCARD("(context: 0x%08x, share: 0x%08x, proto: 0x%08x, reader: \"%s\")", + (unsigned) hContext, (unsigned) dwShareMode, + (unsigned) dwPreferredProtocol, readerName ? readerName : "NULL"); + + rv = SCardConnect(hContext, readerName, (DWORD) dwShareMode, + (DWORD) dwPreferredProtocol, &hCard, (DWORD *) &dwActiveProtocol); + + if (rv != SCARD_S_SUCCESS) + DEBUG_SCARD("Failure: %s 0x%08x", pcsc_stringify_error(rv), (unsigned) rv); + else + DEBUG_SCARD("Success 0x%08x", (unsigned) hCard); + + stream_write_uint32(irp->output, 0x00000000); + stream_write_uint32(irp->output, 0x00000000); + stream_write_uint32(irp->output, 0x00000004); + stream_write_uint32(irp->output, 0x016Cff34); + stream_write_uint32(irp->output, dwActiveProtocol); + stream_write_uint32(irp->output, 0x00000004); + stream_write_uint32(irp->output, hCard); + stream_seek(irp->output, 28); + + sc_output_alignment(irp, 8); + + xfree(readerName); + return rv; +} + + +static uint32 +handle_Reconnect(IRP* irp) // TESTME +{ + LONG rv; + SCARDCONTEXT hContext; + SCARDHANDLE hCard; + DWORD dwShareMode = 0; + DWORD dwPreferredProtocol = 0; + DWORD dwInitialization = 0; + DWORD dwActiveProtocol = 0; + + stream_seek(irp->input, 0x20); + stream_read_uint32(irp->input, dwShareMode); + stream_read_uint32(irp->input, dwPreferredProtocol); + stream_read_uint32(irp->input, dwInitialization); + + stream_seek(irp->input, 0x4); + stream_read_uint32(irp->input, hContext); + stream_seek(irp->input, 0x4); + stream_read_uint32(irp->input, hCard); + + DEBUG_SCARD("(context: 0x%08x, hcard: 0x%08x, share: 0x%08x, proto: 0x%08x, init: 0x%08x)", + (unsigned) hContext, (unsigned) hCard, + (unsigned) dwShareMode, (unsigned) dwPreferredProtocol, (unsigned) dwInitialization); + + rv = SCardReconnect(hCard, (DWORD) dwShareMode, (DWORD) dwPreferredProtocol, + (DWORD) dwInitialization, (LPDWORD) &dwActiveProtocol); + + if (rv != SCARD_S_SUCCESS) + DEBUG_SCARD("Failure: %s (0x%08x)", pcsc_stringify_error(rv), (unsigned) rv); + else + DEBUG_SCARD("Success (proto: 0x%08x)", (unsigned) dwActiveProtocol); + + sc_output_alignment(irp, 8); + stream_write_uint32(irp->output, dwActiveProtocol); // reversed? + + return rv; +} + + +static uint32 +handle_Disconnect(IRP* irp) +{ + LONG rv; + SCARDCONTEXT hContext; + SCARDHANDLE hCard; + DWORD dwDisposition = 0; + + stream_seek(irp->input, 0x20); + stream_read_uint32(irp->input, dwDisposition); + stream_seek(irp->input, 4); + stream_read_uint32(irp->input, hContext); + stream_seek(irp->input, 4); + stream_read_uint32(irp->input, hCard); + + DEBUG_SCARD("(context: 0x%08x, hcard: 0x%08x, disposition: 0x%08x)", + (unsigned) hContext, (unsigned) hCard, (unsigned) dwDisposition); + + rv = SCardDisconnect(hCard, (DWORD) dwDisposition); + + if (rv != SCARD_S_SUCCESS) + DEBUG_SCARD("Failure: %s (0x%08x)", pcsc_stringify_error(rv), (unsigned) rv); + else + DEBUG_SCARD("Success"); + + sc_output_alignment(irp, 8); + + return rv; +} + + +static uint32 +handle_BeginTransaction(IRP* irp) +{ + LONG rv; + SCARDCONTEXT hCard; + + stream_seek(irp->input, 0x30); + stream_read_uint32(irp->input, hCard); + + rv = SCardBeginTransaction(hCard); + + if (rv != SCARD_S_SUCCESS) + DEBUG_SCARD("Failure: %s (0x%08x)", pcsc_stringify_error(rv), (unsigned) rv); + else + DEBUG_SCARD("Success hcard: 0x%08x", (unsigned) hCard); + + sc_output_alignment(irp, 8); + + return rv; +} + + +static uint32 +handle_EndTransaction(IRP* irp) +{ + LONG rv; + SCARDCONTEXT hCard; + DWORD dwDisposition = 0; + + stream_seek(irp->input, 0x20); + stream_read_uint32(irp->input, dwDisposition); + + stream_seek(irp->input, 0x0C); + stream_read_uint32(irp->input, hCard); + + rv = SCardEndTransaction(hCard, dwDisposition); + if (rv != SCARD_S_SUCCESS) + DEBUG_SCARD("Failure: %s (0x%08x)", pcsc_stringify_error(rv), (unsigned) rv); + else + DEBUG_SCARD("Success hcard: 0x%08x", (unsigned) hCard); + + sc_output_alignment(irp, 8); + return rv; +} + + +static uint32 +handle_State(IRP* irp) // TESTME +{ + LONG rv; + SCARDHANDLE hCard; + DWORD state = 0, protocol = 0; + DWORD readerLen; + DWORD atrLen = MAX_ATR_SIZE; + char * readerName; + BYTE pbAtr[MAX_ATR_SIZE]; + +#ifdef WITH_DEBUG_SCARD + int i; +#endif + + stream_seek(irp->input, 0x24); + stream_seek_uint32(irp->input); // atrLen + + stream_seek(irp->input, 0x0c); + stream_read_uint32(irp->input, hCard); + stream_seek(irp->input, 0x04); + +#ifdef SCARD_AUTOALLOCATE + readerLen = SCARD_AUTOALLOCATE; + + rv = SCardStatus(hCard, (LPSTR) &readerName, &readerLen, &state, &protocol, pbAtr, &atrLen); +#else + readerLen = 256; + readerName = xmalloc(readerLen); + + rv = SCardStatus(hCard, (LPSTR) readerName, &readerLen, &state, &protocol, pbAtr, &atrLen); +#endif + + if (rv != SCARD_S_SUCCESS) + { + DEBUG_SCARD("Failure: %s (0x%08x)", pcsc_stringify_error(rv), (unsigned) rv); + return sc_output_return(irp, rv); + } + + DEBUG_SCARD("Success (hcard: 0x%08x len: %d state: 0x%08x, proto: 0x%08x)", + (unsigned) hCard, (int) atrLen, (unsigned) state, (unsigned) protocol); + +#ifdef WITH_DEBUG_SCARD + printf(" ATR: "); + for (i = 0; i < atrLen; i++) + printf("%02x%c", pbAtr[i], (i == atrLen - 1) ? ' ' : ':'); + printf("\n"); +#endif + + state = sc_map_state(state); + + stream_write_uint32(irp->output, state); + stream_write_uint32(irp->output, protocol); + stream_write_uint32(irp->output, atrLen); + stream_write_uint32(irp->output, 0x00000001); + stream_write_uint32(irp->output, atrLen); + stream_write(irp->output, pbAtr, atrLen); + + sc_output_repos(irp, atrLen); + sc_output_alignment(irp, 8); + +#ifdef SCARD_AUTOALLOCATE + SCard +#else + xfree(readerName); +#endif + + return rv; +} + + +static DWORD +handle_Status(IRP *irp, boolean wide) +{ + LONG rv; + SCARDHANDLE hCard; + DWORD state, protocol; + DWORD readerLen = 0; + DWORD atrLen = 0; + char * readerName; + BYTE pbAtr[MAX_ATR_SIZE]; + uint32 dataLength; + int pos, poslen1, poslen2; + +#ifdef WITH_DEBUG_SCARD + int i; +#endif + + stream_seek(irp->input, 0x24); + stream_read_uint32(irp->input, readerLen); + stream_read_uint32(irp->input, atrLen); + stream_seek(irp->input, 0x0c); + stream_read_uint32(irp->input, hCard); + stream_seek(irp->input, 0x4); + + atrLen = MAX_ATR_SIZE; + +#ifdef SCARD_AUTOALLOCATE + readerLen = SCARD_AUTOALLOCATE; + + rv = SCardStatus(hCard, (LPSTR) &readerName, &readerLen, &state, &protocol, pbAtr, &atrLen); +#else + readerLen = 256; + readerName = xmalloc(readerLen); + + rv = SCardStatus(hCard, (LPSTR) readerName, &readerLen, &state, &protocol, pbAtr, &atrLen); +#endif + + if (rv != SCARD_S_SUCCESS) + { + DEBUG_SCARD("Failure: %s (0x%08x)", pcsc_stringify_error(rv), (unsigned) rv); + return sc_output_return(irp, rv); + } + + DEBUG_SCARD("Success (state: 0x%08x, proto: 0x%08x)", (unsigned) state, (unsigned) protocol); + DEBUG_SCARD(" Reader: \"%s\"", readerName ? readerName : "NULL"); + +#ifdef WITH_DEBUG_SCARD + printf(" ATR: "); + for (i = 0; i < atrLen; i++) + printf("%02x%c", pbAtr[i], (i == atrLen - 1) ? ' ' : ':'); + printf("\n"); +#endif + + state = sc_map_state(state); + + poslen1 = stream_get_pos(irp->output); + stream_write_uint32(irp->output, readerLen); + stream_write_uint32(irp->output, 0x00020000); + stream_write_uint32(irp->output, state); + stream_write_uint32(irp->output, protocol); + stream_write(irp->output, pbAtr, atrLen); + + if (atrLen < 32) + stream_write_zero(irp->output, 32 - atrLen); + stream_write_uint32(irp->output, atrLen); + + poslen2 = stream_get_pos(irp->output); + stream_write_uint32(irp->output, readerLen); + + dataLength = sc_output_string(irp, readerName, wide); + dataLength += sc_output_string(irp, "\0", wide); + sc_output_repos(irp, dataLength); + + pos = stream_get_pos(irp->output); + stream_set_pos(irp->output, poslen1); + stream_write_uint32(irp->output,dataLength); + stream_set_pos(irp->output, poslen2); + stream_write_uint32(irp->output,dataLength); + stream_set_pos(irp->output, pos); + + sc_output_alignment(irp, 8); + +#ifdef SCARD_AUTOALLOCATE + //SCardFreeMemory(NULL, readerName); + free(readerName); +#else + xfree(readerName); +#endif + + return rv; +} + + +static uint32 +handle_Transmit(IRP* irp) +{ + LONG rv; + SCARDCONTEXT hCard; + uint32 map[7], linkedLen; + SCARD_IO_REQUEST pioSendPci, pioRecvPci, *pPioRecvPci; + DWORD cbSendLength = 0, cbRecvLength = 0; + BYTE *sendBuf = NULL, *recvBuf = NULL; + + stream_seek(irp->input, 0x14); + stream_read_uint32(irp->input, map[0]); + stream_seek(irp->input, 0x4); + stream_read_uint32(irp->input, map[1]); + + stream_read_uint32(irp->input, pioSendPci.dwProtocol); + stream_read_uint32(irp->input, pioSendPci.cbPciLength); + + stream_read_uint32(irp->input, map[2]); + stream_read_uint32(irp->input, cbSendLength); + stream_read_uint32(irp->input, map[3]); + stream_read_uint32(irp->input, map[4]); + stream_read_uint32(irp->input, map[5]); + stream_read_uint32(irp->input, cbRecvLength); + + if (map[0] & SCARD_INPUT_LINKED) + sc_input_skip_linked(irp); + + stream_seek(irp->input, 4); + stream_read_uint32(irp->input, hCard); + + if (map[2] & SCARD_INPUT_LINKED) + { + /* sendPci */ + stream_read_uint32(irp->input, linkedLen); + + stream_read_uint32(irp->input, pioSendPci.dwProtocol); + stream_seek(irp->input, linkedLen - 4); + + sc_input_repos(irp, linkedLen); + } + pioSendPci.cbPciLength = sizeof(SCARD_IO_REQUEST); + + if (map[3] & SCARD_INPUT_LINKED) + { + /* send buffer */ + stream_read_uint32(irp->input, linkedLen); + + sendBuf = xmalloc(linkedLen); + stream_read(irp->input, sendBuf, linkedLen); + sc_input_repos(irp, linkedLen); + } + + if (cbRecvLength) + recvBuf = xmalloc(cbRecvLength); + + if (map[4] & SCARD_INPUT_LINKED) + { + /* recvPci */ + stream_read_uint32(irp->input, linkedLen); + + stream_read_uint32(irp->input, pioRecvPci.dwProtocol); + stream_seek(irp->input, linkedLen - 4); + + sc_input_repos(irp, linkedLen); + + stream_read_uint32(irp->input, map[6]); + if (map[6] & SCARD_INPUT_LINKED) + { + // not sure what this is + stream_read_uint32(irp->input, linkedLen); + stream_seek(irp->input, linkedLen); + + sc_input_repos(irp, linkedLen); + } + pioRecvPci.cbPciLength = sizeof(SCARD_IO_REQUEST); + pPioRecvPci = &pioRecvPci; + } + else + { + pPioRecvPci = NULL; + } + pPioRecvPci = NULL; + + DEBUG_SCARD("SCardTransmit(hcard: 0x%08lx, send: %d bytes, recv: %d bytes)", + (long unsigned) hCard, (int) cbSendLength, (int) cbRecvLength); + + rv = SCardTransmit(hCard, &pioSendPci, sendBuf, cbSendLength, + pPioRecvPci, recvBuf, &cbRecvLength); + + if (rv != SCARD_S_SUCCESS) + { + DEBUG_SCARD("Failure: %s (0x%08x)", pcsc_stringify_error(rv), (unsigned) rv); + } + else + { + DEBUG_SCARD("Success (%d bytes)", (int) cbRecvLength); + + stream_write_uint32(irp->output, 0); /* pioRecvPci 0x00; */ + + sc_output_buffer_start(irp, cbRecvLength); /* start of recvBuf output */ + + sc_output_buffer(irp, (char *) recvBuf, cbRecvLength); + } + + sc_output_alignment(irp, 8); + + xfree(sendBuf); + xfree(recvBuf); + + return rv; +} + + +static uint32 +handle_Control(IRP* irp) // TESTME +{ + LONG rv; + SCARDCONTEXT hContext; + SCARDHANDLE hCard; + uint32 map[3]; + uint32 controlCode; + BYTE *recvBuffer = NULL, *sendBuffer = NULL; + uint32 recvLength; + DWORD nBytesReturned; + DWORD outBufferSize; + + stream_seek(irp->input, 0x14); + stream_read_uint32(irp->input, map[0]); + stream_seek(irp->input, 0x4); + stream_read_uint32(irp->input, map[1]); + stream_read_uint32(irp->input, controlCode); + stream_read_uint32(irp->input, recvLength); + stream_read_uint32(irp->input, map[2]); + stream_seek(irp->input, 0x4); + stream_read_uint32(irp->input, outBufferSize); + stream_seek(irp->input, 0x4); + stream_read_uint32(irp->input, hContext); + stream_seek(irp->input, 0x4); + stream_read_uint32(irp->input, hCard); + + if (map[2] & SCARD_INPUT_LINKED) + { + /* read real input size */ + stream_read_uint32(irp->input, recvLength); + + recvBuffer = xmalloc(recvLength); + if (!recvBuffer) + return sc_output_return(irp, SCARD_E_NO_MEMORY); + + stream_read(irp->input, recvBuffer, recvLength); + } + + nBytesReturned = outBufferSize; + sendBuffer = xmalloc(outBufferSize); + if (!sendBuffer) + return sc_output_return(irp, SCARD_E_NO_MEMORY); + + rv = SCardControl(hCard, (DWORD) controlCode, recvBuffer, (DWORD) recvLength, + sendBuffer, (DWORD) outBufferSize, &nBytesReturned); + + if (rv != SCARD_S_SUCCESS) + DEBUG_SCARD("Failure: %s (0x%08x)", pcsc_stringify_error(rv), (unsigned) rv); + else + DEBUG_SCARD("Success (out: %u bytes)", (unsigned) nBytesReturned); + + stream_write_uint32(irp->output, (uint32) nBytesReturned); + stream_write_uint32(irp->output, 0x00000004); + stream_write_uint32(irp->output, nBytesReturned); + + if (nBytesReturned > 0) + { + stream_write(irp->output, sendBuffer, nBytesReturned); + sc_output_repos(irp, nBytesReturned); + } + + sc_output_alignment(irp, 8); + + xfree(recvBuffer); + xfree(sendBuffer); + + return rv; +} + + +static uint32 +handle_GetAttrib(IRP* irp) +{ + LONG rv; + SCARDHANDLE hCard; + DWORD dwAttrId = 0, dwAttrLen = 0; + DWORD attrLen = 0; + unsigned char *pbAttr; + + stream_seek(irp->input, 0x20); + stream_read_uint32(irp->input, dwAttrId); + stream_seek(irp->input, 0x4); + stream_read_uint32(irp->input, dwAttrLen); + stream_seek(irp->input, 0xc); + stream_read_uint32(irp->input, hCard); + + DEBUG_SCARD("hcard: 0x%08x, attrib: 0x%08x (%d bytes)\n", + (unsigned) hCard, (unsigned) dwAttrId, (int) dwAttrLen); + +#ifdef SCARD_AUTOALLOCATE + if(dwAttrLen == 0) + { + attrLen = 0; + } + else + { + attrLen = SCARD_AUTOALLOCATE; + } +#endif + + rv = SCardGetAttrib(hCard, dwAttrId, attrLen == 0 ? NULL : (unsigned char *)&pbAttr, &attrLen); + + if(dwAttrId == SCARD_ATTR_DEVICE_FRIENDLY_NAME_A && rv == SCARD_E_UNSUPPORTED_FEATURE) + { + rv = SCardGetAttrib(hCard, SCARD_ATTR_DEVICE_FRIENDLY_NAME_W, + attrLen == 0 ? NULL : (unsigned char *)&pbAttr, &attrLen); + } + if(dwAttrId == SCARD_ATTR_DEVICE_FRIENDLY_NAME_W && rv == SCARD_E_UNSUPPORTED_FEATURE) + { + rv = SCardGetAttrib(hCard, SCARD_ATTR_DEVICE_FRIENDLY_NAME_A, + attrLen == 0 ? NULL : (unsigned char *)&pbAttr, &attrLen); + } + if(attrLen > dwAttrLen && pbAttr != NULL) + { + rv = SCARD_E_INSUFFICIENT_BUFFER; + } + dwAttrLen = attrLen; + + if (rv != SCARD_S_SUCCESS) + { + DEBUG_SCARD("Failure: %s (0x%08x)", pcsc_stringify_error(rv), (unsigned int) rv); + free(pbAttr); + return sc_output_return(irp, rv); + } + else + { + DEBUG_SCARD("Success (%d bytes)", (int) dwAttrLen); + + stream_write_uint32(irp->output, dwAttrLen); + stream_write_uint32(irp->output, 0x00000200); + stream_write_uint32(irp->output, dwAttrLen); + + if (!pbAttr) + { + stream_write_zero(irp->output, dwAttrLen); + } + else + { + stream_write(irp->output, pbAttr, dwAttrLen); + } + sc_output_repos(irp, dwAttrLen); + // Align to multiple of 4 + stream_write_uint32(irp->output, 0); + } + sc_output_alignment(irp, 8); + + xfree(pbAttr); + + return rv; +} + + +static uint32 +handle_AccessStartedEvent(IRP* irp) +{ + stream_write_zero(irp->output, 8); + return SCARD_S_SUCCESS; +} + + +void +scard_error(SCARD_DEVICE* scard, IRP* irp, uint32 ntstatus) +{ + // [MS-RDPESC] 3.1.4.4 + printf("scard processing error %x\n", ntstatus); + + stream_set_pos(irp->output, 0); // CHECKME + irp->IoStatus = ntstatus; + irp->Complete(irp); +} + + +/* http://msdn.microsoft.com/en-gb/library/ms938473.aspx */ +typedef struct _SERVER_SCARD_ATRMASK +{ + uint32 cbAtr; + unsigned char rgbAtr[36]; + unsigned char rgbMask[36]; +} +SERVER_SCARD_ATRMASK; + +static uint32 +handle_LocateCardsByATR(IRP* irp, boolean wide) // TESTME +{ + int i, j, k; + LONG rv; + SCARDCONTEXT hContext; + SERVER_SCARD_ATRMASK *pAtrMasks, *curAtr; + uint32 atrMaskCount = 0; + uint32 readerCount = 0; + SCARD_READERSTATE *readerStates, *rsCur, *cur; + + stream_seek(irp->input, 0x2c); + stream_read_uint32(irp->input, hContext); + stream_read_uint32(irp->input, atrMaskCount); + + pAtrMasks = xmalloc(atrMaskCount * sizeof(SERVER_SCARD_ATRMASK)); + if (!pAtrMasks) + return sc_output_return(irp, SCARD_E_NO_MEMORY); + + for (i = 0; i < atrMaskCount; i++) + { + stream_read_uint32(irp->input, pAtrMasks[i].cbAtr); + stream_read(irp->input, pAtrMasks[i].rgbAtr, 36); + stream_read(irp->input, pAtrMasks[i].rgbMask, 36); + } + + stream_read_uint32(irp->input, readerCount); + + readerStates = xzalloc(readerCount * sizeof(SCARD_READERSTATE)); + if (!readerStates) + return sc_output_return(irp, SCARD_E_NO_MEMORY); + + for (i = 0; i < readerCount; i++) + { + cur = &readerStates[i]; + + stream_seek(irp->input, 4); + + /* + * TODO: on-wire is little endian; need to either + * convert to host endian or fix the headers to + * request the order we want + */ + stream_read_uint32(irp->input, cur->dwCurrentState); + stream_read_uint32(irp->input, cur->dwEventState); + stream_read_uint32(irp->input, cur->cbAtr); + stream_read(irp->input, cur->rgbAtr, 32); + + stream_seek(irp->input, 4); + + /* reset high bytes? */ + cur->dwCurrentState &= 0x0000FFFF; + cur->dwEventState &= 0x0000FFFF; + cur->dwEventState = 0; + } + + for (i = 0; i < readerCount; i++) + { + cur = &readerStates[i]; + uint32 dataLength; + + stream_seek(irp->input, 8); + stream_read_uint32(irp->input, dataLength); + sc_input_repos(irp, sc_input_string(irp, (char **) &cur->szReader, dataLength, wide)); + + DEBUG_SCARD(" \"%s\"", cur->szReader ? cur->szReader : "NULL"); + DEBUG_SCARD(" user: 0x%08x, state: 0x%08x, event: 0x%08x", + (unsigned) cur->pvUserData, (unsigned) cur->dwCurrentState, + (unsigned) cur->dwEventState); + + if (strcmp(cur->szReader, "\\\\?PnP?\\Notification") == 0) + cur->dwCurrentState |= SCARD_STATE_IGNORE; + } + + rv = SCardGetStatusChange(hContext, 0x00000001, readerStates, readerCount); + if (rv != SCARD_S_SUCCESS) + { + DEBUG_SCARD("Failure: %s (0x%08x)", + pcsc_stringify_error(rv), (unsigned) rv); + + return sc_output_return(irp, rv); + } + + DEBUG_SCARD("Success"); + for (i = 0, curAtr = pAtrMasks; i < atrMaskCount; i++, curAtr++) + { + for (j = 0, rsCur = readerStates; j < readerCount; j++, rsCur++) + { + boolean equal = 1; + for (k = 0; k < cur->cbAtr; k++) + { + if ((curAtr->rgbAtr[k] & curAtr->rgbMask[k]) != + (rsCur->rgbAtr[k] & curAtr->rgbMask[k])) + { + equal = 0; + break; + } + } + if (equal) + { + rsCur->dwEventState |= 0x00000040; /* SCARD_STATE_ATRMATCH 0x00000040 */ + } + } + } + + stream_write_uint32(irp->output, readerCount); + stream_write_uint32(irp->output, 0x00084dd8); + stream_write_uint32(irp->output, readerCount); + + for (i = 0, rsCur = readerStates; i < readerCount; i++, rsCur++) + { + stream_write_uint32(irp->output, cur->dwCurrentState); + stream_write_uint32(irp->output, cur->dwEventState); + stream_write_uint32(irp->output, cur->cbAtr); + stream_write(irp->output, cur->rgbAtr, 32); + + stream_write_zero(irp->output, 4); + + xfree((void *)cur->szReader); + } + + sc_output_alignment(irp, 8); + + free(readerStates); + + return rv; +} + + +boolean +scard_async_op(IRP* irp) +{ + uint32 ioctl_code; + + /* peek ahead */ + stream_seek(irp->input, 8); + stream_read_uint32(irp->input, ioctl_code); + stream_rewind(irp->input, 12); + + switch (ioctl_code) + { + /* non-blocking events */ + case SCARD_IOCTL_ACCESS_STARTED_EVENT: + + case SCARD_IOCTL_ESTABLISH_CONTEXT: + case SCARD_IOCTL_RELEASE_CONTEXT: + case SCARD_IOCTL_IS_VALID_CONTEXT: + + return False; + break; + + /* async events */ + case SCARD_IOCTL_GET_STATUS_CHANGE: + case SCARD_IOCTL_GET_STATUS_CHANGE + 4: + + case SCARD_IOCTL_TRANSMIT: + + case SCARD_IOCTL_STATUS: + case SCARD_IOCTL_STATUS + 4: + return True; + break; + + default: + break; + } + + /* default to async */ + return True; +} + + +void +scard_device_control(SCARD_DEVICE* scard, IRP* irp) +{ + uint32 output_len, input_len, ioctl_code; + uint32 stream_len, result; + uint32 pos, pad_len; + uint32 irp_len; + uint32 irp_result_pos, output_len_pos, result_pos; + + stream_read_uint32(irp->input, output_len); + stream_read_uint32(irp->input, input_len); + stream_read_uint32(irp->input, ioctl_code); + + stream_seek(irp->input, 20); /* padding */ + + // stream_seek(irp->input, 4); /* TODO: parse len, le, v1 */ + // stream_seek(irp->input, 4); /* 0xcccccccc */ + // stream_seek(irp->input, 4); /* rpce len */ + + /* [MS-RDPESC] 3.2.5.1 Sending Outgoing Messages */ + stream_extend(irp->output, 2048); + + irp_result_pos = stream_get_pos(irp->output); + + stream_write_uint32(irp->output, 0x00081001); /* len 8, LE, v1 */ + + /* [MS-RPCE] 2.2.6.1 */ + stream_write_uint32(irp->output, 0x00081001); /* len 8, LE, v1 */ + stream_write_uint32(irp->output, 0xcccccccc); /* filler */ + + output_len_pos = stream_get_pos(irp->output); + stream_seek(irp->output, 4); /* size */ + + stream_write_uint32(irp->output, 0x0); /* filler */ + + result_pos = stream_get_pos(irp->output); + stream_seek(irp->output, 4); /* result */ + + /* body */ + switch (ioctl_code) + { + case SCARD_IOCTL_ESTABLISH_CONTEXT: + result = handle_EstablishContext(irp); + break; + + case SCARD_IOCTL_IS_VALID_CONTEXT: + result = handle_IsValidContext(irp); + break; + + case SCARD_IOCTL_RELEASE_CONTEXT: + result = handle_ReleaseContext(irp); + break; + + case SCARD_IOCTL_LIST_READERS: + result = handle_ListReaders(irp, 0); + break; + case SCARD_IOCTL_LIST_READERS + 4: + result = handle_ListReaders(irp, 1); + break; + + case SCARD_IOCTL_LIST_READER_GROUPS: + case SCARD_IOCTL_LIST_READER_GROUPS + 4: + // typically not used unless list_readers fail + result = SCARD_F_INTERNAL_ERROR; + break; + + case SCARD_IOCTL_GET_STATUS_CHANGE: + result = handle_GetStatusChange(irp, 0); + break; + case SCARD_IOCTL_GET_STATUS_CHANGE + 4: + result = handle_GetStatusChange(irp, 1); + break; + + case SCARD_IOCTL_CANCEL: + result = handle_Cancel(irp); + break; + + case SCARD_IOCTL_CONNECT: + result = handle_Connect(irp, 0); + break; + case SCARD_IOCTL_CONNECT + 4: + result = handle_Connect(irp, 1); + break; + + case SCARD_IOCTL_RECONNECT: + result = handle_Reconnect(irp); + break; + + case SCARD_IOCTL_DISCONNECT: + result = handle_Disconnect(irp); + break; + + case SCARD_IOCTL_BEGIN_TRANSACTION: + result = handle_BeginTransaction(irp); + break; + + case SCARD_IOCTL_END_TRANSACTION: + result = handle_EndTransaction(irp); + break; + + case SCARD_IOCTL_STATE: + result = handle_State(irp); + break; + + case SCARD_IOCTL_STATUS: + result = handle_Status(irp, 0); + break; + case SCARD_IOCTL_STATUS + 4: + result = handle_Status(irp, 1); + break; + + case SCARD_IOCTL_TRANSMIT: + result = handle_Transmit(irp); + break; + + case SCARD_IOCTL_CONTROL: + result = handle_Control(irp); + break; + + case SCARD_IOCTL_GETATTRIB: + result = handle_GetAttrib(irp); + break; + + case SCARD_IOCTL_ACCESS_STARTED_EVENT: + result = handle_AccessStartedEvent(irp); + break; + + case SCARD_IOCTL_LOCATE_CARDS_BY_ATR: + result = handle_LocateCardsByATR(irp, 0); + break; + case SCARD_IOCTL_LOCATE_CARDS_BY_ATR + 4: + result = handle_LocateCardsByATR(irp, 1); + break; + + default: + result = 0xc0000001; + printf("scard unknown ioctl 0x%x\n", ioctl_code); + break; + } + + /* look for NTSTATUS errors */ + if ((result & 0xc0000000) == 0xc0000000) + return scard_error(scard, irp, result); + + /* per Ludovic Rousseau, map different usage of this particular + * error code between pcsc-lite & windows */ + if (result == 0x8010001F) + result = 0x80100022; + + /* handle response packet */ + pos = stream_get_pos(irp->output); + stream_len = pos - irp_result_pos - 4; + + stream_set_pos(irp->output, output_len_pos); + stream_write_uint32(irp->output, stream_len - 24); + + stream_set_pos(irp->output, result_pos); + stream_write_uint32(irp->output, result); + + stream_set_pos(irp->output, pos); + + /* pad stream to 16 byte align */ + pad_len = stream_len % 16; + stream_write_zero(irp->output, pad_len); + pos = stream_get_pos(irp->output); + irp_len = stream_len + pad_len; + + stream_set_pos(irp->output, irp_result_pos); + stream_write_uint32(irp->output, irp_len); + stream_set_pos(irp->output, pos); + +#ifdef WITH_DEBUG_SCARD + freerdp_hexdump(stream_get_data(irp->output), stream_get_length(irp->output)); +#endif + irp->IoStatus = 0; + + irp->Complete(irp); + +} + diff --git a/client/X11/xf_gdi.c b/client/X11/xf_gdi.c index 993380475..65dd0b0ae 100644 --- a/client/X11/xf_gdi.c +++ b/client/X11/xf_gdi.c @@ -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); diff --git a/cmake/FindPCSC.cmake b/cmake/FindPCSC.cmake new file mode 100644 index 000000000..a113a0298 --- /dev/null +++ b/cmake/FindPCSC.cmake @@ -0,0 +1,3 @@ +pkg_check_modules(PCSC libpcsclite) + +# TODO: pcsc version diff --git a/config.h.in b/config.h.in index 9b7c01f26..8b4895baa 100644 --- a/config.h.in +++ b/config.h.in @@ -34,5 +34,6 @@ #cmakedefine WITH_DEBUG_X11 #cmakedefine WITH_DEBUG_RAIL #cmakedefine WITH_DEBUG_XV +#cmakedefine WITH_DEBUG_SCARD #endif diff --git a/docs/FreeRDP.vsd b/docs/FreeRDP.vsd new file mode 100644 index 000000000..cc017d4be Binary files /dev/null and b/docs/FreeRDP.vsd differ diff --git a/freerdp.pc.in b/freerdp.pc.in new file mode 100644 index 000000000..7c524daeb --- /dev/null +++ b/freerdp.pc.in @@ -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} + diff --git a/include/freerdp/cache/offscreen.h b/include/freerdp/cache/offscreen.h index ffab74062..211a8e143 100644 --- a/include/freerdp/cache/offscreen.h +++ b/include/freerdp/cache/offscreen.h @@ -43,6 +43,7 @@ struct rdp_offscreen_cache cbSetSurface SetSurface; + uint16 currentSurface; uint16 maxSize; uint16 maxEntries; rdpUpdate* update; diff --git a/include/freerdp/utils/list.h b/include/freerdp/utils/list.h index 52c0279dc..de53a100a 100644 --- a/include/freerdp/utils/list.h +++ b/include/freerdp/utils/list.h @@ -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 */ diff --git a/libfreerdp-cache/offscreen.c b/libfreerdp-cache/offscreen.c index ca87f2e8a..1d111771f 100644 --- a/libfreerdp-cache/offscreen.c +++ b/libfreerdp-cache/offscreen.c @@ -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; diff --git a/libfreerdp-utils/list.c b/libfreerdp-utils/list.c index 06f8bea16..9f7990bff 100644 --- a/libfreerdp-utils/list.c +++ b/libfreerdp-utils/list.c @@ -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; +}