/** * FreeRDP: A Remote Desktop Protocol Implementation * RemoteFX USB Redirection * * Copyright 2012 Atrust corp. * Copyright 2012 Alfred Liu * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "urbdrc_types.h" #include "urbdrc_main.h" #include "data_transfer.h" #include static BOOL Stream_Write_UTF16_String_From_Utf8(wStream* s, const char* utf8, size_t len) { BOOL ret; WCHAR* utf16; int rc; if (len > INT_MAX) return FALSE; rc = ConvertToUnicode(CP_UTF8, 0, utf8, (int)len, &utf16, 0); if (rc < 0) return FALSE; ret = Stream_Write_UTF16_String(s, utf16, (size_t)rc); free(utf16); return ret; } static IWTSVirtualChannel* get_channel(IUDEVMAN* idevman) { IWTSVirtualChannelManager* channel_mgr; URBDRC_PLUGIN* urbdrc; 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, *path; UINT8 containerId[17]; UINT16 idVendor, idProduct; 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; ZeroMemory(containerId, sizeof(containerId)); 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]; 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(URBDRC_CHANNEL_CALLBACK* callback, wStream* s, UINT32 MessageId) { UINT32 InterfaceId; UINT32 Version; UINT32 out_size; wStream* out; if (!callback || !s) return ERROR_INVALID_PARAMETER; if (Stream_GetRemainingLength(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(URBDRC_CHANNEL_CALLBACK* callback, wStream* s, UINT32 MessageId) { UINT32 InterfaceId; UINT32 out_size; UINT32 MajorVersion; UINT32 MinorVersion; UINT32 Capabilities; wStream* out; URBDRC_PLUGIN* urbdrc; if (!callback || !s || !callback->plugin) return ERROR_INVALID_PARAMETER; urbdrc = (URBDRC_PLUGIN*)callback->plugin; if (Stream_GetRemainingLength(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(URBDRC_CHANNEL_CALLBACK* callback, IUDEVICE* pdev) { wStream* out; UINT32 InterfaceId; 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; size_t CompatibilityIdLen[3]; size_t HardwareIdsLen[2]; size_t ContainerIdLen, InstanceIdLen; size_t cchCompatIds; UINT32 bcdUSB; 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, "%s&REV_%04" PRIX16 "", HardwareIds[1], 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, "%s&SubClass_%02" PRIX8 "", CompatibilityIds[2], bDeviceSubClass); sprintf_s(CompatibilityIds[0], DEVICE_COMPATIBILITY_ID_SIZE, "%s&Prot_%02" PRIX8 "", CompatibilityIds[1], bDeviceProtocol); } else { sprintf_s(CompatibilityIds[2], DEVICE_COMPATIBILITY_ID_SIZE, "USB\\DevClass_00"); sprintf_s(CompatibilityIds[1], DEVICE_COMPATIBILITY_ID_SIZE, "%s&SubClass_00", CompatibilityIds[2]); sprintf_s(CompatibilityIds[0], DEVICE_COMPATIBILITY_ID_SIZE, "%s&Prot_00", CompatibilityIds[1]); } } 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 */ Stream_Write_UTF16_String_From_Utf8(out, strInstanceId, InstanceIdLen); Stream_Write_UINT16(out, 0); Stream_Write_UINT32(out, HardwareIdsLen[0] + HardwareIdsLen[1] + 3); /* cchHwIds */ /* HardwareIds 1 */ Stream_Write_UTF16_String_From_Utf8(out, HardwareIds[0], HardwareIdsLen[0]); Stream_Write_UINT16(out, 0); Stream_Write_UTF16_String_From_Utf8(out, HardwareIds[1], HardwareIdsLen[1]); Stream_Write_UINT16(out, 0); Stream_Write_UINT16(out, 0); /* add "\0" */ Stream_Write_UINT32(out, (UINT32)cchCompatIds); /* cchCompatIds */ /* CompatibilityIds */ Stream_Write_UTF16_String_From_Utf8(out, CompatibilityIds[0], CompatibilityIdLen[0]); Stream_Write_UINT16(out, 0); Stream_Write_UTF16_String_From_Utf8(out, CompatibilityIds[1], CompatibilityIdLen[1]); Stream_Write_UINT16(out, 0); Stream_Write_UTF16_String_From_Utf8(out, CompatibilityIds[2], CompatibilityIdLen[2]); Stream_Write_UINT16(out, 0); if (pdev->isCompositeDevice(pdev)) { Stream_Write_UTF16_String_From_Utf8(out, composite_str, composite_len); Stream_Write_UINT16(out, 0); } Stream_Write_UINT16(out, 0x0000); /* add "\0" */ Stream_Write_UINT32(out, (UINT32)ContainerIdLen + 1); /* cchContainerId */ /* ContainerId */ Stream_Write_UTF16_String_From_Utf8(out, strContainerId, ContainerIdLen); 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); } /** * Function description * * @return 0 on success, otherwise a Win32 error code */ static UINT urbdrc_exchange_capabilities(URBDRC_CHANNEL_CALLBACK* callback, wStream* data) { UINT32 MessageId; UINT32 FunctionId; UINT32 InterfaceId; UINT error = CHANNEL_RC_OK; if (!data) return ERROR_INVALID_PARAMETER; if (Stream_GetRemainingLength(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 error = urdbrc_send_virtual_channel_add(udevman->plugin, get_channel(udevman), deviceId); if (error != ERROR_SUCCESS) break; } } udevman->loading_unlock(udevman); return error == ERROR_SUCCESS; } static UINT urbdrc_device_control_channel(URBDRC_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(URBDRC_CHANNEL_CALLBACK* callback, wStream* data) { UINT32 MessageId; UINT32 FunctionId; UINT32 InterfaceId; UINT error = CHANNEL_RC_OK; URBDRC_PLUGIN* urbdrc; if (!callback || !data) return ERROR_INVALID_PARAMETER; urbdrc = (URBDRC_PLUGIN*)callback->plugin; if (!urbdrc) return ERROR_INVALID_PARAMETER; if (Stream_GetRemainingLength(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, "%s: unknown FunctionId 0x%" PRIX32 "", __FUNCTION__, 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) { URBDRC_CHANNEL_CALLBACK* callback = (URBDRC_CHANNEL_CALLBACK*)pChannelCallback; URBDRC_PLUGIN* urbdrc; IUDEVMAN* udevman; UINT32 InterfaceId; 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_GetRemainingLength(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); 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) { URBDRC_CHANNEL_CALLBACK* callback = (URBDRC_CHANNEL_CALLBACK*)pChannelCallback; if (callback) { URBDRC_PLUGIN* urbdrc = (URBDRC_PLUGIN*)callback->plugin; if (urbdrc) urbdrc->status |= URBDRC_DEVICE_CHANNEL_CLOSED; } 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) { URBDRC_LISTENER_CALLBACK* listener_callback = (URBDRC_LISTENER_CALLBACK*)pListenerCallback; URBDRC_CHANNEL_CALLBACK* callback; if (!ppCallback) return ERROR_INVALID_PARAMETER; callback = (URBDRC_CHANNEL_CALLBACK*)calloc(1, sizeof(URBDRC_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) { URBDRC_PLUGIN* urbdrc = (URBDRC_PLUGIN*)pPlugin; char channelName[sizeof(URBDRC_CHANNEL_NAME)] = { URBDRC_CHANNEL_NAME }; if (!urbdrc) return ERROR_INVALID_PARAMETER; urbdrc->listener_callback = (URBDRC_LISTENER_CALLBACK*)calloc(1, sizeof(URBDRC_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); return pChannelMgr->CreateListener(pChannelMgr, channelName, 0, &urbdrc->listener_callback->iface, NULL); } /** * 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; if (!urbdrc) return ERROR_INVALID_DATA; 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, ADDIN_ARGV* args) { URBDRC_PLUGIN* urbdrc = (URBDRC_PLUGIN*)pPlugin; PFREERDP_URBDRC_DEVICE_ENTRY entry; 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; COMMAND_LINE_ARGUMENT_A urbdrc_args[] = { { "dbg", COMMAND_LINE_VALUE_FLAG, "", NULL, BoolValueFalse, -1, NULL, "debug" }, { "sys", COMMAND_LINE_VALUE_REQUIRED, "", NULL, NULL, -1, NULL, "subsystem" }, { NULL, 0, NULL, NULL, NULL, -1, NULL, NULL } }; const DWORD flags = COMMAND_LINE_SIGIL_NONE | COMMAND_LINE_SEPARATOR_COLON | COMMAND_LINE_IGN_UNKNOWN_KEYWORD; COMMAND_LINE_ARGUMENT_A* arg; 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; UINT32 mask, 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; 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; } #ifdef BUILTIN_CHANNELS #define DVCPluginEntry urbdrc_DVCPluginEntry #else #define DVCPluginEntry FREERDP_API DVCPluginEntry #endif /** * Function description * * @return 0 on success, otherwise a Win32 error code */ UINT DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints) { UINT status = 0; ADDIN_ARGV* args; URBDRC_PLUGIN* urbdrc; 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, (IWTSPlugin*)urbdrc); if (status != CHANNEL_RC_OK) 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((IWTSPlugin*)urbdrc, urbdrc->subsystem, args); fail: urbdrc_plugin_terminated(&urbdrc->iface); return status; } UINT stream_write_and_free(IWTSPlugin* plugin, IWTSVirtualChannel* channel, wStream* out) { UINT rc; 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; }