mirror of
git://source.winehq.org/git/wine.git
synced 2024-10-07 04:54:06 +00:00
wineusb.sys: Move the event queue and device list to the Unix library.
Since the device hotplug callbacks both queue events and create unix_device objects, we need to move both at the same time.
This commit is contained in:
parent
0427ccb141
commit
e4cefae126
|
@ -22,10 +22,17 @@
|
||||||
#pragma makedep unix
|
#pragma makedep unix
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include <stdarg.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
#include <stdlib.h>
|
||||||
#include <libusb.h>
|
#include <libusb.h>
|
||||||
|
#include <pthread.h>
|
||||||
#include "ntstatus.h"
|
#include "ntstatus.h"
|
||||||
#define WIN32_NO_STATUS
|
#define WIN32_NO_STATUS
|
||||||
|
#include "windef.h"
|
||||||
|
#include "winternl.h"
|
||||||
|
#include "ddk/wdm.h"
|
||||||
|
#include "ddk/usb.h"
|
||||||
#include "wine/debug.h"
|
#include "wine/debug.h"
|
||||||
#include "wine/list.h"
|
#include "wine/list.h"
|
||||||
|
|
||||||
|
@ -33,32 +40,423 @@
|
||||||
|
|
||||||
WINE_DEFAULT_DEBUG_CHANNEL(wineusb);
|
WINE_DEFAULT_DEBUG_CHANNEL(wineusb);
|
||||||
|
|
||||||
|
static libusb_hotplug_callback_handle hotplug_cb_handle;
|
||||||
|
|
||||||
static bool thread_shutdown;
|
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 NTSTATUS usb_main_loop(void *args)
|
||||||
{
|
{
|
||||||
|
static const struct usb_event shutdown_event = {.type = USB_EVENT_SHUTDOWN};
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
TRACE("Starting libusb event thread.\n");
|
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)
|
while (!thread_shutdown)
|
||||||
{
|
{
|
||||||
if ((ret = libusb_handle_events(NULL)))
|
if ((ret = libusb_handle_events(NULL)))
|
||||||
ERR("Error handling events: %s\n", libusb_strerror(ret));
|
ERR("Error handling events: %s\n", libusb_strerror(ret));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
queue_event(&shutdown_event);
|
||||||
|
|
||||||
TRACE("Shutting down libusb event thread.\n");
|
TRACE("Shutting down libusb event thread.\n");
|
||||||
return STATUS_SUCCESS;
|
return STATUS_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
static NTSTATUS usb_exit(void *args)
|
static NTSTATUS usb_exit(void *args)
|
||||||
{
|
{
|
||||||
|
libusb_hotplug_deregister_callback(NULL, hotplug_cb_handle);
|
||||||
thread_shutdown = true;
|
thread_shutdown = true;
|
||||||
libusb_interrupt_event_handler(NULL);
|
libusb_interrupt_event_handler(NULL);
|
||||||
|
|
||||||
return STATUS_SUCCESS;
|
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)
|
static NTSTATUS usb_cancel_transfer(void *args)
|
||||||
{
|
{
|
||||||
const struct usb_cancel_transfer_params *params = args;
|
const struct usb_cancel_transfer_params *params = args;
|
||||||
|
@ -70,10 +468,27 @@ static NTSTATUS usb_cancel_transfer(void *args)
|
||||||
return STATUS_SUCCESS;
|
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[] =
|
const unixlib_entry_t __wine_unix_call_funcs[] =
|
||||||
{
|
{
|
||||||
#define X(name) [unix_ ## name] = name
|
#define X(name) [unix_ ## name] = name
|
||||||
X(usb_main_loop),
|
X(usb_main_loop),
|
||||||
X(usb_exit),
|
X(usb_exit),
|
||||||
|
X(usb_get_event),
|
||||||
|
X(usb_submit_urb),
|
||||||
X(usb_cancel_transfer),
|
X(usb_cancel_transfer),
|
||||||
|
X(usb_destroy_device),
|
||||||
};
|
};
|
||||||
|
|
|
@ -23,18 +23,65 @@
|
||||||
|
|
||||||
#include "windef.h"
|
#include "windef.h"
|
||||||
#include "winternl.h"
|
#include "winternl.h"
|
||||||
|
#include "ddk/wdm.h"
|
||||||
#include "wine/unixlib.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
|
struct usb_cancel_transfer_params
|
||||||
{
|
{
|
||||||
void *transfer;
|
void *transfer;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct usb_destroy_device_params
|
||||||
|
{
|
||||||
|
struct unix_device *device;
|
||||||
|
};
|
||||||
|
|
||||||
enum unix_funcs
|
enum unix_funcs
|
||||||
{
|
{
|
||||||
unix_usb_main_loop,
|
unix_usb_main_loop,
|
||||||
unix_usb_exit,
|
unix_usb_exit,
|
||||||
|
unix_usb_get_event,
|
||||||
|
unix_usb_submit_urb,
|
||||||
unix_usb_cancel_transfer,
|
unix_usb_cancel_transfer,
|
||||||
|
unix_usb_destroy_device,
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -70,18 +70,8 @@ DECLARE_CRITICAL_SECTION(wineusb_cs);
|
||||||
|
|
||||||
static unixlib_handle_t unix_handle;
|
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);
|
static struct list device_list = LIST_INIT(device_list);
|
||||||
|
|
||||||
struct unix_device
|
|
||||||
{
|
|
||||||
struct list entry;
|
|
||||||
|
|
||||||
libusb_device_handle *handle;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct usb_device
|
struct usb_device
|
||||||
{
|
{
|
||||||
struct list entry;
|
struct list entry;
|
||||||
|
@ -106,90 +96,14 @@ struct usb_device
|
||||||
static DRIVER_OBJECT *driver_obj;
|
static DRIVER_OBJECT *driver_obj;
|
||||||
static DEVICE_OBJECT *bus_fdo, *bus_pdo;
|
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)
|
static void destroy_unix_device(struct unix_device *unix_device)
|
||||||
{
|
{
|
||||||
pthread_mutex_lock(&unix_device_mutex);
|
struct usb_destroy_device_params params =
|
||||||
libusb_close(unix_device->handle);
|
{
|
||||||
list_remove(&unix_device->entry);
|
.device = unix_device,
|
||||||
pthread_mutex_unlock(&unix_device_mutex);
|
};
|
||||||
free(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)
|
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);
|
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)
|
static void remove_unix_device(struct unix_device *unix_device)
|
||||||
{
|
{
|
||||||
struct usb_device *device;
|
struct usb_device *device;
|
||||||
|
@ -358,45 +242,12 @@ static void remove_unix_device(struct unix_device *unix_device)
|
||||||
IoInvalidateDeviceRelations(bus_pdo, BusRelations);
|
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 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 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);
|
__wine_unix_call(unix_handle, unix_usb_main_loop, NULL);
|
||||||
|
|
||||||
queue_event(&shutdown_event);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -418,7 +269,12 @@ static DWORD CALLBACK event_thread_proc(void *arg)
|
||||||
|
|
||||||
for (;;)
|
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)
|
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);
|
libusb_event_thread = CreateThread(NULL, 0, libusb_event_thread_proc, NULL, 0, NULL);
|
||||||
event_thread = CreateThread(NULL, 0, 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;
|
irp->IoStatus.Status = STATUS_SUCCESS;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -510,7 +357,6 @@ static NTSTATUS fdo_pnp(IRP *irp)
|
||||||
{
|
{
|
||||||
struct usb_device *device, *cursor;
|
struct usb_device *device, *cursor;
|
||||||
|
|
||||||
libusb_hotplug_deregister_callback(NULL, hotplug_cb_handle);
|
|
||||||
__wine_unix_call(unix_handle, unix_usb_exit, NULL);
|
__wine_unix_call(unix_handle, unix_usb_exit, NULL);
|
||||||
WaitForSingleObject(libusb_event_thread, INFINITE);
|
WaitForSingleObject(libusb_event_thread, INFINITE);
|
||||||
CloseHandle(libusb_event_thread);
|
CloseHandle(libusb_event_thread);
|
||||||
|
@ -756,257 +602,6 @@ static NTSTATUS WINAPI driver_pnp(DEVICE_OBJECT *device, IRP *irp)
|
||||||
return pdo_pnp(device, 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)
|
static NTSTATUS usb_submit_urb(struct usb_device *device, IRP *irp)
|
||||||
{
|
{
|
||||||
URB *urb = IoGetCurrentIrpStackLocation(irp)->Parameters.Others.Argument1;
|
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_SELECT_CONFIGURATION:
|
||||||
case URB_FUNCTION_VENDOR_INTERFACE:
|
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
|
/* Hold the wineusb lock while submitting and queuing, and
|
||||||
* similarly hold it in complete_irp(). That way, if libusb reports
|
* similarly hold it in complete_irp(). That way, if libusb reports
|
||||||
* completion between submitting and queuing, we won't try to
|
* completion between submitting and queuing, we won't try to
|
||||||
* dequeue the IRP until it's actually been queued. */
|
* dequeue the IRP until it's actually been queued. */
|
||||||
EnterCriticalSection(&wineusb_cs);
|
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)
|
if (status == STATUS_PENDING)
|
||||||
{
|
{
|
||||||
IoMarkIrpPending(irp);
|
IoMarkIrpPending(irp);
|
||||||
|
|
Loading…
Reference in a new issue