summaryrefslogtreecommitdiffstats
path: root/channels/urbdrc
diff options
context:
space:
mode:
Diffstat (limited to 'channels/urbdrc')
-rw-r--r--channels/urbdrc/CMakeLists.txt30
-rw-r--r--channels/urbdrc/ChannelOptions.cmake18
-rw-r--r--channels/urbdrc/client/CMakeLists.txt40
-rw-r--r--channels/urbdrc/client/data_transfer.c1949
-rw-r--r--channels/urbdrc/client/data_transfer.h36
-rw-r--r--channels/urbdrc/client/libusb/CMakeLists.txt36
-rw-r--r--channels/urbdrc/client/libusb/libusb_udevice.c1841
-rw-r--r--channels/urbdrc/client/libusb/libusb_udevice.h76
-rw-r--r--channels/urbdrc/client/libusb/libusb_udevman.c970
-rw-r--r--channels/urbdrc/client/urbdrc_main.c1023
-rw-r--r--channels/urbdrc/client/urbdrc_main.h222
-rw-r--r--channels/urbdrc/common/CMakeLists.txt29
-rw-r--r--channels/urbdrc/common/msusb.c395
-rw-r--r--channels/urbdrc/common/msusb.h98
-rw-r--r--channels/urbdrc/common/urbdrc_helpers.c425
-rw-r--r--channels/urbdrc/common/urbdrc_helpers.h45
-rw-r--r--channels/urbdrc/common/urbdrc_types.h306
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 */