diff options
Diffstat (limited to 'channels/urbdrc')
-rw-r--r-- | channels/urbdrc/CMakeLists.txt | 30 | ||||
-rw-r--r-- | channels/urbdrc/ChannelOptions.cmake | 18 | ||||
-rw-r--r-- | channels/urbdrc/client/CMakeLists.txt | 40 | ||||
-rw-r--r-- | channels/urbdrc/client/data_transfer.c | 1949 | ||||
-rw-r--r-- | channels/urbdrc/client/data_transfer.h | 36 | ||||
-rw-r--r-- | channels/urbdrc/client/libusb/CMakeLists.txt | 36 | ||||
-rw-r--r-- | channels/urbdrc/client/libusb/libusb_udevice.c | 1841 | ||||
-rw-r--r-- | channels/urbdrc/client/libusb/libusb_udevice.h | 76 | ||||
-rw-r--r-- | channels/urbdrc/client/libusb/libusb_udevman.c | 970 | ||||
-rw-r--r-- | channels/urbdrc/client/urbdrc_main.c | 1023 | ||||
-rw-r--r-- | channels/urbdrc/client/urbdrc_main.h | 222 | ||||
-rw-r--r-- | channels/urbdrc/common/CMakeLists.txt | 29 | ||||
-rw-r--r-- | channels/urbdrc/common/msusb.c | 395 | ||||
-rw-r--r-- | channels/urbdrc/common/msusb.h | 98 | ||||
-rw-r--r-- | channels/urbdrc/common/urbdrc_helpers.c | 425 | ||||
-rw-r--r-- | channels/urbdrc/common/urbdrc_helpers.h | 45 | ||||
-rw-r--r-- | channels/urbdrc/common/urbdrc_types.h | 306 |
17 files changed, 7539 insertions, 0 deletions
diff --git a/channels/urbdrc/CMakeLists.txt b/channels/urbdrc/CMakeLists.txt new file mode 100644 index 0000000..0030e27 --- /dev/null +++ b/channels/urbdrc/CMakeLists.txt @@ -0,0 +1,30 @@ +# FreeRDP: A Remote Desktop Protocol Implementation +# FreeRDP cmake build script +# +# Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com> +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +define_channel("urbdrc") + +include_directories(common) +add_subdirectory(common) + +if(WITH_CLIENT_CHANNELS) + option(WITH_DEBUG_URBDRC "Dump data send/received in URBDRC channel" ${DEFAULT_DEBUG_OPTION}) + + find_package(libusb-1.0 REQUIRED) + include_directories(${LIBUSB_1_INCLUDE_DIRS}) + + add_channel_client(${MODULE_PREFIX} ${CHANNEL_NAME}) +endif() diff --git a/channels/urbdrc/ChannelOptions.cmake b/channels/urbdrc/ChannelOptions.cmake new file mode 100644 index 0000000..770ba5e --- /dev/null +++ b/channels/urbdrc/ChannelOptions.cmake @@ -0,0 +1,18 @@ + +if (IOS OR ANDROID) + set(OPTION_DEFAULT OFF) + set(OPTION_CLIENT_DEFAULT OFF) + set(OPTION_SERVER_DEFAULT OFF) +else() + set(OPTION_DEFAULT ON) + set(OPTION_CLIENT_DEFAULT ON) + set(OPTION_SERVER_DEFAULT OFF) +endif() + +define_channel_options(NAME "urbdrc" TYPE "dynamic" + DESCRIPTION "USB Devices Virtual Channel Extension" + SPECIFICATIONS "[MS-RDPEUSB]" + DEFAULT ${OPTION_DEFAULT}) + +define_channel_client_options(${OPTION_CLIENT_DEFAULT}) +define_channel_server_options(${OPTION_SERVER_DEFAULT}) diff --git a/channels/urbdrc/client/CMakeLists.txt b/channels/urbdrc/client/CMakeLists.txt new file mode 100644 index 0000000..1787024 --- /dev/null +++ b/channels/urbdrc/client/CMakeLists.txt @@ -0,0 +1,40 @@ +# FreeRDP: A Remote Desktop Protocol Implementation +# FreeRDP cmake build script +# +# Copyright 2012 Atrust corp. +# Copyright 2012 Alfred Liu <alfred.liu@atruscorp.com> +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +define_channel_client("urbdrc") + +set(${MODULE_PREFIX}_SRCS + data_transfer.c + data_transfer.h + urbdrc_main.c + urbdrc_main.h +) + +set(${MODULE_PREFIX}_LIBS + winpr + freerdp + urbdrc-common +) +if (UDEV_FOUND AND UDEV_LIBRARIES) + list(APPEND ${MODULE_PREFIX}_LIBS ${UDEV_LIBRARIES}) +endif() + +add_channel_client_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} TRUE "DVCPluginEntry") + +# libusb subsystem +add_channel_client_subsystem(${MODULE_PREFIX} ${CHANNEL_NAME} "libusb" "") diff --git a/channels/urbdrc/client/data_transfer.c b/channels/urbdrc/client/data_transfer.c new file mode 100644 index 0000000..7a7e5a2 --- /dev/null +++ b/channels/urbdrc/client/data_transfer.c @@ -0,0 +1,1949 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * RemoteFX USB Redirection + * + * Copyright 2012 Atrust corp. + * Copyright 2012 Alfred Liu <alfred.liu@atruscorp.com> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <winpr/sysinfo.h> + +#include <urbdrc_helpers.h> + +#include "urbdrc_types.h" +#include "data_transfer.h" + +static void usb_process_get_port_status(IUDEVICE* pdev, wStream* out) +{ + int bcdUSB = pdev->query_device_descriptor(pdev, BCD_USB); + + switch (bcdUSB) + { + case USB_v1_0: + Stream_Write_UINT32(out, 0x303); + break; + + case USB_v1_1: + Stream_Write_UINT32(out, 0x103); + break; + + case USB_v2_0: + Stream_Write_UINT32(out, 0x503); + break; + + default: + Stream_Write_UINT32(out, 0x503); + break; + } +} + +static UINT urb_write_completion(IUDEVICE* pdev, GENERIC_CHANNEL_CALLBACK* callback, BOOL noAck, + wStream* out, UINT32 InterfaceId, UINT32 MessageId, + UINT32 RequestId, UINT32 usbd_status, UINT32 OutputBufferSize) +{ + if (!out) + return ERROR_INVALID_PARAMETER; + + if (Stream_Capacity(out) < OutputBufferSize + 36) + { + Stream_Free(out, TRUE); + return ERROR_INVALID_PARAMETER; + } + + Stream_SetPosition(out, 0); + Stream_Write_UINT32(out, InterfaceId); /** interface */ + Stream_Write_UINT32(out, MessageId); /** message id */ + + if (OutputBufferSize != 0) + Stream_Write_UINT32(out, URB_COMPLETION); + else + Stream_Write_UINT32(out, URB_COMPLETION_NO_DATA); + + Stream_Write_UINT32(out, RequestId); /** RequestId */ + Stream_Write_UINT32(out, 8); /** CbTsUrbResult */ + /** TsUrbResult TS_URB_RESULT_HEADER */ + Stream_Write_UINT16(out, 8); /** Size */ + Stream_Write_UINT16(out, 0); /* Padding */ + Stream_Write_UINT32(out, usbd_status); /** UsbdStatus */ + Stream_Write_UINT32(out, 0); /** HResult */ + Stream_Write_UINT32(out, OutputBufferSize); /** OutputBufferSize */ + Stream_Seek(out, OutputBufferSize); + + if (!noAck) + return stream_write_and_free(callback->plugin, callback->channel, out); + else + Stream_Free(out, TRUE); + + return ERROR_SUCCESS; +} + +static wStream* urb_create_iocompletion(UINT32 InterfaceField, UINT32 MessageId, UINT32 RequestId, + UINT32 OutputBufferSize) +{ + const UINT32 InterfaceId = (STREAM_ID_PROXY << 30) | (InterfaceField & 0x3FFFFFFF); + +#if UINT32_MAX >= SIZE_MAX + if (OutputBufferSize > UINT32_MAX - 28ull) + return NULL; +#endif + + wStream* out = Stream_New(NULL, OutputBufferSize + 28ull); + + if (!out) + return NULL; + + Stream_Write_UINT32(out, InterfaceId); /** interface */ + Stream_Write_UINT32(out, MessageId); /** message id */ + Stream_Write_UINT32(out, IOCONTROL_COMPLETION); /** function id */ + Stream_Write_UINT32(out, RequestId); /** RequestId */ + Stream_Write_UINT32(out, USBD_STATUS_SUCCESS); /** HResult */ + Stream_Write_UINT32(out, OutputBufferSize); /** Information */ + Stream_Write_UINT32(out, OutputBufferSize); /** OutputBufferSize */ + return out; +} + +static UINT urbdrc_process_register_request_callback(IUDEVICE* pdev, + GENERIC_CHANNEL_CALLBACK* callback, wStream* s, + IUDEVMAN* udevman) +{ + UINT32 NumRequestCompletion = 0; + UINT32 RequestCompletion = 0; + URBDRC_PLUGIN* urbdrc = NULL; + + if (!callback || !s || !udevman || !pdev) + return ERROR_INVALID_PARAMETER; + + urbdrc = (URBDRC_PLUGIN*)callback->plugin; + + if (!urbdrc) + return ERROR_INVALID_PARAMETER; + + WLog_Print(urbdrc->log, WLOG_DEBUG, "urbdrc_process_register_request_callback"); + + if (Stream_GetRemainingLength(s) >= 8) + { + Stream_Read_UINT32(s, NumRequestCompletion); /** must be 1 */ + /** RequestCompletion: + * unique Request Completion interface for the client to use */ + Stream_Read_UINT32(s, RequestCompletion); + pdev->set_ReqCompletion(pdev, RequestCompletion); + } + else if (Stream_GetRemainingLength(s) >= 4) /** Unregister the device */ + { + Stream_Read_UINT32(s, RequestCompletion); + + if (pdev->get_ReqCompletion(pdev) == RequestCompletion) + pdev->setChannelClosed(pdev); + } + else + return ERROR_INVALID_DATA; + + return ERROR_SUCCESS; +} + +static UINT urbdrc_process_cancel_request(IUDEVICE* pdev, wStream* s, IUDEVMAN* udevman) +{ + UINT32 CancelId = 0; + URBDRC_PLUGIN* urbdrc = NULL; + + if (!s || !udevman || !pdev) + return ERROR_INVALID_PARAMETER; + + urbdrc = (URBDRC_PLUGIN*)udevman->plugin; + + if (!Stream_CheckAndLogRequiredLength(TAG, s, 4)) + return ERROR_INVALID_DATA; + + Stream_Read_UINT32(s, CancelId); + WLog_Print(urbdrc->log, WLOG_DEBUG, "CANCEL_REQUEST: CancelId=%08" PRIx32 "", CancelId); + + if (pdev->cancel_transfer_request(pdev, CancelId) < 0) + return ERROR_INTERNAL_ERROR; + + return ERROR_SUCCESS; +} + +static UINT urbdrc_process_retract_device_request(IUDEVICE* pdev, wStream* s, IUDEVMAN* udevman) +{ + UINT32 Reason = 0; + URBDRC_PLUGIN* urbdrc = NULL; + + if (!s || !udevman) + return ERROR_INVALID_PARAMETER; + + urbdrc = (URBDRC_PLUGIN*)udevman->plugin; + + if (!urbdrc) + return ERROR_INVALID_PARAMETER; + + if (!Stream_CheckAndLogRequiredLength(TAG, s, 4)) + return ERROR_INVALID_DATA; + + Stream_Read_UINT32(s, Reason); /** Reason */ + + switch (Reason) + { + case UsbRetractReason_BlockedByPolicy: + WLog_Print(urbdrc->log, WLOG_DEBUG, + "UsbRetractReason_BlockedByPolicy: now it is not support"); + return ERROR_ACCESS_DENIED; + + default: + WLog_Print(urbdrc->log, WLOG_DEBUG, + "urbdrc_process_retract_device_request: Unknown Reason %" PRIu32 "", Reason); + return ERROR_ACCESS_DENIED; + } + + return ERROR_SUCCESS; +} + +static UINT urbdrc_process_io_control(IUDEVICE* pdev, GENERIC_CHANNEL_CALLBACK* callback, + wStream* s, UINT32 MessageId, IUDEVMAN* udevman) +{ + UINT32 InterfaceId = 0; + UINT32 IoControlCode = 0; + UINT32 InputBufferSize = 0; + UINT32 OutputBufferSize = 0; + UINT32 RequestId = 0; + UINT32 usbd_status = USBD_STATUS_SUCCESS; + wStream* out = NULL; + int success = 0; + URBDRC_PLUGIN* urbdrc = NULL; + + if (!callback || !s || !udevman || !pdev) + return ERROR_INVALID_PARAMETER; + + urbdrc = (URBDRC_PLUGIN*)callback->plugin; + + if (!urbdrc) + return ERROR_INVALID_PARAMETER; + + if (!Stream_CheckAndLogRequiredLength(TAG, s, 8)) + return ERROR_INVALID_DATA; + + Stream_Read_UINT32(s, IoControlCode); + Stream_Read_UINT32(s, InputBufferSize); + + if (!Stream_SafeSeek(s, InputBufferSize)) + return ERROR_INVALID_DATA; + if (!Stream_CheckAndLogRequiredLength(TAG, s, 8ULL)) + return ERROR_INVALID_DATA; + + Stream_Read_UINT32(s, OutputBufferSize); + Stream_Read_UINT32(s, RequestId); + + if (OutputBufferSize > UINT32_MAX - 4) + return ERROR_INVALID_DATA; + + InterfaceId = ((STREAM_ID_PROXY << 30) | pdev->get_ReqCompletion(pdev)); + out = urb_create_iocompletion(InterfaceId, MessageId, RequestId, OutputBufferSize + 4); + + if (!out) + return ERROR_OUTOFMEMORY; + + switch (IoControlCode) + { + case IOCTL_INTERNAL_USB_SUBMIT_URB: /** 0x00220003 */ + WLog_Print(urbdrc->log, WLOG_DEBUG, "ioctl: IOCTL_INTERNAL_USB_SUBMIT_URB"); + WLog_Print(urbdrc->log, WLOG_ERROR, + " Function IOCTL_INTERNAL_USB_SUBMIT_URB: Unchecked"); + break; + + case IOCTL_INTERNAL_USB_RESET_PORT: /** 0x00220007 */ + WLog_Print(urbdrc->log, WLOG_DEBUG, "ioctl: IOCTL_INTERNAL_USB_RESET_PORT"); + break; + + case IOCTL_INTERNAL_USB_GET_PORT_STATUS: /** 0x00220013 */ + WLog_Print(urbdrc->log, WLOG_DEBUG, "ioctl: IOCTL_INTERNAL_USB_GET_PORT_STATUS"); + success = pdev->query_device_port_status(pdev, &usbd_status, &OutputBufferSize, + Stream_Pointer(out)); + + if (success) + { + if (!Stream_SafeSeek(out, OutputBufferSize)) + { + Stream_Free(out, TRUE); + return ERROR_INVALID_DATA; + } + + if (pdev->isExist(pdev) == 0) + Stream_Write_UINT32(out, 0); + else + usb_process_get_port_status(pdev, out); + } + + break; + + case IOCTL_INTERNAL_USB_CYCLE_PORT: /** 0x0022001F */ + WLog_Print(urbdrc->log, WLOG_DEBUG, "ioctl: IOCTL_INTERNAL_USB_CYCLE_PORT"); + WLog_Print(urbdrc->log, WLOG_ERROR, + " Function IOCTL_INTERNAL_USB_CYCLE_PORT: Unchecked"); + break; + + case IOCTL_INTERNAL_USB_SUBMIT_IDLE_NOTIFICATION: /** 0x00220027 */ + WLog_Print(urbdrc->log, WLOG_DEBUG, + "ioctl: IOCTL_INTERNAL_USB_SUBMIT_IDLE_NOTIFICATION"); + WLog_Print(urbdrc->log, WLOG_ERROR, + " Function IOCTL_INTERNAL_USB_SUBMIT_IDLE_NOTIFICATION: Unchecked"); + break; + + default: + WLog_Print(urbdrc->log, WLOG_DEBUG, + "urbdrc_process_io_control: unknown IoControlCode 0x%" PRIX32 "", + IoControlCode); + Stream_Free(out, TRUE); + return ERROR_INVALID_OPERATION; + } + + return stream_write_and_free(callback->plugin, callback->channel, out); +} + +static UINT urbdrc_process_internal_io_control(IUDEVICE* pdev, GENERIC_CHANNEL_CALLBACK* callback, + wStream* s, UINT32 MessageId, IUDEVMAN* udevman) +{ + wStream* out = NULL; + UINT32 IoControlCode = 0; + UINT32 InterfaceId = 0; + UINT32 InputBufferSize = 0; + UINT32 OutputBufferSize = 0; + UINT32 RequestId = 0; + UINT32 frames = 0; + + if (!pdev || !callback || !s || !udevman) + return ERROR_INVALID_PARAMETER; + + if (!Stream_CheckAndLogRequiredLength(TAG, s, 8)) + return ERROR_INVALID_DATA; + + Stream_Read_UINT32(s, IoControlCode); + Stream_Read_UINT32(s, InputBufferSize); + + if (!Stream_SafeSeek(s, InputBufferSize)) + return ERROR_INVALID_DATA; + if (!Stream_CheckAndLogRequiredLength(TAG, s, 8ULL)) + return ERROR_INVALID_DATA; + Stream_Read_UINT32(s, OutputBufferSize); + Stream_Read_UINT32(s, RequestId); + InterfaceId = ((STREAM_ID_PROXY << 30) | pdev->get_ReqCompletion(pdev)); + // TODO: Implement control code. + /** Fixme: Currently this is a FALSE bustime... */ + frames = GetTickCount(); + out = urb_create_iocompletion(InterfaceId, MessageId, RequestId, 4); + + if (!out) + return ERROR_OUTOFMEMORY; + + Stream_Write_UINT32(out, frames); /** OutputBuffer */ + return stream_write_and_free(callback->plugin, callback->channel, out); +} + +static UINT urbdrc_process_query_device_text(IUDEVICE* pdev, GENERIC_CHANNEL_CALLBACK* callback, + wStream* s, UINT32 MessageId, IUDEVMAN* udevman) +{ + UINT32 out_size = 0; + UINT32 TextType = 0; + UINT32 LocaleId = 0; + UINT32 InterfaceId = 0; + UINT8 bufferSize = 0xFF; + UINT32 hr = 0; + wStream* out = NULL; + BYTE DeviceDescription[0x100] = { 0 }; + + if (!pdev || !callback || !s || !udevman) + return ERROR_INVALID_PARAMETER; + if (!Stream_CheckAndLogRequiredLength(TAG, s, 8)) + return ERROR_INVALID_DATA; + + Stream_Read_UINT32(s, TextType); + Stream_Read_UINT32(s, LocaleId); + if (LocaleId > UINT16_MAX) + return ERROR_INVALID_DATA; + + hr = pdev->control_query_device_text(pdev, TextType, (UINT16)LocaleId, &bufferSize, + DeviceDescription); + InterfaceId = ((STREAM_ID_STUB << 30) | pdev->get_UsbDevice(pdev)); + out_size = 16 + bufferSize; + + if (bufferSize != 0) + out_size += 2; + + out = Stream_New(NULL, out_size); + + if (!out) + return ERROR_OUTOFMEMORY; + + Stream_Write_UINT32(out, InterfaceId); /** interface */ + Stream_Write_UINT32(out, MessageId); /** message id */ + Stream_Write_UINT32(out, bufferSize / 2); /** cchDeviceDescription in WCHAR */ + Stream_Write(out, DeviceDescription, bufferSize); /* '\0' terminated unicode */ + Stream_Write_UINT32(out, hr); /** HResult */ + return stream_write_and_free(callback->plugin, callback->channel, out); +} + +static void func_select_all_interface_for_msconfig(IUDEVICE* pdev, + MSUSB_CONFIG_DESCRIPTOR* MsConfig) +{ + MSUSB_INTERFACE_DESCRIPTOR** MsInterfaces = MsConfig->MsInterfaces; + BYTE InterfaceNumber = 0; + BYTE AlternateSetting = 0; + UINT32 NumInterfaces = MsConfig->NumInterfaces; + + for (UINT32 inum = 0; inum < NumInterfaces; inum++) + { + InterfaceNumber = MsInterfaces[inum]->InterfaceNumber; + AlternateSetting = MsInterfaces[inum]->AlternateSetting; + pdev->select_interface(pdev, InterfaceNumber, AlternateSetting); + } +} + +static UINT urb_select_configuration(IUDEVICE* pdev, GENERIC_CHANNEL_CALLBACK* callback, wStream* s, + UINT32 RequestField, UINT32 MessageId, IUDEVMAN* udevman, + int transferDir) +{ + MSUSB_CONFIG_DESCRIPTOR* MsConfig = NULL; + size_t out_size = 0; + UINT32 InterfaceId = 0; + UINT32 NumInterfaces = 0; + UINT32 usbd_status = 0; + BYTE ConfigurationDescriptorIsValid = 0; + wStream* out = NULL; + int MsOutSize = 0; + URBDRC_PLUGIN* urbdrc = NULL; + const BOOL noAck = (RequestField & 0x80000000U) != 0; + const UINT32 RequestId = RequestField & 0x7FFFFFFF; + + if (!callback || !s || !udevman || !pdev) + return ERROR_INVALID_PARAMETER; + + urbdrc = (URBDRC_PLUGIN*)callback->plugin; + + if (!urbdrc) + return ERROR_INVALID_PARAMETER; + + if (transferDir == 0) + { + WLog_Print(urbdrc->log, WLOG_ERROR, "urb_select_configuration: unsupported transfer out"); + return ERROR_INVALID_PARAMETER; + } + + if (!Stream_CheckAndLogRequiredLength(TAG, s, 8)) + return ERROR_INVALID_DATA; + + InterfaceId = ((STREAM_ID_PROXY << 30) | pdev->get_ReqCompletion(pdev)); + Stream_Read_UINT8(s, ConfigurationDescriptorIsValid); + Stream_Seek(s, 3); /* Padding */ + Stream_Read_UINT32(s, NumInterfaces); + + /** if ConfigurationDescriptorIsValid is zero, then just do nothing.*/ + if (ConfigurationDescriptorIsValid) + { + /* parser data for struct config */ + MsConfig = msusb_msconfig_read(s, NumInterfaces); + + if (!MsConfig) + return ERROR_INVALID_DATA; + + /* select config */ + pdev->select_configuration(pdev, MsConfig->bConfigurationValue); + /* select all interface */ + func_select_all_interface_for_msconfig(pdev, MsConfig); + /* complete configuration setup */ + if (!pdev->complete_msconfig_setup(pdev, MsConfig)) + { + msusb_msconfig_free(MsConfig); + MsConfig = NULL; + } + } + + if (MsConfig) + MsOutSize = MsConfig->MsOutSize; + + if (MsOutSize > 0) + { + if ((size_t)MsOutSize > SIZE_MAX - 36) + return ERROR_INVALID_DATA; + + out_size = 36 + MsOutSize; + } + else + out_size = 44; + + out = Stream_New(NULL, out_size); + + if (!out) + return ERROR_OUTOFMEMORY; + + Stream_Write_UINT32(out, InterfaceId); /** interface */ + Stream_Write_UINT32(out, MessageId); /** message id */ + Stream_Write_UINT32(out, URB_COMPLETION_NO_DATA); /** function id */ + Stream_Write_UINT32(out, RequestId); /** RequestId */ + + if (MsOutSize > 0) + { + /** CbTsUrbResult */ + Stream_Write_UINT32(out, 8 + MsOutSize); + /** TS_URB_RESULT_HEADER Size*/ + Stream_Write_UINT16(out, 8 + MsOutSize); + } + else + { + Stream_Write_UINT32(out, 16); + Stream_Write_UINT16(out, 16); + } + + /** Padding, MUST be ignored upon receipt */ + Stream_Write_UINT16(out, TS_URB_SELECT_CONFIGURATION); + Stream_Write_UINT32(out, usbd_status); /** UsbdStatus */ + + /** TS_URB_SELECT_CONFIGURATION_RESULT */ + if (MsOutSize > 0) + msusb_msconfig_write(MsConfig, out); + else + { + Stream_Write_UINT32(out, 0); /** ConfigurationHandle */ + Stream_Write_UINT32(out, NumInterfaces); /** NumInterfaces */ + } + + Stream_Write_UINT32(out, 0); /** HResult */ + Stream_Write_UINT32(out, 0); /** OutputBufferSize */ + + if (!noAck) + return stream_write_and_free(callback->plugin, callback->channel, out); + else + Stream_Free(out, TRUE); + + return ERROR_SUCCESS; +} + +static UINT urb_select_interface(IUDEVICE* pdev, GENERIC_CHANNEL_CALLBACK* callback, wStream* s, + UINT32 RequestField, UINT32 MessageId, IUDEVMAN* udevman, + int transferDir) +{ + MSUSB_CONFIG_DESCRIPTOR* MsConfig = NULL; + MSUSB_INTERFACE_DESCRIPTOR* MsInterface = NULL; + UINT32 out_size = 0; + UINT32 InterfaceId = 0; + UINT32 ConfigurationHandle = 0; + UINT32 OutputBufferSize = 0; + BYTE InterfaceNumber = 0; + wStream* out = NULL; + UINT32 interface_size = 0; + URBDRC_PLUGIN* urbdrc = NULL; + const BOOL noAck = (RequestField & 0x80000000U) != 0; + const UINT32 RequestId = RequestField & 0x7FFFFFFF; + + if (!callback || !s || !udevman || !pdev) + return ERROR_INVALID_PARAMETER; + + urbdrc = (URBDRC_PLUGIN*)callback->plugin; + + if (!urbdrc) + return ERROR_INVALID_PARAMETER; + + if (transferDir == 0) + { + WLog_Print(urbdrc->log, WLOG_ERROR, "urb_select_interface: not support transfer out"); + return ERROR_INVALID_PARAMETER; + } + + if (!Stream_CheckAndLogRequiredLength(TAG, s, 4)) + return ERROR_INVALID_DATA; + + InterfaceId = ((STREAM_ID_PROXY << 30) | pdev->get_ReqCompletion(pdev)); + Stream_Read_UINT32(s, ConfigurationHandle); + MsInterface = msusb_msinterface_read(s); + + if (!Stream_CheckAndLogRequiredLength(TAG, s, 4) || !MsInterface) + { + msusb_msinterface_free(MsInterface); + return ERROR_INVALID_DATA; + } + + Stream_Read_UINT32(s, OutputBufferSize); + pdev->select_interface(pdev, MsInterface->InterfaceNumber, MsInterface->AlternateSetting); + /* replace device's MsInterface */ + MsConfig = pdev->get_MsConfig(pdev); + InterfaceNumber = MsInterface->InterfaceNumber; + if (!msusb_msinterface_replace(MsConfig, InterfaceNumber, MsInterface)) + { + msusb_msconfig_free(MsConfig); + return ERROR_BAD_CONFIGURATION; + } + /* complete configuration setup */ + if (!pdev->complete_msconfig_setup(pdev, MsConfig)) + { + msusb_msconfig_free(MsConfig); + return ERROR_BAD_CONFIGURATION; + } + MsInterface = MsConfig->MsInterfaces[InterfaceNumber]; + interface_size = 16 + (MsInterface->NumberOfPipes * 20); + out_size = 36 + interface_size; + out = Stream_New(NULL, out_size); + + if (!out) + return ERROR_OUTOFMEMORY; + + Stream_Write_UINT32(out, InterfaceId); /** interface */ + Stream_Write_UINT32(out, MessageId); /** message id */ + Stream_Write_UINT32(out, URB_COMPLETION_NO_DATA); /** function id */ + Stream_Write_UINT32(out, RequestId); /** RequestId */ + Stream_Write_UINT32(out, 8 + interface_size); /** CbTsUrbResult */ + /** TS_URB_RESULT_HEADER */ + Stream_Write_UINT16(out, 8 + interface_size); /** Size */ + /** Padding, MUST be ignored upon receipt */ + Stream_Write_UINT16(out, TS_URB_SELECT_INTERFACE); + Stream_Write_UINT32(out, USBD_STATUS_SUCCESS); /** UsbdStatus */ + /** TS_URB_SELECT_INTERFACE_RESULT */ + msusb_msinterface_write(MsInterface, out); + Stream_Write_UINT32(out, 0); /** HResult */ + Stream_Write_UINT32(out, 0); /** OutputBufferSize */ + + if (!noAck) + return stream_write_and_free(callback->plugin, callback->channel, out); + else + Stream_Free(out, TRUE); + + return ERROR_SUCCESS; +} + +static UINT urb_control_transfer(IUDEVICE* pdev, GENERIC_CHANNEL_CALLBACK* callback, wStream* s, + UINT32 RequestField, UINT32 MessageId, IUDEVMAN* udevman, + int transferDir, int External) +{ + UINT32 out_size = 0; + UINT32 InterfaceId = 0; + UINT32 EndpointAddress = 0; + UINT32 PipeHandle = 0; + UINT32 TransferFlags = 0; + UINT32 OutputBufferSize = 0; + UINT32 usbd_status = 0; + UINT32 Timeout = 0; + BYTE bmRequestType = 0; + BYTE Request = 0; + UINT16 Value = 0; + UINT16 Index = 0; + UINT16 length = 0; + BYTE* buffer = NULL; + wStream* out = NULL; + URBDRC_PLUGIN* urbdrc = NULL; + const BOOL noAck = (RequestField & 0x80000000U) != 0; + const UINT32 RequestId = RequestField & 0x7FFFFFFF; + + if (!callback || !s || !udevman || !pdev) + return ERROR_INVALID_PARAMETER; + + urbdrc = (URBDRC_PLUGIN*)callback->plugin; + + if (!urbdrc) + return ERROR_INVALID_PARAMETER; + + if (!Stream_CheckAndLogRequiredLength(TAG, s, 8)) + return ERROR_INVALID_DATA; + + InterfaceId = ((STREAM_ID_PROXY << 30) | pdev->get_ReqCompletion(pdev)); + Stream_Read_UINT32(s, PipeHandle); + Stream_Read_UINT32(s, TransferFlags); /** TransferFlags */ + EndpointAddress = (PipeHandle & 0x000000ff); + Timeout = 2000; + + switch (External) + { + case URB_CONTROL_TRANSFER_EXTERNAL: + if (!Stream_CheckAndLogRequiredLength(TAG, s, 4)) + return ERROR_INVALID_DATA; + + Stream_Read_UINT32(s, Timeout); /** TransferFlags */ + break; + + case URB_CONTROL_TRANSFER_NONEXTERNAL: + break; + } + + /** SetupPacket 8 bytes */ + if (!Stream_CheckAndLogRequiredLength(TAG, s, 12)) + return ERROR_INVALID_DATA; + + Stream_Read_UINT8(s, bmRequestType); + Stream_Read_UINT8(s, Request); + Stream_Read_UINT16(s, Value); + Stream_Read_UINT16(s, Index); + Stream_Read_UINT16(s, length); + Stream_Read_UINT32(s, OutputBufferSize); + + if (length != OutputBufferSize) + { + WLog_Print(urbdrc->log, WLOG_ERROR, "urb_control_transfer ERROR: buf != length"); + return ERROR_INVALID_DATA; + } + + out_size = 36 + OutputBufferSize; + out = Stream_New(NULL, out_size); + + if (!out) + return ERROR_OUTOFMEMORY; + + Stream_Seek(out, 36); + /** Get Buffer Data */ + buffer = Stream_Pointer(out); + + if (transferDir == USBD_TRANSFER_DIRECTION_OUT) + { + if (!Stream_CheckAndLogRequiredLength(TAG, s, OutputBufferSize)) + return ERROR_INVALID_DATA; + Stream_Copy(s, out, OutputBufferSize); + } + + /** process TS_URB_CONTROL_TRANSFER */ + if (!pdev->control_transfer(pdev, RequestId, EndpointAddress, TransferFlags, bmRequestType, + Request, Value, Index, &usbd_status, &OutputBufferSize, buffer, + Timeout)) + { + WLog_Print(urbdrc->log, WLOG_ERROR, "control_transfer failed"); + Stream_Free(out, TRUE); + return ERROR_INTERNAL_ERROR; + } + + return urb_write_completion(pdev, callback, noAck, out, InterfaceId, MessageId, RequestId, + usbd_status, OutputBufferSize); +} + +static void urb_bulk_transfer_cb(IUDEVICE* pdev, GENERIC_CHANNEL_CALLBACK* callback, wStream* out, + UINT32 InterfaceId, BOOL noAck, UINT32 MessageId, UINT32 RequestId, + UINT32 NumberOfPackets, UINT32 status, UINT32 StartFrame, + UINT32 ErrorCount, UINT32 OutputBufferSize) +{ + if (!pdev->isChannelClosed(pdev)) + urb_write_completion(pdev, callback, noAck, out, InterfaceId, MessageId, RequestId, status, + OutputBufferSize); + else + Stream_Free(out, TRUE); +} + +static UINT urb_bulk_or_interrupt_transfer(IUDEVICE* pdev, GENERIC_CHANNEL_CALLBACK* callback, + wStream* s, UINT32 RequestField, UINT32 MessageId, + IUDEVMAN* udevman, int transferDir) +{ + UINT32 EndpointAddress = 0; + UINT32 PipeHandle = 0; + UINT32 TransferFlags = 0; + UINT32 OutputBufferSize = 0; + const BOOL noAck = (RequestField & 0x80000000U) != 0; + const UINT32 RequestId = RequestField & 0x7FFFFFFF; + + if (!pdev || !callback || !s || !udevman) + return ERROR_INVALID_PARAMETER; + + if (!Stream_CheckAndLogRequiredLength(TAG, s, 12)) + return ERROR_INVALID_DATA; + + Stream_Read_UINT32(s, PipeHandle); + Stream_Read_UINT32(s, TransferFlags); /** TransferFlags */ + Stream_Read_UINT32(s, OutputBufferSize); + EndpointAddress = (PipeHandle & 0x000000ff); + + if (transferDir == USBD_TRANSFER_DIRECTION_OUT) + { + if (!Stream_CheckAndLogRequiredLength(TAG, s, OutputBufferSize)) + { + return ERROR_INVALID_DATA; + } + } + + /** process TS_URB_BULK_OR_INTERRUPT_TRANSFER */ + return pdev->bulk_or_interrupt_transfer( + pdev, callback, MessageId, RequestId, EndpointAddress, TransferFlags, noAck, + OutputBufferSize, (transferDir == USBD_TRANSFER_DIRECTION_OUT) ? Stream_Pointer(s) : NULL, + urb_bulk_transfer_cb, 10000); +} + +static void urb_isoch_transfer_cb(IUDEVICE* pdev, GENERIC_CHANNEL_CALLBACK* callback, wStream* out, + UINT32 InterfaceId, BOOL noAck, UINT32 MessageId, + UINT32 RequestId, UINT32 NumberOfPackets, UINT32 status, + UINT32 StartFrame, UINT32 ErrorCount, UINT32 OutputBufferSize) +{ + if (!noAck) + { + UINT32 packetSize = (status == 0) ? NumberOfPackets * 12 : 0; + Stream_SetPosition(out, 0); + /* fill the send data */ + Stream_Write_UINT32(out, InterfaceId); /** interface */ + Stream_Write_UINT32(out, MessageId); /** message id */ + + if (OutputBufferSize == 0) + Stream_Write_UINT32(out, URB_COMPLETION_NO_DATA); /** function id */ + else + Stream_Write_UINT32(out, URB_COMPLETION); /** function id */ + + Stream_Write_UINT32(out, RequestId); /** RequestId */ + Stream_Write_UINT32(out, 20 + packetSize); /** CbTsUrbResult */ + /** TsUrbResult TS_URB_RESULT_HEADER */ + Stream_Write_UINT16(out, 20 + packetSize); /** Size */ + Stream_Write_UINT16(out, 0); /* Padding */ + Stream_Write_UINT32(out, status); /** UsbdStatus */ + Stream_Write_UINT32(out, StartFrame); /** StartFrame */ + + if (status == 0) + { + /** NumberOfPackets */ + Stream_Write_UINT32(out, NumberOfPackets); + Stream_Write_UINT32(out, ErrorCount); /** ErrorCount */ + Stream_Seek(out, packetSize); + } + else + { + Stream_Write_UINT32(out, 0); /** NumberOfPackets */ + Stream_Write_UINT32(out, ErrorCount); /** ErrorCount */ + } + + Stream_Write_UINT32(out, 0); /** HResult */ + Stream_Write_UINT32(out, OutputBufferSize); /** OutputBufferSize */ + Stream_Seek(out, OutputBufferSize); + + stream_write_and_free(callback->plugin, callback->channel, out); + } +} + +static UINT urb_isoch_transfer(IUDEVICE* pdev, GENERIC_CHANNEL_CALLBACK* callback, wStream* s, + UINT32 RequestField, UINT32 MessageId, IUDEVMAN* udevman, + int transferDir) +{ + int rc = 0; + UINT32 EndpointAddress = 0; + UINT32 PipeHandle = 0; + UINT32 TransferFlags = 0; + UINT32 StartFrame = 0; + UINT32 NumberOfPackets = 0; + UINT32 ErrorCount = 0; + UINT32 OutputBufferSize = 0; + BYTE* packetDescriptorData = NULL; + const BOOL noAck = (RequestField & 0x80000000U) != 0; + const UINT32 RequestId = RequestField & 0x7FFFFFFF; + + if (!pdev || !callback || !udevman) + return ERROR_INVALID_PARAMETER; + + if (!Stream_CheckAndLogRequiredLength(TAG, s, 20)) + return ERROR_INVALID_DATA; + + Stream_Read_UINT32(s, PipeHandle); + EndpointAddress = (PipeHandle & 0x000000ff); + Stream_Read_UINT32(s, TransferFlags); /** TransferFlags */ + Stream_Read_UINT32(s, StartFrame); /** StartFrame */ + Stream_Read_UINT32(s, NumberOfPackets); /** NumberOfPackets */ + Stream_Read_UINT32(s, ErrorCount); /** ErrorCount */ + + if (!Stream_CheckAndLogRequiredLengthOfSize(TAG, s, NumberOfPackets, 12ull)) + return ERROR_INVALID_DATA; + + packetDescriptorData = Stream_Pointer(s); + Stream_Seek(s, NumberOfPackets * 12); + + if (!Stream_CheckAndLogRequiredLength(TAG, s, sizeof(UINT32))) + return ERROR_INVALID_DATA; + Stream_Read_UINT32(s, OutputBufferSize); + + if (transferDir == USBD_TRANSFER_DIRECTION_OUT) + { + if (!Stream_CheckAndLogRequiredLength(TAG, s, OutputBufferSize)) + return ERROR_INVALID_DATA; + } + + rc = pdev->isoch_transfer( + pdev, callback, MessageId, RequestId, EndpointAddress, TransferFlags, StartFrame, + ErrorCount, noAck, packetDescriptorData, NumberOfPackets, OutputBufferSize, + (transferDir == USBD_TRANSFER_DIRECTION_OUT) ? Stream_Pointer(s) : NULL, + urb_isoch_transfer_cb, 2000); + + if (rc < 0) + return ERROR_INTERNAL_ERROR; + return (UINT)rc; +} + +static UINT urb_control_descriptor_request(IUDEVICE* pdev, GENERIC_CHANNEL_CALLBACK* callback, + wStream* s, UINT32 RequestField, UINT32 MessageId, + IUDEVMAN* udevman, BYTE func_recipient, int transferDir) +{ + size_t out_size = 0; + UINT32 InterfaceId = 0; + UINT32 OutputBufferSize = 0; + UINT32 usbd_status = 0; + BYTE bmRequestType = 0; + BYTE desc_index = 0; + BYTE desc_type = 0; + UINT16 langId = 0; + wStream* out = NULL; + URBDRC_PLUGIN* urbdrc = NULL; + const BOOL noAck = (RequestField & 0x80000000U) != 0; + const UINT32 RequestId = RequestField & 0x7FFFFFFF; + + if (!callback || !s || !udevman || !pdev) + return ERROR_INVALID_PARAMETER; + + urbdrc = (URBDRC_PLUGIN*)callback->plugin; + + if (!urbdrc) + return ERROR_INVALID_PARAMETER; + + if (!Stream_CheckAndLogRequiredLength(TAG, s, 8)) + return ERROR_INVALID_DATA; + + InterfaceId = ((STREAM_ID_PROXY << 30) | pdev->get_ReqCompletion(pdev)); + Stream_Read_UINT8(s, desc_index); + Stream_Read_UINT8(s, desc_type); + Stream_Read_UINT16(s, langId); + Stream_Read_UINT32(s, OutputBufferSize); + if (OutputBufferSize > UINT32_MAX - 36) + return ERROR_INVALID_DATA; + if (transferDir == USBD_TRANSFER_DIRECTION_OUT) + { + if (!Stream_CheckAndLogRequiredLength(TAG, s, OutputBufferSize)) + return ERROR_INVALID_DATA; + } + + out_size = 36ULL + OutputBufferSize; + out = Stream_New(NULL, out_size); + + if (!out) + return ERROR_OUTOFMEMORY; + + Stream_Seek(out, 36); + bmRequestType = func_recipient; + + switch (transferDir) + { + case USBD_TRANSFER_DIRECTION_IN: + bmRequestType |= 0x80; + break; + + case USBD_TRANSFER_DIRECTION_OUT: + bmRequestType |= 0x00; + Stream_Copy(s, out, OutputBufferSize); + Stream_Rewind(out, OutputBufferSize); + break; + + default: + WLog_Print(urbdrc->log, WLOG_DEBUG, "get error transferDir"); + OutputBufferSize = 0; + usbd_status = USBD_STATUS_STALL_PID; + break; + } + + /** process get usb device descriptor */ + if (!pdev->control_transfer(pdev, RequestId, 0, 0, bmRequestType, + 0x06, /* REQUEST_GET_DESCRIPTOR */ + (desc_type << 8) | desc_index, langId, &usbd_status, + &OutputBufferSize, Stream_Pointer(out), 1000)) + { + WLog_Print(urbdrc->log, WLOG_ERROR, "get_descriptor failed"); + Stream_Free(out, TRUE); + return ERROR_INTERNAL_ERROR; + } + + return urb_write_completion(pdev, callback, noAck, out, InterfaceId, MessageId, RequestId, + usbd_status, OutputBufferSize); +} + +static UINT urb_control_get_status_request(IUDEVICE* pdev, GENERIC_CHANNEL_CALLBACK* callback, + wStream* s, UINT32 RequestField, UINT32 MessageId, + IUDEVMAN* udevman, BYTE func_recipient, int transferDir) +{ + size_t out_size = 0; + UINT32 InterfaceId = 0; + UINT32 OutputBufferSize = 0; + UINT32 usbd_status = 0; + UINT16 Index = 0; + BYTE bmRequestType = 0; + wStream* out = NULL; + URBDRC_PLUGIN* urbdrc = NULL; + const BOOL noAck = (RequestField & 0x80000000U) != 0; + const UINT32 RequestId = RequestField & 0x7FFFFFFF; + + if (!callback || !s || !udevman || !pdev) + return ERROR_INVALID_PARAMETER; + + urbdrc = (URBDRC_PLUGIN*)callback->plugin; + + if (!urbdrc) + return ERROR_INVALID_PARAMETER; + + if (transferDir == 0) + { + WLog_Print(urbdrc->log, WLOG_DEBUG, + "urb_control_get_status_request: transfer out not supported"); + return ERROR_INVALID_PARAMETER; + } + + if (!Stream_CheckAndLogRequiredLength(TAG, s, 8)) + return ERROR_INVALID_DATA; + + InterfaceId = ((STREAM_ID_PROXY << 30) | pdev->get_ReqCompletion(pdev)); + Stream_Read_UINT16(s, Index); /** Index */ + Stream_Seek(s, 2); + Stream_Read_UINT32(s, OutputBufferSize); + if (OutputBufferSize > UINT32_MAX - 36) + return ERROR_INVALID_DATA; + out_size = 36ULL + OutputBufferSize; + out = Stream_New(NULL, out_size); + + if (!out) + return ERROR_OUTOFMEMORY; + + Stream_Seek(out, 36); + bmRequestType = func_recipient | 0x80; + + if (!pdev->control_transfer(pdev, RequestId, 0, 0, bmRequestType, 0x00, /* REQUEST_GET_STATUS */ + 0, Index, &usbd_status, &OutputBufferSize, Stream_Pointer(out), + 1000)) + { + WLog_Print(urbdrc->log, WLOG_ERROR, "control_transfer failed"); + Stream_Free(out, TRUE); + return ERROR_INTERNAL_ERROR; + } + + return urb_write_completion(pdev, callback, noAck, out, InterfaceId, MessageId, RequestId, + usbd_status, OutputBufferSize); +} + +static UINT urb_control_vendor_or_class_request(IUDEVICE* pdev, GENERIC_CHANNEL_CALLBACK* callback, + wStream* s, UINT32 RequestField, UINT32 MessageId, + IUDEVMAN* udevman, BYTE func_type, + BYTE func_recipient, int transferDir) +{ + UINT32 out_size = 0; + UINT32 InterfaceId = 0; + UINT32 TransferFlags = 0; + UINT32 usbd_status = 0; + UINT32 OutputBufferSize = 0; + BYTE ReqTypeReservedBits = 0; + BYTE Request = 0; + BYTE bmRequestType = 0; + UINT16 Value = 0; + UINT16 Index = 0; + UINT16 Padding = 0; + wStream* out = NULL; + URBDRC_PLUGIN* urbdrc = NULL; + const BOOL noAck = (RequestField & 0x80000000U) != 0; + const UINT32 RequestId = RequestField & 0x7FFFFFFF; + + if (!callback || !s || !udevman || !pdev) + return ERROR_INVALID_PARAMETER; + + urbdrc = (URBDRC_PLUGIN*)callback->plugin; + + if (!urbdrc) + return ERROR_INVALID_PARAMETER; + + if (!Stream_CheckAndLogRequiredLength(TAG, s, 16)) + return ERROR_INVALID_DATA; + + InterfaceId = ((STREAM_ID_PROXY << 30) | pdev->get_ReqCompletion(pdev)); + Stream_Read_UINT32(s, TransferFlags); /** TransferFlags */ + Stream_Read_UINT8(s, ReqTypeReservedBits); /** ReqTypeReservedBids */ + Stream_Read_UINT8(s, Request); /** Request */ + Stream_Read_UINT16(s, Value); /** value */ + Stream_Read_UINT16(s, Index); /** index */ + Stream_Read_UINT16(s, Padding); /** Padding */ + Stream_Read_UINT32(s, OutputBufferSize); + if (OutputBufferSize > UINT32_MAX - 36) + return ERROR_INVALID_DATA; + + if (transferDir == USBD_TRANSFER_DIRECTION_OUT) + { + if (!Stream_CheckAndLogRequiredLength(TAG, s, OutputBufferSize)) + return ERROR_INVALID_DATA; + } + + out_size = 36ULL + OutputBufferSize; + out = Stream_New(NULL, out_size); + + if (!out) + return ERROR_OUTOFMEMORY; + + Stream_Seek(out, 36); + + /** Get Buffer */ + if (transferDir == USBD_TRANSFER_DIRECTION_OUT) + { + Stream_Copy(s, out, OutputBufferSize); + Stream_Rewind(out, OutputBufferSize); + } + + /** vendor or class command */ + bmRequestType = func_type | func_recipient; + + if (TransferFlags & USBD_TRANSFER_DIRECTION) + bmRequestType |= 0x80; + + WLog_Print(urbdrc->log, WLOG_DEBUG, + "RequestId 0x%" PRIx32 " TransferFlags: 0x%" PRIx32 " ReqTypeReservedBits: 0x%" PRIx8 + " " + "Request:0x%" PRIx8 " Value: 0x%" PRIx16 " Index: 0x%" PRIx16 + " OutputBufferSize: 0x%" PRIx32 " bmRequestType: 0x%" PRIx8, + RequestId, TransferFlags, ReqTypeReservedBits, Request, Value, Index, + OutputBufferSize, bmRequestType); + + if (!pdev->control_transfer(pdev, RequestId, 0, 0, bmRequestType, Request, Value, Index, + &usbd_status, &OutputBufferSize, Stream_Pointer(out), 2000)) + { + WLog_Print(urbdrc->log, WLOG_ERROR, "control_transfer failed"); + Stream_Free(out, TRUE); + return ERROR_INTERNAL_ERROR; + } + + return urb_write_completion(pdev, callback, noAck, out, InterfaceId, MessageId, RequestId, + usbd_status, OutputBufferSize); +} + +static UINT urb_os_feature_descriptor_request(IUDEVICE* pdev, GENERIC_CHANNEL_CALLBACK* callback, + wStream* s, UINT32 RequestField, UINT32 MessageId, + IUDEVMAN* udevman, int transferDir) +{ + size_t out_size = 0; + UINT32 InterfaceId = 0; + UINT32 OutputBufferSize = 0; + UINT32 usbd_status = 0; + BYTE Recipient = 0; + BYTE InterfaceNumber = 0; + BYTE Ms_PageIndex = 0; + UINT16 Ms_featureDescIndex = 0; + wStream* out = NULL; + int ret = 0; + URBDRC_PLUGIN* urbdrc = NULL; + const BOOL noAck = (RequestField & 0x80000000U) != 0; + const UINT32 RequestId = RequestField & 0x7FFFFFFF; + + if (!callback || !s || !udevman || !pdev) + return ERROR_INVALID_PARAMETER; + + urbdrc = (URBDRC_PLUGIN*)callback->plugin; + + if (!urbdrc) + return ERROR_INVALID_PARAMETER; + + if (!Stream_CheckAndLogRequiredLength(TAG, s, 12)) + return ERROR_INVALID_DATA; + + /* 2.2.9.15 TS_URB_OS_FEATURE_DESCRIPTOR_REQUEST */ + Stream_Read_UINT8(s, Recipient); /** Recipient */ + Recipient = (Recipient & 0x1f); /* Mask out Padding1 */ + Stream_Read_UINT8(s, InterfaceNumber); /** InterfaceNumber */ + Stream_Read_UINT8(s, Ms_PageIndex); /** Ms_PageIndex */ + Stream_Read_UINT16(s, Ms_featureDescIndex); /** Ms_featureDescIndex */ + Stream_Seek(s, 3); /* Padding 2 */ + Stream_Read_UINT32(s, OutputBufferSize); + if (OutputBufferSize > UINT32_MAX - 36) + return ERROR_INVALID_DATA; + + switch (transferDir) + { + case USBD_TRANSFER_DIRECTION_OUT: + if (!Stream_CheckAndLogRequiredLength(TAG, s, OutputBufferSize)) + return ERROR_INVALID_DATA; + + break; + + default: + break; + } + + InterfaceId = ((STREAM_ID_PROXY << 30) | pdev->get_ReqCompletion(pdev)); + out_size = 36ULL + OutputBufferSize; + out = Stream_New(NULL, out_size); + + if (!out) + return ERROR_OUTOFMEMORY; + + Stream_Seek(out, 36); + + switch (transferDir) + { + case USBD_TRANSFER_DIRECTION_OUT: + Stream_Copy(s, out, OutputBufferSize); + Stream_Rewind(out, OutputBufferSize); + break; + + case USBD_TRANSFER_DIRECTION_IN: + break; + } + + WLog_Print(urbdrc->log, WLOG_DEBUG, + "Ms descriptor arg: Recipient:0x%" PRIx8 ", " + "InterfaceNumber:0x%" PRIx8 ", Ms_PageIndex:0x%" PRIx8 ", " + "Ms_featureDescIndex:0x%" PRIx16 ", OutputBufferSize:0x%" PRIx32 "", + Recipient, InterfaceNumber, Ms_PageIndex, Ms_featureDescIndex, OutputBufferSize); + /** get ms string */ + ret = pdev->os_feature_descriptor_request(pdev, RequestId, Recipient, InterfaceNumber, + Ms_PageIndex, Ms_featureDescIndex, &usbd_status, + &OutputBufferSize, Stream_Pointer(out), 1000); + + if (ret < 0) + WLog_Print(urbdrc->log, WLOG_DEBUG, "os_feature_descriptor_request: error num %d", ret); + + return urb_write_completion(pdev, callback, noAck, out, InterfaceId, MessageId, RequestId, + usbd_status, OutputBufferSize); +} + +static UINT urb_pipe_request(IUDEVICE* pdev, GENERIC_CHANNEL_CALLBACK* callback, wStream* s, + UINT32 RequestField, UINT32 MessageId, IUDEVMAN* udevman, + int transferDir, int action) +{ + UINT32 out_size = 0; + UINT32 InterfaceId = 0; + UINT32 PipeHandle = 0; + UINT32 EndpointAddress = 0; + UINT32 OutputBufferSize = 0; + UINT32 usbd_status = 0; + wStream* out = NULL; + UINT32 ret = USBD_STATUS_REQUEST_FAILED; + int rc = 0; + URBDRC_PLUGIN* urbdrc = NULL; + const BOOL noAck = (RequestField & 0x80000000U) != 0; + const UINT32 RequestId = RequestField & 0x7FFFFFFF; + + if (!callback || !s || !udevman || !pdev) + return ERROR_INVALID_PARAMETER; + + urbdrc = (URBDRC_PLUGIN*)callback->plugin; + + if (!urbdrc) + return ERROR_INVALID_PARAMETER; + + if (!Stream_CheckAndLogRequiredLength(TAG, s, 8)) + return ERROR_INVALID_DATA; + + if (transferDir == 0) + { + WLog_Print(urbdrc->log, WLOG_DEBUG, "urb_pipe_request: not support transfer out"); + return ERROR_INVALID_PARAMETER; + } + + InterfaceId = ((STREAM_ID_PROXY << 30) | pdev->get_ReqCompletion(pdev)); + Stream_Read_UINT32(s, PipeHandle); /** PipeHandle */ + Stream_Read_UINT32(s, OutputBufferSize); + EndpointAddress = (PipeHandle & 0x000000ff); + + switch (action) + { + case PIPE_CANCEL: + rc = pdev->control_pipe_request(pdev, RequestId, EndpointAddress, &usbd_status, + PIPE_CANCEL); + + if (rc < 0) + WLog_Print(urbdrc->log, WLOG_DEBUG, "PIPE SET HALT: error %d", ret); + else + ret = USBD_STATUS_SUCCESS; + + break; + + case PIPE_RESET: + WLog_Print(urbdrc->log, WLOG_DEBUG, "urb_pipe_request: PIPE_RESET ep 0x%" PRIx32 "", + EndpointAddress); + rc = pdev->control_pipe_request(pdev, RequestId, EndpointAddress, &usbd_status, + PIPE_RESET); + + if (rc < 0) + WLog_Print(urbdrc->log, WLOG_DEBUG, "PIPE RESET: error %d", ret); + else + ret = USBD_STATUS_SUCCESS; + + break; + + default: + WLog_Print(urbdrc->log, WLOG_DEBUG, "urb_pipe_request action: %d not supported", + action); + ret = USBD_STATUS_INVALID_URB_FUNCTION; + break; + } + + /** send data */ + out_size = 36; + out = Stream_New(NULL, out_size); + + if (!out) + return ERROR_OUTOFMEMORY; + + return urb_write_completion(pdev, callback, noAck, out, InterfaceId, MessageId, RequestId, ret, + 0); +} + +static UINT urb_get_current_frame_number(IUDEVICE* pdev, GENERIC_CHANNEL_CALLBACK* callback, + wStream* s, UINT32 RequestField, UINT32 MessageId, + IUDEVMAN* udevman, int transferDir) +{ + UINT32 out_size = 0; + UINT32 InterfaceId = 0; + UINT32 OutputBufferSize = 0; + UINT32 dummy_frames = 0; + wStream* out = NULL; + URBDRC_PLUGIN* urbdrc = NULL; + const BOOL noAck = (RequestField & 0x80000000U) != 0; + const UINT32 RequestId = RequestField & 0x7FFFFFFF; + + if (!callback || !s || !udevman || !pdev) + return ERROR_INVALID_PARAMETER; + + urbdrc = (URBDRC_PLUGIN*)callback->plugin; + + if (!urbdrc) + return ERROR_INVALID_PARAMETER; + + if (!Stream_CheckAndLogRequiredLength(TAG, s, 4)) + return ERROR_INVALID_DATA; + + if (transferDir == 0) + { + WLog_Print(urbdrc->log, WLOG_DEBUG, + "urb_get_current_frame_number: not support transfer out"); + return ERROR_INVALID_PARAMETER; + } + + InterfaceId = ((STREAM_ID_PROXY << 30) | pdev->get_ReqCompletion(pdev)); + Stream_Read_UINT32(s, OutputBufferSize); + /** Fixme: Need to fill actual frame number!!*/ + dummy_frames = GetTickCount(); + out_size = 40; + out = Stream_New(NULL, out_size); + + if (!out) + return ERROR_OUTOFMEMORY; + + Stream_Write_UINT32(out, InterfaceId); /** interface */ + Stream_Write_UINT32(out, MessageId); /** message id */ + Stream_Write_UINT32(out, URB_COMPLETION_NO_DATA); + Stream_Write_UINT32(out, RequestId); /** RequestId */ + Stream_Write_UINT32(out, 12); /** CbTsUrbResult */ + /** TsUrbResult TS_URB_RESULT_HEADER */ + Stream_Write_UINT16(out, 12); /** Size */ + /** Padding, MUST be ignored upon receipt */ + Stream_Write_UINT16(out, TS_URB_GET_CURRENT_FRAME_NUMBER); + Stream_Write_UINT32(out, USBD_STATUS_SUCCESS); /** UsbdStatus */ + Stream_Write_UINT32(out, dummy_frames); /** FrameNumber */ + Stream_Write_UINT32(out, 0); /** HResult */ + Stream_Write_UINT32(out, 0); /** OutputBufferSize */ + + if (!noAck) + return stream_write_and_free(callback->plugin, callback->channel, out); + else + Stream_Free(out, TRUE); + + return ERROR_SUCCESS; +} + +/* Unused function for current server */ +static UINT urb_control_get_configuration_request(IUDEVICE* pdev, + GENERIC_CHANNEL_CALLBACK* callback, wStream* s, + UINT32 RequestField, UINT32 MessageId, + IUDEVMAN* udevman, int transferDir) +{ + size_t out_size = 0; + UINT32 InterfaceId = 0; + UINT32 OutputBufferSize = 0; + UINT32 usbd_status = 0; + wStream* out = NULL; + URBDRC_PLUGIN* urbdrc = NULL; + const BOOL noAck = (RequestField & 0x80000000U) != 0; + const UINT32 RequestId = RequestField & 0x7FFFFFFF; + + if (!callback || !s || !udevman || !pdev) + return ERROR_INVALID_PARAMETER; + + urbdrc = (URBDRC_PLUGIN*)callback->plugin; + + if (!urbdrc) + return ERROR_INVALID_PARAMETER; + + if (transferDir == 0) + { + WLog_Print(urbdrc->log, WLOG_DEBUG, + "urb_control_get_configuration_request:" + " not support transfer out"); + return ERROR_INVALID_PARAMETER; + } + + if (!Stream_CheckAndLogRequiredLength(TAG, s, 4)) + return ERROR_INVALID_DATA; + + Stream_Read_UINT32(s, OutputBufferSize); + if (OutputBufferSize > UINT32_MAX - 36) + return ERROR_INVALID_DATA; + out_size = 36ULL + OutputBufferSize; + out = Stream_New(NULL, out_size); + + if (!out) + return ERROR_OUTOFMEMORY; + + Stream_Seek(out, 36); + InterfaceId = ((STREAM_ID_PROXY << 30) | pdev->get_ReqCompletion(pdev)); + + if (!pdev->control_transfer(pdev, RequestId, 0, 0, 0x80 | 0x00, + 0x08, /* REQUEST_GET_CONFIGURATION */ + 0, 0, &usbd_status, &OutputBufferSize, Stream_Pointer(out), 1000)) + { + WLog_Print(urbdrc->log, WLOG_DEBUG, "control_transfer failed"); + Stream_Free(out, TRUE); + return ERROR_INTERNAL_ERROR; + } + + return urb_write_completion(pdev, callback, noAck, out, InterfaceId, MessageId, RequestId, + usbd_status, OutputBufferSize); +} + +/* Unused function for current server */ +static UINT urb_control_get_interface_request(IUDEVICE* pdev, GENERIC_CHANNEL_CALLBACK* callback, + wStream* s, UINT32 RequestField, UINT32 MessageId, + IUDEVMAN* udevman, int transferDir) +{ + size_t out_size = 0; + UINT32 InterfaceId = 0; + UINT32 OutputBufferSize = 0; + UINT32 usbd_status = 0; + UINT16 InterfaceNr = 0; + wStream* out = NULL; + URBDRC_PLUGIN* urbdrc = NULL; + const BOOL noAck = (RequestField & 0x80000000U) != 0; + const UINT32 RequestId = RequestField & 0x7FFFFFFF; + + if (!callback || !s || !udevman || !pdev) + return ERROR_INVALID_PARAMETER; + + urbdrc = (URBDRC_PLUGIN*)callback->plugin; + + if (!urbdrc) + return ERROR_INVALID_PARAMETER; + + if (!Stream_CheckAndLogRequiredLength(TAG, s, 8)) + return ERROR_INVALID_DATA; + + if (transferDir == 0) + { + WLog_Print(urbdrc->log, WLOG_DEBUG, + "urb_control_get_interface_request: not support transfer out"); + return ERROR_INVALID_PARAMETER; + } + + InterfaceId = ((STREAM_ID_PROXY << 30) | pdev->get_ReqCompletion(pdev)); + Stream_Read_UINT16(s, InterfaceNr); + Stream_Seek(s, 2); + Stream_Read_UINT32(s, OutputBufferSize); + if (OutputBufferSize > UINT32_MAX - 36) + return ERROR_INVALID_DATA; + out_size = 36ULL + OutputBufferSize; + out = Stream_New(NULL, out_size); + + if (!out) + return ERROR_OUTOFMEMORY; + + Stream_Seek(out, 36); + + if (!pdev->control_transfer( + pdev, RequestId, 0, 0, 0x80 | 0x01, 0x0A, /* REQUEST_GET_INTERFACE */ + 0, InterfaceNr, &usbd_status, &OutputBufferSize, Stream_Pointer(out), 1000)) + { + WLog_Print(urbdrc->log, WLOG_DEBUG, "control_transfer failed"); + Stream_Free(out, TRUE); + return ERROR_INTERNAL_ERROR; + } + + return urb_write_completion(pdev, callback, noAck, out, InterfaceId, MessageId, RequestId, + usbd_status, OutputBufferSize); +} + +static UINT urb_control_feature_request(IUDEVICE* pdev, GENERIC_CHANNEL_CALLBACK* callback, + wStream* s, UINT32 RequestField, UINT32 MessageId, + IUDEVMAN* udevman, BYTE func_recipient, BYTE command, + int transferDir) +{ + UINT32 InterfaceId = 0; + UINT32 OutputBufferSize = 0; + UINT32 usbd_status = 0; + UINT16 FeatureSelector = 0; + UINT16 Index = 0; + BYTE bmRequestType = 0; + BYTE bmRequest = 0; + wStream* out = NULL; + URBDRC_PLUGIN* urbdrc = NULL; + const BOOL noAck = (RequestField & 0x80000000U) != 0; + const UINT32 RequestId = RequestField & 0x7FFFFFFF; + + if (!callback || !s || !udevman || !pdev) + return ERROR_INVALID_PARAMETER; + + urbdrc = (URBDRC_PLUGIN*)callback->plugin; + + if (!urbdrc) + return ERROR_INVALID_PARAMETER; + + if (!Stream_CheckAndLogRequiredLength(TAG, s, 8)) + return ERROR_INVALID_DATA; + + InterfaceId = ((STREAM_ID_PROXY << 30) | pdev->get_ReqCompletion(pdev)); + Stream_Read_UINT16(s, FeatureSelector); + Stream_Read_UINT16(s, Index); + Stream_Read_UINT32(s, OutputBufferSize); + if (OutputBufferSize > UINT32_MAX - 36) + return ERROR_INVALID_DATA; + switch (transferDir) + { + case USBD_TRANSFER_DIRECTION_OUT: + if (!Stream_CheckAndLogRequiredLength(TAG, s, OutputBufferSize)) + return ERROR_INVALID_DATA; + + break; + + default: + break; + } + + out = Stream_New(NULL, 36ULL + OutputBufferSize); + + if (!out) + return ERROR_OUTOFMEMORY; + + Stream_Seek(out, 36); + bmRequestType = func_recipient; + + switch (transferDir) + { + case USBD_TRANSFER_DIRECTION_OUT: + WLog_Print(urbdrc->log, WLOG_ERROR, + "Function urb_control_feature_request: OUT Unchecked"); + Stream_Copy(s, out, OutputBufferSize); + Stream_Rewind(out, OutputBufferSize); + bmRequestType |= 0x00; + break; + + case USBD_TRANSFER_DIRECTION_IN: + bmRequestType |= 0x80; + break; + } + + switch (command) + { + case URB_SET_FEATURE: + bmRequest = 0x03; /* REQUEST_SET_FEATURE */ + break; + + case URB_CLEAR_FEATURE: + bmRequest = 0x01; /* REQUEST_CLEAR_FEATURE */ + break; + + default: + WLog_Print(urbdrc->log, WLOG_ERROR, + "urb_control_feature_request: Error Command 0x%02" PRIx8 "", command); + Stream_Free(out, TRUE); + return ERROR_INTERNAL_ERROR; + } + + if (!pdev->control_transfer(pdev, RequestId, 0, 0, bmRequestType, bmRequest, FeatureSelector, + Index, &usbd_status, &OutputBufferSize, Stream_Pointer(out), 1000)) + { + WLog_Print(urbdrc->log, WLOG_DEBUG, "feature control transfer failed"); + Stream_Free(out, TRUE); + return ERROR_INTERNAL_ERROR; + } + + return urb_write_completion(pdev, callback, noAck, out, InterfaceId, MessageId, RequestId, + usbd_status, OutputBufferSize); +} + +static UINT urbdrc_process_transfer_request(IUDEVICE* pdev, GENERIC_CHANNEL_CALLBACK* callback, + wStream* s, UINT32 MessageId, IUDEVMAN* udevman, + int transferDir) +{ + UINT32 CbTsUrb = 0; + UINT16 Size = 0; + UINT16 URB_Function = 0; + UINT32 RequestId = 0; + UINT error = ERROR_INTERNAL_ERROR; + URBDRC_PLUGIN* urbdrc = NULL; + + if (!callback || !s || !udevman || !pdev) + return ERROR_INVALID_PARAMETER; + + urbdrc = (URBDRC_PLUGIN*)callback->plugin; + + if (!urbdrc) + return ERROR_INVALID_PARAMETER; + + if (!Stream_CheckAndLogRequiredLength(TAG, s, 12)) + return ERROR_INVALID_DATA; + + Stream_Read_UINT32(s, CbTsUrb); /** CbTsUrb */ + Stream_Read_UINT16(s, Size); /** size */ + Stream_Read_UINT16(s, URB_Function); + Stream_Read_UINT32(s, RequestId); + WLog_Print(urbdrc->log, WLOG_DEBUG, "URB %s[%" PRIu16 "]", urb_function_string(URB_Function), + URB_Function); + + switch (URB_Function) + { + case TS_URB_SELECT_CONFIGURATION: /** 0x0000 */ + error = urb_select_configuration(pdev, callback, s, RequestId, MessageId, udevman, + transferDir); + break; + + case TS_URB_SELECT_INTERFACE: /** 0x0001 */ + error = + urb_select_interface(pdev, callback, s, RequestId, MessageId, udevman, transferDir); + break; + + case TS_URB_PIPE_REQUEST: /** 0x0002 */ + error = urb_pipe_request(pdev, callback, s, RequestId, MessageId, udevman, transferDir, + PIPE_CANCEL); + break; + + case TS_URB_TAKE_FRAME_LENGTH_CONTROL: /** 0x0003 */ + /** This URB function is obsolete in Windows 2000 + * and later operating systems + * and is not supported by Microsoft. */ + break; + + case TS_URB_RELEASE_FRAME_LENGTH_CONTROL: /** 0x0004 */ + /** This URB function is obsolete in Windows 2000 + * and later operating systems + * and is not supported by Microsoft. */ + break; + + case TS_URB_GET_FRAME_LENGTH: /** 0x0005 */ + /** This URB function is obsolete in Windows 2000 + * and later operating systems + * and is not supported by Microsoft. */ + break; + + case TS_URB_SET_FRAME_LENGTH: /** 0x0006 */ + /** This URB function is obsolete in Windows 2000 + * and later operating systems + * and is not supported by Microsoft. */ + break; + + case TS_URB_GET_CURRENT_FRAME_NUMBER: /** 0x0007 */ + error = urb_get_current_frame_number(pdev, callback, s, RequestId, MessageId, udevman, + transferDir); + break; + + case TS_URB_CONTROL_TRANSFER: /** 0x0008 */ + error = urb_control_transfer(pdev, callback, s, RequestId, MessageId, udevman, + transferDir, URB_CONTROL_TRANSFER_NONEXTERNAL); + break; + + case TS_URB_BULK_OR_INTERRUPT_TRANSFER: /** 0x0009 */ + error = urb_bulk_or_interrupt_transfer(pdev, callback, s, RequestId, MessageId, udevman, + transferDir); + break; + + case TS_URB_ISOCH_TRANSFER: /** 0x000A */ + error = + urb_isoch_transfer(pdev, callback, s, RequestId, MessageId, udevman, transferDir); + break; + + case TS_URB_GET_DESCRIPTOR_FROM_DEVICE: /** 0x000B */ + error = urb_control_descriptor_request(pdev, callback, s, RequestId, MessageId, udevman, + 0x00, transferDir); + break; + + case TS_URB_SET_DESCRIPTOR_TO_DEVICE: /** 0x000C */ + error = urb_control_descriptor_request(pdev, callback, s, RequestId, MessageId, udevman, + 0x00, transferDir); + break; + + case TS_URB_SET_FEATURE_TO_DEVICE: /** 0x000D */ + error = urb_control_feature_request(pdev, callback, s, RequestId, MessageId, udevman, + 0x00, URB_SET_FEATURE, transferDir); + break; + + case TS_URB_SET_FEATURE_TO_INTERFACE: /** 0x000E */ + error = urb_control_feature_request(pdev, callback, s, RequestId, MessageId, udevman, + 0x01, URB_SET_FEATURE, transferDir); + break; + + case TS_URB_SET_FEATURE_TO_ENDPOINT: /** 0x000F */ + error = urb_control_feature_request(pdev, callback, s, RequestId, MessageId, udevman, + 0x02, URB_SET_FEATURE, transferDir); + break; + + case TS_URB_CLEAR_FEATURE_TO_DEVICE: /** 0x0010 */ + error = urb_control_feature_request(pdev, callback, s, RequestId, MessageId, udevman, + 0x00, URB_CLEAR_FEATURE, transferDir); + break; + + case TS_URB_CLEAR_FEATURE_TO_INTERFACE: /** 0x0011 */ + error = urb_control_feature_request(pdev, callback, s, RequestId, MessageId, udevman, + 0x01, URB_CLEAR_FEATURE, transferDir); + break; + + case TS_URB_CLEAR_FEATURE_TO_ENDPOINT: /** 0x0012 */ + error = urb_control_feature_request(pdev, callback, s, RequestId, MessageId, udevman, + 0x02, URB_CLEAR_FEATURE, transferDir); + break; + + case TS_URB_GET_STATUS_FROM_DEVICE: /** 0x0013 */ + error = urb_control_get_status_request(pdev, callback, s, RequestId, MessageId, udevman, + 0x00, transferDir); + break; + + case TS_URB_GET_STATUS_FROM_INTERFACE: /** 0x0014 */ + error = urb_control_get_status_request(pdev, callback, s, RequestId, MessageId, udevman, + 0x01, transferDir); + break; + + case TS_URB_GET_STATUS_FROM_ENDPOINT: /** 0x0015 */ + error = urb_control_get_status_request(pdev, callback, s, RequestId, MessageId, udevman, + 0x02, transferDir); + break; + + case TS_URB_RESERVED_0X0016: /** 0x0016 */ + break; + + case TS_URB_VENDOR_DEVICE: /** 0x0017 */ + error = urb_control_vendor_or_class_request(pdev, callback, s, RequestId, MessageId, + udevman, (0x02 << 5), /* vendor type */ + 0x00, transferDir); + break; + + case TS_URB_VENDOR_INTERFACE: /** 0x0018 */ + error = urb_control_vendor_or_class_request(pdev, callback, s, RequestId, MessageId, + udevman, (0x02 << 5), /* vendor type */ + 0x01, transferDir); + break; + + case TS_URB_VENDOR_ENDPOINT: /** 0x0019 */ + error = urb_control_vendor_or_class_request(pdev, callback, s, RequestId, MessageId, + udevman, (0x02 << 5), /* vendor type */ + 0x02, transferDir); + break; + + case TS_URB_CLASS_DEVICE: /** 0x001A */ + error = urb_control_vendor_or_class_request(pdev, callback, s, RequestId, MessageId, + udevman, (0x01 << 5), /* class type */ + 0x00, transferDir); + break; + + case TS_URB_CLASS_INTERFACE: /** 0x001B */ + error = urb_control_vendor_or_class_request(pdev, callback, s, RequestId, MessageId, + udevman, (0x01 << 5), /* class type */ + 0x01, transferDir); + break; + + case TS_URB_CLASS_ENDPOINT: /** 0x001C */ + error = urb_control_vendor_or_class_request(pdev, callback, s, RequestId, MessageId, + udevman, (0x01 << 5), /* class type */ + 0x02, transferDir); + break; + + case TS_URB_RESERVE_0X001D: /** 0x001D */ + break; + + case TS_URB_SYNC_RESET_PIPE_AND_CLEAR_STALL: /** 0x001E */ + error = urb_pipe_request(pdev, callback, s, RequestId, MessageId, udevman, transferDir, + PIPE_RESET); + break; + + case TS_URB_CLASS_OTHER: /** 0x001F */ + error = urb_control_vendor_or_class_request(pdev, callback, s, RequestId, MessageId, + udevman, (0x01 << 5), /* class type */ + 0x03, transferDir); + break; + + case TS_URB_VENDOR_OTHER: /** 0x0020 */ + error = urb_control_vendor_or_class_request(pdev, callback, s, RequestId, MessageId, + udevman, (0x02 << 5), /* vendor type */ + 0x03, transferDir); + break; + + case TS_URB_GET_STATUS_FROM_OTHER: /** 0x0021 */ + error = urb_control_get_status_request(pdev, callback, s, RequestId, MessageId, udevman, + 0x03, transferDir); + break; + + case TS_URB_CLEAR_FEATURE_TO_OTHER: /** 0x0022 */ + error = urb_control_feature_request(pdev, callback, s, RequestId, MessageId, udevman, + 0x03, URB_CLEAR_FEATURE, transferDir); + break; + + case TS_URB_SET_FEATURE_TO_OTHER: /** 0x0023 */ + error = urb_control_feature_request(pdev, callback, s, RequestId, MessageId, udevman, + 0x03, URB_SET_FEATURE, transferDir); + break; + + case TS_URB_GET_DESCRIPTOR_FROM_ENDPOINT: /** 0x0024 */ + error = urb_control_descriptor_request(pdev, callback, s, RequestId, MessageId, udevman, + 0x02, transferDir); + break; + + case TS_URB_SET_DESCRIPTOR_TO_ENDPOINT: /** 0x0025 */ + error = urb_control_descriptor_request(pdev, callback, s, RequestId, MessageId, udevman, + 0x02, transferDir); + break; + + case TS_URB_CONTROL_GET_CONFIGURATION_REQUEST: /** 0x0026 */ + error = urb_control_get_configuration_request(pdev, callback, s, RequestId, MessageId, + udevman, transferDir); + break; + + case TS_URB_CONTROL_GET_INTERFACE_REQUEST: /** 0x0027 */ + error = urb_control_get_interface_request(pdev, callback, s, RequestId, MessageId, + udevman, transferDir); + break; + + case TS_URB_GET_DESCRIPTOR_FROM_INTERFACE: /** 0x0028 */ + error = urb_control_descriptor_request(pdev, callback, s, RequestId, MessageId, udevman, + 0x01, transferDir); + break; + + case TS_URB_SET_DESCRIPTOR_TO_INTERFACE: /** 0x0029 */ + error = urb_control_descriptor_request(pdev, callback, s, RequestId, MessageId, udevman, + 0x01, transferDir); + break; + + case TS_URB_GET_OS_FEATURE_DESCRIPTOR_REQUEST: /** 0x002A */ + error = urb_os_feature_descriptor_request(pdev, callback, s, RequestId, MessageId, + udevman, transferDir); + break; + + case TS_URB_RESERVE_0X002B: /** 0x002B */ + case TS_URB_RESERVE_0X002C: /** 0x002C */ + case TS_URB_RESERVE_0X002D: /** 0x002D */ + case TS_URB_RESERVE_0X002E: /** 0x002E */ + case TS_URB_RESERVE_0X002F: /** 0x002F */ + break; + + /** USB 2.0 calls start at 0x0030 */ + case TS_URB_SYNC_RESET_PIPE: /** 0x0030 */ + error = urb_pipe_request(pdev, callback, s, RequestId, MessageId, udevman, transferDir, + PIPE_RESET); + break; + + case TS_URB_SYNC_CLEAR_STALL: /** 0x0031 */ + urb_pipe_request(pdev, callback, s, RequestId, MessageId, udevman, transferDir, + PIPE_RESET); + break; + + case TS_URB_CONTROL_TRANSFER_EX: /** 0x0032 */ + error = urb_control_transfer(pdev, callback, s, RequestId, MessageId, udevman, + transferDir, URB_CONTROL_TRANSFER_EXTERNAL); + break; + + default: + WLog_Print(urbdrc->log, WLOG_DEBUG, "URB_Func: %" PRIx16 " is not found!", + URB_Function); + break; + } + + if (error) + { + WLog_Print(urbdrc->log, WLOG_WARN, + "USB transfer request URB Function '%s' [0x%08x] failed with %08" PRIx32, + urb_function_string(URB_Function), URB_Function, error); + } + + return error; +} + +UINT urbdrc_process_udev_data_transfer(GENERIC_CHANNEL_CALLBACK* callback, URBDRC_PLUGIN* urbdrc, + IUDEVMAN* udevman, wStream* data) +{ + UINT32 InterfaceId = 0; + UINT32 MessageId = 0; + UINT32 FunctionId = 0; + IUDEVICE* pdev = NULL; + UINT error = ERROR_INTERNAL_ERROR; + + if (!urbdrc || !data || !callback || !udevman) + goto fail; + + if (!Stream_CheckAndLogRequiredLength(TAG, data, 8)) + goto fail; + + Stream_Rewind_UINT32(data); + + Stream_Read_UINT32(data, InterfaceId); + Stream_Read_UINT32(data, MessageId); + Stream_Read_UINT32(data, FunctionId); + + pdev = udevman->get_udevice_by_UsbDevice(udevman, InterfaceId); + + /* Device does not exist, ignore this request. */ + if (pdev == NULL) + { + error = ERROR_SUCCESS; + goto fail; + } + + /* Device has been removed, ignore this request. */ + if (pdev->isChannelClosed(pdev)) + { + error = ERROR_SUCCESS; + goto fail; + } + + /* USB kernel driver detach!! */ + pdev->detach_kernel_driver(pdev); + + switch (FunctionId) + { + case CANCEL_REQUEST: + error = urbdrc_process_cancel_request(pdev, data, udevman); + break; + + case REGISTER_REQUEST_CALLBACK: + error = urbdrc_process_register_request_callback(pdev, callback, data, udevman); + break; + + case IO_CONTROL: + error = urbdrc_process_io_control(pdev, callback, data, MessageId, udevman); + break; + + case INTERNAL_IO_CONTROL: + error = urbdrc_process_internal_io_control(pdev, callback, data, MessageId, udevman); + break; + + case QUERY_DEVICE_TEXT: + error = urbdrc_process_query_device_text(pdev, callback, data, MessageId, udevman); + break; + + case TRANSFER_IN_REQUEST: + error = urbdrc_process_transfer_request(pdev, callback, data, MessageId, udevman, + USBD_TRANSFER_DIRECTION_IN); + break; + + case TRANSFER_OUT_REQUEST: + error = urbdrc_process_transfer_request(pdev, callback, data, MessageId, udevman, + USBD_TRANSFER_DIRECTION_OUT); + break; + + case RETRACT_DEVICE: + error = urbdrc_process_retract_device_request(pdev, data, udevman); + break; + + default: + WLog_Print(urbdrc->log, WLOG_WARN, + "urbdrc_process_udev_data_transfer:" + " unknown FunctionId 0x%" PRIX32 "", + FunctionId); + break; + } + +fail: + if (error) + { + WLog_WARN(TAG, "USB request failed with %08" PRIx32, error); + } + + return error; +} diff --git a/channels/urbdrc/client/data_transfer.h b/channels/urbdrc/client/data_transfer.h new file mode 100644 index 0000000..1d7126d --- /dev/null +++ b/channels/urbdrc/client/data_transfer.h @@ -0,0 +1,36 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * RemoteFX USB Redirection + * + * Copyright 2012 Atrust corp. + * Copyright 2012 Alfred Liu <alfred.liu@atruscorp.com> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FREERDP_CHANNEL_URBDRC_CLIENT_DATA_TRANSFER_H +#define FREERDP_CHANNEL_URBDRC_CLIENT_DATA_TRANSFER_H + +#include <winpr/pool.h> + +#include "urbdrc_main.h" + +#define DEVICE_CTX(dev) ((dev)->ctx) +#define HANDLE_CTX(handle) (DEVICE_CTX((handle)->dev)) +#define TRANSFER_CTX(transfer) (HANDLE_CTX((transfer)->dev_handle)) +#define ITRANSFER_CTX(transfer) (TRANSFER_CTX(__USBI_TRANSFER_TO_LIBUSB_TRANSFER(transfer))) + +UINT urbdrc_process_udev_data_transfer(GENERIC_CHANNEL_CALLBACK* callback, URBDRC_PLUGIN* urbdrc, + IUDEVMAN* udevman, wStream* data); + +#endif /* FREERDP_CHANNEL_URBDRC_CLIENT_DATA_TRANSFER_H */ diff --git a/channels/urbdrc/client/libusb/CMakeLists.txt b/channels/urbdrc/client/libusb/CMakeLists.txt new file mode 100644 index 0000000..4e219de --- /dev/null +++ b/channels/urbdrc/client/libusb/CMakeLists.txt @@ -0,0 +1,36 @@ +# FreeRDP: A Remote Desktop Protocol Implementation +# FreeRDP cmake build script +# +# Copyright 2012 Atrust corp. +# Copyright 2012 Alfred Liu <alfred.liu@atruscorp.com> +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +define_channel_client_subsystem("urbdrc" "libusb" "") + +set(${MODULE_PREFIX}_SRCS + libusb_udevman.c + libusb_udevice.c + libusb_udevice.h +) + +set(${MODULE_PREFIX}_LIBS + ${CMAKE_THREAD_LIBS_INIT} + ${LIBUSB_1_LIBRARIES} + winpr freerdp +) + +include_directories(..) + +add_channel_client_subsystem_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} "" TRUE "") + diff --git a/channels/urbdrc/client/libusb/libusb_udevice.c b/channels/urbdrc/client/libusb/libusb_udevice.c new file mode 100644 index 0000000..c226eb8 --- /dev/null +++ b/channels/urbdrc/client/libusb/libusb_udevice.c @@ -0,0 +1,1841 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * RemoteFX USB Redirection + * + * Copyright 2012 Atrust corp. + * Copyright 2012 Alfred Liu <alfred.liu@atruscorp.com> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <winpr/wtypes.h> +#include <winpr/sysinfo.h> +#include <winpr/collections.h> + +#include <errno.h> + +#include "libusb_udevice.h" +#include "../common/urbdrc_types.h" + +#define BASIC_STATE_FUNC_DEFINED(_arg, _type) \ + static _type udev_get_##_arg(IUDEVICE* idev) \ + { \ + UDEVICE* pdev = (UDEVICE*)idev; \ + return pdev->_arg; \ + } \ + static void udev_set_##_arg(IUDEVICE* idev, _type _t) \ + { \ + UDEVICE* pdev = (UDEVICE*)idev; \ + pdev->_arg = _t; \ + } + +#define BASIC_POINT_FUNC_DEFINED(_arg, _type) \ + static _type udev_get_p_##_arg(IUDEVICE* idev) \ + { \ + UDEVICE* pdev = (UDEVICE*)idev; \ + return pdev->_arg; \ + } \ + static void udev_set_p_##_arg(IUDEVICE* idev, _type _t) \ + { \ + UDEVICE* pdev = (UDEVICE*)idev; \ + pdev->_arg = _t; \ + } + +#define BASIC_STATE_FUNC_REGISTER(_arg, _dev) \ + _dev->iface.get_##_arg = udev_get_##_arg; \ + _dev->iface.set_##_arg = udev_set_##_arg + +#if LIBUSB_API_VERSION >= 0x01000103 +#define HAVE_STREAM_ID_API 1 +#endif + +typedef struct +{ + wStream* data; + BOOL noack; + UINT32 MessageId; + UINT32 StartFrame; + UINT32 ErrorCount; + IUDEVICE* idev; + UINT32 OutputBufferSize; + GENERIC_CHANNEL_CALLBACK* callback; + t_isoch_transfer_cb cb; + wArrayList* queue; +#if !defined(HAVE_STREAM_ID_API) + UINT32 streamID; +#endif +} ASYNC_TRANSFER_USER_DATA; + +static void request_free(void* value); + +static struct libusb_transfer* list_contains(wArrayList* list, UINT32 streamID) +{ + size_t count = 0; + if (!list) + return NULL; + count = ArrayList_Count(list); + for (size_t x = 0; x < count; x++) + { + struct libusb_transfer* transfer = ArrayList_GetItem(list, x); + +#if defined(HAVE_STREAM_ID_API) + const UINT32 currentID = libusb_transfer_get_stream_id(transfer); +#else + const ASYNC_TRANSFER_USER_DATA* user_data = (ASYNC_TRANSFER_USER_DATA*)transfer->user_data; + const UINT32 currentID = user_data->streamID; +#endif + if (currentID == streamID) + return transfer; + } + return NULL; +} + +static UINT32 stream_id_from_buffer(struct libusb_transfer* transfer) +{ + if (!transfer) + return 0; +#if defined(HAVE_STREAM_ID_API) + return libusb_transfer_get_stream_id(transfer); +#else + ASYNC_TRANSFER_USER_DATA* user_data = (ASYNC_TRANSFER_USER_DATA*)transfer->user_data; + if (!user_data) + return 0; + return user_data->streamID; +#endif +} + +static void set_stream_id_for_buffer(struct libusb_transfer* transfer, UINT32 streamID) +{ +#if defined(HAVE_STREAM_ID_API) + libusb_transfer_set_stream_id(transfer, streamID); +#else + ASYNC_TRANSFER_USER_DATA* user_data = (ASYNC_TRANSFER_USER_DATA*)transfer->user_data; + if (!user_data) + return; + user_data->streamID = streamID; +#endif +} + +WINPR_ATTR_FORMAT_ARG(3, 8) +static BOOL log_libusb_result_(wLog* log, DWORD lvl, WINPR_FORMAT_ARG const char* fmt, + const char* fkt, const char* file, size_t line, int error, ...) +{ + WINPR_UNUSED(file); + + if (error < 0) + { + char buffer[8192] = { 0 }; + va_list ap; + va_start(ap, error); + vsnprintf(buffer, sizeof(buffer), fmt, ap); + va_end(ap); + + WLog_Print(log, lvl, "[%s:%" PRIuz "]: %s: error %s[%d]", fkt, line, buffer, + libusb_error_name(error), error); + return TRUE; + } + return FALSE; +} + +#define log_libusb_result(log, lvl, fmt, error, ...) \ + log_libusb_result_((log), (lvl), (fmt), __func__, __FILE__, __LINE__, error, ##__VA_ARGS__) + +const char* usb_interface_class_to_string(uint8_t class) +{ + switch (class) + { + case LIBUSB_CLASS_PER_INTERFACE: + return "LIBUSB_CLASS_PER_INTERFACE"; + case LIBUSB_CLASS_AUDIO: + return "LIBUSB_CLASS_AUDIO"; + case LIBUSB_CLASS_COMM: + return "LIBUSB_CLASS_COMM"; + case LIBUSB_CLASS_HID: + return "LIBUSB_CLASS_HID"; + case LIBUSB_CLASS_PHYSICAL: + return "LIBUSB_CLASS_PHYSICAL"; + case LIBUSB_CLASS_PRINTER: + return "LIBUSB_CLASS_PRINTER"; + case LIBUSB_CLASS_IMAGE: + return "LIBUSB_CLASS_IMAGE"; + case LIBUSB_CLASS_MASS_STORAGE: + return "LIBUSB_CLASS_MASS_STORAGE"; + case LIBUSB_CLASS_HUB: + return "LIBUSB_CLASS_HUB"; + case LIBUSB_CLASS_DATA: + return "LIBUSB_CLASS_DATA"; + case LIBUSB_CLASS_SMART_CARD: + return "LIBUSB_CLASS_SMART_CARD"; + case LIBUSB_CLASS_CONTENT_SECURITY: + return "LIBUSB_CLASS_CONTENT_SECURITY"; + case LIBUSB_CLASS_VIDEO: + return "LIBUSB_CLASS_VIDEO"; + case LIBUSB_CLASS_PERSONAL_HEALTHCARE: + return "LIBUSB_CLASS_PERSONAL_HEALTHCARE"; + case LIBUSB_CLASS_DIAGNOSTIC_DEVICE: + return "LIBUSB_CLASS_DIAGNOSTIC_DEVICE"; + case LIBUSB_CLASS_WIRELESS: + return "LIBUSB_CLASS_WIRELESS"; + case LIBUSB_CLASS_APPLICATION: + return "LIBUSB_CLASS_APPLICATION"; + case LIBUSB_CLASS_VENDOR_SPEC: + return "LIBUSB_CLASS_VENDOR_SPEC"; + default: + return "UNKNOWN_DEVICE_CLASS"; + } +} + +static ASYNC_TRANSFER_USER_DATA* async_transfer_user_data_new(IUDEVICE* idev, UINT32 MessageId, + size_t offset, size_t BufferSize, + const BYTE* data, size_t packetSize, + BOOL NoAck, t_isoch_transfer_cb cb, + GENERIC_CHANNEL_CALLBACK* callback) +{ + ASYNC_TRANSFER_USER_DATA* user_data = NULL; + UDEVICE* pdev = (UDEVICE*)idev; + + if (BufferSize > UINT32_MAX) + return NULL; + + user_data = calloc(1, sizeof(ASYNC_TRANSFER_USER_DATA)); + if (!user_data) + return NULL; + + user_data->data = Stream_New(NULL, offset + BufferSize + packetSize); + + if (!user_data->data) + { + free(user_data); + return NULL; + } + + Stream_Seek(user_data->data, offset); /* Skip header offset */ + if (data) + memcpy(Stream_Pointer(user_data->data), data, BufferSize); + else + user_data->OutputBufferSize = (UINT32)BufferSize; + + user_data->noack = NoAck; + user_data->cb = cb; + user_data->callback = callback; + user_data->idev = idev; + user_data->MessageId = MessageId; + + user_data->queue = pdev->request_queue; + + return user_data; +} + +static void async_transfer_user_data_free(ASYNC_TRANSFER_USER_DATA* user_data) +{ + if (user_data) + { + Stream_Free(user_data->data, TRUE); + free(user_data); + } +} + +static void LIBUSB_CALL func_iso_callback(struct libusb_transfer* transfer) +{ + ASYNC_TRANSFER_USER_DATA* user_data = (ASYNC_TRANSFER_USER_DATA*)transfer->user_data; + const UINT32 streamID = stream_id_from_buffer(transfer); + wArrayList* list = user_data->queue; + + ArrayList_Lock(list); + switch (transfer->status) + { + case LIBUSB_TRANSFER_COMPLETED: + { + UINT32 index = 0; + BYTE* dataStart = Stream_Pointer(user_data->data); + Stream_SetPosition(user_data->data, + 40); /* TS_URB_ISOCH_TRANSFER_RESULT IsoPacket offset */ + + for (int i = 0; i < transfer->num_iso_packets; i++) + { + const UINT32 act_len = transfer->iso_packet_desc[i].actual_length; + Stream_Write_UINT32(user_data->data, index); + Stream_Write_UINT32(user_data->data, act_len); + Stream_Write_UINT32(user_data->data, transfer->iso_packet_desc[i].status); + + if (transfer->iso_packet_desc[i].status != USBD_STATUS_SUCCESS) + user_data->ErrorCount++; + else + { + const unsigned char* packetBuffer = + libusb_get_iso_packet_buffer_simple(transfer, i); + BYTE* data = dataStart + index; + + if (data != packetBuffer) + memmove(data, packetBuffer, act_len); + + index += act_len; + } + } + } + /* fallthrough */ + WINPR_FALLTHROUGH + case LIBUSB_TRANSFER_CANCELLED: + /* fallthrough */ + WINPR_FALLTHROUGH + case LIBUSB_TRANSFER_TIMED_OUT: + /* fallthrough */ + WINPR_FALLTHROUGH + case LIBUSB_TRANSFER_ERROR: + { + const UINT32 InterfaceId = + ((STREAM_ID_PROXY << 30) | user_data->idev->get_ReqCompletion(user_data->idev)); + + if (list_contains(list, streamID)) + { + if (!user_data->noack) + { + const UINT32 RequestID = streamID & INTERFACE_ID_MASK; + user_data->cb(user_data->idev, user_data->callback, user_data->data, + InterfaceId, user_data->noack, user_data->MessageId, RequestID, + transfer->num_iso_packets, transfer->status, + user_data->StartFrame, user_data->ErrorCount, + user_data->OutputBufferSize); + user_data->data = NULL; + } + ArrayList_Remove(list, transfer); + } + } + break; + default: + break; + } + ArrayList_Unlock(list); +} + +static const LIBUSB_ENDPOINT_DESCEIPTOR* func_get_ep_desc(LIBUSB_CONFIG_DESCRIPTOR* LibusbConfig, + MSUSB_CONFIG_DESCRIPTOR* MsConfig, + UINT32 EndpointAddress) +{ + MSUSB_INTERFACE_DESCRIPTOR** MsInterfaces = MsConfig->MsInterfaces; + const LIBUSB_INTERFACE* interface = LibusbConfig->interface; + + for (UINT32 inum = 0; inum < MsConfig->NumInterfaces; inum++) + { + BYTE alt = MsInterfaces[inum]->AlternateSetting; + const LIBUSB_ENDPOINT_DESCEIPTOR* endpoint = interface[inum].altsetting[alt].endpoint; + + for (UINT32 pnum = 0; pnum < MsInterfaces[inum]->NumberOfPipes; pnum++) + { + if (endpoint[pnum].bEndpointAddress == EndpointAddress) + { + return &endpoint[pnum]; + } + } + } + + return NULL; +} + +static void LIBUSB_CALL func_bulk_transfer_cb(struct libusb_transfer* transfer) +{ + ASYNC_TRANSFER_USER_DATA* user_data = NULL; + uint32_t streamID = 0; + wArrayList* list = NULL; + + user_data = (ASYNC_TRANSFER_USER_DATA*)transfer->user_data; + if (!user_data) + { + WLog_ERR(TAG, "[%s]: Invalid transfer->user_data!"); + return; + } + list = user_data->queue; + ArrayList_Lock(list); + streamID = stream_id_from_buffer(transfer); + + if (list_contains(list, streamID)) + { + const UINT32 InterfaceId = + ((STREAM_ID_PROXY << 30) | user_data->idev->get_ReqCompletion(user_data->idev)); + const UINT32 RequestID = streamID & INTERFACE_ID_MASK; + + user_data->cb(user_data->idev, user_data->callback, user_data->data, InterfaceId, + user_data->noack, user_data->MessageId, RequestID, transfer->num_iso_packets, + transfer->status, user_data->StartFrame, user_data->ErrorCount, + transfer->actual_length); + user_data->data = NULL; + ArrayList_Remove(list, transfer); + } + ArrayList_Unlock(list); +} + +static BOOL func_set_usbd_status(URBDRC_PLUGIN* urbdrc, UDEVICE* pdev, UINT32* status, + int err_result) +{ + if (!urbdrc || !status) + return FALSE; + + switch (err_result) + { + case LIBUSB_SUCCESS: + *status = USBD_STATUS_SUCCESS; + break; + + case LIBUSB_ERROR_IO: + *status = USBD_STATUS_STALL_PID; + break; + + case LIBUSB_ERROR_INVALID_PARAM: + *status = USBD_STATUS_INVALID_PARAMETER; + break; + + case LIBUSB_ERROR_ACCESS: + *status = USBD_STATUS_NOT_ACCESSED; + break; + + case LIBUSB_ERROR_NO_DEVICE: + *status = USBD_STATUS_DEVICE_GONE; + + if (pdev) + { + if (!(pdev->status & URBDRC_DEVICE_NOT_FOUND)) + pdev->status |= URBDRC_DEVICE_NOT_FOUND; + } + + break; + + case LIBUSB_ERROR_NOT_FOUND: + *status = USBD_STATUS_STALL_PID; + break; + + case LIBUSB_ERROR_BUSY: + *status = USBD_STATUS_STALL_PID; + break; + + case LIBUSB_ERROR_TIMEOUT: + *status = USBD_STATUS_TIMEOUT; + break; + + case LIBUSB_ERROR_OVERFLOW: + *status = USBD_STATUS_STALL_PID; + break; + + case LIBUSB_ERROR_PIPE: + *status = USBD_STATUS_STALL_PID; + break; + + case LIBUSB_ERROR_INTERRUPTED: + *status = USBD_STATUS_STALL_PID; + break; + + case LIBUSB_ERROR_NO_MEM: + *status = USBD_STATUS_NO_MEMORY; + break; + + case LIBUSB_ERROR_NOT_SUPPORTED: + *status = USBD_STATUS_NOT_SUPPORTED; + break; + + case LIBUSB_ERROR_OTHER: + *status = USBD_STATUS_STALL_PID; + break; + + default: + *status = USBD_STATUS_SUCCESS; + break; + } + + return TRUE; +} + +static int func_config_release_all_interface(URBDRC_PLUGIN* urbdrc, + LIBUSB_DEVICE_HANDLE* libusb_handle, + UINT32 NumInterfaces) +{ + for (UINT32 i = 0; i < NumInterfaces; i++) + { + int ret = libusb_release_interface(libusb_handle, i); + + if (log_libusb_result(urbdrc->log, WLOG_WARN, "libusb_release_interface", ret)) + return -1; + } + + return 0; +} + +static int func_claim_all_interface(URBDRC_PLUGIN* urbdrc, LIBUSB_DEVICE_HANDLE* libusb_handle, + int NumInterfaces) +{ + int ret = 0; + + for (int i = 0; i < NumInterfaces; i++) + { + ret = libusb_claim_interface(libusb_handle, i); + + if (log_libusb_result(urbdrc->log, WLOG_ERROR, "libusb_claim_interface", ret)) + return -1; + } + + return 0; +} + +static LIBUSB_DEVICE* udev_get_libusb_dev(libusb_context* context, uint8_t bus_number, + uint8_t dev_number) +{ + LIBUSB_DEVICE** libusb_list = NULL; + LIBUSB_DEVICE* device = NULL; + const ssize_t total_device = libusb_get_device_list(context, &libusb_list); + + for (ssize_t i = 0; i < total_device; i++) + { + LIBUSB_DEVICE* dev = libusb_list[i]; + if ((bus_number == libusb_get_bus_number(dev)) && + (dev_number == libusb_get_device_address(dev))) + device = dev; + else + libusb_unref_device(dev); + } + + libusb_free_device_list(libusb_list, 0); + return device; +} + +static LIBUSB_DEVICE_DESCRIPTOR* udev_new_descript(URBDRC_PLUGIN* urbdrc, LIBUSB_DEVICE* libusb_dev) +{ + int ret = 0; + LIBUSB_DEVICE_DESCRIPTOR* descriptor = + (LIBUSB_DEVICE_DESCRIPTOR*)calloc(1, sizeof(LIBUSB_DEVICE_DESCRIPTOR)); + if (!descriptor) + return NULL; + ret = libusb_get_device_descriptor(libusb_dev, descriptor); + + if (log_libusb_result(urbdrc->log, WLOG_ERROR, "libusb_get_device_descriptor", ret)) + { + free(descriptor); + return NULL; + } + + return descriptor; +} + +static int libusb_udev_select_interface(IUDEVICE* idev, BYTE InterfaceNumber, BYTE AlternateSetting) +{ + int error = 0; + int diff = 0; + UDEVICE* pdev = (UDEVICE*)idev; + URBDRC_PLUGIN* urbdrc = NULL; + MSUSB_CONFIG_DESCRIPTOR* MsConfig = NULL; + MSUSB_INTERFACE_DESCRIPTOR** MsInterfaces = NULL; + + if (!pdev || !pdev->urbdrc) + return -1; + + urbdrc = pdev->urbdrc; + MsConfig = pdev->MsConfig; + + if (MsConfig) + { + MsInterfaces = MsConfig->MsInterfaces; + if (MsInterfaces) + { + WLog_Print(urbdrc->log, WLOG_INFO, + "select Interface(%" PRIu8 ") curr AlternateSetting(%" PRIu8 + ") new AlternateSetting(%" PRIu8 ")", + InterfaceNumber, MsInterfaces[InterfaceNumber]->AlternateSetting, + AlternateSetting); + + if (MsInterfaces[InterfaceNumber]->AlternateSetting != AlternateSetting) + { + diff = 1; + } + } + + if (diff) + { + error = libusb_set_interface_alt_setting(pdev->libusb_handle, InterfaceNumber, + AlternateSetting); + + log_libusb_result(urbdrc->log, WLOG_ERROR, "libusb_set_interface_alt_setting", error); + } + } + + return error; +} + +static MSUSB_CONFIG_DESCRIPTOR* +libusb_udev_complete_msconfig_setup(IUDEVICE* idev, MSUSB_CONFIG_DESCRIPTOR* MsConfig) +{ + UDEVICE* pdev = (UDEVICE*)idev; + MSUSB_INTERFACE_DESCRIPTOR** MsInterfaces = NULL; + MSUSB_INTERFACE_DESCRIPTOR* MsInterface = NULL; + MSUSB_PIPE_DESCRIPTOR** MsPipes = NULL; + MSUSB_PIPE_DESCRIPTOR* MsPipe = NULL; + MSUSB_PIPE_DESCRIPTOR** t_MsPipes = NULL; + MSUSB_PIPE_DESCRIPTOR* t_MsPipe = NULL; + LIBUSB_CONFIG_DESCRIPTOR* LibusbConfig = NULL; + const LIBUSB_INTERFACE* LibusbInterface = NULL; + const LIBUSB_INTERFACE_DESCRIPTOR* LibusbAltsetting = NULL; + const LIBUSB_ENDPOINT_DESCEIPTOR* LibusbEndpoint = NULL; + BYTE LibusbNumEndpoint = 0; + URBDRC_PLUGIN* urbdrc = NULL; + UINT32 MsOutSize = 0; + + if (!pdev || !pdev->LibusbConfig || !pdev->urbdrc || !MsConfig) + return NULL; + + urbdrc = pdev->urbdrc; + LibusbConfig = pdev->LibusbConfig; + + if (LibusbConfig->bNumInterfaces != MsConfig->NumInterfaces) + { + WLog_Print(urbdrc->log, WLOG_ERROR, + "Select Configuration: Libusb NumberInterfaces(%" PRIu8 ") is different " + "with MsConfig NumberInterfaces(%" PRIu32 ")", + LibusbConfig->bNumInterfaces, MsConfig->NumInterfaces); + } + + /* replace MsPipes for libusb */ + MsInterfaces = MsConfig->MsInterfaces; + + for (UINT32 inum = 0; inum < MsConfig->NumInterfaces; inum++) + { + MsInterface = MsInterfaces[inum]; + /* get libusb's number of endpoints */ + LibusbInterface = &LibusbConfig->interface[MsInterface->InterfaceNumber]; + LibusbAltsetting = &LibusbInterface->altsetting[MsInterface->AlternateSetting]; + LibusbNumEndpoint = LibusbAltsetting->bNumEndpoints; + t_MsPipes = + (MSUSB_PIPE_DESCRIPTOR**)calloc(LibusbNumEndpoint, sizeof(MSUSB_PIPE_DESCRIPTOR*)); + + for (UINT32 pnum = 0; pnum < LibusbNumEndpoint; pnum++) + { + t_MsPipe = (MSUSB_PIPE_DESCRIPTOR*)calloc(1, sizeof(MSUSB_PIPE_DESCRIPTOR)); + + if (pnum < MsInterface->NumberOfPipes && MsInterface->MsPipes) + { + MsPipe = MsInterface->MsPipes[pnum]; + t_MsPipe->MaximumPacketSize = MsPipe->MaximumPacketSize; + t_MsPipe->MaximumTransferSize = MsPipe->MaximumTransferSize; + t_MsPipe->PipeFlags = MsPipe->PipeFlags; + } + else + { + t_MsPipe->MaximumPacketSize = 0; + t_MsPipe->MaximumTransferSize = 0xffffffff; + t_MsPipe->PipeFlags = 0; + } + + t_MsPipe->PipeHandle = 0; + t_MsPipe->bEndpointAddress = 0; + t_MsPipe->bInterval = 0; + t_MsPipe->PipeType = 0; + t_MsPipe->InitCompleted = 0; + t_MsPipes[pnum] = t_MsPipe; + } + + msusb_mspipes_replace(MsInterface, t_MsPipes, LibusbNumEndpoint); + } + + /* setup configuration */ + MsOutSize = 8; + /* ConfigurationHandle: 4 bytes + * --------------------------------------------------------------- + * ||<<< 1 byte >>>|<<< 1 byte >>>|<<<<<<<<<< 2 byte >>>>>>>>>>>|| + * || bus_number | dev_number | bConfigurationValue || + * --------------------------------------------------------------- + * ***********************/ + MsConfig->ConfigurationHandle = + MsConfig->bConfigurationValue | (pdev->bus_number << 24) | (pdev->dev_number << 16); + MsInterfaces = MsConfig->MsInterfaces; + + for (UINT32 inum = 0; inum < MsConfig->NumInterfaces; inum++) + { + MsOutSize += 16; + MsInterface = MsInterfaces[inum]; + /* get libusb's interface */ + LibusbInterface = &LibusbConfig->interface[MsInterface->InterfaceNumber]; + LibusbAltsetting = &LibusbInterface->altsetting[MsInterface->AlternateSetting]; + /* InterfaceHandle: 4 bytes + * --------------------------------------------------------------- + * ||<<< 1 byte >>>|<<< 1 byte >>>|<<< 1 byte >>>|<<< 1 byte >>>|| + * || bus_number | dev_number | altsetting | interfaceNum || + * --------------------------------------------------------------- + * ***********************/ + MsInterface->InterfaceHandle = LibusbAltsetting->bInterfaceNumber | + (LibusbAltsetting->bAlternateSetting << 8) | + (pdev->dev_number << 16) | (pdev->bus_number << 24); + MsInterface->Length = 16 + (MsInterface->NumberOfPipes * 20); + MsInterface->bInterfaceClass = LibusbAltsetting->bInterfaceClass; + MsInterface->bInterfaceSubClass = LibusbAltsetting->bInterfaceSubClass; + MsInterface->bInterfaceProtocol = LibusbAltsetting->bInterfaceProtocol; + MsInterface->InitCompleted = 1; + MsPipes = MsInterface->MsPipes; + LibusbNumEndpoint = LibusbAltsetting->bNumEndpoints; + + for (UINT32 pnum = 0; pnum < LibusbNumEndpoint; pnum++) + { + MsOutSize += 20; + MsPipe = MsPipes[pnum]; + /* get libusb's endpoint */ + LibusbEndpoint = &LibusbAltsetting->endpoint[pnum]; + /* PipeHandle: 4 bytes + * --------------------------------------------------------------- + * ||<<< 1 byte >>>|<<< 1 byte >>>|<<<<<<<<<< 2 byte >>>>>>>>>>>|| + * || bus_number | dev_number | bEndpointAddress || + * --------------------------------------------------------------- + * ***********************/ + MsPipe->PipeHandle = LibusbEndpoint->bEndpointAddress | (pdev->dev_number << 16) | + (pdev->bus_number << 24); + /* count endpoint max packet size */ + int max = LibusbEndpoint->wMaxPacketSize & 0x07ff; + BYTE attr = LibusbEndpoint->bmAttributes; + + if ((attr & 0x3) == 1 || (attr & 0x3) == 3) + { + max *= (1 + ((LibusbEndpoint->wMaxPacketSize >> 11) & 3)); + } + + MsPipe->MaximumPacketSize = max; + MsPipe->bEndpointAddress = LibusbEndpoint->bEndpointAddress; + MsPipe->bInterval = LibusbEndpoint->bInterval; + MsPipe->PipeType = attr & 0x3; + MsPipe->InitCompleted = 1; + } + } + + MsConfig->MsOutSize = MsOutSize; + MsConfig->InitCompleted = 1; + + /* replace device's MsConfig */ + if (MsConfig != pdev->MsConfig) + { + msusb_msconfig_free(pdev->MsConfig); + pdev->MsConfig = MsConfig; + } + + return MsConfig; +} + +static int libusb_udev_select_configuration(IUDEVICE* idev, UINT32 bConfigurationValue) +{ + UDEVICE* pdev = (UDEVICE*)idev; + MSUSB_CONFIG_DESCRIPTOR* MsConfig = NULL; + LIBUSB_DEVICE_HANDLE* libusb_handle = NULL; + LIBUSB_DEVICE* libusb_dev = NULL; + URBDRC_PLUGIN* urbdrc = NULL; + LIBUSB_CONFIG_DESCRIPTOR** LibusbConfig = NULL; + int ret = 0; + + if (!pdev || !pdev->MsConfig || !pdev->LibusbConfig || !pdev->urbdrc) + return -1; + + urbdrc = pdev->urbdrc; + MsConfig = pdev->MsConfig; + libusb_handle = pdev->libusb_handle; + libusb_dev = pdev->libusb_dev; + LibusbConfig = &pdev->LibusbConfig; + + if (MsConfig->InitCompleted) + { + func_config_release_all_interface(pdev->urbdrc, libusb_handle, + (*LibusbConfig)->bNumInterfaces); + } + + /* The configuration value -1 is mean to put the device in unconfigured state. */ + if (bConfigurationValue == 0) + ret = libusb_set_configuration(libusb_handle, -1); + else + ret = libusb_set_configuration(libusb_handle, bConfigurationValue); + + if (log_libusb_result(urbdrc->log, WLOG_ERROR, "libusb_set_configuration", ret)) + { + func_claim_all_interface(urbdrc, libusb_handle, (*LibusbConfig)->bNumInterfaces); + return -1; + } + else + { + ret = libusb_get_active_config_descriptor(libusb_dev, LibusbConfig); + + if (log_libusb_result(urbdrc->log, WLOG_ERROR, "libusb_set_configuration", ret)) + { + func_claim_all_interface(urbdrc, libusb_handle, (*LibusbConfig)->bNumInterfaces); + return -1; + } + } + + func_claim_all_interface(urbdrc, libusb_handle, (*LibusbConfig)->bNumInterfaces); + return 0; +} + +static int libusb_udev_control_pipe_request(IUDEVICE* idev, UINT32 RequestId, + UINT32 EndpointAddress, UINT32* UsbdStatus, int command) +{ + int error = 0; + UDEVICE* pdev = (UDEVICE*)idev; + + /* + pdev->request_queue->register_request(pdev->request_queue, RequestId, NULL, 0); + */ + switch (command) + { + case PIPE_CANCEL: + /** cancel bulk or int transfer */ + idev->cancel_all_transfer_request(idev); + // dummy_wait_s_obj(1); + /** set feature to ep (set halt)*/ + error = libusb_control_transfer( + pdev->libusb_handle, LIBUSB_ENDPOINT_OUT | LIBUSB_RECIPIENT_ENDPOINT, + LIBUSB_REQUEST_SET_FEATURE, ENDPOINT_HALT, EndpointAddress, NULL, 0, 1000); + break; + + case PIPE_RESET: + idev->cancel_all_transfer_request(idev); + error = libusb_clear_halt(pdev->libusb_handle, EndpointAddress); + // func_set_usbd_status(pdev, UsbdStatus, error); + break; + + default: + error = -0xff; + break; + } + + *UsbdStatus = 0; + return error; +} + +static UINT32 libusb_udev_control_query_device_text(IUDEVICE* idev, UINT32 TextType, + UINT16 LocaleId, UINT8* BufferSize, + BYTE* Buffer) +{ + UDEVICE* pdev = (UDEVICE*)idev; + LIBUSB_DEVICE_DESCRIPTOR* devDescriptor = NULL; + const char strDesc[] = "Generic Usb String"; + char deviceLocation[25] = { 0 }; + BYTE bus_number = 0; + BYTE device_address = 0; + int ret = 0; + size_t len = 0; + URBDRC_PLUGIN* urbdrc = NULL; + WCHAR* text = (WCHAR*)Buffer; + BYTE slen = 0; + BYTE locale = 0; + const UINT8 inSize = *BufferSize; + + *BufferSize = 0; + if (!pdev || !pdev->devDescriptor || !pdev->urbdrc) + return ERROR_INVALID_DATA; + + urbdrc = pdev->urbdrc; + devDescriptor = pdev->devDescriptor; + + switch (TextType) + { + case DeviceTextDescription: + { + BYTE data[0x100] = { 0 }; + ret = libusb_get_string_descriptor(pdev->libusb_handle, devDescriptor->iProduct, + LocaleId, data, 0xFF); + /* The returned data in the buffer is: + * 1 byte length of following data + * 1 byte descriptor type, must be 0x03 for strings + * n WCHAR unicode string (of length / 2 characters) including '\0' + */ + slen = data[0]; + locale = data[1]; + + if ((ret <= 0) || (ret <= 4) || (slen <= 4) || (locale != LIBUSB_DT_STRING) || + (ret > UINT8_MAX)) + { + const char* msg = "SHORT_DESCRIPTOR"; + if (ret < 0) + msg = libusb_error_name(ret); + WLog_Print(urbdrc->log, WLOG_DEBUG, + "libusb_get_string_descriptor: " + "%s [%d], iProduct: %" PRIu8 "!", + msg, ret, devDescriptor->iProduct); + + len = MIN(sizeof(strDesc), inSize); + for (ssize_t i = 0; i < len; i++) + text[i] = (WCHAR)strDesc[i]; + + *BufferSize = (BYTE)(len * 2); + } + else + { + /* ret and slen should be equals, but you never know creativity + * of device manufacturers... + * So also check the string length returned as server side does + * not honor strings with multi '\0' characters well. + */ + const size_t rchar = _wcsnlen((WCHAR*)&data[2], sizeof(data) / 2); + len = MIN((BYTE)ret, slen); + len = MIN(len, inSize); + len = MIN(len, rchar * 2 + sizeof(WCHAR)); + memcpy(Buffer, &data[2], len); + + /* Just as above, the returned WCHAR string should be '\0' + * terminated, but never trust hardware to conform to specs... */ + Buffer[len - 2] = '\0'; + Buffer[len - 1] = '\0'; + *BufferSize = (BYTE)len; + } + } + break; + + case DeviceTextLocationInformation: + bus_number = libusb_get_bus_number(pdev->libusb_dev); + device_address = libusb_get_device_address(pdev->libusb_dev); + sprintf_s(deviceLocation, sizeof(deviceLocation), + "Port_#%04" PRIu8 ".Hub_#%04" PRIu8 "", device_address, bus_number); + + len = strnlen(deviceLocation, + MIN(sizeof(deviceLocation), (inSize > 0) ? inSize - 1U : 0)); + for (ssize_t i = 0; i < len; i++) + text[i] = (WCHAR)deviceLocation[i]; + text[len++] = '\0'; + *BufferSize = (UINT8)(len * sizeof(WCHAR)); + break; + + default: + WLog_Print(urbdrc->log, WLOG_DEBUG, "Query Text: unknown TextType %" PRIu32 "", + TextType); + return ERROR_INVALID_DATA; + } + + return S_OK; +} + +static int libusb_udev_os_feature_descriptor_request(IUDEVICE* idev, UINT32 RequestId, + BYTE Recipient, BYTE InterfaceNumber, + BYTE Ms_PageIndex, UINT16 Ms_featureDescIndex, + UINT32* UsbdStatus, UINT32* BufferSize, + BYTE* Buffer, UINT32 Timeout) +{ + UDEVICE* pdev = (UDEVICE*)idev; + BYTE ms_string_desc[0x13] = { 0 }; + int error = 0; + + WINPR_ASSERT(idev); + WINPR_ASSERT(UsbdStatus); + WINPR_ASSERT(BufferSize); + WINPR_ASSERT(*BufferSize <= UINT16_MAX); + + /* + pdev->request_queue->register_request(pdev->request_queue, RequestId, NULL, 0); + */ + error = libusb_control_transfer(pdev->libusb_handle, LIBUSB_ENDPOINT_IN | Recipient, + LIBUSB_REQUEST_GET_DESCRIPTOR, 0x03ee, 0, ms_string_desc, 0x12, + Timeout); + + log_libusb_result(pdev->urbdrc->log, WLOG_DEBUG, "libusb_control_transfer", error); + + if (error > 0) + { + const BYTE bMS_Vendorcode = ms_string_desc[16]; + /** get os descriptor */ + error = libusb_control_transfer( + pdev->libusb_handle, LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_VENDOR | Recipient, + bMS_Vendorcode, (UINT16)((InterfaceNumber << 8) | Ms_PageIndex), Ms_featureDescIndex, + Buffer, (UINT16)*BufferSize, Timeout); + log_libusb_result(pdev->urbdrc->log, WLOG_DEBUG, "libusb_control_transfer", error); + + if (error >= 0) + *BufferSize = (UINT32)error; + } + + if (error < 0) + *UsbdStatus = USBD_STATUS_STALL_PID; + else + *UsbdStatus = USBD_STATUS_SUCCESS; + + return ERROR_SUCCESS; +} + +static int libusb_udev_query_device_descriptor(IUDEVICE* idev, int offset) +{ + UDEVICE* pdev = (UDEVICE*)idev; + + switch (offset) + { + case B_LENGTH: + return pdev->devDescriptor->bLength; + + case B_DESCRIPTOR_TYPE: + return pdev->devDescriptor->bDescriptorType; + + case BCD_USB: + return pdev->devDescriptor->bcdUSB; + + case B_DEVICE_CLASS: + return pdev->devDescriptor->bDeviceClass; + + case B_DEVICE_SUBCLASS: + return pdev->devDescriptor->bDeviceSubClass; + + case B_DEVICE_PROTOCOL: + return pdev->devDescriptor->bDeviceProtocol; + + case B_MAX_PACKET_SIZE0: + return pdev->devDescriptor->bMaxPacketSize0; + + case ID_VENDOR: + return pdev->devDescriptor->idVendor; + + case ID_PRODUCT: + return pdev->devDescriptor->idProduct; + + case BCD_DEVICE: + return pdev->devDescriptor->bcdDevice; + + case I_MANUFACTURER: + return pdev->devDescriptor->iManufacturer; + + case I_PRODUCT: + return pdev->devDescriptor->iProduct; + + case I_SERIAL_NUMBER: + return pdev->devDescriptor->iSerialNumber; + + case B_NUM_CONFIGURATIONS: + return pdev->devDescriptor->bNumConfigurations; + + default: + return 0; + } +} + +static BOOL libusb_udev_detach_kernel_driver(IUDEVICE* idev) +{ + int err = 0; + UDEVICE* pdev = (UDEVICE*)idev; + URBDRC_PLUGIN* urbdrc = NULL; + + if (!pdev || !pdev->LibusbConfig || !pdev->libusb_handle || !pdev->urbdrc) + return FALSE; + +#ifdef _WIN32 + return TRUE; +#else + urbdrc = pdev->urbdrc; + + if ((pdev->status & URBDRC_DEVICE_DETACH_KERNEL) == 0) + { + for (int i = 0; i < pdev->LibusbConfig->bNumInterfaces; i++) + { + err = libusb_kernel_driver_active(pdev->libusb_handle, i); + log_libusb_result(urbdrc->log, WLOG_DEBUG, "libusb_kernel_driver_active", err); + + if (err) + { + err = libusb_detach_kernel_driver(pdev->libusb_handle, i); + log_libusb_result(urbdrc->log, WLOG_DEBUG, "libusb_detach_kernel_driver", err); + } + } + + pdev->status |= URBDRC_DEVICE_DETACH_KERNEL; + } + + return TRUE; +#endif +} + +static BOOL libusb_udev_attach_kernel_driver(IUDEVICE* idev) +{ + int err = 0; + UDEVICE* pdev = (UDEVICE*)idev; + + if (!pdev || !pdev->LibusbConfig || !pdev->libusb_handle || !pdev->urbdrc) + return FALSE; + + for (int i = 0; i < pdev->LibusbConfig->bNumInterfaces && err != LIBUSB_ERROR_NO_DEVICE; i++) + { + err = libusb_release_interface(pdev->libusb_handle, i); + + log_libusb_result(pdev->urbdrc->log, WLOG_DEBUG, "libusb_release_interface", err); + +#ifndef _WIN32 + if (err != LIBUSB_ERROR_NO_DEVICE) + { + err = libusb_attach_kernel_driver(pdev->libusb_handle, i); + log_libusb_result(pdev->urbdrc->log, WLOG_DEBUG, "libusb_attach_kernel_driver if=%d", + err, i); + } +#endif + } + + return TRUE; +} + +static int libusb_udev_is_composite_device(IUDEVICE* idev) +{ + UDEVICE* pdev = (UDEVICE*)idev; + return pdev->isCompositeDevice; +} + +static int libusb_udev_is_exist(IUDEVICE* idev) +{ + UDEVICE* pdev = (UDEVICE*)idev; + return (pdev->status & URBDRC_DEVICE_NOT_FOUND) ? 0 : 1; +} + +static int libusb_udev_is_channel_closed(IUDEVICE* idev) +{ + UDEVICE* pdev = (UDEVICE*)idev; + IUDEVMAN* udevman = NULL; + if (!pdev || !pdev->urbdrc) + return 1; + + udevman = pdev->urbdrc->udevman; + if (udevman) + { + if (udevman->status & URBDRC_DEVICE_CHANNEL_CLOSED) + return 1; + } + + if (pdev->status & URBDRC_DEVICE_CHANNEL_CLOSED) + return 1; + + return 0; +} + +static int libusb_udev_is_already_send(IUDEVICE* idev) +{ + UDEVICE* pdev = (UDEVICE*)idev; + return (pdev->status & URBDRC_DEVICE_ALREADY_SEND) ? 1 : 0; +} + +/* This is called from channel cleanup code. + * Avoid double free, just remove the device and mark the channel closed. */ +static void libusb_udev_mark_channel_closed(IUDEVICE* idev) +{ + UDEVICE* pdev = (UDEVICE*)idev; + if (pdev && ((pdev->status & URBDRC_DEVICE_CHANNEL_CLOSED) == 0)) + { + URBDRC_PLUGIN* urbdrc = pdev->urbdrc; + const uint8_t busNr = idev->get_bus_number(idev); + const uint8_t devNr = idev->get_dev_number(idev); + + pdev->status |= URBDRC_DEVICE_CHANNEL_CLOSED; + urbdrc->udevman->unregister_udevice(urbdrc->udevman, busNr, devNr); + } +} + +/* This is called by local events where the device is removed or in an error + * state. Remove the device from redirection and close the channel. */ +static void libusb_udev_channel_closed(IUDEVICE* idev) +{ + UDEVICE* pdev = (UDEVICE*)idev; + if (pdev && ((pdev->status & URBDRC_DEVICE_CHANNEL_CLOSED) == 0)) + { + URBDRC_PLUGIN* urbdrc = pdev->urbdrc; + const uint8_t busNr = idev->get_bus_number(idev); + const uint8_t devNr = idev->get_dev_number(idev); + IWTSVirtualChannel* channel = NULL; + + if (pdev->channelManager) + channel = IFCALLRESULT(NULL, pdev->channelManager->FindChannelById, + pdev->channelManager, pdev->channelID); + + pdev->status |= URBDRC_DEVICE_CHANNEL_CLOSED; + + if (channel) + channel->Write(channel, 0, NULL, NULL); + + urbdrc->udevman->unregister_udevice(urbdrc->udevman, busNr, devNr); + } +} + +static void libusb_udev_set_already_send(IUDEVICE* idev) +{ + UDEVICE* pdev = (UDEVICE*)idev; + pdev->status |= URBDRC_DEVICE_ALREADY_SEND; +} + +static char* libusb_udev_get_path(IUDEVICE* idev) +{ + UDEVICE* pdev = (UDEVICE*)idev; + return pdev->path; +} + +static int libusb_udev_query_device_port_status(IUDEVICE* idev, UINT32* UsbdStatus, + UINT32* BufferSize, BYTE* Buffer) +{ + UDEVICE* pdev = (UDEVICE*)idev; + int success = 0; + int ret = 0; + URBDRC_PLUGIN* urbdrc = NULL; + + if (!pdev || !pdev->urbdrc) + return -1; + + urbdrc = pdev->urbdrc; + + if (pdev->hub_handle != NULL) + { + ret = idev->control_transfer( + idev, 0xffff, 0, 0, + LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_OTHER, + LIBUSB_REQUEST_GET_STATUS, 0, pdev->port_number, UsbdStatus, BufferSize, Buffer, 1000); + + if (log_libusb_result(urbdrc->log, WLOG_DEBUG, "libusb_control_transfer", ret)) + *BufferSize = 0; + else + { + WLog_Print(urbdrc->log, WLOG_DEBUG, + "PORT STATUS:0x%02" PRIx8 "%02" PRIx8 "%02" PRIx8 "%02" PRIx8 "", Buffer[3], + Buffer[2], Buffer[1], Buffer[0]); + success = 1; + } + } + + return success; +} + +static int libusb_udev_isoch_transfer(IUDEVICE* idev, GENERIC_CHANNEL_CALLBACK* callback, + UINT32 MessageId, UINT32 RequestId, UINT32 EndpointAddress, + UINT32 TransferFlags, UINT32 StartFrame, UINT32 ErrorCount, + BOOL NoAck, const BYTE* packetDescriptorData, + UINT32 NumberOfPackets, UINT32 BufferSize, const BYTE* Buffer, + t_isoch_transfer_cb cb, UINT32 Timeout) +{ + int rc = 0; + UINT32 iso_packet_size = 0; + UDEVICE* pdev = (UDEVICE*)idev; + ASYNC_TRANSFER_USER_DATA* user_data = NULL; + struct libusb_transfer* iso_transfer = NULL; + URBDRC_PLUGIN* urbdrc = NULL; + size_t outSize = (NumberOfPackets * 12); + uint32_t streamID = 0x40000000 | RequestId; + + if (!pdev || !pdev->urbdrc) + return -1; + + urbdrc = pdev->urbdrc; + user_data = async_transfer_user_data_new(idev, MessageId, 48, BufferSize, Buffer, + outSize + 1024, NoAck, cb, callback); + + if (!user_data) + return -1; + + user_data->ErrorCount = ErrorCount; + user_data->StartFrame = StartFrame; + + if (!Buffer) + Stream_Seek(user_data->data, (NumberOfPackets * 12)); + + if (NumberOfPackets > 0) + { + iso_packet_size = BufferSize / NumberOfPackets; + iso_transfer = libusb_alloc_transfer((int)NumberOfPackets); + } + + if (iso_transfer == NULL) + { + WLog_Print(urbdrc->log, WLOG_ERROR, + "Error: libusb_alloc_transfer [NumberOfPackets=%" PRIu32 ", BufferSize=%" PRIu32 + " ]", + NumberOfPackets, BufferSize); + async_transfer_user_data_free(user_data); + return -1; + } + + /** process URB_FUNCTION_IOSCH_TRANSFER */ + libusb_fill_iso_transfer(iso_transfer, pdev->libusb_handle, EndpointAddress, + Stream_Pointer(user_data->data), BufferSize, NumberOfPackets, + func_iso_callback, user_data, Timeout); + set_stream_id_for_buffer(iso_transfer, streamID); + libusb_set_iso_packet_lengths(iso_transfer, iso_packet_size); + + if (!ArrayList_Append(pdev->request_queue, iso_transfer)) + { + WLog_Print(urbdrc->log, WLOG_WARN, + "Failed to queue iso transfer, streamID %08" PRIx32 " already in use!", + streamID); + request_free(iso_transfer); + return -1; + } + rc = libusb_submit_transfer(iso_transfer); + if (log_libusb_result(urbdrc->log, WLOG_ERROR, "libusb_submit_transfer", rc)) + return -1; + return rc; +} + +static BOOL libusb_udev_control_transfer(IUDEVICE* idev, UINT32 RequestId, UINT32 EndpointAddress, + UINT32 TransferFlags, BYTE bmRequestType, BYTE Request, + UINT16 Value, UINT16 Index, UINT32* UrbdStatus, + UINT32* BufferSize, BYTE* Buffer, UINT32 Timeout) +{ + int status = 0; + UDEVICE* pdev = (UDEVICE*)idev; + + WINPR_ASSERT(BufferSize); + WINPR_ASSERT(*BufferSize <= UINT16_MAX); + + if (!pdev || !pdev->urbdrc) + return FALSE; + + status = libusb_control_transfer(pdev->libusb_handle, bmRequestType, Request, Value, Index, + Buffer, (UINT16)*BufferSize, Timeout); + + if (status >= 0) + *BufferSize = (UINT32)status; + else + log_libusb_result(pdev->urbdrc->log, WLOG_ERROR, "libusb_control_transfer", status); + + if (!func_set_usbd_status(pdev->urbdrc, pdev, UrbdStatus, status)) + return FALSE; + + return TRUE; +} + +static int libusb_udev_bulk_or_interrupt_transfer(IUDEVICE* idev, + GENERIC_CHANNEL_CALLBACK* callback, + UINT32 MessageId, UINT32 RequestId, + UINT32 EndpointAddress, UINT32 TransferFlags, + BOOL NoAck, UINT32 BufferSize, const BYTE* data, + t_isoch_transfer_cb cb, UINT32 Timeout) +{ + int rc = 0; + UINT32 transfer_type = 0; + UDEVICE* pdev = (UDEVICE*)idev; + const LIBUSB_ENDPOINT_DESCEIPTOR* ep_desc = NULL; + struct libusb_transfer* transfer = NULL; + URBDRC_PLUGIN* urbdrc = NULL; + ASYNC_TRANSFER_USER_DATA* user_data = NULL; + uint32_t streamID = 0x80000000 | RequestId; + + if (!pdev || !pdev->LibusbConfig || !pdev->urbdrc) + return -1; + + urbdrc = pdev->urbdrc; + user_data = + async_transfer_user_data_new(idev, MessageId, 36, BufferSize, data, 0, NoAck, cb, callback); + + if (!user_data) + return -1; + + /* alloc memory for urb transfer */ + transfer = libusb_alloc_transfer(0); + if (!transfer) + { + async_transfer_user_data_free(user_data); + return -1; + } + transfer->user_data = user_data; + + ep_desc = func_get_ep_desc(pdev->LibusbConfig, pdev->MsConfig, EndpointAddress); + + if (!ep_desc) + { + WLog_Print(urbdrc->log, WLOG_ERROR, "func_get_ep_desc: endpoint 0x%" PRIx32 " not found", + EndpointAddress); + request_free(transfer); + return -1; + } + + transfer_type = (ep_desc->bmAttributes) & 0x3; + WLog_Print(urbdrc->log, WLOG_DEBUG, + "urb_bulk_or_interrupt_transfer: ep:0x%" PRIx32 " " + "transfer_type %" PRIu32 " flag:%" PRIu32 " OutputBufferSize:0x%" PRIx32 "", + EndpointAddress, transfer_type, TransferFlags, BufferSize); + + switch (transfer_type) + { + case BULK_TRANSFER: + /** Bulk Transfer */ + libusb_fill_bulk_transfer(transfer, pdev->libusb_handle, EndpointAddress, + Stream_Pointer(user_data->data), BufferSize, + func_bulk_transfer_cb, user_data, Timeout); + break; + + case INTERRUPT_TRANSFER: + /** Interrupt Transfer */ + libusb_fill_interrupt_transfer(transfer, pdev->libusb_handle, EndpointAddress, + Stream_Pointer(user_data->data), BufferSize, + func_bulk_transfer_cb, user_data, Timeout); + break; + + default: + WLog_Print(urbdrc->log, WLOG_DEBUG, + "urb_bulk_or_interrupt_transfer:" + " other transfer type 0x%" PRIX32 "", + transfer_type); + request_free(transfer); + return -1; + } + + set_stream_id_for_buffer(transfer, streamID); + + if (!ArrayList_Append(pdev->request_queue, transfer)) + { + WLog_Print(urbdrc->log, WLOG_WARN, + "Failed to queue transfer, streamID %08" PRIx32 " already in use!", streamID); + request_free(transfer); + return -1; + } + rc = libusb_submit_transfer(transfer); + if (log_libusb_result(urbdrc->log, WLOG_ERROR, "libusb_submit_transfer", rc)) + return -1; + return rc; +} + +static int func_cancel_xact_request(URBDRC_PLUGIN* urbdrc, struct libusb_transfer* transfer) +{ + int status = 0; + + if (!urbdrc || !transfer) + return -1; + + status = libusb_cancel_transfer(transfer); + + if (log_libusb_result(urbdrc->log, WLOG_WARN, "libusb_cancel_transfer", status)) + { + if (status == LIBUSB_ERROR_NOT_FOUND) + return -1; + } + else + return 1; + + return 0; +} + +static void libusb_udev_cancel_all_transfer_request(IUDEVICE* idev) +{ + UDEVICE* pdev = (UDEVICE*)idev; + size_t count = 0; + + if (!pdev || !pdev->request_queue || !pdev->urbdrc) + return; + + ArrayList_Lock(pdev->request_queue); + count = ArrayList_Count(pdev->request_queue); + + for (size_t x = 0; x < count; x++) + { + struct libusb_transfer* transfer = ArrayList_GetItem(pdev->request_queue, x); + func_cancel_xact_request(pdev->urbdrc, transfer); + } + + ArrayList_Unlock(pdev->request_queue); +} + +static int libusb_udev_cancel_transfer_request(IUDEVICE* idev, UINT32 RequestId) +{ + int rc = -1; + UDEVICE* pdev = (UDEVICE*)idev; + struct libusb_transfer* transfer = NULL; + uint32_t cancelID1 = 0x40000000 | RequestId; + uint32_t cancelID2 = 0x80000000 | RequestId; + + if (!idev || !pdev->urbdrc || !pdev->request_queue) + return -1; + + ArrayList_Lock(pdev->request_queue); + transfer = list_contains(pdev->request_queue, cancelID1); + if (!transfer) + transfer = list_contains(pdev->request_queue, cancelID2); + + if (transfer) + { + URBDRC_PLUGIN* urbdrc = (URBDRC_PLUGIN*)pdev->urbdrc; + + rc = func_cancel_xact_request(urbdrc, transfer); + } + ArrayList_Unlock(pdev->request_queue); + return rc; +} + +BASIC_STATE_FUNC_DEFINED(channelManager, IWTSVirtualChannelManager*) +BASIC_STATE_FUNC_DEFINED(channelID, UINT32) +BASIC_STATE_FUNC_DEFINED(ReqCompletion, UINT32) +BASIC_STATE_FUNC_DEFINED(bus_number, BYTE) +BASIC_STATE_FUNC_DEFINED(dev_number, BYTE) +BASIC_STATE_FUNC_DEFINED(port_number, int) +BASIC_STATE_FUNC_DEFINED(MsConfig, MSUSB_CONFIG_DESCRIPTOR*) + +BASIC_POINT_FUNC_DEFINED(udev, void*) +BASIC_POINT_FUNC_DEFINED(prev, void*) +BASIC_POINT_FUNC_DEFINED(next, void*) + +static UINT32 udev_get_UsbDevice(IUDEVICE* idev) +{ + UDEVICE* pdev = (UDEVICE*)idev; + + if (!pdev) + return 0; + + return pdev->UsbDevice; +} + +static void udev_set_UsbDevice(IUDEVICE* idev, UINT32 val) +{ + UDEVICE* pdev = (UDEVICE*)idev; + + if (!pdev) + return; + + pdev->UsbDevice = val; +} + +static void udev_free(IUDEVICE* idev) +{ + int rc = 0; + UDEVICE* udev = (UDEVICE*)idev; + URBDRC_PLUGIN* urbdrc = NULL; + + if (!idev || !udev->urbdrc) + return; + + urbdrc = udev->urbdrc; + + libusb_udev_cancel_all_transfer_request(&udev->iface); + if (udev->libusb_handle) + { + rc = libusb_reset_device(udev->libusb_handle); + + log_libusb_result(urbdrc->log, WLOG_ERROR, "libusb_reset_device", rc); + } + + /* HACK: We need to wait until the cancel transfer has been processed by + * poll_libusb_events + */ + Sleep(100); + + /* release all interface and attach kernel driver */ + udev->iface.attach_kernel_driver(idev); + ArrayList_Free(udev->request_queue); + /* free the config descriptor that send from windows */ + msusb_msconfig_free(udev->MsConfig); + libusb_unref_device(udev->libusb_dev); + libusb_close(udev->libusb_handle); + libusb_close(udev->hub_handle); + free(udev->devDescriptor); + free(idev); +} + +static void udev_load_interface(UDEVICE* pdev) +{ + WINPR_ASSERT(pdev); + + /* load interface */ + /* Basic */ + BASIC_STATE_FUNC_REGISTER(channelManager, pdev); + BASIC_STATE_FUNC_REGISTER(channelID, pdev); + BASIC_STATE_FUNC_REGISTER(UsbDevice, pdev); + BASIC_STATE_FUNC_REGISTER(ReqCompletion, pdev); + BASIC_STATE_FUNC_REGISTER(bus_number, pdev); + BASIC_STATE_FUNC_REGISTER(dev_number, pdev); + BASIC_STATE_FUNC_REGISTER(port_number, pdev); + BASIC_STATE_FUNC_REGISTER(MsConfig, pdev); + BASIC_STATE_FUNC_REGISTER(p_udev, pdev); + BASIC_STATE_FUNC_REGISTER(p_prev, pdev); + BASIC_STATE_FUNC_REGISTER(p_next, pdev); + pdev->iface.isCompositeDevice = libusb_udev_is_composite_device; + pdev->iface.isExist = libusb_udev_is_exist; + pdev->iface.isAlreadySend = libusb_udev_is_already_send; + pdev->iface.isChannelClosed = libusb_udev_is_channel_closed; + pdev->iface.setAlreadySend = libusb_udev_set_already_send; + pdev->iface.setChannelClosed = libusb_udev_channel_closed; + pdev->iface.markChannelClosed = libusb_udev_mark_channel_closed; + pdev->iface.getPath = libusb_udev_get_path; + /* Transfer */ + pdev->iface.isoch_transfer = libusb_udev_isoch_transfer; + pdev->iface.control_transfer = libusb_udev_control_transfer; + pdev->iface.bulk_or_interrupt_transfer = libusb_udev_bulk_or_interrupt_transfer; + pdev->iface.select_interface = libusb_udev_select_interface; + pdev->iface.select_configuration = libusb_udev_select_configuration; + pdev->iface.complete_msconfig_setup = libusb_udev_complete_msconfig_setup; + pdev->iface.control_pipe_request = libusb_udev_control_pipe_request; + pdev->iface.control_query_device_text = libusb_udev_control_query_device_text; + pdev->iface.os_feature_descriptor_request = libusb_udev_os_feature_descriptor_request; + pdev->iface.cancel_all_transfer_request = libusb_udev_cancel_all_transfer_request; + pdev->iface.cancel_transfer_request = libusb_udev_cancel_transfer_request; + pdev->iface.query_device_descriptor = libusb_udev_query_device_descriptor; + pdev->iface.detach_kernel_driver = libusb_udev_detach_kernel_driver; + pdev->iface.attach_kernel_driver = libusb_udev_attach_kernel_driver; + pdev->iface.query_device_port_status = libusb_udev_query_device_port_status; + pdev->iface.free = udev_free; +} + +static int udev_get_device_handle(URBDRC_PLUGIN* urbdrc, libusb_context* ctx, UDEVICE* pdev, + UINT16 bus_number, UINT16 dev_number) +{ + int error = -1; + uint8_t port_numbers[16] = { 0 }; + LIBUSB_DEVICE** libusb_list = NULL; + const ssize_t total_device = libusb_get_device_list(ctx, &libusb_list); + + WINPR_ASSERT(urbdrc); + + /* Look for device. */ + for (ssize_t i = 0; i < total_device; i++) + { + LIBUSB_DEVICE* dev = libusb_list[i]; + + if ((bus_number != libusb_get_bus_number(dev)) || + (dev_number != libusb_get_device_address(dev))) + libusb_unref_device(dev); + else + { + error = libusb_open(dev, &pdev->libusb_handle); + + if (log_libusb_result(urbdrc->log, WLOG_ERROR, "libusb_open", error)) + { + libusb_unref_device(dev); + continue; + } + + /* get port number */ + error = libusb_get_port_numbers(dev, port_numbers, sizeof(port_numbers)); + if (error < 1) + { + /* Prevent open hub, treat as error. */ + log_libusb_result(urbdrc->log, WLOG_ERROR, "libusb_get_port_numbers", error); + libusb_unref_device(dev); + continue; + } + + pdev->port_number = port_numbers[(error - 1)]; + error = 0; + WLog_Print(urbdrc->log, WLOG_DEBUG, " Port: %d", pdev->port_number); + /* gen device path */ + sprintf(pdev->path, "%" PRIu16 "-%d", bus_number, pdev->port_number); + + WLog_Print(urbdrc->log, WLOG_DEBUG, " DevPath: %s", pdev->path); + } + } + libusb_free_device_list(libusb_list, 0); + + if (error < 0) + return -1; + return 0; +} + +static int udev_get_hub_handle(URBDRC_PLUGIN* urbdrc, libusb_context* ctx, UDEVICE* pdev, + UINT16 bus_number, UINT16 dev_number) +{ + int error = -1; + LIBUSB_DEVICE** libusb_list = NULL; + LIBUSB_DEVICE_HANDLE* handle = NULL; + const ssize_t total_device = libusb_get_device_list(ctx, &libusb_list); + + WINPR_ASSERT(urbdrc); + + /* Look for device hub. */ + for (ssize_t i = 0; i < total_device; i++) + { + LIBUSB_DEVICE* dev = libusb_list[i]; + + if ((bus_number != libusb_get_bus_number(dev)) || + (1 != libusb_get_device_address(dev))) /* Root hub allways first on bus. */ + libusb_unref_device(dev); + else + { + WLog_Print(urbdrc->log, WLOG_DEBUG, " Open hub: %" PRIu16 "", bus_number); + error = libusb_open(dev, &handle); + + if (!log_libusb_result(urbdrc->log, WLOG_ERROR, "libusb_open", error)) + pdev->hub_handle = handle; + else + libusb_unref_device(dev); + } + } + + libusb_free_device_list(libusb_list, 0); + + if (error < 0) + return -1; + + return 0; +} + +static void request_free(void* value) +{ + ASYNC_TRANSFER_USER_DATA* user_data = NULL; + struct libusb_transfer* transfer = (struct libusb_transfer*)value; + if (!transfer) + return; + + user_data = (ASYNC_TRANSFER_USER_DATA*)transfer->user_data; + async_transfer_user_data_free(user_data); + transfer->user_data = NULL; + libusb_free_transfer(transfer); +} + +static IUDEVICE* udev_init(URBDRC_PLUGIN* urbdrc, libusb_context* context, LIBUSB_DEVICE* device, + BYTE bus_number, BYTE dev_number) +{ + UDEVICE* pdev = NULL; + int status = LIBUSB_ERROR_OTHER; + LIBUSB_DEVICE_DESCRIPTOR* devDescriptor = NULL; + LIBUSB_CONFIG_DESCRIPTOR* config_temp = NULL; + LIBUSB_INTERFACE_DESCRIPTOR interface_temp; + + WINPR_ASSERT(urbdrc); + + pdev = (PUDEVICE)calloc(1, sizeof(UDEVICE)); + + if (!pdev) + return NULL; + + pdev->urbdrc = urbdrc; + udev_load_interface(pdev); + + if (device) + pdev->libusb_dev = device; + else + pdev->libusb_dev = udev_get_libusb_dev(context, bus_number, dev_number); + + if (pdev->libusb_dev == NULL) + goto fail; + + if (urbdrc->listener_callback) + udev_set_channelManager(&pdev->iface, urbdrc->listener_callback->channel_mgr); + + /* Get DEVICE handle */ + status = udev_get_device_handle(urbdrc, context, pdev, bus_number, dev_number); + if (status != LIBUSB_SUCCESS) + { + struct libusb_device_descriptor desc; + const uint8_t port = libusb_get_port_number(pdev->libusb_dev); + libusb_get_device_descriptor(pdev->libusb_dev, &desc); + + log_libusb_result(urbdrc->log, WLOG_ERROR, + "libusb_open [b=0x%02X,p=0x%02X,a=0x%02X,VID=0x%04X,PID=0x%04X]", status, + bus_number, port, dev_number, desc.idVendor, desc.idProduct); + goto fail; + } + + /* Get HUB handle */ + status = udev_get_hub_handle(urbdrc, context, pdev, bus_number, dev_number); + + if (status < 0) + pdev->hub_handle = NULL; + + pdev->devDescriptor = udev_new_descript(urbdrc, pdev->libusb_dev); + + if (!pdev->devDescriptor) + goto fail; + + status = libusb_get_active_config_descriptor(pdev->libusb_dev, &pdev->LibusbConfig); + + if (status == LIBUSB_ERROR_NOT_FOUND) + status = libusb_get_config_descriptor(pdev->libusb_dev, 0, &pdev->LibusbConfig); + + if (status < 0) + goto fail; + + config_temp = pdev->LibusbConfig; + /* get the first interface and first altsetting */ + interface_temp = config_temp->interface[0].altsetting[0]; + WLog_Print(urbdrc->log, WLOG_DEBUG, + "Registered Device: Vid: 0x%04" PRIX16 " Pid: 0x%04" PRIX16 "" + " InterfaceClass = %s", + pdev->devDescriptor->idVendor, pdev->devDescriptor->idProduct, + usb_interface_class_to_string(interface_temp.bInterfaceClass)); + /* Check composite device */ + devDescriptor = pdev->devDescriptor; + + if ((devDescriptor->bNumConfigurations == 1) && (config_temp->bNumInterfaces > 1) && + (devDescriptor->bDeviceClass == LIBUSB_CLASS_PER_INTERFACE)) + { + pdev->isCompositeDevice = 1; + } + else if ((devDescriptor->bDeviceClass == 0xef) && + (devDescriptor->bDeviceSubClass == LIBUSB_CLASS_COMM) && + (devDescriptor->bDeviceProtocol == 0x01)) + { + pdev->isCompositeDevice = 1; + } + else + pdev->isCompositeDevice = 0; + + /* set device class to first interface class */ + devDescriptor->bDeviceClass = interface_temp.bInterfaceClass; + devDescriptor->bDeviceSubClass = interface_temp.bInterfaceSubClass; + devDescriptor->bDeviceProtocol = interface_temp.bInterfaceProtocol; + /* initialize pdev */ + pdev->bus_number = bus_number; + pdev->dev_number = dev_number; + pdev->request_queue = ArrayList_New(TRUE); + + if (!pdev->request_queue) + goto fail; + + ArrayList_Object(pdev->request_queue)->fnObjectFree = request_free; + + /* set config of windows */ + pdev->MsConfig = msusb_msconfig_new(); + + if (!pdev->MsConfig) + goto fail; + + // deb_config_msg(pdev->libusb_dev, config_temp, devDescriptor->bNumConfigurations); + return &pdev->iface; +fail: + pdev->iface.free(&pdev->iface); + return NULL; +} + +size_t udev_new_by_id(URBDRC_PLUGIN* urbdrc, libusb_context* ctx, UINT16 idVendor, UINT16 idProduct, + IUDEVICE*** devArray) +{ + LIBUSB_DEVICE** libusb_list = NULL; + UDEVICE** array = NULL; + ssize_t total_device = 0; + size_t num = 0; + + if (!urbdrc || !devArray) + return 0; + + WLog_Print(urbdrc->log, WLOG_INFO, "VID: 0x%04" PRIX16 ", PID: 0x%04" PRIX16 "", idVendor, + idProduct); + total_device = libusb_get_device_list(ctx, &libusb_list); + if (total_device < 0) + return 0; + + array = (UDEVICE**)calloc((size_t)total_device, sizeof(UDEVICE*)); + + if (!array) + goto fail; + + for (ssize_t i = 0; i < total_device; i++) + { + LIBUSB_DEVICE* dev = libusb_list[i]; + LIBUSB_DEVICE_DESCRIPTOR* descriptor = udev_new_descript(urbdrc, dev); + + if ((descriptor->idVendor == idVendor) && (descriptor->idProduct == idProduct)) + { + array[num] = (PUDEVICE)udev_init(urbdrc, ctx, dev, libusb_get_bus_number(dev), + libusb_get_device_address(dev)); + + if (array[num] != NULL) + num++; + } + else + libusb_unref_device(dev); + + free(descriptor); + } + +fail: + libusb_free_device_list(libusb_list, 0); + *devArray = (IUDEVICE**)array; + return num; +} + +IUDEVICE* udev_new_by_addr(URBDRC_PLUGIN* urbdrc, libusb_context* context, BYTE bus_number, + BYTE dev_number) +{ + WLog_Print(urbdrc->log, WLOG_DEBUG, "bus:%d dev:%d", bus_number, dev_number); + return udev_init(urbdrc, context, NULL, bus_number, dev_number); +} diff --git a/channels/urbdrc/client/libusb/libusb_udevice.h b/channels/urbdrc/client/libusb/libusb_udevice.h new file mode 100644 index 0000000..33705e3 --- /dev/null +++ b/channels/urbdrc/client/libusb/libusb_udevice.h @@ -0,0 +1,76 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * RemoteFX USB Redirection + * + * Copyright 2012 Atrust corp. + * Copyright 2012 Alfred Liu <alfred.liu@atruscorp.com> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FREERDP_CHANNEL_URBDRC_CLIENT_LIBUSB_UDEVICE_H +#define FREERDP_CHANNEL_URBDRC_CLIENT_LIBUSB_UDEVICE_H + +#include <winpr/windows.h> +#include <libusb.h> + +#include "urbdrc_types.h" +#include "urbdrc_main.h" + +typedef struct libusb_device LIBUSB_DEVICE; +typedef struct libusb_device_handle LIBUSB_DEVICE_HANDLE; +typedef struct libusb_device_descriptor LIBUSB_DEVICE_DESCRIPTOR; +typedef struct libusb_config_descriptor LIBUSB_CONFIG_DESCRIPTOR; +typedef struct libusb_interface LIBUSB_INTERFACE; +typedef struct libusb_interface_descriptor LIBUSB_INTERFACE_DESCRIPTOR; +typedef struct libusb_endpoint_descriptor LIBUSB_ENDPOINT_DESCEIPTOR; + +typedef struct +{ + IUDEVICE iface; + + void* udev; + void* prev; + void* next; + + UINT32 UsbDevice; /* An unique interface ID */ + UINT32 ReqCompletion; /* An unique interface ID */ + IWTSVirtualChannelManager* channelManager; + UINT32 channelID; + UINT16 status; + BYTE bus_number; + BYTE dev_number; + char path[17]; + int port_number; + int isCompositeDevice; + + LIBUSB_DEVICE_HANDLE* libusb_handle; + LIBUSB_DEVICE_HANDLE* hub_handle; + LIBUSB_DEVICE* libusb_dev; + LIBUSB_DEVICE_DESCRIPTOR* devDescriptor; + MSUSB_CONFIG_DESCRIPTOR* MsConfig; + LIBUSB_CONFIG_DESCRIPTOR* LibusbConfig; + + wArrayList* request_queue; + + URBDRC_PLUGIN* urbdrc; +} UDEVICE; +typedef UDEVICE* PUDEVICE; + +size_t udev_new_by_id(URBDRC_PLUGIN* urbdrc, libusb_context* ctx, UINT16 idVendor, UINT16 idProduct, + IUDEVICE*** devArray); +IUDEVICE* udev_new_by_addr(URBDRC_PLUGIN* urbdrc, libusb_context* ctx, BYTE bus_number, + BYTE dev_number); +const char* usb_interface_class_to_string(uint8_t class); + +#endif /* FREERDP_CHANNEL_URBDRC_CLIENT_LIBUSB_UDEVICE_H */ diff --git a/channels/urbdrc/client/libusb/libusb_udevman.c b/channels/urbdrc/client/libusb/libusb_udevman.c new file mode 100644 index 0000000..d52c307 --- /dev/null +++ b/channels/urbdrc/client/libusb/libusb_udevman.c @@ -0,0 +1,970 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * RemoteFX USB Redirection + * + * Copyright 2012 Atrust corp. + * Copyright 2012 Alfred Liu <alfred.liu@atruscorp.com> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> + +#include <winpr/crt.h> +#include <winpr/cmdline.h> +#include <winpr/collections.h> + +#include <freerdp/addin.h> + +#include "urbdrc_types.h" +#include "urbdrc_main.h" + +#include "libusb_udevice.h" + +#include <libusb.h> + +#if !defined(LIBUSB_HOTPLUG_NO_FLAGS) +#define LIBUSB_HOTPLUG_NO_FLAGS 0 +#endif + +#define BASIC_STATE_FUNC_DEFINED(_arg, _type) \ + static _type udevman_get_##_arg(IUDEVMAN* idevman) \ + { \ + UDEVMAN* udevman = (UDEVMAN*)idevman; \ + return udevman->_arg; \ + } \ + static void udevman_set_##_arg(IUDEVMAN* idevman, _type _t) \ + { \ + UDEVMAN* udevman = (UDEVMAN*)idevman; \ + udevman->_arg = _t; \ + } + +#define BASIC_STATE_FUNC_REGISTER(_arg, _man) \ + _man->iface.get_##_arg = udevman_get_##_arg; \ + _man->iface.set_##_arg = udevman_set_##_arg + +typedef struct +{ + UINT16 vid; + UINT16 pid; +} VID_PID_PAIR; + +typedef struct +{ + IUDEVMAN iface; + + IUDEVICE* idev; /* iterator device */ + IUDEVICE* head; /* head device in linked list */ + IUDEVICE* tail; /* tail device in linked list */ + + LPCSTR devices_vid_pid; + LPCSTR devices_addr; + wArrayList* hotplug_vid_pids; + UINT16 flags; + UINT32 device_num; + UINT32 next_device_id; + UINT32 channel_id; + + HANDLE devman_loading; + libusb_context* context; + HANDLE thread; + BOOL running; +} UDEVMAN; +typedef UDEVMAN* PUDEVMAN; + +static BOOL poll_libusb_events(UDEVMAN* udevman); + +static void udevman_rewind(IUDEVMAN* idevman) +{ + UDEVMAN* udevman = (UDEVMAN*)idevman; + udevman->idev = udevman->head; +} + +static BOOL udevman_has_next(IUDEVMAN* idevman) +{ + UDEVMAN* udevman = (UDEVMAN*)idevman; + + if (!udevman || !udevman->idev) + return FALSE; + else + return TRUE; +} + +static IUDEVICE* udevman_get_next(IUDEVMAN* idevman) +{ + UDEVMAN* udevman = (UDEVMAN*)idevman; + IUDEVICE* pdev = NULL; + pdev = udevman->idev; + udevman->idev = (IUDEVICE*)((UDEVICE*)udevman->idev)->next; + return pdev; +} + +static IUDEVICE* udevman_get_udevice_by_addr(IUDEVMAN* idevman, BYTE bus_number, BYTE dev_number) +{ + IUDEVICE* dev = NULL; + + if (!idevman) + return NULL; + + idevman->loading_lock(idevman); + idevman->rewind(idevman); + + while (idevman->has_next(idevman)) + { + IUDEVICE* pdev = idevman->get_next(idevman); + + if ((pdev->get_bus_number(pdev) == bus_number) && + (pdev->get_dev_number(pdev) == dev_number)) + { + dev = pdev; + break; + } + } + + idevman->loading_unlock(idevman); + return dev; +} + +static size_t udevman_register_udevice(IUDEVMAN* idevman, BYTE bus_number, BYTE dev_number, + UINT16 idVendor, UINT16 idProduct, UINT32 flag) +{ + UDEVMAN* udevman = (UDEVMAN*)idevman; + IUDEVICE* pdev = NULL; + IUDEVICE** devArray = NULL; + URBDRC_PLUGIN* urbdrc = NULL; + size_t num = 0; + size_t addnum = 0; + + if (!idevman || !idevman->plugin) + return 0; + + urbdrc = (URBDRC_PLUGIN*)idevman->plugin; + pdev = (IUDEVICE*)udevman_get_udevice_by_addr(idevman, bus_number, dev_number); + + if (pdev != NULL) + return 0; + + if (flag & UDEVMAN_FLAG_ADD_BY_ADDR) + { + UINT32 id = 0; + IUDEVICE* tdev = udev_new_by_addr(urbdrc, udevman->context, bus_number, dev_number); + + if (tdev == NULL) + return 0; + + id = idevman->get_next_device_id(idevman); + tdev->set_UsbDevice(tdev, id); + idevman->loading_lock(idevman); + + if (udevman->head == NULL) + { + /* linked list is empty */ + udevman->head = tdev; + udevman->tail = tdev; + } + else + { + /* append device to the end of the linked list */ + udevman->tail->set_p_next(udevman->tail, tdev); + tdev->set_p_prev(tdev, udevman->tail); + udevman->tail = tdev; + } + + udevman->device_num += 1; + idevman->loading_unlock(idevman); + } + else if (flag & UDEVMAN_FLAG_ADD_BY_VID_PID) + { + addnum = 0; + /* register all device that match pid vid */ + num = udev_new_by_id(urbdrc, udevman->context, idVendor, idProduct, &devArray); + + if (num == 0) + { + WLog_Print(urbdrc->log, WLOG_WARN, + "Could not find or redirect any usb devices by id %04x:%04x", idVendor, + idProduct); + } + + for (size_t i = 0; i < num; i++) + { + UINT32 id = 0; + IUDEVICE* tdev = devArray[i]; + + if (udevman_get_udevice_by_addr(idevman, tdev->get_bus_number(tdev), + tdev->get_dev_number(tdev)) != NULL) + { + tdev->free(tdev); + devArray[i] = NULL; + continue; + } + + id = idevman->get_next_device_id(idevman); + tdev->set_UsbDevice(tdev, id); + idevman->loading_lock(idevman); + + if (udevman->head == NULL) + { + /* linked list is empty */ + udevman->head = tdev; + udevman->tail = tdev; + } + else + { + /* append device to the end of the linked list */ + udevman->tail->set_p_next(udevman->tail, tdev); + tdev->set_p_prev(tdev, udevman->tail); + udevman->tail = tdev; + } + + udevman->device_num += 1; + idevman->loading_unlock(idevman); + addnum++; + } + + free(devArray); + return addnum; + } + else + { + WLog_Print(urbdrc->log, WLOG_ERROR, "udevman_register_udevice: Invalid flag=%08" PRIx32, + flag); + return 0; + } + + return 1; +} + +static BOOL udevman_unregister_udevice(IUDEVMAN* idevman, BYTE bus_number, BYTE dev_number) +{ + UDEVMAN* udevman = (UDEVMAN*)idevman; + UDEVICE* pdev = NULL; + UDEVICE* dev = (UDEVICE*)udevman_get_udevice_by_addr(idevman, bus_number, dev_number); + + if (!dev || !idevman) + return FALSE; + + idevman->loading_lock(idevman); + idevman->rewind(idevman); + + while (idevman->has_next(idevman)) + { + pdev = (UDEVICE*)idevman->get_next(idevman); + + if (pdev == dev) /* device exists */ + { + /* set previous device to point to next device */ + if (dev->prev != NULL) + { + /* unregistered device is not the head */ + pdev = dev->prev; + pdev->next = dev->next; + } + else + { + /* unregistered device is the head, update head */ + udevman->head = (IUDEVICE*)dev->next; + } + + /* set next device to point to previous device */ + + if (dev->next != NULL) + { + /* unregistered device is not the tail */ + pdev = (UDEVICE*)dev->next; + pdev->prev = dev->prev; + } + else + { + /* unregistered device is the tail, update tail */ + udevman->tail = (IUDEVICE*)dev->prev; + } + + udevman->device_num--; + break; + } + } + + idevman->loading_unlock(idevman); + + if (dev) + { + dev->iface.free(&dev->iface); + return TRUE; /* unregistration successful */ + } + + /* if we reach this point, the device wasn't found */ + return FALSE; +} + +static BOOL udevman_unregister_all_udevices(IUDEVMAN* idevman) +{ + UDEVMAN* udevman = (UDEVMAN*)idevman; + + if (!idevman) + return FALSE; + + if (!udevman->head) + return TRUE; + + idevman->loading_lock(idevman); + idevman->rewind(idevman); + + while (idevman->has_next(idevman)) + { + UDEVICE* dev = (UDEVICE*)idevman->get_next(idevman); + + if (!dev) + continue; + + /* set previous device to point to next device */ + if (dev->prev != NULL) + { + /* unregistered device is not the head */ + UDEVICE* pdev = dev->prev; + pdev->next = dev->next; + } + else + { + /* unregistered device is the head, update head */ + udevman->head = (IUDEVICE*)dev->next; + } + + /* set next device to point to previous device */ + + if (dev->next != NULL) + { + /* unregistered device is not the tail */ + UDEVICE* pdev = (UDEVICE*)dev->next; + pdev->prev = dev->prev; + } + else + { + /* unregistered device is the tail, update tail */ + udevman->tail = (IUDEVICE*)dev->prev; + } + + dev->iface.free(&dev->iface); + udevman->device_num--; + } + + idevman->loading_unlock(idevman); + + return TRUE; +} + +static int udevman_is_auto_add(IUDEVMAN* idevman) +{ + UDEVMAN* udevman = (UDEVMAN*)idevman; + return (udevman->flags & UDEVMAN_FLAG_ADD_BY_AUTO) ? 1 : 0; +} + +static IUDEVICE* udevman_get_udevice_by_UsbDevice(IUDEVMAN* idevman, UINT32 UsbDevice) +{ + UDEVICE* pdev = NULL; + URBDRC_PLUGIN* urbdrc = NULL; + + if (!idevman || !idevman->plugin) + return NULL; + + /* Mask highest 2 bits, must be ignored */ + UsbDevice = UsbDevice & INTERFACE_ID_MASK; + urbdrc = (URBDRC_PLUGIN*)idevman->plugin; + idevman->loading_lock(idevman); + idevman->rewind(idevman); + + while (idevman->has_next(idevman)) + { + pdev = (UDEVICE*)idevman->get_next(idevman); + + if (pdev->UsbDevice == UsbDevice) + { + idevman->loading_unlock(idevman); + return (IUDEVICE*)pdev; + } + } + + idevman->loading_unlock(idevman); + WLog_Print(urbdrc->log, WLOG_WARN, "Failed to find a USB device mapped to deviceId=%08" PRIx32, + UsbDevice); + return NULL; +} + +static IUDEVICE* udevman_get_udevice_by_ChannelID(IUDEVMAN* idevman, UINT32 channelID) +{ + UDEVICE* pdev = NULL; + URBDRC_PLUGIN* urbdrc = NULL; + + if (!idevman || !idevman->plugin) + return NULL; + + /* Mask highest 2 bits, must be ignored */ + urbdrc = (URBDRC_PLUGIN*)idevman->plugin; + idevman->loading_lock(idevman); + idevman->rewind(idevman); + + while (idevman->has_next(idevman)) + { + pdev = (UDEVICE*)idevman->get_next(idevman); + + if (pdev->channelID == channelID) + { + idevman->loading_unlock(idevman); + return (IUDEVICE*)pdev; + } + } + + idevman->loading_unlock(idevman); + WLog_Print(urbdrc->log, WLOG_WARN, "Failed to find a USB device mapped to channelID=%08" PRIx32, + channelID); + return NULL; +} + +static void udevman_loading_lock(IUDEVMAN* idevman) +{ + UDEVMAN* udevman = (UDEVMAN*)idevman; + WaitForSingleObject(udevman->devman_loading, INFINITE); +} + +static void udevman_loading_unlock(IUDEVMAN* idevman) +{ + UDEVMAN* udevman = (UDEVMAN*)idevman; + ReleaseMutex(udevman->devman_loading); +} + +BASIC_STATE_FUNC_DEFINED(device_num, UINT32) + +static UINT32 udevman_get_next_device_id(IUDEVMAN* idevman) +{ + UDEVMAN* udevman = (UDEVMAN*)idevman; + return udevman->next_device_id++; +} + +static void udevman_set_next_device_id(IUDEVMAN* idevman, UINT32 _t) +{ + UDEVMAN* udevman = (UDEVMAN*)idevman; + udevman->next_device_id = _t; +} + +static void udevman_free(IUDEVMAN* idevman) +{ + UDEVMAN* udevman = (UDEVMAN*)idevman; + + if (!udevman) + return; + + udevman->running = FALSE; + if (udevman->thread) + { + WaitForSingleObject(udevman->thread, INFINITE); + CloseHandle(udevman->thread); + } + + udevman_unregister_all_udevices(idevman); + + if (udevman->devman_loading) + CloseHandle(udevman->devman_loading); + + libusb_exit(udevman->context); + + ArrayList_Free(udevman->hotplug_vid_pids); + free(udevman); +} + +static BOOL filter_by_class(uint8_t bDeviceClass, uint8_t bDeviceSubClass) +{ + switch (bDeviceClass) + { + case LIBUSB_CLASS_AUDIO: + case LIBUSB_CLASS_HID: + case LIBUSB_CLASS_MASS_STORAGE: + case LIBUSB_CLASS_HUB: + case LIBUSB_CLASS_SMART_CARD: + return TRUE; + default: + break; + } + + switch (bDeviceSubClass) + { + default: + break; + } + + return FALSE; +} + +static BOOL append(char* dst, size_t length, const char* src) +{ + return winpr_str_append(src, dst, length, NULL); +} + +static BOOL device_is_filtered(struct libusb_device* dev, + const struct libusb_device_descriptor* desc, + libusb_hotplug_event event) +{ + char buffer[8192] = { 0 }; + char* what = NULL; + BOOL filtered = FALSE; + append(buffer, sizeof(buffer), usb_interface_class_to_string(desc->bDeviceClass)); + if (filter_by_class(desc->bDeviceClass, desc->bDeviceSubClass)) + filtered = TRUE; + + switch (desc->bDeviceClass) + { + case LIBUSB_CLASS_PER_INTERFACE: + { + struct libusb_config_descriptor* config = NULL; + int rc = libusb_get_active_config_descriptor(dev, &config); + if (rc == LIBUSB_SUCCESS) + { + for (uint8_t x = 0; x < config->bNumInterfaces; x++) + { + const struct libusb_interface* ifc = &config->interface[x]; + for (int y = 0; y < ifc->num_altsetting; y++) + { + const struct libusb_interface_descriptor* const alt = &ifc->altsetting[y]; + if (filter_by_class(alt->bInterfaceClass, alt->bInterfaceSubClass)) + filtered = TRUE; + + append(buffer, sizeof(buffer), "|"); + append(buffer, sizeof(buffer), + usb_interface_class_to_string(alt->bInterfaceClass)); + } + } + } + libusb_free_config_descriptor(config); + } + break; + default: + break; + } + + if (filtered) + what = "Filtered"; + else + { + switch (event) + { + case LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT: + what = "Hotplug remove"; + break; + case LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED: + what = "Hotplug add"; + break; + default: + what = "Hotplug unknown"; + break; + } + } + + WLog_DBG(TAG, "%s device VID=0x%04X,PID=0x%04X class %s", what, desc->idVendor, desc->idProduct, + buffer); + return filtered; +} + +static int LIBUSB_CALL hotplug_callback(struct libusb_context* ctx, struct libusb_device* dev, + libusb_hotplug_event event, void* user_data) +{ + VID_PID_PAIR pair; + struct libusb_device_descriptor desc; + UDEVMAN* udevman = (UDEVMAN*)user_data; + const uint8_t bus = libusb_get_bus_number(dev); + const uint8_t addr = libusb_get_device_address(dev); + int rc = libusb_get_device_descriptor(dev, &desc); + + WINPR_UNUSED(ctx); + + if (rc != LIBUSB_SUCCESS) + return rc; + + switch (event) + { + case LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED: + pair.vid = desc.idVendor; + pair.pid = desc.idProduct; + if ((ArrayList_Contains(udevman->hotplug_vid_pids, &pair)) || + (udevman->iface.isAutoAdd(&udevman->iface) && + !device_is_filtered(dev, &desc, event))) + { + add_device(&udevman->iface, DEVICE_ADD_FLAG_ALL, bus, addr, desc.idVendor, + desc.idProduct); + } + break; + + case LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT: + del_device(&udevman->iface, DEVICE_ADD_FLAG_ALL, bus, addr, desc.idVendor, + desc.idProduct); + break; + + default: + break; + } + + return 0; +} + +static BOOL udevman_initialize(IUDEVMAN* idevman, UINT32 channelId) +{ + UDEVMAN* udevman = (UDEVMAN*)idevman; + + if (!udevman) + return FALSE; + + idevman->status &= ~URBDRC_DEVICE_CHANNEL_CLOSED; + idevman->controlChannelId = channelId; + return TRUE; +} + +static BOOL udevman_vid_pid_pair_equals(const void* objA, const void* objB) +{ + const VID_PID_PAIR* a = objA; + const VID_PID_PAIR* b = objB; + + return (a->vid == b->vid) && (a->pid == b->pid); +} + +static BOOL udevman_parse_device_id_addr(const char** str, UINT16* id1, UINT16* id2, UINT16 max, + char split_sign, char delimiter) +{ + char* mid = NULL; + char* end = NULL; + unsigned long rc = 0; + + rc = strtoul(*str, &mid, 16); + + if ((mid == *str) || (*mid != split_sign) || (rc > max)) + return FALSE; + + *id1 = (UINT16)rc; + rc = strtoul(++mid, &end, 16); + + if ((end == mid) || (rc > max)) + return FALSE; + + *id2 = (UINT16)rc; + + *str += end - *str; + if (*end == '\0') + return TRUE; + if (*end == delimiter) + { + (*str)++; + return TRUE; + } + + return FALSE; +} + +static BOOL urbdrc_udevman_register_devices(UDEVMAN* udevman, const char* devices, BOOL add_by_addr) +{ + const char* pos = devices; + VID_PID_PAIR* idpair = NULL; + UINT16 id1 = 0; + UINT16 id2 = 0; + + while (*pos != '\0') + { + if (!udevman_parse_device_id_addr(&pos, &id1, &id2, (add_by_addr) ? UINT8_MAX : UINT16_MAX, + ':', '#')) + { + WLog_ERR(TAG, "Invalid device argument: \"%s\"", devices); + return FALSE; + } + + if (add_by_addr) + { + add_device(&udevman->iface, DEVICE_ADD_FLAG_BUS | DEVICE_ADD_FLAG_DEV, (UINT8)id1, + (UINT8)id2, 0, 0); + } + else + { + idpair = calloc(1, sizeof(VID_PID_PAIR)); + if (!idpair) + return CHANNEL_RC_NO_MEMORY; + idpair->vid = id1; + idpair->pid = id2; + if (!ArrayList_Append(udevman->hotplug_vid_pids, idpair)) + { + free(idpair); + return CHANNEL_RC_NO_MEMORY; + } + + add_device(&udevman->iface, DEVICE_ADD_FLAG_VENDOR | DEVICE_ADD_FLAG_PRODUCT, 0, 0, id1, + id2); + } + } + + // NOLINTNEXTLINE(clang-analyzer-unix.Malloc): ArrayList_Append owns idpair + return CHANNEL_RC_OK; +} + +static UINT urbdrc_udevman_parse_addin_args(UDEVMAN* udevman, const ADDIN_ARGV* args) +{ + LPCSTR devices = NULL; + + for (int x = 0; x < args->argc; x++) + { + const char* arg = args->argv[x]; + if (strcmp(arg, "dbg") == 0) + { + WLog_SetLogLevel(WLog_Get(TAG), WLOG_TRACE); + } + else if (_strnicmp(arg, "device:", 7) == 0) + { + /* Redirect all local devices */ + const char* val = &arg[7]; + const size_t len = strlen(val); + if (strcmp(val, "*") == 0) + { + udevman->flags |= UDEVMAN_FLAG_ADD_BY_AUTO; + } + else if (_strnicmp(arg, "USBInstanceID:", 14) == 0) + { + // TODO: Usb instance ID + } + else if ((val[0] == '{') && (val[len - 1] == '}')) + { + // TODO: Usb device class + } + } + else if (_strnicmp(arg, "dev:", 4) == 0) + { + devices = &arg[4]; + } + else if (_strnicmp(arg, "id", 2) == 0) + { + const char* p = strchr(arg, ':'); + if (p) + udevman->devices_vid_pid = p + 1; + else + udevman->flags = UDEVMAN_FLAG_ADD_BY_VID_PID; + } + else if (_strnicmp(arg, "addr", 4) == 0) + { + const char* p = strchr(arg, ':'); + if (p) + udevman->devices_addr = p + 1; + else + udevman->flags = UDEVMAN_FLAG_ADD_BY_ADDR; + } + else if (strcmp(arg, "auto") == 0) + { + udevman->flags |= UDEVMAN_FLAG_ADD_BY_AUTO; + } + else + { + const size_t len = strlen(arg); + if ((arg[0] == '{') && (arg[len - 1] == '}')) + { + // TODO: Check for {Device Setup Class GUID}: + } + } + } + if (devices) + { + if (udevman->flags & UDEVMAN_FLAG_ADD_BY_VID_PID) + udevman->devices_vid_pid = devices; + else if (udevman->flags & UDEVMAN_FLAG_ADD_BY_ADDR) + udevman->devices_addr = devices; + } + + return CHANNEL_RC_OK; +} + +static UINT udevman_listener_created_callback(IUDEVMAN* iudevman) +{ + UINT status = 0; + UDEVMAN* udevman = (UDEVMAN*)iudevman; + + if (udevman->devices_vid_pid) + { + status = urbdrc_udevman_register_devices(udevman, udevman->devices_vid_pid, FALSE); + if (status != CHANNEL_RC_OK) + return status; + } + + if (udevman->devices_addr) + return urbdrc_udevman_register_devices(udevman, udevman->devices_addr, TRUE); + + return CHANNEL_RC_OK; +} + +static void udevman_load_interface(UDEVMAN* udevman) +{ + /* standard */ + udevman->iface.free = udevman_free; + /* manage devices */ + udevman->iface.rewind = udevman_rewind; + udevman->iface.get_next = udevman_get_next; + udevman->iface.has_next = udevman_has_next; + udevman->iface.register_udevice = udevman_register_udevice; + udevman->iface.unregister_udevice = udevman_unregister_udevice; + udevman->iface.get_udevice_by_UsbDevice = udevman_get_udevice_by_UsbDevice; + udevman->iface.get_udevice_by_ChannelID = udevman_get_udevice_by_ChannelID; + /* Extension */ + udevman->iface.isAutoAdd = udevman_is_auto_add; + /* Basic state */ + BASIC_STATE_FUNC_REGISTER(device_num, udevman); + BASIC_STATE_FUNC_REGISTER(next_device_id, udevman); + + /* control semaphore or mutex lock */ + udevman->iface.loading_lock = udevman_loading_lock; + udevman->iface.loading_unlock = udevman_loading_unlock; + udevman->iface.initialize = udevman_initialize; + udevman->iface.listener_created_callback = udevman_listener_created_callback; +} + +static BOOL poll_libusb_events(UDEVMAN* udevman) +{ + int rc = LIBUSB_SUCCESS; + struct timeval tv = { 0, 500 }; + if (libusb_try_lock_events(udevman->context) == 0) + { + if (libusb_event_handling_ok(udevman->context)) + { + rc = libusb_handle_events_locked(udevman->context, &tv); + if (rc != LIBUSB_SUCCESS) + WLog_WARN(TAG, "libusb_handle_events_locked %d", rc); + } + libusb_unlock_events(udevman->context); + } + else + { + libusb_lock_event_waiters(udevman->context); + if (libusb_event_handler_active(udevman->context)) + { + rc = libusb_wait_for_event(udevman->context, &tv); + if (rc < LIBUSB_SUCCESS) + WLog_WARN(TAG, "libusb_wait_for_event %d", rc); + } + libusb_unlock_event_waiters(udevman->context); + } + + return rc > 0; +} + +static DWORD WINAPI poll_thread(LPVOID lpThreadParameter) +{ + libusb_hotplug_callback_handle handle = 0; + UDEVMAN* udevman = (UDEVMAN*)lpThreadParameter; + BOOL hasHotplug = libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG); + + if (hasHotplug) + { + int rc = libusb_hotplug_register_callback( + udevman->context, + LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED | LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT, + LIBUSB_HOTPLUG_NO_FLAGS, LIBUSB_HOTPLUG_MATCH_ANY, LIBUSB_HOTPLUG_MATCH_ANY, + LIBUSB_HOTPLUG_MATCH_ANY, hotplug_callback, udevman, &handle); + + if (rc != LIBUSB_SUCCESS) + udevman->running = FALSE; + } + else + WLog_WARN(TAG, "Platform does not support libusb hotplug. USB devices plugged in later " + "will not be detected."); + + while (udevman->running) + { + poll_libusb_events(udevman); + } + + if (hasHotplug) + libusb_hotplug_deregister_callback(udevman->context, handle); + + /* Process remaining usb events */ + while (poll_libusb_events(udevman)) + ; + + ExitThread(0); + return 0; +} + +FREERDP_ENTRY_POINT(UINT libusb_freerdp_urbdrc_client_subsystem_entry( + PFREERDP_URBDRC_SERVICE_ENTRY_POINTS pEntryPoints)) +{ + wObject* obj = NULL; + UINT rc = 0; + UINT status = 0; + UDEVMAN* udevman = NULL; + const ADDIN_ARGV* args = pEntryPoints->args; + udevman = (PUDEVMAN)calloc(1, sizeof(UDEVMAN)); + + if (!udevman) + goto fail; + + udevman->hotplug_vid_pids = ArrayList_New(TRUE); + if (!udevman->hotplug_vid_pids) + goto fail; + obj = ArrayList_Object(udevman->hotplug_vid_pids); + obj->fnObjectFree = free; + obj->fnObjectEquals = udevman_vid_pid_pair_equals; + + udevman->next_device_id = BASE_USBDEVICE_NUM; + udevman->iface.plugin = pEntryPoints->plugin; + rc = libusb_init(&udevman->context); + + if (rc != LIBUSB_SUCCESS) + goto fail; + +#ifdef _WIN32 +#if LIBUSB_API_VERSION >= 0x01000106 + /* Prefer usbDK backend on windows. Not supported on other platforms. */ + rc = libusb_set_option(udevman->context, LIBUSB_OPTION_USE_USBDK); + switch (rc) + { + case LIBUSB_SUCCESS: + break; + case LIBUSB_ERROR_NOT_FOUND: + case LIBUSB_ERROR_NOT_SUPPORTED: + WLog_WARN(TAG, "LIBUSB_OPTION_USE_USBDK %s [%d]", libusb_strerror(rc), rc); + break; + default: + WLog_ERR(TAG, "LIBUSB_OPTION_USE_USBDK %s [%d]", libusb_strerror(rc), rc); + goto fail; + } +#endif +#endif + + udevman->flags = UDEVMAN_FLAG_ADD_BY_VID_PID; + udevman->devman_loading = CreateMutexA(NULL, FALSE, "devman_loading"); + + if (!udevman->devman_loading) + goto fail; + + /* load usb device service management */ + udevman_load_interface(udevman); + status = urbdrc_udevman_parse_addin_args(udevman, args); + + if (status != CHANNEL_RC_OK) + goto fail; + + udevman->running = TRUE; + udevman->thread = CreateThread(NULL, 0, poll_thread, udevman, 0, NULL); + + if (!udevman->thread) + goto fail; + + if (!pEntryPoints->pRegisterUDEVMAN(pEntryPoints->plugin, (IUDEVMAN*)udevman)) + goto fail; + + WLog_DBG(TAG, "UDEVMAN device registered."); + return 0; +fail: + udevman_free(&udevman->iface); + return ERROR_INTERNAL_ERROR; +} diff --git a/channels/urbdrc/client/urbdrc_main.c b/channels/urbdrc/client/urbdrc_main.c new file mode 100644 index 0000000..96e443d --- /dev/null +++ b/channels/urbdrc/client/urbdrc_main.c @@ -0,0 +1,1023 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * RemoteFX USB Redirection + * + * Copyright 2012 Atrust corp. + * Copyright 2012 Alfred Liu <alfred.liu@atruscorp.com> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <winpr/assert.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> + +#include <winpr/pool.h> +#include <winpr/print.h> + +#include <winpr/crt.h> +#include <winpr/synch.h> +#include <winpr/string.h> +#include <winpr/cmdline.h> + +#include <freerdp/dvc.h> +#include <freerdp/addin.h> +#include <freerdp/channels/log.h> +#include <freerdp/channels/urbdrc.h> + +#include "urbdrc_types.h" +#include "urbdrc_main.h" +#include "data_transfer.h" + +#include <urbdrc_helpers.h> + +static IWTSVirtualChannel* get_channel(IUDEVMAN* idevman) +{ + IWTSVirtualChannelManager* channel_mgr = NULL; + URBDRC_PLUGIN* urbdrc = NULL; + + if (!idevman) + return NULL; + + urbdrc = (URBDRC_PLUGIN*)idevman->plugin; + + if (!urbdrc || !urbdrc->listener_callback) + return NULL; + + channel_mgr = urbdrc->listener_callback->channel_mgr; + + if (!channel_mgr) + return NULL; + + return channel_mgr->FindChannelById(channel_mgr, idevman->controlChannelId); +} + +static int func_container_id_generate(IUDEVICE* pdev, char* strContainerId) +{ + char* p = NULL; + char* path = NULL; + UINT8 containerId[17] = { 0 }; + UINT16 idVendor = 0; + UINT16 idProduct = 0; + idVendor = (UINT16)pdev->query_device_descriptor(pdev, ID_VENDOR); + idProduct = (UINT16)pdev->query_device_descriptor(pdev, ID_PRODUCT); + path = pdev->getPath(pdev); + + if (strlen(path) > 8) + p = (path + strlen(path)) - 8; + else + p = path; + + sprintf_s((char*)containerId, sizeof(containerId), "%04" PRIX16 "%04" PRIX16 "%s", idVendor, + idProduct, p); + /* format */ + sprintf_s(strContainerId, DEVICE_CONTAINER_STR_SIZE, + "{%02" PRIx8 "%02" PRIx8 "%02" PRIx8 "%02" PRIx8 "-%02" PRIx8 "%02" PRIx8 "-%02" PRIx8 + "%02" PRIx8 "-%02" PRIx8 "%02" PRIx8 "-%02" PRIx8 "%02" PRIx8 "%02" PRIx8 "%02" PRIx8 + "%02" PRIx8 "%02" PRIx8 "}", + containerId[0], containerId[1], containerId[2], containerId[3], containerId[4], + containerId[5], containerId[6], containerId[7], containerId[8], containerId[9], + containerId[10], containerId[11], containerId[12], containerId[13], containerId[14], + containerId[15]); + return 0; +} + +static int func_instance_id_generate(IUDEVICE* pdev, char* strInstanceId, size_t len) +{ + char instanceId[17] = { 0 }; + sprintf_s(instanceId, sizeof(instanceId), "\\%s", pdev->getPath(pdev)); + /* format */ + sprintf_s(strInstanceId, len, + "%02" PRIx8 "%02" PRIx8 "%02" PRIx8 "%02" PRIx8 "-%02" PRIx8 "%02" PRIx8 "-%02" PRIx8 + "%02" PRIx8 "-%02" PRIx8 "%02" PRIx8 "-%02" PRIx8 "%02" PRIx8 "%02" PRIx8 "%02" PRIx8 + "%02" PRIx8 "%02" PRIx8 "", + instanceId[0], instanceId[1], instanceId[2], instanceId[3], instanceId[4], + instanceId[5], instanceId[6], instanceId[7], instanceId[8], instanceId[9], + instanceId[10], instanceId[11], instanceId[12], instanceId[13], instanceId[14], + instanceId[15]); + return 0; +} + +/** + * Function description + * + * @return 0 on success, otherwise a Win32 error code + */ +static UINT urbdrc_process_capability_request(GENERIC_CHANNEL_CALLBACK* callback, wStream* s, + UINT32 MessageId) +{ + UINT32 InterfaceId = 0; + UINT32 Version = 0; + UINT32 out_size = 0; + wStream* out = NULL; + + if (!callback || !s) + return ERROR_INVALID_PARAMETER; + + if (!Stream_CheckAndLogRequiredLength(TAG, s, 4)) + return ERROR_INVALID_DATA; + + Stream_Read_UINT32(s, Version); + + if (Version > RIM_CAPABILITY_VERSION_01) + Version = RIM_CAPABILITY_VERSION_01; + + InterfaceId = ((STREAM_ID_NONE << 30) | CAPABILITIES_NEGOTIATOR); + out_size = 16; + out = Stream_New(NULL, out_size); + + if (!out) + return ERROR_OUTOFMEMORY; + + Stream_Write_UINT32(out, InterfaceId); /* interface id */ + Stream_Write_UINT32(out, MessageId); /* message id */ + Stream_Write_UINT32(out, Version); /* usb protocol version */ + Stream_Write_UINT32(out, 0x00000000); /* HRESULT */ + return stream_write_and_free(callback->plugin, callback->channel, out); +} + +/** + * Function description + * + * @return 0 on success, otherwise a Win32 error code + */ +static UINT urbdrc_process_channel_create(GENERIC_CHANNEL_CALLBACK* callback, wStream* s, + UINT32 MessageId) +{ + UINT32 InterfaceId = 0; + UINT32 out_size = 0; + UINT32 MajorVersion = 0; + UINT32 MinorVersion = 0; + UINT32 Capabilities = 0; + wStream* out = NULL; + URBDRC_PLUGIN* urbdrc = NULL; + + if (!callback || !s || !callback->plugin) + return ERROR_INVALID_PARAMETER; + + urbdrc = (URBDRC_PLUGIN*)callback->plugin; + + if (!Stream_CheckAndLogRequiredLength(TAG, s, 12)) + return ERROR_INVALID_DATA; + + Stream_Read_UINT32(s, MajorVersion); + Stream_Read_UINT32(s, MinorVersion); + Stream_Read_UINT32(s, Capabilities); + + /* Version check, we only support version 1.0 */ + if ((MajorVersion != 1) || (MinorVersion != 0)) + { + WLog_Print(urbdrc->log, WLOG_WARN, + "server supports USB channel version %" PRIu32 ".%" PRIu32); + WLog_Print(urbdrc->log, WLOG_WARN, "we only support channel version 1.0"); + MajorVersion = 1; + MinorVersion = 0; + } + + InterfaceId = ((STREAM_ID_PROXY << 30) | CLIENT_CHANNEL_NOTIFICATION); + out_size = 24; + out = Stream_New(NULL, out_size); + + if (!out) + return ERROR_OUTOFMEMORY; + + Stream_Write_UINT32(out, InterfaceId); /* interface id */ + Stream_Write_UINT32(out, MessageId); /* message id */ + Stream_Write_UINT32(out, CHANNEL_CREATED); /* function id */ + Stream_Write_UINT32(out, MajorVersion); + Stream_Write_UINT32(out, MinorVersion); + Stream_Write_UINT32(out, Capabilities); /* capabilities version */ + return stream_write_and_free(callback->plugin, callback->channel, out); +} + +static UINT urdbrc_send_virtual_channel_add(IWTSPlugin* plugin, IWTSVirtualChannel* channel, + UINT32 MessageId) +{ + const UINT32 InterfaceId = ((STREAM_ID_PROXY << 30) | CLIENT_DEVICE_SINK); + wStream* out = Stream_New(NULL, 12); + + if (!out) + return ERROR_OUTOFMEMORY; + + Stream_Write_UINT32(out, InterfaceId); /* interface */ + Stream_Write_UINT32(out, MessageId); /* message id */ + Stream_Write_UINT32(out, ADD_VIRTUAL_CHANNEL); /* function id */ + return stream_write_and_free(plugin, channel, out); +} + +/** + * Function description + * + * @return 0 on success, otherwise a Win32 error code + */ +static UINT urdbrc_send_usb_device_add(GENERIC_CHANNEL_CALLBACK* callback, IUDEVICE* pdev) +{ + wStream* out = NULL; + UINT32 InterfaceId = 0; + char HardwareIds[2][DEVICE_HARDWARE_ID_SIZE] = { { 0 } }; + char CompatibilityIds[3][DEVICE_COMPATIBILITY_ID_SIZE] = { { 0 } }; + char strContainerId[DEVICE_CONTAINER_STR_SIZE] = { 0 }; + char strInstanceId[DEVICE_INSTANCE_STR_SIZE] = { 0 }; + const char* composite_str = "USB\\COMPOSITE"; + const size_t composite_len = 13; + size_t size = 0; + size_t CompatibilityIdLen[3]; + size_t HardwareIdsLen[2]; + size_t ContainerIdLen = 0; + size_t InstanceIdLen = 0; + size_t cchCompatIds = 0; + UINT32 bcdUSB = 0; + InterfaceId = ((STREAM_ID_PROXY << 30) | CLIENT_DEVICE_SINK); + /* USB kernel driver detach!! */ + pdev->detach_kernel_driver(pdev); + { + const UINT16 idVendor = (UINT16)pdev->query_device_descriptor(pdev, ID_VENDOR); + const UINT16 idProduct = (UINT16)pdev->query_device_descriptor(pdev, ID_PRODUCT); + const UINT16 bcdDevice = (UINT16)pdev->query_device_descriptor(pdev, BCD_DEVICE); + sprintf_s(HardwareIds[1], DEVICE_HARDWARE_ID_SIZE, + "USB\\VID_%04" PRIX16 "&PID_%04" PRIX16 "", idVendor, idProduct); + sprintf_s(HardwareIds[0], DEVICE_HARDWARE_ID_SIZE, + "USB\\VID_%04" PRIX16 "&PID_%04" PRIX16 "&REV_%04" PRIX16 "", idVendor, idProduct, + bcdDevice); + } + { + const UINT8 bDeviceClass = (UINT8)pdev->query_device_descriptor(pdev, B_DEVICE_CLASS); + const UINT8 bDeviceSubClass = (UINT8)pdev->query_device_descriptor(pdev, B_DEVICE_SUBCLASS); + const UINT8 bDeviceProtocol = (UINT8)pdev->query_device_descriptor(pdev, B_DEVICE_PROTOCOL); + + if (!(pdev->isCompositeDevice(pdev))) + { + sprintf_s(CompatibilityIds[2], DEVICE_COMPATIBILITY_ID_SIZE, "USB\\Class_%02" PRIX8 "", + bDeviceClass); + sprintf_s(CompatibilityIds[1], DEVICE_COMPATIBILITY_ID_SIZE, + "USB\\Class_%02" PRIX8 "&SubClass_%02" PRIX8 "", bDeviceClass, + bDeviceSubClass); + sprintf_s(CompatibilityIds[0], DEVICE_COMPATIBILITY_ID_SIZE, + "USB\\Class_%02" PRIX8 "&SubClass_%02" PRIX8 "&Prot_%02" PRIX8 "", + bDeviceClass, bDeviceSubClass, bDeviceProtocol); + } + else + { + sprintf_s(CompatibilityIds[2], DEVICE_COMPATIBILITY_ID_SIZE, "USB\\DevClass_00"); + sprintf_s(CompatibilityIds[1], DEVICE_COMPATIBILITY_ID_SIZE, + "USB\\DevClass_00&SubClass_00"); + sprintf_s(CompatibilityIds[0], DEVICE_COMPATIBILITY_ID_SIZE, + "USB\\DevClass_00&SubClass_00&Prot_00"); + } + } + func_instance_id_generate(pdev, strInstanceId, DEVICE_INSTANCE_STR_SIZE); + func_container_id_generate(pdev, strContainerId); + CompatibilityIdLen[0] = strnlen(CompatibilityIds[0], sizeof(CompatibilityIds[0])); + CompatibilityIdLen[1] = strnlen(CompatibilityIds[1], sizeof(CompatibilityIds[1])); + CompatibilityIdLen[2] = strnlen(CompatibilityIds[2], sizeof(CompatibilityIds[2])); + HardwareIdsLen[0] = strnlen(HardwareIds[0], sizeof(HardwareIds[0])); + HardwareIdsLen[1] = strnlen(HardwareIds[1], sizeof(HardwareIds[1])); + cchCompatIds = + CompatibilityIdLen[0] + 1 + CompatibilityIdLen[1] + 1 + CompatibilityIdLen[2] + 2; + InstanceIdLen = strnlen(strInstanceId, sizeof(strInstanceId)); + ContainerIdLen = strnlen(strContainerId, sizeof(strContainerId)); + + if (pdev->isCompositeDevice(pdev)) + cchCompatIds += composite_len + 1; + + size = 24; + size += (InstanceIdLen + 1) * 2 + (HardwareIdsLen[0] + 1) * 2 + 4 + + (HardwareIdsLen[1] + 1) * 2 + 2 + 4 + (cchCompatIds)*2 + (ContainerIdLen + 1) * 2 + 4 + + 28; + out = Stream_New(NULL, size); + + if (!out) + return ERROR_OUTOFMEMORY; + + Stream_Write_UINT32(out, InterfaceId); /* interface */ + Stream_Write_UINT32(out, 0); + Stream_Write_UINT32(out, ADD_DEVICE); /* function id */ + Stream_Write_UINT32(out, 0x00000001); /* NumUsbDevice */ + Stream_Write_UINT32(out, pdev->get_UsbDevice(pdev)); /* UsbDevice */ + Stream_Write_UINT32(out, (UINT32)InstanceIdLen + 1); /* cchDeviceInstanceId */ + if (Stream_Write_UTF16_String_From_UTF8(out, InstanceIdLen, strInstanceId, InstanceIdLen, + TRUE) < 0) + goto fail; + Stream_Write_UINT16(out, 0); + Stream_Write_UINT32(out, HardwareIdsLen[0] + HardwareIdsLen[1] + 3); /* cchHwIds */ + /* HardwareIds 1 */ + if (Stream_Write_UTF16_String_From_UTF8(out, HardwareIdsLen[0], HardwareIds[0], + HardwareIdsLen[0], TRUE) < 0) + goto fail; + Stream_Write_UINT16(out, 0); + if (Stream_Write_UTF16_String_From_UTF8(out, HardwareIdsLen[1], HardwareIds[1], + HardwareIdsLen[1], TRUE) < 0) + goto fail; + Stream_Write_UINT16(out, 0); + Stream_Write_UINT16(out, 0); /* add "\0" */ + Stream_Write_UINT32(out, (UINT32)cchCompatIds); /* cchCompatIds */ + /* CompatibilityIds */ + if (Stream_Write_UTF16_String_From_UTF8(out, CompatibilityIdLen[0], CompatibilityIds[0], + CompatibilityIdLen[0], TRUE) < 0) + goto fail; + Stream_Write_UINT16(out, 0); + if (Stream_Write_UTF16_String_From_UTF8(out, CompatibilityIdLen[1], CompatibilityIds[1], + CompatibilityIdLen[1], TRUE) < 0) + goto fail; + Stream_Write_UINT16(out, 0); + if (Stream_Write_UTF16_String_From_UTF8(out, CompatibilityIdLen[2], CompatibilityIds[2], + CompatibilityIdLen[2], TRUE) < 0) + goto fail; + Stream_Write_UINT16(out, 0); + + if (pdev->isCompositeDevice(pdev)) + { + if (Stream_Write_UTF16_String_From_UTF8(out, composite_len, composite_str, composite_len, + TRUE) < 0) + goto fail; + Stream_Write_UINT16(out, 0); + } + + Stream_Write_UINT16(out, 0x0000); /* add "\0" */ + Stream_Write_UINT32(out, (UINT32)ContainerIdLen + 1); /* cchContainerId */ + /* ContainerId */ + if (Stream_Write_UTF16_String_From_UTF8(out, ContainerIdLen, strContainerId, ContainerIdLen, + TRUE) < 0) + goto fail; + Stream_Write_UINT16(out, 0); + /* USB_DEVICE_CAPABILITIES 28 bytes */ + Stream_Write_UINT32(out, 0x0000001c); /* CbSize */ + Stream_Write_UINT32(out, 2); /* UsbBusInterfaceVersion, 0 ,1 or 2 */ // TODO: Get from libusb + Stream_Write_UINT32(out, 0x600); /* USBDI_Version, 0x500 or 0x600 */ // TODO: Get from libusb + /* Supported_USB_Version, 0x110,0x110 or 0x200(usb2.0) */ + bcdUSB = pdev->query_device_descriptor(pdev, BCD_USB); + Stream_Write_UINT32(out, bcdUSB); + Stream_Write_UINT32(out, 0x00000000); /* HcdCapabilities, MUST always be zero */ + + if (bcdUSB < 0x200) + Stream_Write_UINT32(out, 0x00000000); /* DeviceIsHighSpeed */ + else + Stream_Write_UINT32(out, 0x00000001); /* DeviceIsHighSpeed */ + + Stream_Write_UINT32(out, 0x50); /* NoAckIsochWriteJitterBufferSizeInMs, >=10 or <=512 */ + return stream_write_and_free(callback->plugin, callback->channel, out); + +fail: + Stream_Free(out, TRUE); + return ERROR_INTERNAL_ERROR; +} + +/** + * Function description + * + * @return 0 on success, otherwise a Win32 error code + */ +static UINT urbdrc_exchange_capabilities(GENERIC_CHANNEL_CALLBACK* callback, wStream* data) +{ + UINT32 MessageId = 0; + UINT32 FunctionId = 0; + UINT32 InterfaceId = 0; + UINT error = CHANNEL_RC_OK; + + if (!data) + return ERROR_INVALID_PARAMETER; + + if (!Stream_CheckAndLogRequiredLength(TAG, data, 8)) + return ERROR_INVALID_DATA; + + Stream_Rewind_UINT32(data); + Stream_Read_UINT32(data, InterfaceId); + Stream_Read_UINT32(data, MessageId); + Stream_Read_UINT32(data, FunctionId); + + switch (FunctionId) + { + case RIM_EXCHANGE_CAPABILITY_REQUEST: + error = urbdrc_process_capability_request(callback, data, MessageId); + break; + + case RIMCALL_RELEASE: + break; + + default: + error = ERROR_NOT_FOUND; + break; + } + + return error; +} + +static BOOL urbdrc_announce_devices(IUDEVMAN* udevman) +{ + UINT error = ERROR_SUCCESS; + + udevman->loading_lock(udevman); + udevman->rewind(udevman); + + while (udevman->has_next(udevman)) + { + IUDEVICE* pdev = udevman->get_next(udevman); + + if (!pdev->isAlreadySend(pdev)) + { + const UINT32 deviceId = pdev->get_UsbDevice(pdev); + UINT cerror = + urdbrc_send_virtual_channel_add(udevman->plugin, get_channel(udevman), deviceId); + + if (cerror != ERROR_SUCCESS) + break; + } + } + + udevman->loading_unlock(udevman); + + return error == ERROR_SUCCESS; +} + +static UINT urbdrc_device_control_channel(GENERIC_CHANNEL_CALLBACK* callback, wStream* s) +{ + URBDRC_PLUGIN* urbdrc = (URBDRC_PLUGIN*)callback->plugin; + IUDEVMAN* udevman = urbdrc->udevman; + IWTSVirtualChannel* channel = callback->channel; + IUDEVICE* pdev = NULL; + BOOL found = FALSE; + UINT error = ERROR_INTERNAL_ERROR; + UINT32 channelId = callback->channel_mgr->GetChannelId(channel); + + switch (urbdrc->vchannel_status) + { + case INIT_CHANNEL_IN: + /* Control channel was established */ + error = ERROR_SUCCESS; + udevman->initialize(udevman, channelId); + + if (!urbdrc_announce_devices(udevman)) + goto fail; + + urbdrc->vchannel_status = INIT_CHANNEL_OUT; + break; + + case INIT_CHANNEL_OUT: + /* A new device channel was created, add the channel + * to the device */ + udevman->loading_lock(udevman); + udevman->rewind(udevman); + + while (udevman->has_next(udevman)) + { + pdev = udevman->get_next(udevman); + + if (!pdev->isAlreadySend(pdev)) + { + const UINT32 channelID = callback->channel_mgr->GetChannelId(channel); + found = TRUE; + pdev->setAlreadySend(pdev); + pdev->set_channelManager(pdev, callback->channel_mgr); + pdev->set_channelID(pdev, channelID); + break; + } + } + + udevman->loading_unlock(udevman); + error = ERROR_SUCCESS; + + if (found && pdev->isAlreadySend(pdev)) + error = urdbrc_send_usb_device_add(callback, pdev); + + break; + + default: + WLog_Print(urbdrc->log, WLOG_ERROR, "vchannel_status unknown value %" PRIu32 "", + urbdrc->vchannel_status); + break; + } + +fail: + return error; +} + +/** + * Function description + * + * @return 0 on success, otherwise a Win32 error code + */ +static UINT urbdrc_process_channel_notification(GENERIC_CHANNEL_CALLBACK* callback, wStream* data) +{ + UINT32 MessageId = 0; + UINT32 FunctionId = 0; + UINT32 InterfaceId = 0; + UINT error = CHANNEL_RC_OK; + URBDRC_PLUGIN* urbdrc = NULL; + + if (!callback || !data) + return ERROR_INVALID_PARAMETER; + + urbdrc = (URBDRC_PLUGIN*)callback->plugin; + + if (!urbdrc) + return ERROR_INVALID_PARAMETER; + + if (!Stream_CheckAndLogRequiredLength(TAG, data, 8)) + return ERROR_INVALID_DATA; + + Stream_Rewind(data, 4); + Stream_Read_UINT32(data, InterfaceId); + Stream_Read_UINT32(data, MessageId); + Stream_Read_UINT32(data, FunctionId); + WLog_Print(urbdrc->log, WLOG_TRACE, "%s [%" PRIu32 "]", + call_to_string(FALSE, InterfaceId, FunctionId), FunctionId); + + switch (FunctionId) + { + case CHANNEL_CREATED: + error = urbdrc_process_channel_create(callback, data, MessageId); + break; + + case RIMCALL_RELEASE: + error = urbdrc_device_control_channel(callback, data); + break; + + default: + WLog_Print(urbdrc->log, WLOG_TRACE, "unknown FunctionId 0x%" PRIX32 "", FunctionId); + error = 1; + break; + } + + return error; +} + +/** + * Function description + * + * @return 0 on success, otherwise a Win32 error code + */ +static UINT urbdrc_on_data_received(IWTSVirtualChannelCallback* pChannelCallback, wStream* data) +{ + GENERIC_CHANNEL_CALLBACK* callback = (GENERIC_CHANNEL_CALLBACK*)pChannelCallback; + URBDRC_PLUGIN* urbdrc = NULL; + IUDEVMAN* udevman = NULL; + UINT32 InterfaceId = 0; + UINT error = ERROR_INTERNAL_ERROR; + + if (callback == NULL) + return ERROR_INVALID_PARAMETER; + + if (callback->plugin == NULL) + return error; + + urbdrc = (URBDRC_PLUGIN*)callback->plugin; + + if (urbdrc->udevman == NULL) + return error; + + udevman = (IUDEVMAN*)urbdrc->udevman; + + if (!Stream_CheckAndLogRequiredLength(TAG, data, 12)) + return ERROR_INVALID_DATA; + + urbdrc_dump_message(urbdrc->log, FALSE, FALSE, data); + Stream_Read_UINT32(data, InterfaceId); + + /* Need to check InterfaceId and mask values */ + switch (InterfaceId) + { + case CAPABILITIES_NEGOTIATOR | (STREAM_ID_NONE << 30): + error = urbdrc_exchange_capabilities(callback, data); + break; + + case SERVER_CHANNEL_NOTIFICATION | (STREAM_ID_PROXY << 30): + error = urbdrc_process_channel_notification(callback, data); + break; + + default: + error = urbdrc_process_udev_data_transfer(callback, urbdrc, udevman, data); + WLog_DBG(TAG, "urbdrc_process_udev_data_transfer returned 0x%08" PRIx32, error); + error = ERROR_SUCCESS; /* Ignore errors, the device may have been unplugged. */ + break; + } + + return error; +} + +/** + * Function description + * + * @return 0 on success, otherwise a Win32 error code + */ +static UINT urbdrc_on_close(IWTSVirtualChannelCallback* pChannelCallback) +{ + GENERIC_CHANNEL_CALLBACK* callback = (GENERIC_CHANNEL_CALLBACK*)pChannelCallback; + if (callback) + { + URBDRC_PLUGIN* urbdrc = (URBDRC_PLUGIN*)callback->plugin; + if (urbdrc) + { + IUDEVMAN* udevman = urbdrc->udevman; + if (udevman && callback->channel_mgr) + { + UINT32 control = callback->channel_mgr->GetChannelId(callback->channel); + if (udevman->controlChannelId == control) + udevman->status |= URBDRC_DEVICE_CHANNEL_CLOSED; + else + { /* Need to notify the local backend the device is gone */ + IUDEVICE* pdev = udevman->get_udevice_by_ChannelID(udevman, control); + if (pdev) + pdev->markChannelClosed(pdev); + } + } + } + } + free(callback); + return CHANNEL_RC_OK; +} + +/** + * Function description + * + * @return 0 on success, otherwise a Win32 error code + */ +static UINT urbdrc_on_new_channel_connection(IWTSListenerCallback* pListenerCallback, + IWTSVirtualChannel* pChannel, BYTE* pData, + BOOL* pbAccept, + IWTSVirtualChannelCallback** ppCallback) +{ + GENERIC_LISTENER_CALLBACK* listener_callback = (GENERIC_LISTENER_CALLBACK*)pListenerCallback; + GENERIC_CHANNEL_CALLBACK* callback = NULL; + + if (!ppCallback) + return ERROR_INVALID_PARAMETER; + + callback = (GENERIC_CHANNEL_CALLBACK*)calloc(1, sizeof(GENERIC_CHANNEL_CALLBACK)); + + if (!callback) + return ERROR_OUTOFMEMORY; + + callback->iface.OnDataReceived = urbdrc_on_data_received; + callback->iface.OnClose = urbdrc_on_close; + callback->plugin = listener_callback->plugin; + callback->channel_mgr = listener_callback->channel_mgr; + callback->channel = pChannel; + *ppCallback = (IWTSVirtualChannelCallback*)callback; + return CHANNEL_RC_OK; +} + +/** + * Function description + * + * @return 0 on success, otherwise a Win32 error code + */ +static UINT urbdrc_plugin_initialize(IWTSPlugin* pPlugin, IWTSVirtualChannelManager* pChannelMgr) +{ + UINT status = 0; + URBDRC_PLUGIN* urbdrc = (URBDRC_PLUGIN*)pPlugin; + IUDEVMAN* udevman = NULL; + char channelName[sizeof(URBDRC_CHANNEL_NAME)] = { URBDRC_CHANNEL_NAME }; + + if (!urbdrc || !urbdrc->udevman) + return ERROR_INVALID_PARAMETER; + + if (urbdrc->initialized) + { + WLog_ERR(TAG, "[%s] channel initialized twice, aborting", URBDRC_CHANNEL_NAME); + return ERROR_INVALID_DATA; + } + udevman = urbdrc->udevman; + urbdrc->listener_callback = + (GENERIC_LISTENER_CALLBACK*)calloc(1, sizeof(GENERIC_LISTENER_CALLBACK)); + + if (!urbdrc->listener_callback) + return CHANNEL_RC_NO_MEMORY; + + urbdrc->listener_callback->iface.OnNewChannelConnection = urbdrc_on_new_channel_connection; + urbdrc->listener_callback->plugin = pPlugin; + urbdrc->listener_callback->channel_mgr = pChannelMgr; + + /* [MS-RDPEUSB] 2.1 Transport defines the channel name in uppercase letters */ + CharUpperA(channelName); + status = pChannelMgr->CreateListener(pChannelMgr, channelName, 0, + &urbdrc->listener_callback->iface, &urbdrc->listener); + if (status != CHANNEL_RC_OK) + return status; + + status = CHANNEL_RC_OK; + if (udevman->listener_created_callback) + status = udevman->listener_created_callback(udevman); + + urbdrc->initialized = status == CHANNEL_RC_OK; + return status; +} + +/** + * Function description + * + * @return 0 on success, otherwise a Win32 error code + */ +static UINT urbdrc_plugin_terminated(IWTSPlugin* pPlugin) +{ + URBDRC_PLUGIN* urbdrc = (URBDRC_PLUGIN*)pPlugin; + IUDEVMAN* udevman = NULL; + + if (!urbdrc) + return ERROR_INVALID_DATA; + if (urbdrc->listener_callback) + { + IWTSVirtualChannelManager* mgr = urbdrc->listener_callback->channel_mgr; + if (mgr) + IFCALL(mgr->DestroyListener, mgr, urbdrc->listener); + } + udevman = urbdrc->udevman; + + if (udevman) + { + udevman->free(udevman); + udevman = NULL; + } + + free(urbdrc->subsystem); + free(urbdrc->listener_callback); + free(urbdrc); + return CHANNEL_RC_OK; +} + +static BOOL urbdrc_register_udevman_addin(IWTSPlugin* pPlugin, IUDEVMAN* udevman) +{ + URBDRC_PLUGIN* urbdrc = (URBDRC_PLUGIN*)pPlugin; + + if (urbdrc->udevman) + { + WLog_Print(urbdrc->log, WLOG_ERROR, "existing device, abort."); + return FALSE; + } + + DEBUG_DVC("device registered."); + urbdrc->udevman = udevman; + return TRUE; +} + +/** + * Function description + * + * @return 0 on success, otherwise a Win32 error code + */ +static UINT urbdrc_load_udevman_addin(IWTSPlugin* pPlugin, LPCSTR name, const ADDIN_ARGV* args) +{ + URBDRC_PLUGIN* urbdrc = (URBDRC_PLUGIN*)pPlugin; + PFREERDP_URBDRC_DEVICE_ENTRY entry = NULL; + FREERDP_URBDRC_SERVICE_ENTRY_POINTS entryPoints; + entry = (PFREERDP_URBDRC_DEVICE_ENTRY)freerdp_load_channel_addin_entry(URBDRC_CHANNEL_NAME, + name, NULL, 0); + + if (!entry) + return ERROR_INVALID_OPERATION; + + entryPoints.plugin = pPlugin; + entryPoints.pRegisterUDEVMAN = urbdrc_register_udevman_addin; + entryPoints.args = args; + + if (entry(&entryPoints) != 0) + { + WLog_Print(urbdrc->log, WLOG_ERROR, "%s entry returns error.", name); + return ERROR_INVALID_OPERATION; + } + + return CHANNEL_RC_OK; +} + +static BOOL urbdrc_set_subsystem(URBDRC_PLUGIN* urbdrc, const char* subsystem) +{ + free(urbdrc->subsystem); + urbdrc->subsystem = _strdup(subsystem); + return (urbdrc->subsystem != NULL); +} + +/** + * Function description + * + * @return 0 on success, otherwise a Win32 error code + */ +static UINT urbdrc_process_addin_args(URBDRC_PLUGIN* urbdrc, const ADDIN_ARGV* args) +{ + int status = 0; + COMMAND_LINE_ARGUMENT_A urbdrc_args[] = { + { "dbg", COMMAND_LINE_VALUE_FLAG, "", NULL, BoolValueFalse, -1, NULL, "debug" }, + { "sys", COMMAND_LINE_VALUE_REQUIRED, "<subsystem>", NULL, NULL, -1, NULL, "subsystem" }, + { "dev", COMMAND_LINE_VALUE_REQUIRED, "<device list>", NULL, NULL, -1, NULL, "devices" }, + { "encode", COMMAND_LINE_VALUE_FLAG, "", NULL, NULL, -1, NULL, "encode" }, + { "quality", COMMAND_LINE_VALUE_REQUIRED, "<[0-2] -> [high-medium-low]>", NULL, NULL, -1, + NULL, "quality" }, + { NULL, 0, NULL, NULL, NULL, -1, NULL, NULL } + }; + + const DWORD flags = + COMMAND_LINE_SIGIL_NONE | COMMAND_LINE_SEPARATOR_COLON | COMMAND_LINE_IGN_UNKNOWN_KEYWORD; + const COMMAND_LINE_ARGUMENT_A* arg = NULL; + status = + CommandLineParseArgumentsA(args->argc, args->argv, urbdrc_args, flags, urbdrc, NULL, NULL); + + if (status < 0) + return ERROR_INVALID_DATA; + + arg = urbdrc_args; + + do + { + if (!(arg->Flags & COMMAND_LINE_VALUE_PRESENT)) + continue; + + CommandLineSwitchStart(arg) CommandLineSwitchCase(arg, "dbg") + { + WLog_SetLogLevel(urbdrc->log, WLOG_TRACE); + } + CommandLineSwitchCase(arg, "sys") + { + if (!urbdrc_set_subsystem(urbdrc, arg->Value)) + return ERROR_OUTOFMEMORY; + } + CommandLineSwitchDefault(arg) + { + } + CommandLineSwitchEnd(arg) + } while ((arg = CommandLineFindNextArgumentA(arg)) != NULL); + + return CHANNEL_RC_OK; +} + +BOOL add_device(IUDEVMAN* idevman, UINT32 flags, BYTE busnum, BYTE devnum, UINT16 idVendor, + UINT16 idProduct) +{ + size_t success = 0; + URBDRC_PLUGIN* urbdrc = NULL; + UINT32 mask = 0; + UINT32 regflags = 0; + + if (!idevman) + return FALSE; + + urbdrc = (URBDRC_PLUGIN*)idevman->plugin; + + if (!urbdrc || !urbdrc->listener_callback) + return FALSE; + + mask = (DEVICE_ADD_FLAG_VENDOR | DEVICE_ADD_FLAG_PRODUCT); + if ((flags & mask) == mask) + regflags |= UDEVMAN_FLAG_ADD_BY_VID_PID; + mask = (DEVICE_ADD_FLAG_BUS | DEVICE_ADD_FLAG_DEV); + if ((flags & mask) == mask) + regflags |= UDEVMAN_FLAG_ADD_BY_ADDR; + + success = idevman->register_udevice(idevman, busnum, devnum, idVendor, idProduct, regflags); + + if ((success > 0) && (flags & DEVICE_ADD_FLAG_REGISTER)) + { + if (!urbdrc_announce_devices(idevman)) + return FALSE; + } + + return TRUE; +} + +BOOL del_device(IUDEVMAN* idevman, UINT32 flags, BYTE busnum, BYTE devnum, UINT16 idVendor, + UINT16 idProduct) +{ + IUDEVICE* pdev = NULL; + URBDRC_PLUGIN* urbdrc = NULL; + + if (!idevman) + return FALSE; + + urbdrc = (URBDRC_PLUGIN*)idevman->plugin; + + if (!urbdrc || !urbdrc->listener_callback) + return FALSE; + + idevman->loading_lock(idevman); + idevman->rewind(idevman); + + while (idevman->has_next(idevman)) + { + BOOL match = TRUE; + IUDEVICE* dev = idevman->get_next(idevman); + + if ((flags & (DEVICE_ADD_FLAG_BUS | DEVICE_ADD_FLAG_DEV | DEVICE_ADD_FLAG_VENDOR | + DEVICE_ADD_FLAG_PRODUCT)) == 0) + match = FALSE; + if (flags & DEVICE_ADD_FLAG_BUS) + { + if (dev->get_bus_number(dev) != busnum) + match = FALSE; + } + if (flags & DEVICE_ADD_FLAG_DEV) + { + if (dev->get_dev_number(dev) != devnum) + match = FALSE; + } + if (flags & DEVICE_ADD_FLAG_VENDOR) + { + int vid = dev->query_device_descriptor(dev, ID_VENDOR); + if (vid != idVendor) + match = FALSE; + } + if (flags & DEVICE_ADD_FLAG_PRODUCT) + { + int pid = dev->query_device_descriptor(dev, ID_PRODUCT); + if (pid != idProduct) + match = FALSE; + } + + if (match) + { + pdev = dev; + break; + } + } + + if (pdev) + pdev->setChannelClosed(pdev); + + idevman->loading_unlock(idevman); + return TRUE; +} + +/** + * Function description + * + * @return 0 on success, otherwise a Win32 error code + */ +FREERDP_ENTRY_POINT(UINT urbdrc_DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints)) +{ + UINT status = 0; + const ADDIN_ARGV* args = NULL; + URBDRC_PLUGIN* urbdrc = NULL; + urbdrc = (URBDRC_PLUGIN*)pEntryPoints->GetPlugin(pEntryPoints, URBDRC_CHANNEL_NAME); + args = pEntryPoints->GetPluginData(pEntryPoints); + + if (urbdrc == NULL) + { + urbdrc = (URBDRC_PLUGIN*)calloc(1, sizeof(URBDRC_PLUGIN)); + + if (!urbdrc) + return CHANNEL_RC_NO_MEMORY; + + urbdrc->iface.Initialize = urbdrc_plugin_initialize; + urbdrc->iface.Terminated = urbdrc_plugin_terminated; + urbdrc->vchannel_status = INIT_CHANNEL_IN; + status = pEntryPoints->RegisterPlugin(pEntryPoints, URBDRC_CHANNEL_NAME, &urbdrc->iface); + + /* After we register the plugin free will be taken care of by dynamic channel */ + if (status != CHANNEL_RC_OK) + { + free(urbdrc); + goto fail; + } + + urbdrc->log = WLog_Get(TAG); + + if (!urbdrc->log) + goto fail; + } + + status = urbdrc_process_addin_args(urbdrc, args); + + if (status != CHANNEL_RC_OK) + goto fail; + + if (!urbdrc->subsystem && !urbdrc_set_subsystem(urbdrc, "libusb")) + goto fail; + + return urbdrc_load_udevman_addin(&urbdrc->iface, urbdrc->subsystem, args); +fail: + return status; +} + +UINT stream_write_and_free(IWTSPlugin* plugin, IWTSVirtualChannel* channel, wStream* out) +{ + UINT rc = 0; + URBDRC_PLUGIN* urbdrc = (URBDRC_PLUGIN*)plugin; + + if (!out) + return ERROR_INVALID_PARAMETER; + + if (!channel || !out || !urbdrc) + { + Stream_Free(out, TRUE); + return ERROR_INVALID_PARAMETER; + } + + if (!channel->Write) + { + Stream_Free(out, TRUE); + return ERROR_INTERNAL_ERROR; + } + + urbdrc_dump_message(urbdrc->log, TRUE, TRUE, out); + rc = channel->Write(channel, Stream_GetPosition(out), Stream_Buffer(out), NULL); + Stream_Free(out, TRUE); + return rc; +} diff --git a/channels/urbdrc/client/urbdrc_main.h b/channels/urbdrc/client/urbdrc_main.h new file mode 100644 index 0000000..d13ed95 --- /dev/null +++ b/channels/urbdrc/client/urbdrc_main.h @@ -0,0 +1,222 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * RemoteFX USB Redirection + * + * Copyright 2012 Atrust corp. + * Copyright 2012 Alfred Liu <alfred.liu@atruscorp.com> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FREERDP_CHANNEL_URBDRC_CLIENT_MAIN_H +#define FREERDP_CHANNEL_URBDRC_CLIENT_MAIN_H + +#include <winpr/pool.h> +#include <freerdp/channels/log.h> +#include <freerdp/client/channels.h> + +#define DEVICE_HARDWARE_ID_SIZE 32 +#define DEVICE_COMPATIBILITY_ID_SIZE 36 +#define DEVICE_INSTANCE_STR_SIZE 37 +#define DEVICE_CONTAINER_STR_SIZE 39 + +#define TAG CHANNELS_TAG("urbdrc.client") +#ifdef WITH_DEBUG_DVC +#define DEBUG_DVC(...) WLog_DBG(TAG, __VA_ARGS__) +#else +#define DEBUG_DVC(...) \ + do \ + { \ + } while (0) +#endif + +typedef struct S_IUDEVICE IUDEVICE; +typedef struct S_IUDEVMAN IUDEVMAN; + +#define BASIC_DEV_STATE_DEFINED(_arg, _type) \ + _type (*get_##_arg)(IUDEVICE * pdev); \ + void (*set_##_arg)(IUDEVICE * pdev, _type _arg) + +#define BASIC_DEVMAN_STATE_DEFINED(_arg, _type) \ + _type (*get_##_arg)(IUDEVMAN * udevman); \ + void (*set_##_arg)(IUDEVMAN * udevman, _type _arg) + +typedef struct +{ + IWTSPlugin iface; + + GENERIC_LISTENER_CALLBACK* listener_callback; + + IUDEVMAN* udevman; + UINT32 vchannel_status; + char* subsystem; + + wLog* log; + IWTSListener* listener; + BOOL initialized; +} URBDRC_PLUGIN; + +typedef BOOL (*PREGISTERURBDRCSERVICE)(IWTSPlugin* plugin, IUDEVMAN* udevman); +typedef struct +{ + IWTSPlugin* plugin; + PREGISTERURBDRCSERVICE pRegisterUDEVMAN; + const ADDIN_ARGV* args; +} FREERDP_URBDRC_SERVICE_ENTRY_POINTS; +typedef FREERDP_URBDRC_SERVICE_ENTRY_POINTS* PFREERDP_URBDRC_SERVICE_ENTRY_POINTS; + +typedef int (*PFREERDP_URBDRC_DEVICE_ENTRY)(PFREERDP_URBDRC_SERVICE_ENTRY_POINTS pEntryPoints); + +typedef struct +{ + GENERIC_CHANNEL_CALLBACK* callback; + URBDRC_PLUGIN* urbdrc; + IUDEVMAN* udevman; + IWTSVirtualChannel* channel; + wStream* s; +} TRANSFER_DATA; + +typedef void (*t_isoch_transfer_cb)(IUDEVICE* idev, GENERIC_CHANNEL_CALLBACK* callback, + wStream* out, UINT32 InterfaceId, BOOL noAck, UINT32 MessageId, + UINT32 RequestId, UINT32 NumberOfPackets, UINT32 status, + UINT32 StartFrame, UINT32 ErrorCount, UINT32 OutputBufferSize); + +struct S_IUDEVICE +{ + /* Transfer */ + int (*isoch_transfer)(IUDEVICE* idev, GENERIC_CHANNEL_CALLBACK* callback, UINT32 MessageId, + UINT32 RequestId, UINT32 EndpointAddress, UINT32 TransferFlags, + UINT32 StartFrame, UINT32 ErrorCount, BOOL NoAck, + const BYTE* packetDescriptorData, UINT32 NumberOfPackets, + UINT32 BufferSize, const BYTE* Buffer, t_isoch_transfer_cb cb, + UINT32 Timeout); + + BOOL(*control_transfer) + (IUDEVICE* idev, UINT32 RequestId, UINT32 EndpointAddress, UINT32 TransferFlags, + BYTE bmRequestType, BYTE Request, UINT16 Value, UINT16 Index, UINT32* UrbdStatus, + UINT32* BufferSize, BYTE* Buffer, UINT32 Timeout); + + int (*bulk_or_interrupt_transfer)(IUDEVICE* idev, GENERIC_CHANNEL_CALLBACK* callback, + UINT32 MessageId, UINT32 RequestId, UINT32 EndpointAddress, + UINT32 TransferFlags, BOOL NoAck, UINT32 BufferSize, + const BYTE* data, t_isoch_transfer_cb cb, UINT32 Timeout); + + int (*select_configuration)(IUDEVICE* idev, UINT32 bConfigurationValue); + + int (*select_interface)(IUDEVICE* idev, BYTE InterfaceNumber, BYTE AlternateSetting); + + int (*control_pipe_request)(IUDEVICE* idev, UINT32 RequestId, UINT32 EndpointAddress, + UINT32* UsbdStatus, int command); + + UINT32(*control_query_device_text) + (IUDEVICE* idev, UINT32 TextType, UINT16 LocaleId, UINT8* BufferSize, BYTE* Buffer); + + int (*os_feature_descriptor_request)(IUDEVICE* idev, UINT32 RequestId, BYTE Recipient, + BYTE InterfaceNumber, BYTE Ms_PageIndex, + UINT16 Ms_featureDescIndex, UINT32* UsbdStatus, + UINT32* BufferSize, BYTE* Buffer, UINT32 Timeout); + + void (*cancel_all_transfer_request)(IUDEVICE* idev); + + int (*cancel_transfer_request)(IUDEVICE* idev, UINT32 RequestId); + + int (*query_device_descriptor)(IUDEVICE* idev, int offset); + + BOOL (*detach_kernel_driver)(IUDEVICE* idev); + + BOOL (*attach_kernel_driver)(IUDEVICE* idev); + + int (*query_device_port_status)(IUDEVICE* idev, UINT32* UsbdStatus, UINT32* BufferSize, + BYTE* Buffer); + + MSUSB_CONFIG_DESCRIPTOR* (*complete_msconfig_setup)(IUDEVICE* idev, + MSUSB_CONFIG_DESCRIPTOR* MsConfig); + /* Basic state */ + int (*isCompositeDevice)(IUDEVICE* idev); + + int (*isExist)(IUDEVICE* idev); + int (*isAlreadySend)(IUDEVICE* idev); + int (*isChannelClosed)(IUDEVICE* idev); + + void (*setAlreadySend)(IUDEVICE* idev); + void (*setChannelClosed)(IUDEVICE* idev); + void (*markChannelClosed)(IUDEVICE* idev); + char* (*getPath)(IUDEVICE* idev); + + void (*free)(IUDEVICE* idev); + + BASIC_DEV_STATE_DEFINED(channelManager, IWTSVirtualChannelManager*); + BASIC_DEV_STATE_DEFINED(channelID, UINT32); + BASIC_DEV_STATE_DEFINED(UsbDevice, UINT32); + BASIC_DEV_STATE_DEFINED(ReqCompletion, UINT32); + BASIC_DEV_STATE_DEFINED(bus_number, BYTE); + BASIC_DEV_STATE_DEFINED(dev_number, BYTE); + BASIC_DEV_STATE_DEFINED(port_number, int); + BASIC_DEV_STATE_DEFINED(MsConfig, MSUSB_CONFIG_DESCRIPTOR*); + + BASIC_DEV_STATE_DEFINED(p_udev, void*); + BASIC_DEV_STATE_DEFINED(p_prev, void*); + BASIC_DEV_STATE_DEFINED(p_next, void*); +}; + +struct S_IUDEVMAN +{ + /* Standard */ + void (*free)(IUDEVMAN* idevman); + + /* Manage devices */ + void (*rewind)(IUDEVMAN* idevman); + BOOL (*has_next)(IUDEVMAN* idevman); + BOOL (*unregister_udevice)(IUDEVMAN* idevman, BYTE bus_number, BYTE dev_number); + size_t (*register_udevice)(IUDEVMAN* idevman, BYTE bus_number, BYTE dev_number, UINT16 idVendor, + UINT16 idProduct, UINT32 flag); + IUDEVICE* (*get_next)(IUDEVMAN* idevman); + IUDEVICE* (*get_udevice_by_UsbDevice)(IUDEVMAN* idevman, UINT32 UsbDevice); + IUDEVICE* (*get_udevice_by_ChannelID)(IUDEVMAN* idevman, UINT32 channelID); + + /* Extension */ + int (*isAutoAdd)(IUDEVMAN* idevman); + + /* Basic state */ + BASIC_DEVMAN_STATE_DEFINED(device_num, UINT32); + BASIC_DEVMAN_STATE_DEFINED(next_device_id, UINT32); + + /* control semaphore or mutex lock */ + void (*loading_lock)(IUDEVMAN* idevman); + void (*loading_unlock)(IUDEVMAN* idevman); + BOOL (*initialize)(IUDEVMAN* idevman, UINT32 channelId); + UINT (*listener_created_callback)(IUDEVMAN* idevman); + + IWTSPlugin* plugin; + UINT32 controlChannelId; + UINT32 status; +}; + +#define DEVICE_ADD_FLAG_BUS 0x01 +#define DEVICE_ADD_FLAG_DEV 0x02 +#define DEVICE_ADD_FLAG_VENDOR 0x04 +#define DEVICE_ADD_FLAG_PRODUCT 0x08 +#define DEVICE_ADD_FLAG_REGISTER 0x10 + +#define DEVICE_ADD_FLAG_ALL \ + (DEVICE_ADD_FLAG_BUS | DEVICE_ADD_FLAG_DEV | DEVICE_ADD_FLAG_VENDOR | \ + DEVICE_ADD_FLAG_PRODUCT | DEVICE_ADD_FLAG_REGISTER) + +FREERDP_API BOOL add_device(IUDEVMAN* idevman, UINT32 flags, BYTE busnum, BYTE devnum, + UINT16 idVendor, UINT16 idProduct); +FREERDP_API BOOL del_device(IUDEVMAN* idevman, UINT32 flags, BYTE busnum, BYTE devnum, + UINT16 idVendor, UINT16 idProduct); + +UINT stream_write_and_free(IWTSPlugin* plugin, IWTSVirtualChannel* channel, wStream* s); + +#endif /* FREERDP_CHANNEL_URBDRC_CLIENT_MAIN_H */ diff --git a/channels/urbdrc/common/CMakeLists.txt b/channels/urbdrc/common/CMakeLists.txt new file mode 100644 index 0000000..df9a8a9 --- /dev/null +++ b/channels/urbdrc/common/CMakeLists.txt @@ -0,0 +1,29 @@ +# FreeRDP: A Remote Desktop Protocol Implementation +# FreeRDP cmake build script +# +# Copyright 2019 Armin Novak <armin.novak@thincast.com> +# Copyright 2019 Thincast Technologies GmbH +# +# 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(SRCS + urbdrc_types.h + urbdrc_helpers.h + urbdrc_helpers.c + msusb.h + msusb.c) + +add_library(urbdrc-common STATIC ${SRCS}) + +channel_install(urbdrc-common ${FREERDP_ADDIN_PATH} "FreeRDPTargets") + diff --git a/channels/urbdrc/common/msusb.c b/channels/urbdrc/common/msusb.c new file mode 100644 index 0000000..dd76b1d --- /dev/null +++ b/channels/urbdrc/common/msusb.c @@ -0,0 +1,395 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * RemoteFX USB Redirection + * + * Copyright 2012 Atrust corp. + * Copyright 2012 Alfred Liu <alfred.liu@atruscorp.com> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <freerdp/log.h> +#include <msusb.h> + +#define TAG FREERDP_TAG("utils") + +static MSUSB_PIPE_DESCRIPTOR* msusb_mspipe_new(void) +{ + return (MSUSB_PIPE_DESCRIPTOR*)calloc(1, sizeof(MSUSB_PIPE_DESCRIPTOR)); +} + +static void msusb_mspipes_free(MSUSB_PIPE_DESCRIPTOR** MsPipes, UINT32 NumberOfPipes) +{ + if (MsPipes) + { + for (UINT32 pnum = 0; pnum < NumberOfPipes && MsPipes[pnum]; pnum++) + free(MsPipes[pnum]); + + free(MsPipes); + } +} + +BOOL msusb_mspipes_replace(MSUSB_INTERFACE_DESCRIPTOR* MsInterface, + MSUSB_PIPE_DESCRIPTOR** NewMsPipes, UINT32 NewNumberOfPipes) +{ + if (!MsInterface || !NewMsPipes) + return FALSE; + + /* free orignal MsPipes */ + msusb_mspipes_free(MsInterface->MsPipes, MsInterface->NumberOfPipes); + /* And replace it */ + MsInterface->MsPipes = NewMsPipes; + MsInterface->NumberOfPipes = NewNumberOfPipes; + return TRUE; +} + +static MSUSB_PIPE_DESCRIPTOR** msusb_mspipes_read(wStream* s, UINT32 NumberOfPipes) +{ + MSUSB_PIPE_DESCRIPTOR** MsPipes = NULL; + + if (!Stream_CheckAndLogRequiredCapacityOfSize(TAG, (s), NumberOfPipes, 12ull)) + return NULL; + + MsPipes = (MSUSB_PIPE_DESCRIPTOR**)calloc(NumberOfPipes, sizeof(MSUSB_PIPE_DESCRIPTOR*)); + + if (!MsPipes) + return NULL; + + for (UINT32 pnum = 0; pnum < NumberOfPipes; pnum++) + { + MSUSB_PIPE_DESCRIPTOR* MsPipe = msusb_mspipe_new(); + + if (!MsPipe) + goto out_error; + + Stream_Read_UINT16(s, MsPipe->MaximumPacketSize); + Stream_Seek(s, 2); + Stream_Read_UINT32(s, MsPipe->MaximumTransferSize); + Stream_Read_UINT32(s, MsPipe->PipeFlags); + /* Already set to zero by memset + MsPipe->PipeHandle = 0; + MsPipe->bEndpointAddress = 0; + MsPipe->bInterval = 0; + MsPipe->PipeType = 0; + MsPipe->InitCompleted = 0; + */ + MsPipes[pnum] = MsPipe; + } + + return MsPipes; +out_error: + + for (UINT32 pnum = 0; pnum < NumberOfPipes; pnum++) + free(MsPipes[pnum]); + + free(MsPipes); + return NULL; +} + +static MSUSB_INTERFACE_DESCRIPTOR* msusb_msinterface_new(void) +{ + return (MSUSB_INTERFACE_DESCRIPTOR*)calloc(1, sizeof(MSUSB_INTERFACE_DESCRIPTOR)); +} + +void msusb_msinterface_free(MSUSB_INTERFACE_DESCRIPTOR* MsInterface) +{ + if (MsInterface) + { + msusb_mspipes_free(MsInterface->MsPipes, MsInterface->NumberOfPipes); + MsInterface->MsPipes = NULL; + free(MsInterface); + } +} + +static void msusb_msinterface_free_list(MSUSB_INTERFACE_DESCRIPTOR** MsInterfaces, + UINT32 NumInterfaces) +{ + if (MsInterfaces) + { + for (UINT32 inum = 0; inum < NumInterfaces; inum++) + { + msusb_msinterface_free(MsInterfaces[inum]); + } + + free(MsInterfaces); + } +} + +BOOL msusb_msinterface_replace(MSUSB_CONFIG_DESCRIPTOR* MsConfig, BYTE InterfaceNumber, + MSUSB_INTERFACE_DESCRIPTOR* NewMsInterface) +{ + if (!MsConfig || !MsConfig->MsInterfaces) + return FALSE; + + msusb_msinterface_free(MsConfig->MsInterfaces[InterfaceNumber]); + MsConfig->MsInterfaces[InterfaceNumber] = NewMsInterface; + return TRUE; +} + +MSUSB_INTERFACE_DESCRIPTOR* msusb_msinterface_read(wStream* s) +{ + MSUSB_INTERFACE_DESCRIPTOR* MsInterface = NULL; + + if (!Stream_CheckAndLogRequiredCapacity(TAG, (s), 12)) + return NULL; + + MsInterface = msusb_msinterface_new(); + + if (!MsInterface) + return NULL; + + Stream_Read_UINT16(s, MsInterface->Length); + Stream_Read_UINT16(s, MsInterface->NumberOfPipesExpected); + Stream_Read_UINT8(s, MsInterface->InterfaceNumber); + Stream_Read_UINT8(s, MsInterface->AlternateSetting); + Stream_Seek(s, 2); + Stream_Read_UINT32(s, MsInterface->NumberOfPipes); + MsInterface->InterfaceHandle = 0; + MsInterface->bInterfaceClass = 0; + MsInterface->bInterfaceSubClass = 0; + MsInterface->bInterfaceProtocol = 0; + MsInterface->InitCompleted = 0; + MsInterface->MsPipes = NULL; + + if (MsInterface->NumberOfPipes > 0) + { + MsInterface->MsPipes = msusb_mspipes_read(s, MsInterface->NumberOfPipes); + + if (!MsInterface->MsPipes) + goto out_error; + } + + return MsInterface; +out_error: + msusb_msinterface_free(MsInterface); + return NULL; +} + +BOOL msusb_msinterface_write(MSUSB_INTERFACE_DESCRIPTOR* MsInterface, wStream* out) +{ + MSUSB_PIPE_DESCRIPTOR** MsPipes = NULL; + MSUSB_PIPE_DESCRIPTOR* MsPipe = NULL; + + if (!MsInterface) + return FALSE; + + if (!Stream_EnsureRemainingCapacity(out, 16 + MsInterface->NumberOfPipes * 20)) + return FALSE; + + /* Length */ + Stream_Write_UINT16(out, MsInterface->Length); + /* InterfaceNumber */ + Stream_Write_UINT8(out, MsInterface->InterfaceNumber); + /* AlternateSetting */ + Stream_Write_UINT8(out, MsInterface->AlternateSetting); + /* bInterfaceClass */ + Stream_Write_UINT8(out, MsInterface->bInterfaceClass); + /* bInterfaceSubClass */ + Stream_Write_UINT8(out, MsInterface->bInterfaceSubClass); + /* bInterfaceProtocol */ + Stream_Write_UINT8(out, MsInterface->bInterfaceProtocol); + /* Padding */ + Stream_Write_UINT8(out, 0); + /* InterfaceHandle */ + Stream_Write_UINT32(out, MsInterface->InterfaceHandle); + /* NumberOfPipes */ + Stream_Write_UINT32(out, MsInterface->NumberOfPipes); + /* Pipes */ + MsPipes = MsInterface->MsPipes; + + for (UINT32 pnum = 0; pnum < MsInterface->NumberOfPipes; pnum++) + { + MsPipe = MsPipes[pnum]; + /* MaximumPacketSize */ + Stream_Write_UINT16(out, MsPipe->MaximumPacketSize); + /* EndpointAddress */ + Stream_Write_UINT8(out, MsPipe->bEndpointAddress); + /* Interval */ + Stream_Write_UINT8(out, MsPipe->bInterval); + /* PipeType */ + Stream_Write_UINT32(out, MsPipe->PipeType); + /* PipeHandle */ + Stream_Write_UINT32(out, MsPipe->PipeHandle); + /* MaximumTransferSize */ + Stream_Write_UINT32(out, MsPipe->MaximumTransferSize); + /* PipeFlags */ + Stream_Write_UINT32(out, MsPipe->PipeFlags); + } + + return TRUE; +} + +static MSUSB_INTERFACE_DESCRIPTOR** msusb_msinterface_read_list(wStream* s, UINT32 NumInterfaces) +{ + MSUSB_INTERFACE_DESCRIPTOR** MsInterfaces = NULL; + MsInterfaces = + (MSUSB_INTERFACE_DESCRIPTOR**)calloc(NumInterfaces, sizeof(MSUSB_INTERFACE_DESCRIPTOR*)); + + if (!MsInterfaces) + return NULL; + + for (UINT32 inum = 0; inum < NumInterfaces; inum++) + { + MsInterfaces[inum] = msusb_msinterface_read(s); + + if (!MsInterfaces[inum]) + goto fail; + } + + return MsInterfaces; +fail: + + for (UINT32 inum = 0; inum < NumInterfaces; inum++) + msusb_msinterface_free(MsInterfaces[inum]); + + free(MsInterfaces); + return NULL; +} + +BOOL msusb_msconfig_write(MSUSB_CONFIG_DESCRIPTOR* MsConfg, wStream* out) +{ + MSUSB_INTERFACE_DESCRIPTOR** MsInterfaces = NULL; + MSUSB_INTERFACE_DESCRIPTOR* MsInterface = NULL; + + if (!MsConfg) + return FALSE; + + if (!Stream_EnsureRemainingCapacity(out, 8)) + return FALSE; + + /* ConfigurationHandle*/ + Stream_Write_UINT32(out, MsConfg->ConfigurationHandle); + /* NumInterfaces*/ + Stream_Write_UINT32(out, MsConfg->NumInterfaces); + /* Interfaces */ + MsInterfaces = MsConfg->MsInterfaces; + + for (UINT32 inum = 0; inum < MsConfg->NumInterfaces; inum++) + { + MsInterface = MsInterfaces[inum]; + + if (!msusb_msinterface_write(MsInterface, out)) + return FALSE; + } + + return TRUE; +} + +MSUSB_CONFIG_DESCRIPTOR* msusb_msconfig_new(void) +{ + return (MSUSB_CONFIG_DESCRIPTOR*)calloc(1, sizeof(MSUSB_CONFIG_DESCRIPTOR)); +} + +void msusb_msconfig_free(MSUSB_CONFIG_DESCRIPTOR* MsConfig) +{ + if (MsConfig) + { + msusb_msinterface_free_list(MsConfig->MsInterfaces, MsConfig->NumInterfaces); + MsConfig->MsInterfaces = NULL; + free(MsConfig); + } +} + +MSUSB_CONFIG_DESCRIPTOR* msusb_msconfig_read(wStream* s, UINT32 NumInterfaces) +{ + MSUSB_CONFIG_DESCRIPTOR* MsConfig = NULL; + BYTE lenConfiguration = 0; + BYTE typeConfiguration = 0; + + if (!Stream_CheckAndLogRequiredCapacityOfSize(TAG, (s), 3ULL + NumInterfaces, 2ULL)) + return NULL; + + MsConfig = msusb_msconfig_new(); + + if (!MsConfig) + goto fail; + + MsConfig->MsInterfaces = msusb_msinterface_read_list(s, NumInterfaces); + + if (!MsConfig->MsInterfaces) + goto fail; + + Stream_Read_UINT8(s, lenConfiguration); + Stream_Read_UINT8(s, typeConfiguration); + + if (lenConfiguration != 0x9 || typeConfiguration != 0x2) + { + WLog_ERR(TAG, "len and type must be 0x9 and 0x2 , but it is 0x%" PRIx8 " and 0x%" PRIx8 "", + lenConfiguration, typeConfiguration); + goto fail; + } + + Stream_Read_UINT16(s, MsConfig->wTotalLength); + Stream_Seek(s, 1); + Stream_Read_UINT8(s, MsConfig->bConfigurationValue); + MsConfig->NumInterfaces = NumInterfaces; + return MsConfig; +fail: + msusb_msconfig_free(MsConfig); + return NULL; +} + +void msusb_msconfig_dump(MSUSB_CONFIG_DESCRIPTOR* MsConfig) +{ + MSUSB_INTERFACE_DESCRIPTOR** MsInterfaces = NULL; + MSUSB_INTERFACE_DESCRIPTOR* MsInterface = NULL; + MSUSB_PIPE_DESCRIPTOR** MsPipes = NULL; + MSUSB_PIPE_DESCRIPTOR* MsPipe = NULL; + + WLog_INFO(TAG, "=================MsConfig:========================"); + WLog_INFO(TAG, "wTotalLength:%" PRIu16 "", MsConfig->wTotalLength); + WLog_INFO(TAG, "bConfigurationValue:%" PRIu8 "", MsConfig->bConfigurationValue); + WLog_INFO(TAG, "ConfigurationHandle:0x%08" PRIx32 "", MsConfig->ConfigurationHandle); + WLog_INFO(TAG, "InitCompleted:%d", MsConfig->InitCompleted); + WLog_INFO(TAG, "MsOutSize:%d", MsConfig->MsOutSize); + WLog_INFO(TAG, "NumInterfaces:%" PRIu32 "", MsConfig->NumInterfaces); + MsInterfaces = MsConfig->MsInterfaces; + + for (UINT32 inum = 0; inum < MsConfig->NumInterfaces; inum++) + { + MsInterface = MsInterfaces[inum]; + WLog_INFO(TAG, " Interface: %" PRIu8 "", MsInterface->InterfaceNumber); + WLog_INFO(TAG, " Length: %" PRIu16 "", MsInterface->Length); + WLog_INFO(TAG, " NumberOfPipesExpected: %" PRIu16 "", + MsInterface->NumberOfPipesExpected); + WLog_INFO(TAG, " AlternateSetting: %" PRIu8 "", MsInterface->AlternateSetting); + WLog_INFO(TAG, " NumberOfPipes: %" PRIu32 "", MsInterface->NumberOfPipes); + WLog_INFO(TAG, " InterfaceHandle: 0x%08" PRIx32 "", MsInterface->InterfaceHandle); + WLog_INFO(TAG, " bInterfaceClass: 0x%02" PRIx8 "", MsInterface->bInterfaceClass); + WLog_INFO(TAG, " bInterfaceSubClass: 0x%02" PRIx8 "", MsInterface->bInterfaceSubClass); + WLog_INFO(TAG, " bInterfaceProtocol: 0x%02" PRIx8 "", MsInterface->bInterfaceProtocol); + WLog_INFO(TAG, " InitCompleted: %d", MsInterface->InitCompleted); + MsPipes = MsInterface->MsPipes; + + for (UINT32 pnum = 0; pnum < MsInterface->NumberOfPipes; pnum++) + { + MsPipe = MsPipes[pnum]; + WLog_INFO(TAG, " Pipe: %" PRIu32, pnum); + WLog_INFO(TAG, " MaximumPacketSize: 0x%04" PRIx16 "", MsPipe->MaximumPacketSize); + WLog_INFO(TAG, " MaximumTransferSize: 0x%08" PRIx32 "", + MsPipe->MaximumTransferSize); + WLog_INFO(TAG, " PipeFlags: 0x%08" PRIx32 "", MsPipe->PipeFlags); + WLog_INFO(TAG, " PipeHandle: 0x%08" PRIx32 "", MsPipe->PipeHandle); + WLog_INFO(TAG, " bEndpointAddress: 0x%02" PRIx8 "", MsPipe->bEndpointAddress); + WLog_INFO(TAG, " bInterval: %" PRIu8 "", MsPipe->bInterval); + WLog_INFO(TAG, " PipeType: 0x%02" PRIx8 "", MsPipe->PipeType); + WLog_INFO(TAG, " InitCompleted: %d", MsPipe->InitCompleted); + } + } + + WLog_INFO(TAG, "=================================================="); +} diff --git a/channels/urbdrc/common/msusb.h b/channels/urbdrc/common/msusb.h new file mode 100644 index 0000000..6ce843f --- /dev/null +++ b/channels/urbdrc/common/msusb.h @@ -0,0 +1,98 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * RemoteFX USB Redirection + * + * Copyright 2012 Atrust corp. + * Copyright 2012 Alfred Liu <alfred.liu@atruscorp.com> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FREERDP_UTILS_MSCONFIG_H +#define FREERDP_UTILS_MSCONFIG_H + +#include <winpr/stream.h> +#include <freerdp/api.h> + +typedef struct +{ + UINT16 MaximumPacketSize; + UINT32 MaximumTransferSize; + UINT32 PipeFlags; + UINT32 PipeHandle; + BYTE bEndpointAddress; + BYTE bInterval; + BYTE PipeType; + int InitCompleted; +} MSUSB_PIPE_DESCRIPTOR; + +typedef struct +{ + UINT16 Length; + UINT16 NumberOfPipesExpected; + BYTE InterfaceNumber; + BYTE AlternateSetting; + UINT32 NumberOfPipes; + UINT32 InterfaceHandle; + BYTE bInterfaceClass; + BYTE bInterfaceSubClass; + BYTE bInterfaceProtocol; + MSUSB_PIPE_DESCRIPTOR** MsPipes; + int InitCompleted; +} MSUSB_INTERFACE_DESCRIPTOR; + +typedef struct +{ + UINT16 wTotalLength; + BYTE bConfigurationValue; + UINT32 ConfigurationHandle; + UINT32 NumInterfaces; + MSUSB_INTERFACE_DESCRIPTOR** MsInterfaces; + int InitCompleted; + int MsOutSize; +} MSUSB_CONFIG_DESCRIPTOR; + +#ifdef __cplusplus +extern "C" +{ +#endif + + /* MSUSB_PIPE exported functions */ + FREERDP_API BOOL msusb_mspipes_replace(MSUSB_INTERFACE_DESCRIPTOR* MsInterface, + MSUSB_PIPE_DESCRIPTOR** NewMsPipes, + UINT32 NewNumberOfPipes); + + /* MSUSB_INTERFACE exported functions */ + FREERDP_API BOOL msusb_msinterface_replace(MSUSB_CONFIG_DESCRIPTOR* MsConfig, + BYTE InterfaceNumber, + MSUSB_INTERFACE_DESCRIPTOR* NewMsInterface); + FREERDP_API MSUSB_INTERFACE_DESCRIPTOR* msusb_msinterface_read(wStream* out); + FREERDP_API BOOL msusb_msinterface_write(MSUSB_INTERFACE_DESCRIPTOR* MsInterface, wStream* out); + FREERDP_API void msusb_msinterface_free(MSUSB_INTERFACE_DESCRIPTOR* MsInterface); + + /* MSUSB_CONFIG exported functions */ + FREERDP_API void msusb_msconfig_free(MSUSB_CONFIG_DESCRIPTOR* MsConfig); + + WINPR_ATTR_MALLOC(msusb_msconfig_free, 1) + FREERDP_API MSUSB_CONFIG_DESCRIPTOR* msusb_msconfig_new(void); + + WINPR_ATTR_MALLOC(msusb_msconfig_free, 1) + FREERDP_API MSUSB_CONFIG_DESCRIPTOR* msusb_msconfig_read(wStream* s, UINT32 NumInterfaces); + FREERDP_API BOOL msusb_msconfig_write(MSUSB_CONFIG_DESCRIPTOR* MsConfg, wStream* out); + FREERDP_API void msusb_msconfig_dump(MSUSB_CONFIG_DESCRIPTOR* MsConfg); + +#ifdef __cplusplus +} +#endif + +#endif /* FREERDP_UTILS_MSCONFIG_H */ diff --git a/channels/urbdrc/common/urbdrc_helpers.c b/channels/urbdrc/common/urbdrc_helpers.c new file mode 100644 index 0000000..9d6bb24 --- /dev/null +++ b/channels/urbdrc/common/urbdrc_helpers.c @@ -0,0 +1,425 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * Server USB redirection channel - helper functions + * + * Copyright 2019 Armin Novak <armin.novak@thincast.com> + * Copyright 2019 Thincast Technologies GmbH + * + * 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 <freerdp/config.h> + +#include "urbdrc_helpers.h" +#include "urbdrc_types.h" +#include <winpr/print.h> + +const char* mask_to_string(UINT32 mask) +{ + switch (mask) + { + case STREAM_ID_NONE: + return "STREAM_ID_NONE"; + + case STREAM_ID_PROXY: + return "STREAM_ID_PROXY"; + + case STREAM_ID_STUB: + return "STREAM_ID_STUB"; + + default: + return "UNKNOWN"; + } +} +const char* interface_to_string(UINT32 id) +{ + switch (id) + { + case CAPABILITIES_NEGOTIATOR: + return "CAPABILITIES_NEGOTIATOR"; + + case SERVER_CHANNEL_NOTIFICATION: + return "SERVER_CHANNEL_NOTIFICATION"; + + case CLIENT_CHANNEL_NOTIFICATION: + return "CLIENT_CHANNEL_NOTIFICATION"; + + default: + return "DEVICE_MESSAGE"; + } +} + +static const char* call_to_string_none(BOOL client, UINT32 interfaceId, UINT32 functionId) +{ + WINPR_UNUSED(interfaceId); + + if (client) + return "RIM_EXCHANGE_CAPABILITY_RESPONSE [none |client]"; + else + { + switch (functionId) + { + case RIM_EXCHANGE_CAPABILITY_REQUEST: + return "RIM_EXCHANGE_CAPABILITY_REQUEST [none |server]"; + + case RIMCALL_RELEASE: + return "RIMCALL_RELEASE [none |server]"; + + case RIMCALL_QUERYINTERFACE: + return "RIMCALL_QUERYINTERFACE [none |server]"; + + default: + return "UNKNOWN [none |server]"; + } + } +} + +static const char* call_to_string_proxy_server(UINT32 functionId) +{ + switch (functionId) + { + case QUERY_DEVICE_TEXT: + return "QUERY_DEVICE_TEXT [proxy|server]"; + + case INTERNAL_IO_CONTROL: + return "INTERNAL_IO_CONTROL [proxy|server]"; + + case IO_CONTROL: + return "IO_CONTROL [proxy|server]"; + + case REGISTER_REQUEST_CALLBACK: + return "REGISTER_REQUEST_CALLBACK [proxy|server]"; + + case CANCEL_REQUEST: + return "CANCEL_REQUEST [proxy|server]"; + + case RETRACT_DEVICE: + return "RETRACT_DEVICE [proxy|server]"; + + case TRANSFER_IN_REQUEST: + return "TRANSFER_IN_REQUEST [proxy|server]"; + + case TRANSFER_OUT_REQUEST: + return "TRANSFER_OUT_REQUEST [proxy|server]"; + + default: + return "UNKNOWN [proxy|server]"; + } +} + +static const char* call_to_string_proxy_client(UINT32 functionId) +{ + switch (functionId) + { + case URB_COMPLETION_NO_DATA: + return "URB_COMPLETION_NO_DATA [proxy|client]"; + + case URB_COMPLETION: + return "URB_COMPLETION [proxy|client]"; + + case IOCONTROL_COMPLETION: + return "IOCONTROL_COMPLETION [proxy|client]"; + + case TRANSFER_OUT_REQUEST: + return "TRANSFER_OUT_REQUEST [proxy|client]"; + + default: + return "UNKNOWN [proxy|client]"; + } +} + +static const char* call_to_string_proxy(BOOL client, UINT32 interfaceId, UINT32 functionId) +{ + switch (interfaceId & INTERFACE_ID_MASK) + { + case CLIENT_DEVICE_SINK: + switch (functionId) + { + case ADD_VIRTUAL_CHANNEL: + return "ADD_VIRTUAL_CHANNEL [proxy|sink ]"; + + case ADD_DEVICE: + return "ADD_DEVICE [proxy|sink ]"; + case RIMCALL_RELEASE: + return "RIMCALL_RELEASE [proxy|sink ]"; + + case RIMCALL_QUERYINTERFACE: + return "RIMCALL_QUERYINTERFACE [proxy|sink ]"; + default: + return "UNKNOWN [proxy|sink ]"; + } + + case SERVER_CHANNEL_NOTIFICATION: + switch (functionId) + { + case CHANNEL_CREATED: + return "CHANNEL_CREATED [proxy|server]"; + + case RIMCALL_RELEASE: + return "RIMCALL_RELEASE [proxy|server]"; + + case RIMCALL_QUERYINTERFACE: + return "RIMCALL_QUERYINTERFACE [proxy|server]"; + + default: + return "UNKNOWN [proxy|server]"; + } + + case CLIENT_CHANNEL_NOTIFICATION: + switch (functionId) + { + case CHANNEL_CREATED: + return "CHANNEL_CREATED [proxy|client]"; + case RIMCALL_RELEASE: + return "RIMCALL_RELEASE [proxy|client]"; + case RIMCALL_QUERYINTERFACE: + return "RIMCALL_QUERYINTERFACE [proxy|client]"; + default: + return "UNKNOWN [proxy|client]"; + } + + default: + if (client) + return call_to_string_proxy_client(functionId); + else + return call_to_string_proxy_server(functionId); + } +} + +static const char* call_to_string_stub(BOOL client, UINT32 interfaceNr, UINT32 functionId) +{ + return "QUERY_DEVICE_TEXT_RSP [stub |client]"; +} + +const char* call_to_string(BOOL client, UINT32 interfaceNr, UINT32 functionId) +{ + const UINT32 mask = (interfaceNr & STREAM_ID_MASK) >> 30; + const UINT32 interfaceId = interfaceNr & INTERFACE_ID_MASK; + + switch (mask) + { + case STREAM_ID_NONE: + return call_to_string_none(client, interfaceId, functionId); + + case STREAM_ID_PROXY: + return call_to_string_proxy(client, interfaceId, functionId); + + case STREAM_ID_STUB: + return call_to_string_stub(client, interfaceId, functionId); + + default: + return "UNKNOWN[mask]"; + } +} + +const char* urb_function_string(UINT16 urb) +{ + switch (urb) + { + case TS_URB_SELECT_CONFIGURATION: + return "TS_URB_SELECT_CONFIGURATION"; + + case TS_URB_SELECT_INTERFACE: + return "TS_URB_SELECT_INTERFACE"; + + case TS_URB_PIPE_REQUEST: + return "TS_URB_PIPE_REQUEST"; + + case TS_URB_TAKE_FRAME_LENGTH_CONTROL: + return "TS_URB_TAKE_FRAME_LENGTH_CONTROL"; + + case TS_URB_RELEASE_FRAME_LENGTH_CONTROL: + return "TS_URB_RELEASE_FRAME_LENGTH_CONTROL"; + + case TS_URB_GET_FRAME_LENGTH: + return "TS_URB_GET_FRAME_LENGTH"; + + case TS_URB_SET_FRAME_LENGTH: + return "TS_URB_SET_FRAME_LENGTH"; + + case TS_URB_GET_CURRENT_FRAME_NUMBER: + return "TS_URB_GET_CURRENT_FRAME_NUMBER"; + + case TS_URB_CONTROL_TRANSFER: + return "TS_URB_CONTROL_TRANSFER"; + + case TS_URB_BULK_OR_INTERRUPT_TRANSFER: + return "TS_URB_BULK_OR_INTERRUPT_TRANSFER"; + + case TS_URB_ISOCH_TRANSFER: + return "TS_URB_ISOCH_TRANSFER"; + + case TS_URB_GET_DESCRIPTOR_FROM_DEVICE: + return "TS_URB_GET_DESCRIPTOR_FROM_DEVICE"; + + case TS_URB_SET_DESCRIPTOR_TO_DEVICE: + return "TS_URB_SET_DESCRIPTOR_TO_DEVICE"; + + case TS_URB_SET_FEATURE_TO_DEVICE: + return "TS_URB_SET_FEATURE_TO_DEVICE"; + + case TS_URB_SET_FEATURE_TO_INTERFACE: + return "TS_URB_SET_FEATURE_TO_INTERFACE"; + + case TS_URB_SET_FEATURE_TO_ENDPOINT: + return "TS_URB_SET_FEATURE_TO_ENDPOINT"; + + case TS_URB_CLEAR_FEATURE_TO_DEVICE: + return "TS_URB_CLEAR_FEATURE_TO_DEVICE"; + + case TS_URB_CLEAR_FEATURE_TO_INTERFACE: + return "TS_URB_CLEAR_FEATURE_TO_INTERFACE"; + + case TS_URB_CLEAR_FEATURE_TO_ENDPOINT: + return "TS_URB_CLEAR_FEATURE_TO_ENDPOINT"; + + case TS_URB_GET_STATUS_FROM_DEVICE: + return "TS_URB_GET_STATUS_FROM_DEVICE"; + + case TS_URB_GET_STATUS_FROM_INTERFACE: + return "TS_URB_GET_STATUS_FROM_INTERFACE"; + + case TS_URB_GET_STATUS_FROM_ENDPOINT: + return "TS_URB_GET_STATUS_FROM_ENDPOINT"; + + case TS_URB_RESERVED_0X0016: + return "TS_URB_RESERVED_0X0016"; + + case TS_URB_VENDOR_DEVICE: + return "TS_URB_VENDOR_DEVICE"; + + case TS_URB_VENDOR_INTERFACE: + return "TS_URB_VENDOR_INTERFACE"; + + case TS_URB_VENDOR_ENDPOINT: + return "TS_URB_VENDOR_ENDPOINT"; + + case TS_URB_CLASS_DEVICE: + return "TS_URB_CLASS_DEVICE"; + + case TS_URB_CLASS_INTERFACE: + return "TS_URB_CLASS_INTERFACE"; + + case TS_URB_CLASS_ENDPOINT: + return "TS_URB_CLASS_ENDPOINT"; + + case TS_URB_RESERVE_0X001D: + return "TS_URB_RESERVE_0X001D"; + + case TS_URB_SYNC_RESET_PIPE_AND_CLEAR_STALL: + return "TS_URB_SYNC_RESET_PIPE_AND_CLEAR_STALL"; + + case TS_URB_CLASS_OTHER: + return "TS_URB_CLASS_OTHER"; + + case TS_URB_VENDOR_OTHER: + return "TS_URB_VENDOR_OTHER"; + + case TS_URB_GET_STATUS_FROM_OTHER: + return "TS_URB_GET_STATUS_FROM_OTHER"; + + case TS_URB_CLEAR_FEATURE_TO_OTHER: + return "TS_URB_CLEAR_FEATURE_TO_OTHER"; + + case TS_URB_SET_FEATURE_TO_OTHER: + return "TS_URB_SET_FEATURE_TO_OTHER"; + + case TS_URB_GET_DESCRIPTOR_FROM_ENDPOINT: + return "TS_URB_GET_DESCRIPTOR_FROM_ENDPOINT"; + + case TS_URB_SET_DESCRIPTOR_TO_ENDPOINT: + return "TS_URB_SET_DESCRIPTOR_TO_ENDPOINT"; + + case TS_URB_CONTROL_GET_CONFIGURATION_REQUEST: + return "TS_URB_CONTROL_GET_CONFIGURATION_REQUEST"; + + case TS_URB_CONTROL_GET_INTERFACE_REQUEST: + return "TS_URB_CONTROL_GET_INTERFACE_REQUEST"; + + case TS_URB_GET_DESCRIPTOR_FROM_INTERFACE: + return "TS_URB_GET_DESCRIPTOR_FROM_INTERFACE"; + + case TS_URB_SET_DESCRIPTOR_TO_INTERFACE: + return "TS_URB_SET_DESCRIPTOR_TO_INTERFACE"; + + case TS_URB_GET_OS_FEATURE_DESCRIPTOR_REQUEST: + return "TS_URB_GET_OS_FEATURE_DESCRIPTOR_REQUEST"; + + case TS_URB_RESERVE_0X002B: + return "TS_URB_RESERVE_0X002B"; + + case TS_URB_RESERVE_0X002C: + return "TS_URB_RESERVE_0X002C"; + + case TS_URB_RESERVE_0X002D: + return "TS_URB_RESERVE_0X002D"; + + case TS_URB_RESERVE_0X002E: + return "TS_URB_RESERVE_0X002E"; + + case TS_URB_RESERVE_0X002F: + return "TS_URB_RESERVE_0X002F"; + + case TS_URB_SYNC_RESET_PIPE: + return "TS_URB_SYNC_RESET_PIPE"; + + case TS_URB_SYNC_CLEAR_STALL: + return "TS_URB_SYNC_CLEAR_STALL"; + + case TS_URB_CONTROL_TRANSFER_EX: + return "TS_URB_CONTROL_TRANSFER_EX"; + + default: + return "UNKNOWN"; + } +} + +void urbdrc_dump_message(wLog* log, BOOL client, BOOL write, wStream* s) +{ + const char* type = write ? "WRITE" : "READ"; + UINT32 InterfaceId = 0; + UINT32 MessageId = 0; + UINT32 FunctionId = 0; + size_t length = 0; + size_t pos = 0; + + pos = Stream_GetPosition(s); + if (write) + { + length = pos; + Stream_SetPosition(s, 0); + } + else + length = Stream_GetRemainingLength(s); + + if (length < 12) + return; + + Stream_Read_UINT32(s, InterfaceId); + Stream_Read_UINT32(s, MessageId); + Stream_Read_UINT32(s, FunctionId); + Stream_SetPosition(s, pos); + + WLog_Print(log, WLOG_DEBUG, + "[%-5s] %s [%08" PRIx32 "] InterfaceId=%08" PRIx32 ", MessageId=%08" PRIx32 + ", FunctionId=%08" PRIx32 ", length=%" PRIuz, + type, call_to_string(client, InterfaceId, FunctionId), FunctionId, InterfaceId, + MessageId, FunctionId, length); +#if defined(WITH_DEBUG_URBDRC) + if (write) + WLog_Print(log, WLOG_TRACE, "-------------------------- URBDRC sent: ---"); + else + WLog_Print(log, WLOG_TRACE, "-------------------------- URBDRC received:"); + winpr_HexLogDump(log, WLOG_TRACE, Stream_Buffer(s), length); + WLog_Print(log, WLOG_TRACE, "-------------------------- URBDRC end -----"); +#endif +} diff --git a/channels/urbdrc/common/urbdrc_helpers.h b/channels/urbdrc/common/urbdrc_helpers.h new file mode 100644 index 0000000..d766ac5 --- /dev/null +++ b/channels/urbdrc/common/urbdrc_helpers.h @@ -0,0 +1,45 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * Server USB redirection channel - helper functions + * + * Copyright 2019 Armin Novak <armin.novak@thincast.com> + * Copyright 2019 Thincast Technologies GmbH + * + * 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 FREERDP_CHANNEL_URBDRC_HELPERS_H +#define FREERDP_CHANNEL_URBDRC_HELPERS_H + +#include <winpr/wtypes.h> + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include <winpr/wlog.h> +#include <winpr/stream.h> + + const char* urb_function_string(UINT16 urb); + const char* mask_to_string(UINT32 mask); + const char* interface_to_string(UINT32 id); + const char* call_to_string(BOOL client, UINT32 interfaceNr, UINT32 functionId); + + void urbdrc_dump_message(wLog* log, BOOL client, BOOL write, wStream* s); + +#ifdef __cplusplus +} +#endif + +#endif /* FREERDP_CHANNEL_URBDRC_HELPERS_H */ diff --git a/channels/urbdrc/common/urbdrc_types.h b/channels/urbdrc/common/urbdrc_types.h new file mode 100644 index 0000000..a3deaf1 --- /dev/null +++ b/channels/urbdrc/common/urbdrc_types.h @@ -0,0 +1,306 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * RemoteFX USB Redirection + * + * Copyright 2012 Atrust corp. + * Copyright 2012 Alfred Liu <alfred.liu@atruscorp.com> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FREERDP_CHANNEL_URBDRC_CLIENT_TYPES_H +#define FREERDP_CHANNEL_URBDRC_CLIENT_TYPES_H + +#include <freerdp/config.h> + +#include <freerdp/dvc.h> +#include <freerdp/types.h> + +#include <msusb.h> + +#include <winpr/stream.h> + +#define RIM_CAPABILITY_VERSION_01 0x00000001 + +#define CAPABILITIES_NEGOTIATOR 0x00000000 +#define CLIENT_DEVICE_SINK 0x00000001 +#define SERVER_CHANNEL_NOTIFICATION 0x00000002 +#define CLIENT_CHANNEL_NOTIFICATION 0x00000003 +#define BASE_USBDEVICE_NUM 0x00000005 + +#define RIMCALL_RELEASE 0x00000001 +#define RIMCALL_QUERYINTERFACE 0x00000002 +#define RIM_EXCHANGE_CAPABILITY_REQUEST 0x00000100 +#define CHANNEL_CREATED 0x00000100 +#define ADD_VIRTUAL_CHANNEL 0x00000100 +#define ADD_DEVICE 0x00000101 + +#define INIT_CHANNEL_IN 1 +#define INIT_CHANNEL_OUT 0 + +/* InterfaceClass */ +#define CLASS_RESERVE 0x00 +#define CLASS_AUDIO 0x01 +#define CLASS_COMMUNICATION_IF 0x02 +#define CLASS_HID 0x03 +#define CLASS_PHYSICAL 0x05 +#define CLASS_IMAGE 0x06 +#define CLASS_PRINTER 0x07 +#define CLASS_MASS_STORAGE 0x08 +#define CLASS_HUB 0x09 +#define CLASS_COMMUNICATION_DATA_IF 0x0a +#define CLASS_SMART_CARD 0x0b +#define CLASS_CONTENT_SECURITY 0x0d +#define CLASS_VIDEO 0x0e +#define CLASS_PERSONAL_HEALTHCARE 0x0f +#define CLASS_DIAGNOSTIC 0xdc +#define CLASS_WIRELESS_CONTROLLER 0xe0 +#define CLASS_ELSE_DEVICE 0xef +#define CLASS_DEPENDENCE 0xfe +#define CLASS_VENDOR_DEPENDENCE 0xff + +/* usb version */ +#define USB_v1_0 0x100 +#define USB_v1_1 0x110 +#define USB_v2_0 0x200 +#define USB_v3_0 0x300 + +#define STREAM_ID_NONE 0x0UL +#define STREAM_ID_PROXY 0x1UL +#define STREAM_ID_STUB 0x2UL +#define STREAM_ID_MASK 0xC0000000 +#define INTERFACE_ID_MASK 0x3FFFFFFF + +#define CANCEL_REQUEST 0x00000100 +#define REGISTER_REQUEST_CALLBACK 0x00000101 +#define IO_CONTROL 0x00000102 +#define INTERNAL_IO_CONTROL 0x00000103 +#define QUERY_DEVICE_TEXT 0x00000104 + +#define TRANSFER_IN_REQUEST 0x00000105 +#define TRANSFER_OUT_REQUEST 0x00000106 +#define RETRACT_DEVICE 0x00000107 + +#define IOCONTROL_COMPLETION 0x00000100 +#define URB_COMPLETION 0x00000101 +#define URB_COMPLETION_NO_DATA 0x00000102 + +/* The USB device is to be stopped from being redirected because the + * device is blocked by the server's policy. */ +#define UsbRetractReason_BlockedByPolicy 0x00000001 + +enum device_text_type +{ + DeviceTextDescription = 0, + DeviceTextLocationInformation = 1, +}; + +enum device_descriptor_table +{ + B_LENGTH = 0, + B_DESCRIPTOR_TYPE = 1, + BCD_USB = 2, + B_DEVICE_CLASS = 4, + B_DEVICE_SUBCLASS = 5, + B_DEVICE_PROTOCOL = 6, + B_MAX_PACKET_SIZE0 = 7, + ID_VENDOR = 8, + ID_PRODUCT = 10, + BCD_DEVICE = 12, + I_MANUFACTURER = 14, + I_PRODUCT = 15, + I_SERIAL_NUMBER = 16, + B_NUM_CONFIGURATIONS = 17 +}; + +#define PIPE_CANCEL 0 +#define PIPE_RESET 1 + +#define IOCTL_INTERNAL_USB_SUBMIT_URB 0x00220003 +#define IOCTL_INTERNAL_USB_RESET_PORT 0x00220007 +#define IOCTL_INTERNAL_USB_GET_PORT_STATUS 0x00220013 +#define IOCTL_INTERNAL_USB_CYCLE_PORT 0x0022001F +#define IOCTL_INTERNAL_USB_SUBMIT_IDLE_NOTIFICATION 0x00220027 + +#define TS_URB_SELECT_CONFIGURATION 0x0000 +#define TS_URB_SELECT_INTERFACE 0x0001 +#define TS_URB_PIPE_REQUEST 0x0002 +#define TS_URB_TAKE_FRAME_LENGTH_CONTROL 0x0003 +#define TS_URB_RELEASE_FRAME_LENGTH_CONTROL 0x0004 +#define TS_URB_GET_FRAME_LENGTH 0x0005 +#define TS_URB_SET_FRAME_LENGTH 0x0006 +#define TS_URB_GET_CURRENT_FRAME_NUMBER 0x0007 +#define TS_URB_CONTROL_TRANSFER 0x0008 +#define TS_URB_BULK_OR_INTERRUPT_TRANSFER 0x0009 +#define TS_URB_ISOCH_TRANSFER 0x000A +#define TS_URB_GET_DESCRIPTOR_FROM_DEVICE 0x000B +#define TS_URB_SET_DESCRIPTOR_TO_DEVICE 0x000C +#define TS_URB_SET_FEATURE_TO_DEVICE 0x000D +#define TS_URB_SET_FEATURE_TO_INTERFACE 0x000E +#define TS_URB_SET_FEATURE_TO_ENDPOINT 0x000F +#define TS_URB_CLEAR_FEATURE_TO_DEVICE 0x0010 +#define TS_URB_CLEAR_FEATURE_TO_INTERFACE 0x0011 +#define TS_URB_CLEAR_FEATURE_TO_ENDPOINT 0x0012 +#define TS_URB_GET_STATUS_FROM_DEVICE 0x0013 +#define TS_URB_GET_STATUS_FROM_INTERFACE 0x0014 +#define TS_URB_GET_STATUS_FROM_ENDPOINT 0x0015 +#define TS_URB_RESERVED_0X0016 0x0016 +#define TS_URB_VENDOR_DEVICE 0x0017 +#define TS_URB_VENDOR_INTERFACE 0x0018 +#define TS_URB_VENDOR_ENDPOINT 0x0019 +#define TS_URB_CLASS_DEVICE 0x001A +#define TS_URB_CLASS_INTERFACE 0x001B +#define TS_URB_CLASS_ENDPOINT 0x001C +#define TS_URB_RESERVE_0X001D 0x001D +#define TS_URB_SYNC_RESET_PIPE_AND_CLEAR_STALL 0x001E +#define TS_URB_CLASS_OTHER 0x001F +#define TS_URB_VENDOR_OTHER 0x0020 +#define TS_URB_GET_STATUS_FROM_OTHER 0x0021 +#define TS_URB_CLEAR_FEATURE_TO_OTHER 0x0022 +#define TS_URB_SET_FEATURE_TO_OTHER 0x0023 +#define TS_URB_GET_DESCRIPTOR_FROM_ENDPOINT 0x0024 +#define TS_URB_SET_DESCRIPTOR_TO_ENDPOINT 0x0025 +#define TS_URB_CONTROL_GET_CONFIGURATION_REQUEST 0x0026 +#define TS_URB_CONTROL_GET_INTERFACE_REQUEST 0x0027 +#define TS_URB_GET_DESCRIPTOR_FROM_INTERFACE 0x0028 +#define TS_URB_SET_DESCRIPTOR_TO_INTERFACE 0x0029 +#define TS_URB_GET_OS_FEATURE_DESCRIPTOR_REQUEST 0x002A +#define TS_URB_RESERVE_0X002B 0x002B +#define TS_URB_RESERVE_0X002C 0x002C +#define TS_URB_RESERVE_0X002D 0x002D +#define TS_URB_RESERVE_0X002E 0x002E +#define TS_URB_RESERVE_0X002F 0x002F +// USB 2.0 calls start at 0x0030 +#define TS_URB_SYNC_RESET_PIPE 0x0030 +#define TS_URB_SYNC_CLEAR_STALL 0x0031 +#define TS_URB_CONTROL_TRANSFER_EX 0x0032 + +#define USBD_STATUS_SUCCESS 0x0 +#define USBD_STATUS_PENDING 0x40000000 +#define USBD_STATUS_CANCELED 0xC0010000 + +#define USBD_STATUS_INVALID_URB_FUNCTION 0x80000200 +#define USBD_STATUS_CRC 0xC0000001 +#define USBD_STATUS_BTSTUFF 0xC0000002 +#define USBD_STATUS_DATA_TOGGLE_MISMATCH 0xC0000003 +#define USBD_STATUS_STALL_PID 0xC0000004 +#define USBD_STATUS_DEV_NOT_RESPONDING 0xC0000005 +#define USBD_STATUS_PID_CHECK_FAILURE 0xC0000006 +#define USBD_STATUS_UNEXPECTED_PID 0xC0000007 +#define USBD_STATUS_DATA_OVERRUN 0xC0000008 +#define USBD_STATUS_DATA_UNDERRUN 0xC0000009 +#define USBD_STATUS_RESERVED1 0xC000000A +#define USBD_STATUS_RESERVED2 0xC000000B +#define USBD_STATUS_BUFFER_OVERRUN 0xC000000C +#define USBD_STATUS_BUFFER_UNDERRUN 0xC000000D + +/* unknown */ +#define USBD_STATUS_NO_DATA 0xC000000E + +#define USBD_STATUS_NOT_ACCESSED 0xC000000F +#define USBD_STATUS_FIFO 0xC0000010 +#define USBD_STATUS_XACT_ERROR 0xC0000011 +#define USBD_STATUS_BABBLE_DETECTED 0xC0000012 +#define USBD_STATUS_DATA_BUFFER_ERROR 0xC0000013 + +#define USBD_STATUS_NOT_SUPPORTED 0xC0000E00 +#define USBD_STATUS_BUFFER_TOO_SMALL 0xC0003000 +#define USBD_STATUS_TIMEOUT 0xC0006000 +#define USBD_STATUS_DEVICE_GONE 0xC0007000 + +#define USBD_STATUS_NO_MEMORY 0x80000100 +#define USBD_STATUS_INVALID_URB_FUNCTION 0x80000200 +#define USBD_STATUS_INVALID_PARAMETER 0x80000300 +#define USBD_STATUS_REQUEST_FAILED 0x80000500 +#define USBD_STATUS_INVALID_PIPE_HANDLE 0x80000600 +#define USBD_STATUS_ERROR_SHORT_TRANSFER 0x80000900 + +// Values for URB TransferFlags Field +// + +/* + Set if data moves device->host +*/ +#define USBD_TRANSFER_DIRECTION 0x00000001 +/* + This bit if not set indicates that a short packet, and hence, + a short transfer is an error condition +*/ +#define USBD_SHORT_TRANSFER_OK 0x00000002 +/* + Subit the iso transfer on the next frame +*/ +#define USBD_START_ISO_TRANSFER_ASAP 0x00000004 +#define USBD_DEFAULT_PIPE_TRANSFER 0x00000008 + +#define USBD_TRANSFER_DIRECTION_FLAG(flags) ((flags)&USBD_TRANSFER_DIRECTION) + +#define USBD_TRANSFER_DIRECTION_OUT 0 +#define USBD_TRANSFER_DIRECTION_IN 1 + +#define VALID_TRANSFER_FLAGS_MASK USBD_SHORT_TRANSFER_OK | \ + USBD_TRANSFER_DIRECTION | \ + USBD_START_ISO_TRANSFER_ASAP | \ + USBD_DEFAULT_PIPE_TRANSFER) + +#define ENDPOINT_HALT 0x00 +#define DEVICE_REMOTE_WAKEUP 0x01 + +/* transfer type */ +#define CONTROL_TRANSFER 0x00 +#define ISOCHRONOUS_TRANSFER 0x01 +#define BULK_TRANSFER 0x02 +#define INTERRUPT_TRANSFER 0x03 + +#define ClearHubFeature (0x2000 | LIBUSB_REQUEST_CLEAR_FEATURE) +#define ClearPortFeature (0x2300 | LIBUSB_REQUEST_CLEAR_FEATURE) +#define GetHubDescriptor (0xa000 | LIBUSB_REQUEST_GET_DESCRIPTOR) +#define GetHubStatus (0xa000 | LIBUSB_REQUEST_GET_STATUS) +#define GetPortStatus (0xa300 | LIBUSB_REQUEST_GET_STATUS) +#define SetHubFeature (0x2000 | LIBUSB_REQUEST_SET_FEATURE) +#define SetPortFeature (0x2300 | LIBUSB_REQUEST_SET_FEATURE) + +#define USBD_PF_CHANGE_MAX_PACKET 0x00000001 +#define USBD_PF_SHORT_PACKET_OPT 0x00000002 +#define USBD_PF_ENABLE_RT_THREAD_ACCESS 0x00000004 +#define USBD_PF_MAP_ADD_TRANSFERS 0x00000008 + +/* feature request */ +#define URB_SET_FEATURE 0x00 +#define URB_CLEAR_FEATURE 0x01 + +#define USBD_PF_CHANGE_MAX_PACKET 0x00000001 +#define USBD_PF_SHORT_PACKET_OPT 0x00000002 +#define USBD_PF_ENABLE_RT_THREAD_ACCESS 0x00000004 +#define USBD_PF_MAP_ADD_TRANSFERS 0x00000008 + +#define URB_CONTROL_TRANSFER_EXTERNAL 0x1 +#define URB_CONTROL_TRANSFER_NONEXTERNAL 0x0 + +#define USBFS_URB_SHORT_NOT_OK 0x01 +#define USBFS_URB_ISO_ASAP 0x02 +#define USBFS_URB_BULK_CONTINUATION 0x04 +#define USBFS_URB_QUEUE_BULK 0x10 + +#define URBDRC_DEVICE_INITIALIZED 0x01 +#define URBDRC_DEVICE_NOT_FOUND 0x02 +#define URBDRC_DEVICE_CHANNEL_CLOSED 0x08 +#define URBDRC_DEVICE_ALREADY_SEND 0x10 +#define URBDRC_DEVICE_DETACH_KERNEL 0x20 + +#define UDEVMAN_FLAG_ADD_BY_VID_PID 0x01 +#define UDEVMAN_FLAG_ADD_BY_ADDR 0x02 +#define UDEVMAN_FLAG_ADD_BY_AUTO 0x04 +#define UDEVMAN_FLAG_DEBUG 0x08 + +#endif /* FREERDP_CHANNEL_URBDRC_CLIENT_TYPES_H */ |