Zebediah Figura : wineusb.sys: Expose composite device interfaces.

Alexandre Julliard julliard at winehq.org
Thu May 12 15:55:50 CDT 2022


Module: wine
Branch: master
Commit: 4e5eaba51f447100f19a92e3756c44bec1df2bff
URL:    https://source.winehq.org/git/wine.git/?a=commit;h=4e5eaba51f447100f19a92e3756c44bec1df2bff

Author: Zebediah Figura <z.figura12 at gmail.com>
Date:   Wed May 11 19:01:50 2022 -0500

wineusb.sys: Expose composite device interfaces.

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.

This patch helps us get closer to installing and running the Hauppauge cx231xx
drivers.

Signed-off-by: Zebediah Figura <zfigura at codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard at winehq.org>

---

 dlls/wineusb.sys/wineusb.c | 115 +++++++++++++++++++++++++++++++++++++++------
 1 file changed, 100 insertions(+), 15 deletions(-)

diff --git a/dlls/wineusb.sys/wineusb.c b/dlls/wineusb.sys/wineusb.c
index 612c62f3f5f..87669b5c65d 100644
--- a/dlls/wineusb.sys/wineusb.c
+++ b/dlls/wineusb.sys/wineusb.c
@@ -73,6 +73,13 @@ 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;
+    uint8_t interface_index;
+
+    uint8_t class, subclass, protocol;
+
     libusb_device *libusb_device;
     libusb_device_handle *handle;
 
@@ -84,9 +91,39 @@ static DEVICE_OBJECT *bus_fdo, *bus_pdo;
 
 static libusb_hotplug_callback_handle hotplug_cb_handle;
 
+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->handle = parent->handle;
+    device->libusb_device = parent->libusb_device;
+    device->interface_index = desc->bInterfaceNumber;
+    device->class = desc->bInterfaceClass;
+    device->subclass = desc->bInterfaceSubClass;
+    device->protocol = desc->bInterfaceProtocol;
+    InitializeListHead(&device->irp_list);
+
+    EnterCriticalSection(&wineusb_cs);
+    list_add_tail(&device_list, &device->entry);
+    LeaveCriticalSection(&wineusb_cs);
+}
+
 static void add_usb_device(libusb_device *libusb_device)
 {
     static const WCHAR formatW[] = {'\\','D','e','v','i','c','e','\\','U','S','B','P','D','O','-','%','u',0};
+    struct libusb_config_descriptor *config_desc;
     struct libusb_device_descriptor device_desc;
     static unsigned int name_index;
     libusb_device_handle *handle;
@@ -129,6 +166,43 @@ static void add_usb_device(libusb_device *libusb_device)
     device->removed = FALSE;
     LeaveCriticalSection(&wineusb_cs);
 
+    device->class = device_desc.bDeviceClass;
+    device->subclass = device_desc.bDeviceSubClass;
+    device->protocol = device_desc.bDeviceProtocol;
+
+    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);
 }
 
@@ -277,8 +351,11 @@ static NTSTATUS fdo_pnp(IRP *irp)
             LIST_FOR_EACH_ENTRY_SAFE(device, cursor, &device_list, struct usb_device, entry)
             {
                 assert(!device->removed);
-                libusb_unref_device(device->libusb_device);
-                libusb_close(device->handle);
+                if (!device->parent)
+                {
+                    libusb_unref_device(device->libusb_device);
+                    libusb_close(device->handle);
+                }
                 list_remove(&device->entry);
                 IoDeleteDevice(device->device_obj);
             }
@@ -338,23 +415,33 @@ static const WCHAR emptyW[] = {0};
 
 static void get_device_id(const struct usb_device *device, struct string_buffer *buffer)
 {
+    static const WCHAR interface_formatW[] = {'U','S','B','\\','V','I','D','_','%','0','4','X',
+            '&','P','I','D','_','%','0','4','X','&','M','I','_','%','0','2','X',0};
     static const WCHAR formatW[] = {'U','S','B','\\','V','I','D','_','%','0','4','X',
             '&','P','I','D','_','%','0','4','X',0};
     struct libusb_device_descriptor desc;
 
     libusb_get_device_descriptor(device->libusb_device, &desc);
-    append_id(buffer, formatW, desc.idVendor, desc.idProduct);
+    if (device->parent)
+        append_id(buffer, interface_formatW, desc.idVendor, desc.idProduct, device->interface_index);
+    else
+        append_id(buffer, formatW, desc.idVendor, desc.idProduct);
 }
 
 static void get_hardware_ids(const struct usb_device *device, struct string_buffer *buffer)
 {
+    static const WCHAR interface_formatW[] = {'U','S','B','\\','V','I','D','_','%','0','4','X',
+                '&','P','I','D','_','%','0','4','X','&','R','E','V','_','%','0','4','X','&','M','I','_','%','0','2','X',0};
     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};
     struct libusb_device_descriptor desc;
 
     libusb_get_device_descriptor(device->libusb_device, &desc);
 
-    append_id(buffer, formatW, desc.idVendor, desc.idProduct, desc.bcdDevice);
+    if (device->parent)
+        append_id(buffer, interface_formatW, desc.idVendor, desc.idProduct, desc.bcdDevice, device->interface_index);
+    else
+        append_id(buffer, formatW, desc.idVendor, desc.idProduct, desc.bcdDevice);
     get_device_id(device, buffer);
     append_id(buffer, emptyW);
 }
@@ -368,18 +455,13 @@ static void get_compatible_ids(const struct usb_device *device, struct string_bu
             '&','S','u','b','C','l','a','s','s','_','%','0','2','x',0};
     static const WCHAR class_format[] = {'U','S','B','\\','C','l','a','s','s','_','%','0','2','x',0};
 
-    struct libusb_device_descriptor device_desc;
-
-    libusb_get_device_descriptor(device->libusb_device, &device_desc);
-
-    append_id(buffer, prot_format, device_desc.bDeviceClass,
-            device_desc.bDeviceSubClass, device_desc.bDeviceProtocol);
-    append_id(buffer, subclass_format, device_desc.bDeviceClass, device_desc.bDeviceSubClass);
-    append_id(buffer, class_format, device_desc.bDeviceClass);
+    append_id(buffer, prot_format, device->class, device->subclass, device->protocol);
+    append_id(buffer, subclass_format, device->class, device->subclass);
+    append_id(buffer, class_format, device->class);
     append_id(buffer, emptyW);
 }
 
-static NTSTATUS query_id(const struct usb_device *device, IRP *irp, BUS_QUERY_ID_TYPE type)
+static NTSTATUS query_id(struct usb_device *device, IRP *irp, BUS_QUERY_ID_TYPE type)
 {
     static const WCHAR instance_idW[] = {'0',0};
     struct string_buffer buffer = {0};
@@ -474,8 +556,11 @@ static NTSTATUS pdo_pnp(DEVICE_OBJECT *device_obj, IRP *irp)
             assert(device->removed);
             remove_pending_irps(device);
 
-            libusb_unref_device(device->libusb_device);
-            libusb_close(device->handle);
+            if (!device->parent)
+            {
+                libusb_unref_device(device->libusb_device);
+                libusb_close(device->handle);
+            }
 
             IoDeleteDevice(device->device_obj);
             ret = STATUS_SUCCESS;




More information about the wine-cvs mailing list