From b41fe10fd487ca18183a65ff33090801ff80dfb1 Mon Sep 17 00:00:00 2001 From: Zebediah Figura Date: Wed, 25 May 2022 18:03:11 -0500 Subject: [PATCH] wineusb.sys: Create separate unix devices for each interface. --- dlls/wineusb.sys/unixlib.c | 65 ++++++++++++++++++++++++++++++ dlls/wineusb.sys/unixlib.h | 9 +---- dlls/wineusb.sys/wineusb.c | 81 +++----------------------------------- 3 files changed, 72 insertions(+), 83 deletions(-) diff --git a/dlls/wineusb.sys/unixlib.c b/dlls/wineusb.sys/unixlib.c index 90847bc6a7d..f2356fcb06a 100644 --- a/dlls/wineusb.sys/unixlib.c +++ b/dlls/wineusb.sys/unixlib.c @@ -40,6 +40,15 @@ WINE_DEFAULT_DEBUG_CHANNEL(wineusb); +struct unix_device +{ + struct list entry; + + libusb_device_handle *handle; + + bool interface; +}; + static libusb_hotplug_callback_handle hotplug_cb_handle; static bool thread_shutdown; @@ -109,6 +118,7 @@ static NTSTATUS usb_get_event(void *args) static void add_usb_device(libusb_device *libusb_device) { + struct libusb_config_descriptor *config_desc; struct libusb_device_descriptor device_desc; struct unix_device *unix_device; struct usb_event usb_event; @@ -128,6 +138,8 @@ static void add_usb_device(libusb_device *libusb_device) free(unix_device); return; } + unix_device->interface = false; + pthread_mutex_lock(&device_mutex); list_add_tail(&device_list, &unix_device->entry); pthread_mutex_unlock(&device_mutex); @@ -140,7 +152,60 @@ static void add_usb_device(libusb_device *libusb_device) usb_event.u.added_device.class = device_desc.bDeviceClass; usb_event.u.added_device.subclass = device_desc.bDeviceSubClass; usb_event.u.added_device.protocol = device_desc.bDeviceProtocol; + usb_event.u.added_device.interface = false; queue_event(&usb_event); + + if (!(ret = libusb_get_active_config_descriptor(libusb_device, &config_desc))) + { + /* Create new devices for interfaces of composite devices. + * + * On Windows this is the job of usbccgp.sys, a separate driver that + * layers on top of the base USB driver. While we could take this + * approach as well, implementing usbccgp is a lot more work, whereas + * interface support is effectively built into libusb. + * + * FIXME: usbccgp does not create separate interfaces in some cases, + * e.g. when there is an interface association descriptor available. + */ + if (config_desc->bNumInterfaces > 1) + { + uint8_t i; + + for (i = 0; i < config_desc->bNumInterfaces; ++i) + { + const struct libusb_interface *interface = &config_desc->interface[i]; + const struct libusb_interface_descriptor *iface_desc; + struct unix_device *unix_iface; + + if (interface->num_altsetting != 1) + FIXME("Interface %u has %u alternate settings; using the first one.\n", + i, interface->num_altsetting); + iface_desc = &interface->altsetting[0]; + + if (!(unix_iface = calloc(1, sizeof(*unix_iface)))) + return; + + unix_iface->handle = unix_device->handle; + unix_iface->interface = true; + pthread_mutex_lock(&device_mutex); + list_add_tail(&device_list, &unix_iface->entry); + pthread_mutex_unlock(&device_mutex); + + usb_event.u.added_device.device = unix_iface; + usb_event.u.added_device.class = iface_desc->bInterfaceClass; + usb_event.u.added_device.subclass = iface_desc->bInterfaceSubClass; + usb_event.u.added_device.protocol = iface_desc->bInterfaceProtocol; + usb_event.u.added_device.interface = true; + usb_event.u.added_device.interface_index = iface_desc->bInterfaceNumber; + queue_event(&usb_event); + } + } + libusb_free_config_descriptor(config_desc); + } + else + { + ERR("Failed to get configuration descriptor: %s\n", libusb_strerror(ret)); + } } static void remove_usb_device(libusb_device *libusb_device) diff --git a/dlls/wineusb.sys/unixlib.h b/dlls/wineusb.sys/unixlib.h index d76818cc907..0c3319969b7 100644 --- a/dlls/wineusb.sys/unixlib.h +++ b/dlls/wineusb.sys/unixlib.h @@ -26,13 +26,6 @@ #include "ddk/wdm.h" #include "wine/unixlib.h" -struct unix_device -{ - struct list entry; - - libusb_device_handle *handle; -}; - enum usb_event_type { USB_EVENT_ADD_DEVICE, @@ -52,6 +45,8 @@ struct usb_event struct unix_device *device; UINT16 vendor, product, revision; UINT8 class, subclass, protocol; + bool interface; + UINT8 interface_index; } added_device; struct unix_device *removed_device; IRP *completed_irp; diff --git a/dlls/wineusb.sys/wineusb.c b/dlls/wineusb.sys/wineusb.c index b833dbc9824..d432bdc5666 100644 --- a/dlls/wineusb.sys/wineusb.c +++ b/dlls/wineusb.sys/wineusb.c @@ -79,9 +79,7 @@ struct usb_device DEVICE_OBJECT *device_obj; - /* Points to the parent USB device if this is a USB interface; otherwise - * NULL. */ - struct usb_device *parent; + bool interface; uint8_t interface_index; uint8_t class, subclass, protocol; @@ -106,49 +104,15 @@ static void destroy_unix_device(struct unix_device *unix_device) __wine_unix_call(unix_handle, unix_usb_destroy_device, ¶ms); } -static void add_usb_interface(struct usb_device *parent, const struct libusb_interface_descriptor *desc) -{ - struct usb_device *device; - DEVICE_OBJECT *device_obj; - NTSTATUS status; - - if ((status = IoCreateDevice(driver_obj, sizeof(*device), NULL, - FILE_DEVICE_USB, FILE_AUTOGENERATED_DEVICE_NAME, FALSE, &device_obj))) - { - ERR("Failed to create device, status %#x.\n", status); - return; - } - - device = device_obj->DeviceExtension; - device->device_obj = device_obj; - device->parent = parent; - device->unix_device = parent->unix_device; - device->interface_index = desc->bInterfaceNumber; - device->class = desc->bInterfaceClass; - device->subclass = desc->bInterfaceSubClass; - device->protocol = desc->bInterfaceProtocol; - device->vendor = parent->vendor; - device->product = parent->product; - device->revision = parent->revision; - InitializeListHead(&device->irp_list); - - EnterCriticalSection(&wineusb_cs); - list_add_tail(&device_list, &device->entry); - LeaveCriticalSection(&wineusb_cs); -} - static void add_unix_device(const struct usb_add_device_event *event) { static const WCHAR formatW[] = {'\\','D','e','v','i','c','e','\\','U','S','B','P','D','O','-','%','u',0}; - libusb_device *libusb_device = libusb_get_device(event->device->handle); - struct libusb_config_descriptor *config_desc; static unsigned int name_index; struct usb_device *device; DEVICE_OBJECT *device_obj; UNICODE_STRING string; NTSTATUS status; WCHAR name[26]; - int ret; TRACE("Adding new device %p, vendor %04x, product %04x.\n", event->device, event->vendor, event->product); @@ -179,39 +143,6 @@ static void add_unix_device(const struct usb_add_device_event *event) list_add_tail(&device_list, &device->entry); LeaveCriticalSection(&wineusb_cs); - if (!(ret = libusb_get_active_config_descriptor(libusb_device, &config_desc))) - { - /* Create new devices for interfaces of composite devices. - * - * On Windows this is the job of usbccgp.sys, a separate driver that - * layers on top of the base USB driver. While we could take this - * approach as well, implementing usbccgp is a lot more work, whereas - * interface support is effectively built into libusb. - * - * FIXME: usbccgp does not create separate interfaces in some cases, - * e.g. when there is an interface association descriptor available. - */ - if (config_desc->bNumInterfaces > 1) - { - uint8_t i; - - for (i = 0; i < config_desc->bNumInterfaces; ++i) - { - const struct libusb_interface *interface = &config_desc->interface[i]; - - if (interface->num_altsetting != 1) - FIXME("Interface %u has %u alternate settings; using the first one.\n", - i, interface->num_altsetting); - add_usb_interface(device, &interface->altsetting[0]); - } - } - libusb_free_config_descriptor(config_desc); - } - else - { - ERR("Failed to get configuration descriptor: %s\n", libusb_strerror(ret)); - } - IoInvalidateDeviceRelations(bus_pdo, BusRelations); } @@ -379,8 +310,7 @@ static NTSTATUS fdo_pnp(IRP *irp) LIST_FOR_EACH_ENTRY_SAFE(device, cursor, &device_list, struct usb_device, entry) { assert(!device->removed); - if (!device->parent) - destroy_unix_device(device->unix_device); + destroy_unix_device(device->unix_device); list_remove(&device->entry); IoDeleteDevice(device->device_obj); } @@ -445,7 +375,7 @@ static void get_device_id(const struct usb_device *device, struct string_buffer static const WCHAR formatW[] = {'U','S','B','\\','V','I','D','_','%','0','4','X', '&','P','I','D','_','%','0','4','X',0}; - if (device->parent) + if (device->interface) append_id(buffer, interface_formatW, device->vendor, device->product, device->interface_index); else append_id(buffer, formatW, device->vendor, device->product); @@ -458,7 +388,7 @@ static void get_hardware_ids(const struct usb_device *device, struct string_buff static const WCHAR formatW[] = {'U','S','B','\\','V','I','D','_','%','0','4','X', '&','P','I','D','_','%','0','4','X','&','R','E','V','_','%','0','4','X',0}; - if (device->parent) + if (device->interface) append_id(buffer, interface_formatW, device->vendor, device->product, device->revision, device->interface_index); else append_id(buffer, formatW, device->vendor, device->product, device->revision); @@ -576,8 +506,7 @@ static NTSTATUS pdo_pnp(DEVICE_OBJECT *device_obj, IRP *irp) assert(device->removed); remove_pending_irps(device); - if (!device->parent) - destroy_unix_device(device->unix_device); + destroy_unix_device(device->unix_device); IoDeleteDevice(device->device_obj); ret = STATUS_SUCCESS;