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@codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
Zebediah Figura 2022-05-11 19:01:50 -05:00 committed by Alexandre Julliard
parent c3bdb5d156
commit 4e5eaba51f

View file

@ -73,6 +73,13 @@ struct usb_device
DEVICE_OBJECT *device_obj; 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 *libusb_device;
libusb_device_handle *handle; libusb_device_handle *handle;
@ -84,9 +91,39 @@ static DEVICE_OBJECT *bus_fdo, *bus_pdo;
static libusb_hotplug_callback_handle hotplug_cb_handle; 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 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}; 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; struct libusb_device_descriptor device_desc;
static unsigned int name_index; static unsigned int name_index;
libusb_device_handle *handle; libusb_device_handle *handle;
@ -129,6 +166,43 @@ static void add_usb_device(libusb_device *libusb_device)
device->removed = FALSE; device->removed = FALSE;
LeaveCriticalSection(&wineusb_cs); 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); 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) LIST_FOR_EACH_ENTRY_SAFE(device, cursor, &device_list, struct usb_device, entry)
{ {
assert(!device->removed); assert(!device->removed);
libusb_unref_device(device->libusb_device); if (!device->parent)
libusb_close(device->handle); {
libusb_unref_device(device->libusb_device);
libusb_close(device->handle);
}
list_remove(&device->entry); list_remove(&device->entry);
IoDeleteDevice(device->device_obj); 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 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', static const WCHAR formatW[] = {'U','S','B','\\','V','I','D','_','%','0','4','X',
'&','P','I','D','_','%','0','4','X',0}; '&','P','I','D','_','%','0','4','X',0};
struct libusb_device_descriptor desc; struct libusb_device_descriptor desc;
libusb_get_device_descriptor(device->libusb_device, &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 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', 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}; '&','P','I','D','_','%','0','4','X','&','R','E','V','_','%','0','4','X',0};
struct libusb_device_descriptor desc; struct libusb_device_descriptor desc;
libusb_get_device_descriptor(device->libusb_device, &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); get_device_id(device, buffer);
append_id(buffer, emptyW); 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}; '&','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}; static const WCHAR class_format[] = {'U','S','B','\\','C','l','a','s','s','_','%','0','2','x',0};
struct libusb_device_descriptor device_desc; append_id(buffer, prot_format, device->class, device->subclass, device->protocol);
append_id(buffer, subclass_format, device->class, device->subclass);
libusb_get_device_descriptor(device->libusb_device, &device_desc); append_id(buffer, class_format, device->class);
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, emptyW); 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}; static const WCHAR instance_idW[] = {'0',0};
struct string_buffer buffer = {0}; struct string_buffer buffer = {0};
@ -474,8 +556,11 @@ static NTSTATUS pdo_pnp(DEVICE_OBJECT *device_obj, IRP *irp)
assert(device->removed); assert(device->removed);
remove_pending_irps(device); remove_pending_irps(device);
libusb_unref_device(device->libusb_device); if (!device->parent)
libusb_close(device->handle); {
libusb_unref_device(device->libusb_device);
libusb_close(device->handle);
}
IoDeleteDevice(device->device_obj); IoDeleteDevice(device->device_obj);
ret = STATUS_SUCCESS; ret = STATUS_SUCCESS;