Signed-off-by: Zebediah Figura z.figura12@gmail.com --- dlls/wineusb.sys/wineusb.c | 147 +++++++++++++++++++++++++++++++++++++ 1 file changed, 147 insertions(+)
diff --git a/dlls/wineusb.sys/wineusb.c b/dlls/wineusb.sys/wineusb.c index eb1b7a261f..ed82bd3a13 100644 --- a/dlls/wineusb.sys/wineusb.c +++ b/dlls/wineusb.sys/wineusb.c @@ -74,6 +74,8 @@ struct usb_device
libusb_device *libusb_device; libusb_device_handle *handle; + + LIST_ENTRY irp_list; };
static DRIVER_OBJECT *driver_obj; @@ -120,6 +122,7 @@ static void add_usb_device(libusb_device *libusb_device) device->device_obj = device_obj; device->libusb_device = libusb_ref_device(libusb_device); device->handle = handle; + InitializeListHead(&device->irp_list);
EnterCriticalSection(&wineusb_cs); list_add_tail(&device_list, &device->entry); @@ -394,6 +397,149 @@ static NTSTATUS WINAPI driver_pnp(DEVICE_OBJECT *device, IRP *irp) return pdo_pnp(device, irp); }
+static NTSTATUS usbd_status_from_libusb(enum libusb_transfer_status status) +{ + switch (status) + { + case LIBUSB_TRANSFER_CANCELLED: + return USBD_STATUS_CANCELED; + case LIBUSB_TRANSFER_COMPLETED: + return USBD_STATUS_SUCCESS; + case LIBUSB_TRANSFER_NO_DEVICE: + return USBD_STATUS_DEVICE_GONE; + case LIBUSB_TRANSFER_STALL: + return USBD_STATUS_ENDPOINT_HALTED; + case LIBUSB_TRANSFER_TIMED_OUT: + return USBD_STATUS_TIMEOUT; + default: + FIXME("Unhandled status %#x.\n", status); + case LIBUSB_TRANSFER_ERROR: + return USBD_STATUS_REQUEST_FAILED; + } +} + +static void transfer_cb(struct libusb_transfer *transfer) +{ + IRP *irp = transfer->user_data; + URB *urb = IoGetCurrentIrpStackLocation(irp)->Parameters.Others.Argument1; + + TRACE("Completing IRP %p, status %#x.\n", irp, transfer->status); + + urb->UrbHeader.Status = usbd_status_from_libusb(transfer->status); + + if (transfer->status == LIBUSB_TRANSFER_COMPLETED) + { + switch (urb->UrbHeader.Function) + { + case URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE: + { + struct _URB_CONTROL_DESCRIPTOR_REQUEST *req = &urb->UrbControlDescriptorRequest; + req->TransferBufferLength = transfer->actual_length; + memcpy(req->TransferBuffer, libusb_control_transfer_get_data(transfer), transfer->actual_length); + break; + } + + default: + ERR("Unexpected function %#x.\n", urb->UrbHeader.Function); + } + } + + EnterCriticalSection(&wineusb_cs); + RemoveEntryList(&irp->Tail.Overlay.ListEntry); + LeaveCriticalSection(&wineusb_cs); + + irp->IoStatus.Status = STATUS_SUCCESS; + IoCompleteRequest(irp, IO_NO_INCREMENT); +} + +static void queue_irp(struct usb_device *device, IRP *irp, struct libusb_transfer *transfer) +{ + EnterCriticalSection(&wineusb_cs); + irp->Tail.Overlay.DriverContext[0] = transfer; + InsertTailList(&device->irp_list, &irp->Tail.Overlay.ListEntry); + LeaveCriticalSection(&wineusb_cs); +} + +static NTSTATUS usb_submit_urb(struct usb_device *device, IRP *irp) +{ + URB *urb = IoGetCurrentIrpStackLocation(irp)->Parameters.Others.Argument1; + struct libusb_transfer *transfer; + int ret; + + TRACE("type %#x.\n", urb->UrbHeader.Function); + + switch (urb->UrbHeader.Function) + { + case URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE: + { + struct _URB_CONTROL_DESCRIPTOR_REQUEST *req = &urb->UrbControlDescriptorRequest; + unsigned char *buffer; + + if (req->TransferBufferMDL) + FIXME("Unhandled MDL output buffer.\n"); + + if (!(transfer = libusb_alloc_transfer(0))) + return STATUS_NO_MEMORY; + + if (!(buffer = malloc(sizeof(struct libusb_control_setup) + req->TransferBufferLength))) + { + libusb_free_transfer(transfer); + return STATUS_NO_MEMORY; + } + + queue_irp(device, irp, transfer); + libusb_fill_control_setup(buffer, + LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_STANDARD | LIBUSB_RECIPIENT_DEVICE, + LIBUSB_REQUEST_GET_DESCRIPTOR, (req->DescriptorType << 8) | req->Index, + req->LanguageId, req->TransferBufferLength); + libusb_fill_control_transfer(transfer, device->handle, buffer, transfer_cb, irp, 0); + transfer->flags = LIBUSB_TRANSFER_FREE_BUFFER | LIBUSB_TRANSFER_FREE_TRANSFER; + ret = libusb_submit_transfer(transfer); + if (ret < 0) + ERR("Failed to submit GET_DESRIPTOR transfer: %s\n", libusb_strerror(ret)); + + return STATUS_PENDING; + } + + default: + FIXME("Unhandled function %#x.\n", urb->UrbHeader.Function); + } + + return STATUS_NOT_IMPLEMENTED; +} + +static NTSTATUS WINAPI driver_internal_ioctl(DEVICE_OBJECT *device_obj, IRP *irp) +{ + IO_STACK_LOCATION *stack = IoGetCurrentIrpStackLocation(irp); + ULONG code = stack->Parameters.DeviceIoControl.IoControlCode; + struct usb_device *device = device_obj->DeviceExtension; + NTSTATUS status = STATUS_NOT_IMPLEMENTED; + + TRACE("device_obj %p, irp %p, code %#x.\n", device_obj, irp, code); + + switch (code) + { + case IOCTL_INTERNAL_USB_SUBMIT_URB: + status = usb_submit_urb(device, irp); + break; + + default: + FIXME("Unhandled ioctl %#x (device %#x, access %#x, function %#x, method %#x).\n", + code, code >> 16, (code >> 14) & 3, (code >> 2) & 0xfff, code & 3); + } + + if (status == STATUS_PENDING) + { + IoMarkIrpPending(irp); + } + else + { + irp->IoStatus.Status = status; + IoCompleteRequest(irp, IO_NO_INCREMENT); + } + return status; +} + static NTSTATUS WINAPI driver_add_device(DRIVER_OBJECT *driver, DEVICE_OBJECT *pdo) { NTSTATUS ret; @@ -437,6 +583,7 @@ NTSTATUS WINAPI DriverEntry(DRIVER_OBJECT *driver, UNICODE_STRING *path) driver->DriverExtension->AddDevice = driver_add_device; driver->DriverUnload = driver_unload; driver->MajorFunction[IRP_MJ_PNP] = driver_pnp; + driver->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] = driver_internal_ioctl;
return STATUS_SUCCESS; }
Signed-off-by: Zebediah Figura z.figura12@gmail.com --- dlls/wineusb.sys/wineusb.c | 39 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+)
diff --git a/dlls/wineusb.sys/wineusb.c b/dlls/wineusb.sys/wineusb.c index ed82bd3a13..2f436c1bd7 100644 --- a/dlls/wineusb.sys/wineusb.c +++ b/dlls/wineusb.sys/wineusb.c @@ -460,6 +460,25 @@ static void queue_irp(struct usb_device *device, IRP *irp, struct libusb_transfe LeaveCriticalSection(&wineusb_cs); }
+struct pipe +{ + unsigned char endpoint; + unsigned char type; +}; + +static HANDLE make_pipe_handle(unsigned char endpoint, USBD_PIPE_TYPE type) +{ + union + { + struct pipe pipe; + HANDLE handle; + } u; + + u.pipe.endpoint = endpoint; + u.pipe.type = type; + return u.handle; +} + static NTSTATUS usb_submit_urb(struct usb_device *device, IRP *irp) { URB *urb = IoGetCurrentIrpStackLocation(irp)->Parameters.Others.Argument1; @@ -501,6 +520,26 @@ static NTSTATUS usb_submit_urb(struct usb_device *device, IRP *irp) return STATUS_PENDING; }
+ case URB_FUNCTION_SELECT_CONFIGURATION: + { + struct _URB_SELECT_CONFIGURATION *req = &urb->UrbSelectConfiguration; + ULONG i; + + /* FIXME: In theory, we'd call libusb_set_configuration() here, but + * the CASIO FX-9750GII (which has only one configuration) goes into + * an error state if it receives a SET_CONFIGURATION request. Maybe + * we should skip setting that if and only if the configuration is + * already active? */ + + for (i = 0; i < req->Interface.NumberOfPipes; ++i) + { + USBD_PIPE_INFORMATION *pipe = &req->Interface.Pipes[i]; + pipe->PipeHandle = make_pipe_handle(pipe->EndpointAddress, pipe->PipeType); + } + + return STATUS_SUCCESS; + } + default: FIXME("Unhandled function %#x.\n", urb->UrbHeader.Function); }
Signed-off-by: Zebediah Figura z.figura12@gmail.com --- dlls/wineusb.sys/wineusb.c | 46 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+)
diff --git a/dlls/wineusb.sys/wineusb.c b/dlls/wineusb.sys/wineusb.c index 2f436c1bd7..a926970047 100644 --- a/dlls/wineusb.sys/wineusb.c +++ b/dlls/wineusb.sys/wineusb.c @@ -439,6 +439,15 @@ static void transfer_cb(struct libusb_transfer *transfer) break; }
+ case URB_FUNCTION_VENDOR_INTERFACE: + { + struct _URB_CONTROL_VENDOR_OR_CLASS_REQUEST *req = &urb->UrbControlVendorClassRequest; + req->TransferBufferLength = transfer->actual_length; + if (req->TransferFlags & USBD_TRANSFER_DIRECTION_IN) + memcpy(req->TransferBuffer, libusb_control_transfer_get_data(transfer), transfer->actual_length); + break; + } + default: ERR("Unexpected function %#x.\n", urb->UrbHeader.Function); } @@ -540,6 +549,43 @@ static NTSTATUS usb_submit_urb(struct usb_device *device, IRP *irp) return STATUS_SUCCESS; }
+ case URB_FUNCTION_VENDOR_INTERFACE: + { + struct _URB_CONTROL_VENDOR_OR_CLASS_REQUEST *req = &urb->UrbControlVendorClassRequest; + uint8_t req_type = LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_INTERFACE; + unsigned char *buffer; + + if (req->TransferFlags & USBD_TRANSFER_DIRECTION_IN) + req_type |= LIBUSB_ENDPOINT_IN; + if (req->TransferFlags & ~USBD_TRANSFER_DIRECTION_IN) + FIXME("Unhandled flags %#x.\n", req->TransferFlags); + + if (req->TransferBufferMDL) + FIXME("Unhandled MDL output buffer.\n"); + + if (!(transfer = libusb_alloc_transfer(0))) + return STATUS_NO_MEMORY; + + if (!(buffer = malloc(sizeof(struct libusb_control_setup) + req->TransferBufferLength))) + { + libusb_free_transfer(transfer); + return STATUS_NO_MEMORY; + } + + queue_irp(device, irp, transfer); + libusb_fill_control_setup(buffer, req_type, req->Request, + req->Value, req->Index, req->TransferBufferLength); + if (!(req->TransferFlags & USBD_TRANSFER_DIRECTION_IN)) + memcpy(buffer + LIBUSB_CONTROL_SETUP_SIZE, req->TransferBuffer, req->TransferBufferLength); + libusb_fill_control_transfer(transfer, device->handle, buffer, transfer_cb, irp, 0); + transfer->flags = LIBUSB_TRANSFER_FREE_BUFFER | LIBUSB_TRANSFER_FREE_TRANSFER; + ret = libusb_submit_transfer(transfer); + if (ret < 0) + ERR("Failed to submit vendor-specific interface transfer: %s\n", libusb_strerror(ret)); + + return STATUS_PENDING; + } + default: FIXME("Unhandled function %#x.\n", urb->UrbHeader.Function); }
Signed-off-by: Zebediah Figura z.figura12@gmail.com --- dlls/wineusb.sys/wineusb.c | 53 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+)
diff --git a/dlls/wineusb.sys/wineusb.c b/dlls/wineusb.sys/wineusb.c index a926970047..aa4f204415 100644 --- a/dlls/wineusb.sys/wineusb.c +++ b/dlls/wineusb.sys/wineusb.c @@ -431,6 +431,10 @@ static void transfer_cb(struct libusb_transfer *transfer) { switch (urb->UrbHeader.Function) { + case URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER: + urb->UrbBulkOrInterruptTransfer.TransferBufferLength = transfer->actual_length; + break; + case URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE: { struct _URB_CONTROL_DESCRIPTOR_REQUEST *req = &urb->UrbControlDescriptorRequest; @@ -488,6 +492,18 @@ static HANDLE make_pipe_handle(unsigned char endpoint, USBD_PIPE_TYPE type) return u.handle; }
+static struct pipe get_pipe(HANDLE handle) +{ + union + { + struct pipe pipe; + HANDLE handle; + } u; + + u.handle = handle; + return u.pipe; +} + static NTSTATUS usb_submit_urb(struct usb_device *device, IRP *irp) { URB *urb = IoGetCurrentIrpStackLocation(irp)->Parameters.Others.Argument1; @@ -498,6 +514,43 @@ static NTSTATUS usb_submit_urb(struct usb_device *device, IRP *irp)
switch (urb->UrbHeader.Function) { + case URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER: + { + struct _URB_BULK_OR_INTERRUPT_TRANSFER *req = &urb->UrbBulkOrInterruptTransfer; + struct pipe pipe = get_pipe(req->PipeHandle); + + if (req->TransferBufferMDL) + FIXME("Unhandled MDL output buffer.\n"); + + if (!(transfer = libusb_alloc_transfer(0))) + return STATUS_NO_MEMORY; + + if (pipe.type == UsbdPipeTypeBulk) + { + libusb_fill_bulk_transfer(transfer, device->handle, pipe.endpoint, + req->TransferBuffer, req->TransferBufferLength, transfer_cb, irp, 0); + } + else if (pipe.type == UsbdPipeTypeInterrupt) + { + libusb_fill_interrupt_transfer(transfer, device->handle, pipe.endpoint, + req->TransferBuffer, req->TransferBufferLength, transfer_cb, irp, 0); + } + else + { + WARN("Invalid pipe type %#x.\n", pipe.type); + libusb_free_transfer(transfer); + return USBD_STATUS_INVALID_PIPE_HANDLE; + } + + queue_irp(device, irp, transfer); + transfer->flags = LIBUSB_TRANSFER_FREE_TRANSFER; + ret = libusb_submit_transfer(transfer); + if (ret < 0) + ERR("Failed to submit bulk transfer: %s\n", libusb_strerror(ret)); + + return STATUS_PENDING; + } + case URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE: { struct _URB_CONTROL_DESCRIPTOR_REQUEST *req = &urb->UrbControlDescriptorRequest;
Signed-off-by: Zebediah Figura z.figura12@gmail.com --- dlls/wineusb.sys/wineusb.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+)
diff --git a/dlls/wineusb.sys/wineusb.c b/dlls/wineusb.sys/wineusb.c index aa4f204415..6a979b9126 100644 --- a/dlls/wineusb.sys/wineusb.c +++ b/dlls/wineusb.sys/wineusb.c @@ -514,6 +514,27 @@ static NTSTATUS usb_submit_urb(struct usb_device *device, IRP *irp)
switch (urb->UrbHeader.Function) { + case URB_FUNCTION_ABORT_PIPE: + { + LIST_ENTRY *entry, *mark; + + /* The documentation states that URB_FUNCTION_ABORT_PIPE may + * complete before outstanding requests complete, so we don't need + * to wait for them. */ + EnterCriticalSection(&wineusb_cs); + mark = &device->irp_list; + for (entry = mark->Flink; entry != mark; entry = entry->Flink) + { + IRP *queued_irp = CONTAINING_RECORD(entry, IRP, Tail.Overlay.ListEntry); + + if ((ret = libusb_cancel_transfer(queued_irp->Tail.Overlay.DriverContext[0])) < 0) + ERR("Failed to cancel transfer: %s\n", libusb_strerror(ret)); + } + LeaveCriticalSection(&wineusb_cs); + + return STATUS_SUCCESS; + } + case URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER: { struct _URB_BULK_OR_INTERRUPT_TRANSFER *req = &urb->UrbBulkOrInterruptTransfer;
Signed-off-by: Zebediah Figura z.figura12@gmail.com --- dlls/wineusb.sys/wineusb.c | 11 +++++++++++ 1 file changed, 11 insertions(+)
diff --git a/dlls/wineusb.sys/wineusb.c b/dlls/wineusb.sys/wineusb.c index 6a979b9126..55bd84ea30 100644 --- a/dlls/wineusb.sys/wineusb.c +++ b/dlls/wineusb.sys/wineusb.c @@ -535,6 +535,17 @@ static NTSTATUS usb_submit_urb(struct usb_device *device, IRP *irp) return STATUS_SUCCESS; }
+ case URB_FUNCTION_SYNC_RESET_PIPE_AND_CLEAR_STALL: + { + struct _URB_PIPE_REQUEST *req = &urb->UrbPipeRequest; + struct pipe pipe = get_pipe(req->PipeHandle); + + if ((ret = libusb_clear_halt(device->handle, pipe.endpoint)) < 0) + ERR("Failed to clear halt: %s\n", libusb_strerror(ret)); + + return STATUS_SUCCESS; + } + case URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER: { struct _URB_BULK_OR_INTERRUPT_TRANSFER *req = &urb->UrbBulkOrInterruptTransfer;
I'm a bit late here, but FYI for future work:
On 2020-04-18 01:03, Zebediah Figura wrote:
+static NTSTATUS WINAPI driver_internal_ioctl(DEVICE_OBJECT *device_obj, IRP *irp) +{
- IO_STACK_LOCATION *stack = IoGetCurrentIrpStackLocation(irp);
- ULONG code = stack->Parameters.DeviceIoControl.IoControlCode;
- struct usb_device *device = device_obj->DeviceExtension;
- NTSTATUS status = STATUS_NOT_IMPLEMENTED;
- TRACE("device_obj %p, irp %p, code %#x.\n", device_obj, irp, code);
- switch (code)
- {
case IOCTL_INTERNAL_USB_SUBMIT_URB:
status = usb_submit_urb(device, irp);
break;
default:
FIXME("Unhandled ioctl %#x (device %#x, access %#x, function %#x, method %#x).\n",
code, code >> 16, (code >> 14) & 3, (code >> 2) & 0xfff, code & 3);
- }
- if (status == STATUS_PENDING)
- {
IoMarkIrpPending(irp);
This code pattern is almost always incorrect. If your handler function returned STATUS_PENDING, it normally means the IRP has already been added to a queue and a worker thread may pick it up and complete it concurrently. Because IoCompleteRequest frees the memory associated with the IRP, this can therefore result in a use after free. The correct pattern is to call IoMarkIrpPending before enqueuing the IRP.
Thanks. -Thomas