diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-04 01:24:41 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-04 01:24:41 +0000 |
commit | a9bcc81f821d7c66f623779fa5147e728eb3c388 (patch) | |
tree | 98676963bcdd537ae5908a067a8eb110b93486a6 /channels/urbdrc/client/libusb/libusb_udevman.c | |
parent | Initial commit. (diff) | |
download | freerdp3-a9bcc81f821d7c66f623779fa5147e728eb3c388.tar.xz freerdp3-a9bcc81f821d7c66f623779fa5147e728eb3c388.zip |
Adding upstream version 3.3.0+dfsg1.upstream/3.3.0+dfsg1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'channels/urbdrc/client/libusb/libusb_udevman.c')
-rw-r--r-- | channels/urbdrc/client/libusb/libusb_udevman.c | 970 |
1 files changed, 970 insertions, 0 deletions
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; +} |