diff --git a/dlls/wineusb.sys/unixlib.c b/dlls/wineusb.sys/unixlib.c index ee8679e33a2..091d85d1ef1 100644 --- a/dlls/wineusb.sys/unixlib.c +++ b/dlls/wineusb.sys/unixlib.c @@ -22,10 +22,17 @@ #pragma makedep unix #endif +#include #include +#include #include +#include #include "ntstatus.h" #define WIN32_NO_STATUS +#include "windef.h" +#include "winternl.h" +#include "ddk/wdm.h" +#include "ddk/usb.h" #include "wine/debug.h" #include "wine/list.h" @@ -33,32 +40,423 @@ WINE_DEFAULT_DEBUG_CHANNEL(wineusb); +static libusb_hotplug_callback_handle hotplug_cb_handle; + static bool thread_shutdown; +static pthread_cond_t event_cond = PTHREAD_COND_INITIALIZER; +static pthread_mutex_t event_mutex = PTHREAD_MUTEX_INITIALIZER; + +static struct usb_event *usb_events; +static size_t usb_event_count, usb_events_capacity; + +static pthread_mutex_t device_mutex = PTHREAD_MUTEX_INITIALIZER; + +static struct list device_list = LIST_INIT(device_list); + +static bool array_reserve(void **elements, size_t *capacity, size_t count, size_t size) +{ + unsigned int new_capacity, max_capacity; + void *new_elements; + + if (count <= *capacity) + return true; + + max_capacity = ~(size_t)0 / size; + if (count > max_capacity) + return false; + + new_capacity = max(4, *capacity); + while (new_capacity < count && new_capacity <= max_capacity / 2) + new_capacity *= 2; + if (new_capacity < count) + new_capacity = max_capacity; + + if (!(new_elements = realloc(*elements, new_capacity * size))) + return false; + + *elements = new_elements; + *capacity = new_capacity; + + return true; +} + +static void queue_event(const struct usb_event *event) +{ + pthread_mutex_lock(&event_mutex); + if (array_reserve((void **)&usb_events, &usb_events_capacity, usb_event_count + 1, sizeof(*usb_events))) + usb_events[usb_event_count++] = *event; + else + ERR("Failed to queue event.\n"); + pthread_mutex_unlock(&event_mutex); + pthread_cond_signal(&event_cond); +} + +static NTSTATUS usb_get_event(void *args) +{ + const struct usb_get_event_params *params = args; + + pthread_mutex_lock(&event_mutex); + while (!usb_event_count) + pthread_cond_wait(&event_cond, &event_mutex); + *params->event = usb_events[0]; + if (--usb_event_count) + memmove(usb_events, usb_events + 1, usb_event_count * sizeof(*usb_events)); + pthread_mutex_unlock(&event_mutex); + + return STATUS_SUCCESS; +} + +static void add_usb_device(libusb_device *libusb_device) +{ + struct libusb_device_descriptor device_desc; + struct unix_device *unix_device; + struct usb_event usb_event; + int ret; + + libusb_get_device_descriptor(libusb_device, &device_desc); + + TRACE("Adding new device %p, vendor %04x, product %04x.\n", libusb_device, + device_desc.idVendor, device_desc.idProduct); + + if (!(unix_device = calloc(1, sizeof(*unix_device)))) + return; + + if ((ret = libusb_open(libusb_device, &unix_device->handle))) + { + WARN("Failed to open device: %s\n", libusb_strerror(ret)); + free(unix_device); + return; + } + pthread_mutex_lock(&device_mutex); + list_add_tail(&device_list, &unix_device->entry); + pthread_mutex_unlock(&device_mutex); + + usb_event.type = USB_EVENT_ADD_DEVICE; + usb_event.u.added_device = unix_device; + queue_event(&usb_event); +} + +static void remove_usb_device(libusb_device *libusb_device) +{ + struct unix_device *unix_device; + struct usb_event usb_event; + + TRACE("Removing device %p.\n", libusb_device); + + LIST_FOR_EACH_ENTRY(unix_device, &device_list, struct unix_device, entry) + { + if (libusb_get_device(unix_device->handle) == libusb_device) + { + usb_event.type = USB_EVENT_REMOVE_DEVICE; + usb_event.u.removed_device = unix_device; + queue_event(&usb_event); + } + } +} + +static int LIBUSB_CALL hotplug_cb(libusb_context *context, libusb_device *device, + libusb_hotplug_event event, void *user_data) +{ + if (event == LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED) + add_usb_device(device); + else + remove_usb_device(device); + + return 0; +} + static NTSTATUS usb_main_loop(void *args) { + static const struct usb_event shutdown_event = {.type = USB_EVENT_SHUTDOWN}; int ret; TRACE("Starting libusb event thread.\n"); + if ((ret = libusb_hotplug_register_callback(NULL, + LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED | LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT, + LIBUSB_HOTPLUG_ENUMERATE, LIBUSB_HOTPLUG_MATCH_ANY, LIBUSB_HOTPLUG_MATCH_ANY, + LIBUSB_HOTPLUG_MATCH_ANY, hotplug_cb, NULL, &hotplug_cb_handle))) + { + ERR("Failed to register callback: %s\n", libusb_strerror(ret)); + return STATUS_UNSUCCESSFUL; + } + while (!thread_shutdown) { if ((ret = libusb_handle_events(NULL))) ERR("Error handling events: %s\n", libusb_strerror(ret)); } + queue_event(&shutdown_event); + TRACE("Shutting down libusb event thread.\n"); return STATUS_SUCCESS; } static NTSTATUS usb_exit(void *args) { + libusb_hotplug_deregister_callback(NULL, hotplug_cb_handle); thread_shutdown = true; libusb_interrupt_event_handler(NULL); return STATUS_SUCCESS; } +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 LIBUSB_CALL transfer_cb(struct libusb_transfer *transfer) +{ + IRP *irp = transfer->user_data; + URB *urb = IoGetCurrentIrpStackLocation(irp)->Parameters.Others.Argument1; + struct usb_event event; + + 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_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; + req->TransferBufferLength = transfer->actual_length; + memcpy(req->TransferBuffer, libusb_control_transfer_get_data(transfer), transfer->actual_length); + 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); + } + } + + event.type = USB_EVENT_TRANSFER_COMPLETE; + event.u.completed_irp = irp; + queue_event(&event); +} + +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 struct pipe get_pipe(HANDLE handle) +{ + union + { + struct pipe pipe; + HANDLE handle; + } u; + + u.handle = handle; + return u.pipe; +} + +static NTSTATUS usb_submit_urb(void *args) +{ + const struct usb_submit_urb_params *params = args; + IRP *irp = params->irp; + + URB *urb = IoGetCurrentIrpStackLocation(irp)->Parameters.Others.Argument1; + libusb_device_handle *handle = params->device->handle; + struct libusb_transfer *transfer; + int ret; + + TRACE("type %#x.\n", urb->UrbHeader.Function); + + switch (urb->UrbHeader.Function) + { + 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(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; + 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; + irp->Tail.Overlay.DriverContext[0] = transfer; + + if (pipe.type == UsbdPipeTypeBulk) + { + libusb_fill_bulk_transfer(transfer, handle, pipe.endpoint, + req->TransferBuffer, req->TransferBufferLength, transfer_cb, irp, 0); + } + else if (pipe.type == UsbdPipeTypeInterrupt) + { + libusb_fill_interrupt_transfer(transfer, 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; + } + + 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; + unsigned char *buffer; + + if (req->TransferBufferMDL) + FIXME("Unhandled MDL output buffer.\n"); + + if (!(transfer = libusb_alloc_transfer(0))) + return STATUS_NO_MEMORY; + irp->Tail.Overlay.DriverContext[0] = transfer; + + if (!(buffer = malloc(sizeof(struct libusb_control_setup) + req->TransferBufferLength))) + { + libusb_free_transfer(transfer); + return STATUS_NO_MEMORY; + } + + 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, 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_DESCRIPTOR transfer: %s\n", libusb_strerror(ret)); + + 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; + } + + 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; + irp->Tail.Overlay.DriverContext[0] = transfer; + + if (!(buffer = malloc(sizeof(struct libusb_control_setup) + req->TransferBufferLength))) + { + libusb_free_transfer(transfer); + return STATUS_NO_MEMORY; + } + + 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, 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); + } + + return STATUS_NOT_IMPLEMENTED; +} + static NTSTATUS usb_cancel_transfer(void *args) { const struct usb_cancel_transfer_params *params = args; @@ -70,10 +468,27 @@ static NTSTATUS usb_cancel_transfer(void *args) return STATUS_SUCCESS; } +static NTSTATUS usb_destroy_device(void *args) +{ + const struct usb_destroy_device_params *params = args; + struct unix_device *device = params->device; + + pthread_mutex_lock(&device_mutex); + libusb_close(device->handle); + list_remove(&device->entry); + pthread_mutex_unlock(&device_mutex); + free(device); + + return STATUS_SUCCESS; +} + const unixlib_entry_t __wine_unix_call_funcs[] = { #define X(name) [unix_ ## name] = name X(usb_main_loop), X(usb_exit), + X(usb_get_event), + X(usb_submit_urb), X(usb_cancel_transfer), + X(usb_destroy_device), }; diff --git a/dlls/wineusb.sys/unixlib.h b/dlls/wineusb.sys/unixlib.h index 6c037b20eef..e8e2850c57a 100644 --- a/dlls/wineusb.sys/unixlib.h +++ b/dlls/wineusb.sys/unixlib.h @@ -23,18 +23,65 @@ #include "windef.h" #include "winternl.h" +#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, + USB_EVENT_REMOVE_DEVICE, + USB_EVENT_TRANSFER_COMPLETE, + USB_EVENT_SHUTDOWN, +}; + +struct usb_event +{ + enum usb_event_type type; + + union + { + struct unix_device *added_device; + struct unix_device *removed_device; + IRP *completed_irp; + } u; +}; + +struct usb_get_event_params +{ + struct usb_event *event; +}; + +struct usb_submit_urb_params +{ + struct unix_device *device; + IRP *irp; +}; + struct usb_cancel_transfer_params { void *transfer; }; +struct usb_destroy_device_params +{ + struct unix_device *device; +}; + enum unix_funcs { unix_usb_main_loop, unix_usb_exit, + unix_usb_get_event, + unix_usb_submit_urb, unix_usb_cancel_transfer, + unix_usb_destroy_device, }; #endif diff --git a/dlls/wineusb.sys/wineusb.c b/dlls/wineusb.sys/wineusb.c index c7363739bcf..49033c76213 100644 --- a/dlls/wineusb.sys/wineusb.c +++ b/dlls/wineusb.sys/wineusb.c @@ -70,18 +70,8 @@ DECLARE_CRITICAL_SECTION(wineusb_cs); static unixlib_handle_t unix_handle; -static pthread_mutex_t unix_device_mutex = PTHREAD_MUTEX_INITIALIZER; - -static struct list unix_device_list = LIST_INIT(unix_device_list); static struct list device_list = LIST_INIT(device_list); -struct unix_device -{ - struct list entry; - - libusb_device_handle *handle; -}; - struct usb_device { struct list entry; @@ -106,90 +96,14 @@ struct usb_device static DRIVER_OBJECT *driver_obj; static DEVICE_OBJECT *bus_fdo, *bus_pdo; -static libusb_hotplug_callback_handle hotplug_cb_handle; - -static pthread_cond_t event_cond = PTHREAD_COND_INITIALIZER; -static pthread_mutex_t event_mutex = PTHREAD_MUTEX_INITIALIZER; - -enum usb_event_type -{ - USB_EVENT_ADD_DEVICE, - USB_EVENT_REMOVE_DEVICE, - USB_EVENT_TRANSFER_COMPLETE, - USB_EVENT_SHUTDOWN, -}; - -struct usb_event -{ - enum usb_event_type type; - - union - { - struct unix_device *added_device; - struct unix_device *removed_device; - IRP *completed_irp; - } u; -}; - -static struct usb_event *usb_events; -static size_t usb_event_count, usb_events_capacity; - -static bool array_reserve(void **elements, size_t *capacity, size_t count, size_t size) -{ - unsigned int new_capacity, max_capacity; - void *new_elements; - - if (count <= *capacity) - return true; - - max_capacity = ~(size_t)0 / size; - if (count > max_capacity) - return false; - - new_capacity = max(4, *capacity); - while (new_capacity < count && new_capacity <= max_capacity / 2) - new_capacity *= 2; - if (new_capacity < count) - new_capacity = max_capacity; - - if (!(new_elements = realloc(*elements, new_capacity * size))) - return false; - - *elements = new_elements; - *capacity = new_capacity; - - return true; -} - -static void queue_event(const struct usb_event *event) -{ - pthread_mutex_lock(&event_mutex); - if (array_reserve((void **)&usb_events, &usb_events_capacity, usb_event_count + 1, sizeof(*usb_events))) - usb_events[usb_event_count++] = *event; - else - ERR("Failed to queue event.\n"); - pthread_mutex_unlock(&event_mutex); - pthread_cond_signal(&event_cond); -} - -static void get_event(struct usb_event *event) -{ - pthread_mutex_lock(&event_mutex); - while (!usb_event_count) - pthread_cond_wait(&event_cond, &event_mutex); - *event = usb_events[0]; - if (--usb_event_count) - memmove(usb_events, usb_events + 1, usb_event_count * sizeof(*usb_events)); - pthread_mutex_unlock(&event_mutex); -} - static void destroy_unix_device(struct unix_device *unix_device) { - pthread_mutex_lock(&unix_device_mutex); - libusb_close(unix_device->handle); - list_remove(&unix_device->entry); - pthread_mutex_unlock(&unix_device_mutex); - free(unix_device); + struct usb_destroy_device_params params = + { + .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) @@ -304,36 +218,6 @@ static void add_unix_device(struct unix_device *unix_device) IoInvalidateDeviceRelations(bus_pdo, BusRelations); } -static void add_usb_device(libusb_device *libusb_device) -{ - struct libusb_device_descriptor device_desc; - struct unix_device *unix_device; - struct usb_event usb_event; - int ret; - - libusb_get_device_descriptor(libusb_device, &device_desc); - - TRACE("Adding new device %p, vendor %04x, product %04x.\n", libusb_device, - device_desc.idVendor, device_desc.idProduct); - - if (!(unix_device = calloc(1, sizeof(*unix_device)))) - return; - - if ((ret = libusb_open(libusb_device, &unix_device->handle))) - { - WARN("Failed to open device: %s\n", libusb_strerror(ret)); - free(unix_device); - return; - } - pthread_mutex_lock(&unix_device_mutex); - list_add_tail(&unix_device_list, &unix_device->entry); - pthread_mutex_unlock(&unix_device_mutex); - - usb_event.type = USB_EVENT_ADD_DEVICE; - usb_event.u.added_device = unix_device; - queue_event(&usb_event); -} - static void remove_unix_device(struct unix_device *unix_device) { struct usb_device *device; @@ -358,45 +242,12 @@ static void remove_unix_device(struct unix_device *unix_device) IoInvalidateDeviceRelations(bus_pdo, BusRelations); } -static void remove_usb_device(libusb_device *libusb_device) -{ - struct unix_device *unix_device; - struct usb_event usb_event; - - TRACE("Removing device %p.\n", libusb_device); - - LIST_FOR_EACH_ENTRY(unix_device, &unix_device_list, struct unix_device, entry) - { - if (libusb_get_device(unix_device->handle) == libusb_device) - { - usb_event.type = USB_EVENT_REMOVE_DEVICE; - usb_event.u.removed_device = unix_device; - queue_event(&usb_event); - } - } -} - static HANDLE libusb_event_thread, event_thread; -static int LIBUSB_CALL hotplug_cb(libusb_context *context, libusb_device *device, - libusb_hotplug_event event, void *user_data) -{ - if (event == LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED) - add_usb_device(device); - else - remove_usb_device(device); - - return 0; -} - static DWORD CALLBACK libusb_event_thread_proc(void *arg) { - static const struct usb_event shutdown_event = {.type = USB_EVENT_SHUTDOWN}; - __wine_unix_call(unix_handle, unix_usb_main_loop, NULL); - queue_event(&shutdown_event); - return 0; } @@ -418,7 +269,12 @@ static DWORD CALLBACK event_thread_proc(void *arg) for (;;) { - get_event(&event); + struct usb_get_event_params params = + { + .event = &event, + }; + + __wine_unix_call(unix_handle, unix_usb_get_event, ¶ms); switch (event.type) { @@ -490,15 +346,6 @@ static NTSTATUS fdo_pnp(IRP *irp) libusb_event_thread = CreateThread(NULL, 0, libusb_event_thread_proc, NULL, 0, NULL); event_thread = CreateThread(NULL, 0, event_thread_proc, NULL, 0, NULL); - if ((ret = libusb_hotplug_register_callback(NULL, - LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED | LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT, - LIBUSB_HOTPLUG_ENUMERATE, LIBUSB_HOTPLUG_MATCH_ANY, LIBUSB_HOTPLUG_MATCH_ANY, - LIBUSB_HOTPLUG_MATCH_ANY, hotplug_cb, NULL, &hotplug_cb_handle))) - { - ERR("Failed to register callback: %s\n", libusb_strerror(ret)); - irp->IoStatus.Status = STATUS_UNSUCCESSFUL; - break; - } irp->IoStatus.Status = STATUS_SUCCESS; break; @@ -510,7 +357,6 @@ static NTSTATUS fdo_pnp(IRP *irp) { struct usb_device *device, *cursor; - libusb_hotplug_deregister_callback(NULL, hotplug_cb_handle); __wine_unix_call(unix_handle, unix_usb_exit, NULL); WaitForSingleObject(libusb_event_thread, INFINITE); CloseHandle(libusb_event_thread); @@ -756,257 +602,6 @@ 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 LIBUSB_CALL transfer_cb(struct libusb_transfer *transfer) -{ - IRP *irp = transfer->user_data; - URB *urb = IoGetCurrentIrpStackLocation(irp)->Parameters.Others.Argument1; - struct usb_event event; - - 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_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; - req->TransferBufferLength = transfer->actual_length; - memcpy(req->TransferBuffer, libusb_control_transfer_get_data(transfer), transfer->actual_length); - 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); - } - } - - event.type = USB_EVENT_TRANSFER_COMPLETE; - event.u.completed_irp = irp; - queue_event(&event); -} - -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 struct pipe get_pipe(HANDLE handle) -{ - union - { - struct pipe pipe; - HANDLE handle; - } u; - - u.handle = handle; - return u.pipe; -} - -static NTSTATUS unix_submit_urb(struct unix_device *device, IRP *irp) -{ - URB *urb = IoGetCurrentIrpStackLocation(irp)->Parameters.Others.Argument1; - libusb_device_handle *handle = device->handle; - struct libusb_transfer *transfer; - int ret; - - TRACE("type %#x.\n", urb->UrbHeader.Function); - - switch (urb->UrbHeader.Function) - { - 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(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; - 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; - irp->Tail.Overlay.DriverContext[0] = transfer; - - if (pipe.type == UsbdPipeTypeBulk) - { - libusb_fill_bulk_transfer(transfer, handle, pipe.endpoint, - req->TransferBuffer, req->TransferBufferLength, transfer_cb, irp, 0); - } - else if (pipe.type == UsbdPipeTypeInterrupt) - { - libusb_fill_interrupt_transfer(transfer, 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; - } - - 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; - unsigned char *buffer; - - if (req->TransferBufferMDL) - FIXME("Unhandled MDL output buffer.\n"); - - if (!(transfer = libusb_alloc_transfer(0))) - return STATUS_NO_MEMORY; - irp->Tail.Overlay.DriverContext[0] = transfer; - - if (!(buffer = malloc(sizeof(struct libusb_control_setup) + req->TransferBufferLength))) - { - libusb_free_transfer(transfer); - return STATUS_NO_MEMORY; - } - - 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, 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_DESCRIPTOR transfer: %s\n", libusb_strerror(ret)); - - 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; - } - - 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; - irp->Tail.Overlay.DriverContext[0] = transfer; - - if (!(buffer = malloc(sizeof(struct libusb_control_setup) + req->TransferBufferLength))) - { - libusb_free_transfer(transfer); - return STATUS_NO_MEMORY; - } - - 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, 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); - } - - return STATUS_NOT_IMPLEMENTED; -} - static NTSTATUS usb_submit_urb(struct usb_device *device, IRP *irp) { URB *urb = IoGetCurrentIrpStackLocation(irp)->Parameters.Others.Argument1; @@ -1046,12 +641,18 @@ static NTSTATUS usb_submit_urb(struct usb_device *device, IRP *irp) case URB_FUNCTION_SELECT_CONFIGURATION: case URB_FUNCTION_VENDOR_INTERFACE: { + struct usb_submit_urb_params params = + { + .device = device->unix_device, + .irp = irp, + }; + /* Hold the wineusb lock while submitting and queuing, and * similarly hold it in complete_irp(). That way, if libusb reports * completion between submitting and queuing, we won't try to * dequeue the IRP until it's actually been queued. */ EnterCriticalSection(&wineusb_cs); - status = unix_submit_urb(device->unix_device, irp); + status = __wine_unix_call(unix_handle, unix_usb_submit_urb, ¶ms); if (status == STATUS_PENDING) { IoMarkIrpPending(irp);