From dea5a8e96891134829b97a75adfdf5586f908b6d Mon Sep 17 00:00:00 2001 From: philemonf Date: Fri, 5 Apr 2019 00:27:39 -0700 Subject: Add support for isochronous transfers with WinUSB. Closes #284 Signed-off-by: Nathan Hjelm --- libusb/core.c | 30 +++- libusb/os/windows_nt_shared_types.h | 9 + libusb/os/windows_winusb.c | 335 +++++++++++++++++++++++++++++++----- libusb/os/windows_winusb.h | 63 +++++++ libusb/version_nano.h | 2 +- 5 files changed, 391 insertions(+), 48 deletions(-) diff --git a/libusb/core.c b/libusb/core.c index ddba089..6206b85 100644 --- a/libusb/core.c +++ b/libusb/core.c @@ -1079,7 +1079,9 @@ out: * If acting on an isochronous or interrupt endpoint, this function will * multiply the value found in bits 0:10 by the number of transactions per * microframe (determined by bits 11:12). Otherwise, this function just - * returns the numeric value found in bits 0:10. + * returns the numeric value found in bits 0:10. For USB 3.0 device, it + * will attempts to retrieve the Endpoint Companion Descriptor to return + * wBytesPerInterval. * * This function is useful for setting up isochronous transfers, for example * you might pass the return value from this function to @@ -1099,9 +1101,11 @@ int API_EXPORTED libusb_get_max_iso_packet_size(libusb_device *dev, { struct libusb_config_descriptor *config; const struct libusb_endpoint_descriptor *ep; + struct libusb_ss_endpoint_companion_descriptor *ss_ep_cmp; enum libusb_transfer_type ep_type; uint16_t val; int r; + int speed; r = libusb_get_active_config_descriptor(dev, &config); if (r < 0) { @@ -1116,13 +1120,25 @@ int API_EXPORTED libusb_get_max_iso_packet_size(libusb_device *dev, goto out; } - val = ep->wMaxPacketSize; - ep_type = (enum libusb_transfer_type) (ep->bmAttributes & 0x3); + speed = libusb_get_device_speed( dev ); + if (speed == LIBUSB_SPEED_SUPER) { + r = libusb_get_ss_endpoint_companion_descriptor(dev->ctx, ep, &ss_ep_cmp); + if (r == LIBUSB_SUCCESS) { + r = ss_ep_cmp->wBytesPerInterval; + libusb_free_ss_endpoint_companion_descriptor(ss_ep_cmp); + } + } - r = val & 0x07ff; - if (ep_type == LIBUSB_TRANSFER_TYPE_ISOCHRONOUS - || ep_type == LIBUSB_TRANSFER_TYPE_INTERRUPT) - r *= (1 + ((val >> 11) & 3)); + /* If the device isn't a SuperSpeed device or retrieving the SS endpoint didn't worked. */ + if (speed != LIBUSB_SPEED_SUPER || r < 0) { + val = ep->wMaxPacketSize; + ep_type = (enum libusb_transfer_type) (ep->bmAttributes & 0x3); + + r = val & 0x07ff; + if (ep_type == LIBUSB_TRANSFER_TYPE_ISOCHRONOUS + || ep_type == LIBUSB_TRANSFER_TYPE_INTERRUPT) + r *= (1 + ((val >> 11) & 3)); + } out: libusb_free_config_descriptor(config); diff --git a/libusb/os/windows_nt_shared_types.h b/libusb/os/windows_nt_shared_types.h index 68bf261..d809bfd 100644 --- a/libusb/os/windows_nt_shared_types.h +++ b/libusb/os/windows_nt_shared_types.h @@ -97,6 +97,7 @@ struct winusb_device_priv { int sub_api; int8_t nb_endpoints; // and a set of endpoint addresses (USB_MAXENDPOINTS) uint8_t *endpoint; + int current_altsetting; bool restricted_functionality; // indicates if the interface functionality is restricted // by Windows (eg. HID keyboards or mice cannot do R/W) } usb_interface[USB_MAXINTERFACES]; @@ -134,5 +135,13 @@ struct winusb_transfer_priv { uint8_t *hid_buffer; // 1 byte extended data buffer, required for HID uint8_t *hid_dest; // transfer buffer destination, required for HID size_t hid_expected_size; + + // For isochronous transfers with LibUSBk driver: void *iso_context; + + // For isochronous transfers with Microsoft WinUSB driver: + void *isoch_buffer_handle; // The isoch_buffer_handle to free at the end of the transfer + BOOL iso_break_stream; // Whether the isoch. stream was to be continued in the last call of libusb_submit_transfer. + // As we this structure is zeroed out upon initialization, we need to use inverse logic here. + libusb_transfer_cb_fn iso_user_callback; // Original transfer callback of the user. Might be used for isochronous transfers. }; diff --git a/libusb/os/windows_winusb.c b/libusb/os/windows_winusb.c index 3292e33..423fb3d 100644 --- a/libusb/os/windows_winusb.c +++ b/libusb/os/windows_winusb.c @@ -511,6 +511,7 @@ static int windows_assign_endpoints(struct libusb_device_handle *dev_handle, int if (if_desc->bNumEndpoints == 0) { usbi_dbg("no endpoints found for interface %d", iface); libusb_free_config_descriptor(conf_desc); + priv->usb_interface[iface].current_altsetting = altsetting; return LIBUSB_SUCCESS; } @@ -531,6 +532,9 @@ static int windows_assign_endpoints(struct libusb_device_handle *dev_handle, int if (priv->apib->configure_endpoints) r = priv->apib->configure_endpoints(SUB_API_NOTSET, dev_handle, iface); + if (r == LIBUSB_SUCCESS) + priv->usb_interface[iface].current_altsetting = altsetting; + return r; } @@ -1734,11 +1738,25 @@ static void winusb_destroy_device(struct libusb_device *dev) static void winusb_clear_transfer_priv(struct usbi_transfer *itransfer) { struct winusb_transfer_priv *transfer_priv = usbi_transfer_get_os_priv(itransfer); + struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + struct winusb_device_priv *priv = _device_priv(transfer->dev_handle->dev); + int sub_api = priv->sub_api; usbi_close(transfer_priv->pollable_fd.fd); transfer_priv->pollable_fd = INVALID_WINFD; transfer_priv->handle = NULL; safe_free(transfer_priv->hid_buffer); + + if (transfer->type == LIBUSB_TRANSFER_TYPE_ISOCHRONOUS && sub_api == SUB_API_WINUSB) { + if (transfer_priv->isoch_buffer_handle != NULL) { + if (WinUSBX[sub_api].UnregisterIsochBuffer(transfer_priv->isoch_buffer_handle)) { + transfer_priv->isoch_buffer_handle = NULL; + } else { + usbi_dbg("Couldn't unregister isoch buffer!"); + } + } + } + safe_free(transfer_priv->iso_context); // When auto claim is in use, attempt to release the auto-claimed interface @@ -2011,6 +2029,14 @@ const struct windows_usb_api_backend usb_api_backend[USB_API_MAX] = { pLibK_GetProcAddress((PVOID *)&WinUSBX[i].fn, i, KUSB_FNID_##fn); \ } while (0) +#define NativeWinUSBOnly_Set(fn) \ + do { \ + if (native_winusb) \ + WinUSBX[i].fn = (WinUsb_##fn##_t)GetProcAddress(h, "WinUsb_" #fn); \ + else \ + WinUSBX[i].fn = NULL; \ + } while (0) + static int winusbx_init(struct libusb_context *ctx) { HMODULE h; @@ -2064,6 +2090,11 @@ static int winusbx_init(struct libusb_context *ctx) WinUSBX_Set(WritePipe); WinUSBX_Set(IsoReadPipe); WinUSBX_Set(IsoWritePipe); + NativeWinUSBOnly_Set(RegisterIsochBuffer); + NativeWinUSBOnly_Set(UnregisterIsochBuffer); + NativeWinUSBOnly_Set(WriteIsochPipeAsap); + NativeWinUSBOnly_Set(ReadIsochPipeAsap); + NativeWinUSBOnly_Set(QueryPipeEx); if (WinUSBX[i].Initialize != NULL) { WinUSBX[i].initialized = true; @@ -2507,6 +2538,66 @@ static int winusbx_set_interface_altsetting(int sub_api, struct libusb_device_ha return LIBUSB_SUCCESS; } +static enum libusb_transfer_status usbd_status_to_libusb_transfer_status(USBD_STATUS status) +{ + /* Based on https://msdn.microsoft.com/en-us/library/windows/hardware/ff539136(v=vs.85).aspx : + * USBD_STATUS have the most significant 4 bits indicating overall status and the rest gives the details. */ + switch (status >> 28) { + case 0x00: /* USBD_STATUS_SUCCESS */ + return LIBUSB_TRANSFER_COMPLETED; + case 0x01: /* USBD_STATUS_PENDING */ + return LIBUSB_TRANSFER_COMPLETED; + default: /* USBD_STATUS_ERROR */ + switch (status & 0x0fffffff) { + case 0xC0006000: /* USBD_STATUS_TIMEOUT */ + return LIBUSB_TRANSFER_TIMED_OUT; + case 0xC0010000: /* USBD_STATUS_CANCELED */ + return LIBUSB_TRANSFER_CANCELLED; + case 0xC0000030: /* USBD_STATUS_ENDPOINT_HALTED */ + return LIBUSB_TRANSFER_STALL; + case 0xC0007000: /* USBD_STATUS_DEVICE_GONE */ + return LIBUSB_TRANSFER_NO_DEVICE; + default: + usbi_dbg("USBD_STATUS 0x%08x translated to LIBUSB_TRANSFER_ERROR", status); + return LIBUSB_TRANSFER_ERROR; + } + } +} + +static void WINAPI winusbx_native_iso_transfer_continue_stream_callback(struct libusb_transfer *transfer) +{ + // If this callback is invoked, this means that we attempted to set ContinueStream + // to TRUE when calling Read/WriteIsochPipeAsap in winusbx_do_iso_transfer. + // The role of this callback is to fallback to ContinueStream = FALSE if the transfer + // did not succeed. + + struct winusb_transfer_priv *transfer_priv = (struct winusb_transfer_priv *) + usbi_transfer_get_os_priv(LIBUSB_TRANSFER_TO_USBI_TRANSFER(transfer)); + BOOL fallback = (transfer->status != LIBUSB_TRANSFER_COMPLETED); + int idx; + + // Restore the user callback + transfer->callback = transfer_priv->iso_user_callback; + + for (idx = 0; idx < transfer->num_iso_packets && !fallback; ++idx) { + if (transfer->iso_packet_desc[idx].status != LIBUSB_TRANSFER_COMPLETED) { + fallback = TRUE; + } + } + + if (!fallback) { + // If the transfer was successful, we restore the user callback and call it. + if (transfer->callback) { + transfer->callback(transfer); + } + } + else { + // If the transfer wasn't successful we reschedule the transfer while forcing it + // not to continue the stream. This might results in a 5-ms delay. + transfer_priv->iso_break_stream = TRUE; + libusb_submit_transfer(transfer); + } +} static int winusbx_submit_iso_transfer(int sub_api, struct usbi_transfer *itransfer) { struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); @@ -2518,66 +2609,208 @@ static int winusbx_submit_iso_transfer(int sub_api, struct usbi_transfer *itrans OVERLAPPED *overlapped; bool ret; int current_interface; - int i; - UINT offset; - PKISO_CONTEXT iso_context; - size_t iso_ctx_size; CHECK_WINUSBX_AVAILABLE(sub_api); - if ((sub_api != SUB_API_LIBUSBK) && (sub_api != SUB_API_LIBUSB0)) { - // iso only supported on libusbk-based backends - PRINT_UNSUPPORTED_API(submit_iso_transfer); - return LIBUSB_ERROR_NOT_SUPPORTED; - }; - current_interface = interface_by_endpoint(priv, handle_priv, transfer->endpoint); if (current_interface < 0) { usbi_err(ctx, "unable to match endpoint to an open interface - cancelling transfer"); return LIBUSB_ERROR_NOT_FOUND; + } else { + usbi_dbg("matched endpoint %02X with interface %d", transfer->endpoint, current_interface); } - usbi_dbg("matched endpoint %02X with interface %d", transfer->endpoint, current_interface); - transfer_priv->handle = winusb_handle = handle_priv->interface_handle[current_interface].api_handle; overlapped = transfer_priv->pollable_fd.overlapped; - iso_ctx_size = sizeof(KISO_CONTEXT) + (transfer->num_iso_packets * sizeof(KISO_PACKET)); - transfer_priv->iso_context = iso_context = calloc(1, iso_ctx_size); - if (transfer_priv->iso_context == NULL) - return LIBUSB_ERROR_NO_MEM; + if ((sub_api == SUB_API_LIBUSBK) || (sub_api == SUB_API_LIBUSB0)) { + int i; + UINT offset; + size_t iso_ctx_size; + PKISO_CONTEXT iso_context; - // start ASAP - iso_context->StartFrame = 0; - iso_context->NumberOfPackets = (SHORT)transfer->num_iso_packets; + iso_ctx_size = sizeof(KISO_CONTEXT) + (transfer->num_iso_packets * sizeof(KISO_PACKET)); + transfer_priv->iso_context = iso_context = calloc(1, iso_ctx_size); + if (transfer_priv->iso_context == NULL) + return LIBUSB_ERROR_NO_MEM; - // convert the transfer packet lengths to iso_packet offsets - offset = 0; - for (i = 0; i < transfer->num_iso_packets; i++) { - iso_context->IsoPackets[i].offset = offset; - offset += transfer->iso_packet_desc[i].length; - } + // start ASAP + iso_context->StartFrame = 0; + iso_context->NumberOfPackets = (SHORT)transfer->num_iso_packets; - if (IS_XFERIN(transfer)) { - usbi_dbg("reading %d iso packets", transfer->num_iso_packets); - ret = WinUSBX[sub_api].IsoReadPipe(winusb_handle, transfer->endpoint, transfer->buffer, transfer->length, overlapped, iso_context); - } else { - usbi_dbg("writing %d iso packets", transfer->num_iso_packets); - ret = WinUSBX[sub_api].IsoWritePipe(winusb_handle, transfer->endpoint, transfer->buffer, transfer->length, overlapped, iso_context); - } + // convert the transfer packet lengths to iso_packet offsets + offset = 0; + for (i = 0; i < transfer->num_iso_packets; i++) { + iso_context->IsoPackets[i].offset = offset; + offset += transfer->iso_packet_desc[i].length; + } - if (!ret) { - if (GetLastError() != ERROR_IO_PENDING) { - usbi_err(ctx, "IsoReadPipe/IsoWritePipe failed: %s", windows_error_str(0)); - return LIBUSB_ERROR_IO; + if (IS_XFERIN(transfer)) { + usbi_dbg("reading %d iso packets", transfer->num_iso_packets); + ret = WinUSBX[sub_api].IsoReadPipe(winusb_handle, transfer->endpoint, transfer->buffer, transfer->length, overlapped, iso_context); + } else { + usbi_dbg("writing %d iso packets", transfer->num_iso_packets); + ret = WinUSBX[sub_api].IsoWritePipe(winusb_handle, transfer->endpoint, transfer->buffer, transfer->length, overlapped, iso_context); } - } else { - windows_force_sync_completion(overlapped, (ULONG)transfer->length); + + if (!ret) { + if (GetLastError() != ERROR_IO_PENDING) { + usbi_err(ctx, "IsoReadPipe/IsoWritePipe failed: %s", windows_error_str(0)); + return LIBUSB_ERROR_IO; + } + } else { + windows_force_sync_completion(overlapped, (ULONG)transfer->length); + } + + transfer_priv->interface_number = (uint8_t)current_interface; + + return LIBUSB_SUCCESS; } + else if (sub_api == SUB_API_WINUSB) { + WINUSB_PIPE_INFORMATION_EX pipe_info_ex = { 0 }; + WINUSB_ISOCH_BUFFER_HANDLE buffer_handle; + ULONG iso_transfer_size_multiple; + int out_transfer_length = 0; + int idx; + +# define WINUSBX_CHECK_API_SUPPORTED(API) \ + if (WinUSBX[sub_api].API == NULL) \ + { \ + usbi_dbg(#API " isn't available"); \ + return LIBUSB_ERROR_NOT_SUPPORTED; \ + } - transfer_priv->interface_number = (uint8_t)current_interface; + // Depending on the version of Microsoft WinUSB, isochronous transfers may not be supported. + WINUSBX_CHECK_API_SUPPORTED(RegisterIsochBuffer); + WINUSBX_CHECK_API_SUPPORTED(ReadIsochPipeAsap); + WINUSBX_CHECK_API_SUPPORTED(WriteIsochPipeAsap); + WINUSBX_CHECK_API_SUPPORTED(UnregisterIsochBuffer); + WINUSBX_CHECK_API_SUPPORTED(QueryPipeEx); - return LIBUSB_SUCCESS; + if (sizeof(struct libusb_iso_packet_descriptor) != sizeof(USBD_ISO_PACKET_DESCRIPTOR)) { + usbi_dbg("The size of Microsoft WinUsb and libusb isochronous packet descriptor doesn't match."); + return LIBUSB_ERROR_NOT_SUPPORTED; + } + + // Query the pipe extended information to find the pipe index corresponding to the endpoint. + for (idx = 0; idx < priv->usb_interface[current_interface].nb_endpoints; ++idx) { + ret = WinUSBX[sub_api].QueryPipeEx(winusb_handle, (UINT8)priv->usb_interface[current_interface].current_altsetting, (UCHAR)idx, &pipe_info_ex); + if (!ret) { + usbi_dbg("Couldn't query interface settings for USB pipe with index %d. Error: %s", idx, windows_error_str(0)); + return LIBUSB_ERROR_NOT_FOUND; + } + + if (pipe_info_ex.PipeId == transfer->endpoint && pipe_info_ex.PipeType == UsbdPipeTypeIsochronous) { + break; + } + } + + // Make sure we found the index. + if (idx >= priv->usb_interface[current_interface].nb_endpoints) { + usbi_dbg("Couldn't find the isochronous endpoint %02x.", transfer->endpoint); + return LIBUSB_ERROR_NOT_FOUND; + } + + if (IS_XFERIN(transfer)) { + int interval = pipe_info_ex.Interval; + + // For high-speed and SuperSpeed device, the interval is 2**(bInterval-1). + if (libusb_get_device_speed(libusb_get_device(transfer->dev_handle)) >= LIBUSB_SPEED_HIGH) { + interval = (1 << (pipe_info_ex.Interval - 1)); + } + + // WinUSB only supports isochronous transfers spanning a full USB frames. Later, we might be smarter about this + // and allocate a temporary buffer. However, this is harder than it seems as its destruction would depend on overlapped + // IO... + iso_transfer_size_multiple = (pipe_info_ex.MaximumBytesPerInterval * 8) / interval; + if (transfer->length % iso_transfer_size_multiple != 0) { + usbi_dbg("The length of isochronous buffer must be a multiple of the MaximumBytesPerInterval * 8 / Interval"); + return LIBUSB_ERROR_INVALID_PARAM; + } + } + else { + // If this is an OUT transfer, we make sure the isochronous packets are contiguous as this isn't supported otherwise. + BOOL size_should_be_zero = FALSE; + out_transfer_length = 0; + for (idx = 0; idx < transfer->num_iso_packets; ++idx) { + if ((size_should_be_zero && transfer->iso_packet_desc[idx].length != 0) || + (transfer->iso_packet_desc[idx].length != pipe_info_ex.MaximumBytesPerInterval && idx + 1 < transfer->num_iso_packets && transfer->iso_packet_desc[idx + 1].length > 0)) { + usbi_dbg("Isochronous packets for OUT transfer with Microsoft WinUSB must be contiguous in memory."); + return LIBUSB_ERROR_INVALID_PARAM; + } + + size_should_be_zero = (transfer->iso_packet_desc[idx].length == 0); + out_transfer_length += transfer->iso_packet_desc[idx].length; + } + } + + if (transfer_priv->isoch_buffer_handle != NULL) { + if (WinUSBX[sub_api].UnregisterIsochBuffer(transfer_priv->isoch_buffer_handle)) { + transfer_priv->isoch_buffer_handle = NULL; + } else { + usbi_dbg("Couldn't unregister the Microsoft WinUSB isochronous buffer: %s", windows_error_str(0)); + return LIBUSB_ERROR_OTHER; + } + } + + // Register the isochronous buffer to the operating system. + ret = WinUSBX[sub_api].RegisterIsochBuffer(winusb_handle, transfer->endpoint, transfer->buffer, transfer->length, &buffer_handle); + if (!ret) { + usbi_dbg("Microsoft WinUSB refused to allocate an isochronous buffer."); + return LIBUSB_ERROR_NO_MEM; + } + + // Important note: the WinUSB_Read/WriteIsochPipeAsap API requires a ContinueStream parameter that tells whether the isochronous + // stream must be continued or if the WinUSB driver can schedule the transfer at its conveniance. Profiling subsequent transfers + // with ContinueStream = FALSE showed that 5 frames, i.e. about 5 milliseconds, were left empty between each transfer. This + // is critical as this greatly diminish the achievable isochronous bandwidth. We solved the problem using the following strategy: + // - Transfers are first scheduled with ContinueStream = TRUE and with winusbx_iso_transfer_continue_stream_callback as user callback. + // - If the transfer succeeds, winusbx_iso_transfer_continue_stream_callback restore the user callback and calls its. + // - If the transfer fails, winusbx_iso_transfer_continue_stream_callback reschedule the transfer and force ContinueStream = FALSE. + if (!transfer_priv->iso_break_stream) { + transfer_priv->iso_user_callback = transfer->callback; + transfer->callback = winusbx_native_iso_transfer_continue_stream_callback; + } + + // Initiate the transfers. + if (IS_XFERIN(transfer)) { + ret = WinUSBX[sub_api].ReadIsochPipeAsap(buffer_handle, 0, transfer->length, !transfer_priv->iso_break_stream, transfer->num_iso_packets, (PUSBD_ISO_PACKET_DESCRIPTOR)transfer->iso_packet_desc, overlapped); + } + else { + ret = WinUSBX[sub_api].WriteIsochPipeAsap(buffer_handle, 0, out_transfer_length, !transfer_priv->iso_break_stream, overlapped); + } + + // Restore the ContinueStream parameter to TRUE. + transfer_priv->iso_break_stream = FALSE; + + if (!ret) { + if (GetLastError() == ERROR_IO_PENDING) { + transfer_priv->isoch_buffer_handle = buffer_handle; + } else { + usbi_err(ctx, "ReadIsochPipeAsap/WriteIsochPipeAsap failed: %s", windows_error_str(0)); + if (WinUSBX[sub_api].UnregisterIsochBuffer(buffer_handle)) { + transfer_priv->isoch_buffer_handle = NULL; + return LIBUSB_ERROR_IO; + } else { + usbi_dbg("Couldn't unregister the Microsoft WinUSB isochronous buffer: %s", windows_error_str(0)); + return LIBUSB_ERROR_OTHER; + } + } + } else { + windows_force_sync_completion(overlapped, (ULONG)transfer->length); + if (!WinUSBX[sub_api].UnregisterIsochBuffer(buffer_handle)) { + usbi_dbg("Couldn't unregister the Microsoft WinUSB isochronous buffer: %s", windows_error_str(0)); + return LIBUSB_ERROR_OTHER; + } + } + + transfer_priv->interface_number = (uint8_t)current_interface; + + return LIBUSB_SUCCESS; + } else { + PRINT_UNSUPPORTED_API(winusbx_submit_iso_transfer); + return LIBUSB_ERROR_NOT_SUPPORTED; + } } static int winusbx_submit_bulk_transfer(int sub_api, struct usbi_transfer *itransfer) @@ -2779,6 +3012,28 @@ static int winusbx_copy_transfer_data(int sub_api, struct usbi_transfer *itransf // TODO translate USDB_STATUS codes http://msdn.microsoft.com/en-us/library/ff539136(VS.85).aspx to libusb_transfer_status //transfer->iso_packet_desc[i].status = transfer_priv->iso_context->IsoPackets[i].status; } + } else if (sub_api == SUB_API_WINUSB) { + if (IS_XFERIN(transfer)) { + /* Convert isochronous packet descriptor between Windows and libusb representation. + * Both representation are guaranteed to have the same length in bytes.*/ + PUSBD_ISO_PACKET_DESCRIPTOR usbd_iso_packet_desc = (PUSBD_ISO_PACKET_DESCRIPTOR)transfer->iso_packet_desc; + for (i = 0; i < transfer->num_iso_packets; ++i) + { + int length = (i < transfer->num_iso_packets - 1) ? (usbd_iso_packet_desc[i + 1].Offset - usbd_iso_packet_desc[i].Offset) : usbd_iso_packet_desc[i].Length; + int actual_length = usbd_iso_packet_desc[i].Length; + USBD_STATUS status = usbd_iso_packet_desc[i].Status; + + transfer->iso_packet_desc[i].length = length; + transfer->iso_packet_desc[i].actual_length = actual_length; + transfer->iso_packet_desc[i].status = usbd_status_to_libusb_transfer_status(status); + } + } + else { + for (i = 0; i < transfer->num_iso_packets; ++i) + { + transfer->iso_packet_desc[i].status = LIBUSB_TRANSFER_COMPLETED; + } + } } else { // This should only occur if backend is not set correctly or other backend isoc is partially implemented PRINT_UNSUPPORTED_API(copy_transfer_data); diff --git a/libusb/os/windows_winusb.h b/libusb/os/windows_winusb.h index 597384a..651d7c4 100644 --- a/libusb/os/windows_winusb.h +++ b/libusb/os/windows_winusb.h @@ -505,6 +505,60 @@ typedef BOOL (WINAPI *WinUsb_WritePipe_t)( LPOVERLAPPED Overlapped ); +typedef PVOID WINUSB_ISOCH_BUFFER_HANDLE, *PWINUSB_ISOCH_BUFFER_HANDLE; + +typedef BOOL (WINAPI *WinUsb_RegisterIsochBuffer_t)( + WINUSB_INTERFACE_HANDLE InterfaceHandle, + UCHAR PipeID, + PVOID Buffer, + ULONG BufferLength, + PWINUSB_ISOCH_BUFFER_HANDLE BufferHandle +); + +typedef BOOL (WINAPI *WinUsb_UnregisterIsochBuffer_t)( + WINUSB_ISOCH_BUFFER_HANDLE BufferHandle +); + +typedef BOOL (WINAPI *WinUsb_WriteIsochPipeAsap_t)( + WINUSB_ISOCH_BUFFER_HANDLE BufferHandle, + ULONG Offset, + ULONG Length, + BOOL ContinueStream, + LPOVERLAPPED Overlapped +); + +typedef LONG USBD_STATUS; +typedef struct { + ULONG Offset; + ULONG Length; + USBD_STATUS Status; +} USBD_ISO_PACKET_DESCRIPTOR, *PUSBD_ISO_PACKET_DESCRIPTOR; + +typedef BOOL (WINAPI *WinUsb_ReadIsochPipeAsap_t)( + PWINUSB_ISOCH_BUFFER_HANDLE BufferHandle, + ULONG Offset, + ULONG Length, + BOOL ContinueStream, + ULONG NumberOfPackets, + PUSBD_ISO_PACKET_DESCRIPTOR IsoPacketDescriptors, + LPOVERLAPPED Overlapped +); + +typedef struct { + USBD_PIPE_TYPE PipeType; + UCHAR PipeId; + USHORT MaximumPacketSize; + UCHAR Interval; + ULONG MaximumBytesPerInterval; +} WINUSB_PIPE_INFORMATION_EX, *PWINUSB_PIPE_INFORMATION_EX; + +typedef BOOL (WINAPI *WinUsb_QueryPipeEx_t)( + WINUSB_INTERFACE_HANDLE InterfaceHandle, + UCHAR AlternateInterfaceHandle, + UCHAR PipeIndex, + PWINUSB_PIPE_INFORMATION_EX PipeInformationEx +); + /* /!\ These must match the ones from the official libusbk.h */ typedef enum _KUSB_FNID { KUSB_FNID_Init, @@ -616,8 +670,17 @@ struct winusb_interface { WinUsb_SetCurrentAlternateSetting_t SetCurrentAlternateSetting; WinUsb_SetPipePolicy_t SetPipePolicy; WinUsb_WritePipe_t WritePipe; + + // Isochoronous functions for LibUSBk sub api: WinUsb_IsoReadPipe_t IsoReadPipe; WinUsb_IsoWritePipe_t IsoWritePipe; + + // Isochronous functions for Microsoft WinUSB sub api (native WinUSB): + WinUsb_RegisterIsochBuffer_t RegisterIsochBuffer; + WinUsb_UnregisterIsochBuffer_t UnregisterIsochBuffer; + WinUsb_WriteIsochPipeAsap_t WriteIsochPipeAsap; + WinUsb_ReadIsochPipeAsap_t ReadIsochPipeAsap; + WinUsb_QueryPipeEx_t QueryPipeEx; }; /* hid.dll interface */ diff --git a/libusb/version_nano.h b/libusb/version_nano.h index 8af14bf..d242308 100644 --- a/libusb/version_nano.h +++ b/libusb/version_nano.h @@ -1 +1 @@ -#define LIBUSB_NANO 11363 +#define LIBUSB_NANO 11364 -- cgit v1.2.1