From: Ivo Ivanov logos128@gmail.com
Allow drivers such as NaturalPoint's npusbio_x64.sys to communicate with the lower level USB bus driver.
Makes NaturalPoint's TrackIR5 head tracking system fully functional in Wine. --- dlls/wineusb.sys/unixlib.c | 48 ++++++++++++++++++++++++++------------ dlls/wineusb.sys/unixlib.h | 1 + dlls/wineusb.sys/wineusb.c | 30 ++++++++++++++++++++++++ 3 files changed, 64 insertions(+), 15 deletions(-)
diff --git a/dlls/wineusb.sys/unixlib.c b/dlls/wineusb.sys/unixlib.c index 278750c37f6..21105e0bb6b 100644 --- a/dlls/wineusb.sys/unixlib.c +++ b/dlls/wineusb.sys/unixlib.c @@ -303,14 +303,23 @@ static NTSTATUS usbd_status_from_libusb(enum libusb_transfer_status status) } }
+struct user_data +{ + IRP *irp; + void *transfer_buffer; +}; + static void LIBUSB_CALL transfer_cb(struct libusb_transfer *transfer) { - IRP *irp = transfer->user_data; + struct user_data *user_data = transfer->user_data; + IRP *irp = user_data->irp; URB *urb = IoGetCurrentIrpStackLocation(irp)->Parameters.Others.Argument1; + unsigned char *transfer_buffer = user_data->transfer_buffer; struct usb_event event;
TRACE("Completing IRP %p, status %#x.\n", irp, transfer->status);
+ free(user_data); urb->UrbHeader.Status = usbd_status_from_libusb(transfer->status);
if (transfer->status == LIBUSB_TRANSFER_COMPLETED) @@ -325,7 +334,7 @@ static void LIBUSB_CALL transfer_cb(struct libusb_transfer *transfer) { 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); + memcpy(transfer_buffer, libusb_control_transfer_get_data(transfer), transfer->actual_length); break; }
@@ -334,7 +343,7 @@ static void LIBUSB_CALL transfer_cb(struct libusb_transfer *transfer) 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); + memcpy(transfer_buffer, libusb_control_transfer_get_data(transfer), transfer->actual_length); break; }
@@ -408,9 +417,12 @@ static NTSTATUS usb_submit_urb(void *args) { struct _URB_BULK_OR_INTERRUPT_TRANSFER *req = &urb->UrbBulkOrInterruptTransfer; struct pipe pipe = get_pipe(req->PipeHandle); + struct user_data *user_data;
- if (req->TransferBufferMDL) - FIXME("Unhandled MDL output buffer.\n"); + if (!(user_data = calloc(1, sizeof(*user_data)))) + return STATUS_NO_MEMORY; + user_data->irp = irp; + user_data->transfer_buffer = params->transfer_buffer;
if (!(transfer = libusb_alloc_transfer(0))) return STATUS_NO_MEMORY; @@ -419,12 +431,12 @@ static NTSTATUS usb_submit_urb(void *args) if (pipe.type == UsbdPipeTypeBulk) { libusb_fill_bulk_transfer(transfer, handle, pipe.endpoint, - req->TransferBuffer, req->TransferBufferLength, transfer_cb, irp, 0); + params->transfer_buffer, req->TransferBufferLength, transfer_cb, user_data, 0); } else if (pipe.type == UsbdPipeTypeInterrupt) { libusb_fill_interrupt_transfer(transfer, handle, pipe.endpoint, - req->TransferBuffer, req->TransferBufferLength, transfer_cb, irp, 0); + params->transfer_buffer, req->TransferBufferLength, transfer_cb, user_data, 0); } else { @@ -444,10 +456,13 @@ static NTSTATUS usb_submit_urb(void *args) case URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE: { struct _URB_CONTROL_DESCRIPTOR_REQUEST *req = &urb->UrbControlDescriptorRequest; + struct user_data *user_data; unsigned char *buffer;
- if (req->TransferBufferMDL) - FIXME("Unhandled MDL output buffer.\n"); + if (!(user_data = calloc(1, sizeof(*user_data)))) + return STATUS_NO_MEMORY; + user_data->irp = irp; + user_data->transfer_buffer = params->transfer_buffer;
if (!(transfer = libusb_alloc_transfer(0))) return STATUS_NO_MEMORY; @@ -463,7 +478,7 @@ static NTSTATUS usb_submit_urb(void *args) 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); + libusb_fill_control_transfer(transfer, handle, buffer, transfer_cb, user_data, 0); transfer->flags = LIBUSB_TRANSFER_FREE_BUFFER | LIBUSB_TRANSFER_FREE_TRANSFER; ret = libusb_submit_transfer(transfer); if (ret < 0) @@ -496,16 +511,19 @@ static NTSTATUS usb_submit_urb(void *args) { struct _URB_CONTROL_VENDOR_OR_CLASS_REQUEST *req = &urb->UrbControlVendorClassRequest; uint8_t req_type = LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_INTERFACE; + struct user_data *user_data; unsigned char *buffer;
+ if (!(user_data = calloc(1, sizeof(*user_data)))) + return STATUS_NO_MEMORY; + user_data->irp = irp; + user_data->transfer_buffer = params->transfer_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", (int)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; @@ -519,8 +537,8 @@ static NTSTATUS usb_submit_urb(void *args) 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); + memcpy(buffer + LIBUSB_CONTROL_SETUP_SIZE, params->transfer_buffer, req->TransferBufferLength); + libusb_fill_control_transfer(transfer, handle, buffer, transfer_cb, user_data, 0); transfer->flags = LIBUSB_TRANSFER_FREE_BUFFER | LIBUSB_TRANSFER_FREE_TRANSFER; ret = libusb_submit_transfer(transfer); if (ret < 0) diff --git a/dlls/wineusb.sys/unixlib.h b/dlls/wineusb.sys/unixlib.h index eee16c8fc2b..3d27d3715c3 100644 --- a/dlls/wineusb.sys/unixlib.h +++ b/dlls/wineusb.sys/unixlib.h @@ -61,6 +61,7 @@ struct usb_submit_urb_params { struct unix_device *device; IRP *irp; + void *transfer_buffer; };
struct usb_cancel_transfer_params diff --git a/dlls/wineusb.sys/wineusb.c b/dlls/wineusb.sys/wineusb.c index 7848b31105e..cafc2cf2266 100644 --- a/dlls/wineusb.sys/wineusb.c +++ b/dlls/wineusb.sys/wineusb.c @@ -542,6 +542,36 @@ static NTSTATUS usb_submit_urb(struct usb_device *device, IRP *irp) .irp = irp, };
+ switch (urb->UrbHeader.Function) + { + case URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER: + { + struct _URB_BULK_OR_INTERRUPT_TRANSFER *req = &urb->UrbBulkOrInterruptTransfer; + if (req->TransferBufferMDL) + params.transfer_buffer = MmGetSystemAddressForMdlSafe(req->TransferBufferMDL, NormalPagePriority); + else params.transfer_buffer = req->TransferBuffer; + break; + } + + case URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE: + { + struct _URB_CONTROL_DESCRIPTOR_REQUEST *req = &urb->UrbControlDescriptorRequest; + if (req->TransferBufferMDL) + params.transfer_buffer = MmGetSystemAddressForMdlSafe(req->TransferBufferMDL, NormalPagePriority); + else params.transfer_buffer = req->TransferBuffer; + break; + } + + case URB_FUNCTION_VENDOR_INTERFACE: + { + struct _URB_CONTROL_VENDOR_OR_CLASS_REQUEST *req = &urb->UrbControlVendorClassRequest; + if (req->TransferBufferMDL) + params.transfer_buffer = MmGetSystemAddressForMdlSafe(req->TransferBufferMDL, NormalPagePriority); + else params.transfer_buffer = req->TransferBuffer; + break; + } + } + /* 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
Zebediah Figura (@zfigura) commented about dlls/wineusb.sys/unixlib.c:
}
}
+struct user_data
Nitpick, but how about "struct transfer_ctx"?
Zebediah Figura (@zfigura) commented about dlls/wineusb.sys/wineusb.c:
.irp = irp, };
switch (urb->UrbHeader.Function)
{
case URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER:
{
struct _URB_BULK_OR_INTERRUPT_TRANSFER *req = &urb->UrbBulkOrInterruptTransfer;
if (req->TransferBufferMDL)
params.transfer_buffer = MmGetSystemAddressForMdlSafe(req->TransferBufferMDL, NormalPagePriority);
else params.transfer_buffer = req->TransferBuffer;
Can you please put "else" statements on their own line?
On Thu Dec 1 17:55:22 2022 +0000, Zebediah Figura wrote:
Nitpick, but how about "struct transfer_ctx"?
Yep. That'd be better.
On Thu Dec 1 17:55:22 2022 +0000, Zebediah Figura wrote:
Can you please put "else" statements on their own line?
Sorry. Should have followed the coding style of the other parts of the module.