summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorphilemonf <philemonf@gmail.com>2019-04-05 00:27:39 -0700
committerNathan Hjelm <hjelmn@me.com>2019-04-05 09:56:22 -0600
commitdea5a8e96891134829b97a75adfdf5586f908b6d (patch)
tree7ac4a6f93267dcd0e26c25d210caf7b6f038ddf7
parenteb164d7529677c48d1b6c7a9dd9d152d71e38d43 (diff)
downloadlibusb-dea5a8e96891134829b97a75adfdf5586f908b6d.tar.gz
Add support for isochronous transfers with WinUSB.
Closes #284 Signed-off-by: Nathan Hjelm <hjelmn@me.com>
-rw-r--r--libusb/core.c30
-rw-r--r--libusb/os/windows_nt_shared_types.h9
-rw-r--r--libusb/os/windows_winusb.c335
-rw-r--r--libusb/os/windows_winusb.h63
-rw-r--r--libusb/version_nano.h2
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