diff --git a/libusb/src/driver/libusb_driver.c b/libusb/src/driver/libusb_driver.c index 388cc89..a40e354 100644 --- a/libusb/src/driver/libusb_driver.c +++ b/libusb/src/driver/libusb_driver.c @@ -386,10 +386,53 @@ NTSTATUS DDKAPI add_device(DRIVER_OBJECT *driver_object, return STATUS_NO_SUCH_DEVICE; } - if (dev->is_filter) + status = USBD_CreateHandle(device_object, + dev->next_stack_device, + USBD_CLIENT_CONTRACT_VERSION_602, + POOL_TAG, + &dev->handle); + if (!NT_SUCCESS(status)) { - USBDBG("[filter-mode] id=#%d %s\n",dev->id, dev->device_id); + USBERR("failed to create USBD handle\n"); + IoDeleteSymbolicLink(&symbolic_link_name); + IoDeleteDevice(device_object); + remove_lock_release(dev); // always release acquired locks + return status; + } + /* Detect super speed */ + status = USBD_QueryUsbCapability(dev->handle, + (GUID*)&GUID_USB_CAPABILITY_DEVICE_CONNECTION_SUPER_SPEED_COMPATIBLE, + 0, NULL, NULL); + if(NT_SUCCESS(status)) + { + dev->speed = SuperSpeed; + } + else + { + /* Detect high speed */ + status = USBD_QueryUsbCapability(dev->handle, + (GUID*)&GUID_USB_CAPABILITY_DEVICE_CONNECTION_HIGH_SPEED_COMPATIBLE, + 0, NULL, NULL); + if(NT_SUCCESS(status)) + { + dev->speed = HighSpeed; + } + else + { + /* Default to full speed. At this point we really do not know if it is + * really full speed or low speed + * Todo: detect low speed + */ + dev->speed = FullSpeed; + status = STATUS_SUCCESS; + } + } + + USBDBG("[%s] id=#%d %s, speed=%d\n", dev->is_filter ? "filter_mode" : "normal-mode", dev->id, dev->device_id, dev->speed); + + if (dev->is_filter) + { /* send all USB requests to the PDO in filter driver mode */ dev->target_device = dev->physical_device_object; @@ -405,8 +448,6 @@ NTSTATUS DDKAPI add_device(DRIVER_OBJECT *driver_object, } else { - USBDBG("[normal-mode] id=#%d %s\n",dev->id, dev->device_id); - /* send all USB requests to the lower object in device driver mode */ dev->target_device = dev->next_stack_device; device_object->Flags |= DO_DIRECT_IO | DO_POWER_PAGABLE; @@ -690,11 +731,6 @@ bool_t update_pipe_info(libusb_device_t *dev, maxPacketSize = interface_info->Pipes[i].MaximumPacketSize; maxTransferSize = interface_info->Pipes[i].MaximumTransferSize; - USBMSG("EP%02Xh maximum-packet-size=%d maximum-transfer-size=%d\n", - interface_info->Pipes[i].EndpointAddress, - maxPacketSize, - maxTransferSize); - dev->config.interfaces[number].endpoints[i].handle = interface_info->Pipes[i].PipeHandle; dev->config.interfaces[number].endpoints[i].address = interface_info->Pipes[i].EndpointAddress; dev->config.interfaces[number].endpoints[i].maximum_packet_size = maxPacketSize; @@ -702,35 +738,55 @@ bool_t update_pipe_info(libusb_device_t *dev, dev->config.interfaces[number].endpoints[i].pipe_type = interface_info->Pipes[i].PipeType; dev->config.interfaces[number].endpoints[i].pipe_flags = interface_info->Pipes[i].PipeFlags; - if (maxPacketSize) - { - // set max the maximum transfer size default to an interval of max packet size. - maxTransferSize = maxTransferSize - (maxTransferSize % maxPacketSize); - if (maxTransferSize < maxPacketSize) - { - maxTransferSize = LIBUSB_MAX_READ_WRITE; - } - else if (maxTransferSize > LIBUSB_MAX_READ_WRITE) - { - maxTransferSize = LIBUSB_MAX_READ_WRITE - (LIBUSB_MAX_READ_WRITE % maxPacketSize); - } + /* These are Windows new rules for max transfer sizes + * Currently we take the speed into account but not the controller type + * + * https://learn.microsoft.com/en-us/windows-hardware/drivers/usbcon/usb-bandwidth-allocation + * + */ + switch (interface_info->Pipes[i].PipeType) + { + case UsbdPipeTypeControl: + switch(dev->speed) + { + case SuperSpeed: + maxTransferSize = 64 * 1024; + break; + default: + maxTransferSize = 4 * 1024; + break; + } + break; + + case UsbdPipeTypeInterrupt: + maxTransferSize = 4 * 1024 * 1024; + break; + + case UsbdPipeTypeBulk: + switch (dev->speed) + { + case SuperSpeed: + maxTransferSize = 32 * 1024 * 1024; + break; + case HighSpeed: + maxTransferSize = 4 * 1024 * 1024; + break; + default: + maxTransferSize = 256 * 1024; + break; + } + break; + } - if (maxTransferSize != interface_info->Pipes[i].MaximumTransferSize) - { - USBWRN("overriding EP%02Xh maximum-transfer-size=%d\n", - dev->config.interfaces[number].endpoints[i].address, - maxTransferSize); - } - } - else - { - if (!maxTransferSize) - { - // use the libusb-win32 default - maxTransferSize = LIBUSB_MAX_READ_WRITE; - } - } - dev->config.interfaces[number].endpoints[i].maximum_transfer_size = maxTransferSize; + // set max the maximum transfer size default to an interval of max packet size. + maxTransferSize = maxTransferSize - (maxTransferSize % maxPacketSize); + + USBMSG("EP%02Xh maximum-packet-size=%d maximum-transfer-size=%d\n", + interface_info->Pipes[i].EndpointAddress, + maxPacketSize, + maxTransferSize); + + dev->config.interfaces[number].endpoints[i].maximum_transfer_size = maxTransferSize; } } return TRUE; diff --git a/libusb/src/driver/libusb_driver.h b/libusb/src/driver/libusb_driver.h index 33ad70e..5f2ef9f 100644 --- a/libusb/src/driver/libusb_driver.h +++ b/libusb/src/driver/libusb_driver.h @@ -123,6 +123,7 @@ typedef int bool_t; #define LowSpeed 0x01 #define FullSpeed 0x02 #define HighSpeed 0x03 +#define SuperSpeed 0x04 #endif @@ -183,6 +184,7 @@ typedef struct DEVICE_OBJECT *physical_device_object; DEVICE_OBJECT *next_stack_device; DEVICE_OBJECT *target_device; + USBD_HANDLE handle; libusb_remove_lock_t remove_lock; bool_t is_filter; bool_t is_started; @@ -209,6 +211,7 @@ typedef struct UNICODE_STRING device_interface_name; int control_read_timeout; int control_write_timeout; + int speed; } libusb_device_t, DEVICE_EXTENSION, *PDEVICE_EXTENSION; diff --git a/libusb/src/driver/pnp.c b/libusb/src/driver/pnp.c index bb886fa..1f30ea3 100644 --- a/libusb/src/driver/pnp.c +++ b/libusb/src/driver/pnp.c @@ -73,6 +73,9 @@ NTSTATUS dispatch_pnp(libusb_device_t *dev, IRP *irp) /* wait until all outstanding requests are finished */ remove_lock_release_and_wait(dev); + /* Close handle to USBD */ + USBD_CloseHandle(dev->handle); + status = pass_irp_down(dev, irp, NULL, NULL); USBMSG("deleting device #%d %s\n", dev->id, dev->device_id);