summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMartin Ling <martin-git@earth.li>2022-10-11 15:21:21 +0100
committerTormod Volden <debian.tormod@gmail.com>2023-01-20 11:05:01 +0100
commit9e4bb9cbe59050d29f7662363c0a7bfc5cf35550 (patch)
treefab15263ed94701008f053aecbb79bafdfa50a80
parentaac2e12e5f439d401029499b7cdd77c40ee8ed45 (diff)
downloadlibusb-9e4bb9cbe59050d29f7662363c0a7bfc5cf35550.tar.gz
windows: Add option for WinUSB RAW_IO endpoint policy
Add support for enabling or disabling the WinUSB RAW_IO pipe policy for a given endpoint, which is documented here: https://learn.microsoft.com/en-us/windows-hardware/drivers/usbcon/winusb-functions-for-pipe-policy-modification This is necessary to increase performance. Without this option the WinUSB backend will only queue one inbound operation at a time, even if the libusb async API is used to submit multiple transfers. For real-time sampling devices with high sample rates and small buffers, the use of RAW_IO combined with queued async transfers is essential to maintaining the necessary throughput and avoiding lost samples or buffer overruns. Examples of devices affected include Cypress FX2 based logic analyzers accessed using Sigrok, and the HackRF software defined radio. The new option must be set by calling libusb_set_option with the arguments: libusb_set_option(ctx, LIBUSB_OPTION_WINUSB_RAW_IO, dev_handle, endpoint_address, enable, max_transfer_size_ptr) where the types of the variadic arguments are: libusb_device_handle *dev_handle; unsigned int endpoint_address; unsigned int enable; unsigned int *max_transfer_size_ptr; The dev_handle and endpoint_address parameters must identify a valid IN endpoint on an open device. If enable is nonzero, RAW_IO is enabled, otherwise it is disabled. Unless max_transfer_size_ptr is NULL, then on a successful call to enable RAW_IO, the pointer destination will be written with the MAXIMUM_TRANSFER_SIZE value for the endpoint. Whilst RAW_IO is enabled for an endpoint, all transfers on that endpoint must meet the following two requirements: - The buffer length must be a multiple of the maximum endpoint packet size. - The length must be less than or equal to the MAXIMUM_TRANSFER_SIZE value. This option should not be changed when any transfer is in progress on the specified endpoint. This option only affects the WinUSB backend. On other backends it is ignored and returns LIBUSB_ERROR_NOT_SUPPORTED, without modifying the value pointed to by max_transfer_size_ptr. A great deal of credit is due to Petteri Aimonen and Patrick Stewart for previous work, and to everyone else who participated in discussions. Fixes #490 Closes #1208
-rw-r--r--libusb/core.c1
-rw-r--r--libusb/libusb.h35
-rw-r--r--libusb/os/windows_common.c6
-rw-r--r--libusb/os/windows_common.h1
-rw-r--r--libusb/os/windows_usbdk.c1
-rw-r--r--libusb/os/windows_winusb.c108
-rw-r--r--libusb/version_nano.h2
7 files changed, 150 insertions, 4 deletions
diff --git a/libusb/core.c b/libusb/core.c
index 497c7f6..e43e47c 100644
--- a/libusb/core.c
+++ b/libusb/core.c
@@ -2233,6 +2233,7 @@ int API_EXPORTEDV libusb_set_option(libusb_context *ctx,
/* Handle all backend-specific options here */
case LIBUSB_OPTION_USE_USBDK:
case LIBUSB_OPTION_NO_DEVICE_DISCOVERY:
+ case LIBUSB_OPTION_WINUSB_RAW_IO:
if (usbi_backend.set_option)
return usbi_backend.set_option(ctx, option, ap);
diff --git a/libusb/libusb.h b/libusb/libusb.h
index a865bc8..ed54d91 100644
--- a/libusb/libusb.h
+++ b/libusb/libusb.h
@@ -1501,7 +1501,40 @@ enum libusb_option {
#define LIBUSB_OPTION_WEAK_AUTHORITY LIBUSB_OPTION_NO_DEVICE_DISCOVERY
- LIBUSB_OPTION_MAX = 3
+ /** Enable or disable WinUSB RAW_IO mode on an endpoint
+ *
+ * Requires four additional arguments of:
+ *
+ * libusb_device_handle *dev_handle
+ * unsigned int endpoint_address
+ * unsigned int enable
+ * unsigned int *max_transfer_size_ptr
+ *
+ * The dev_handle and endpoint_address parameters must identify a valid
+ * IN endpoint on an open device. If enable is nonzero, RAW_IO is
+ * enabled, otherwise it is disabled. Unless max_transfer_size_ptr is
+ * NULL, then on a successful call to enable RAW_IO, it will be written
+ * with the MAXIMUM_TRANSFER_SIZE value for the endpoint.
+ *
+ * Whilst RAW_IO is enabled for an endpoint, all transfers on that endpoint
+ * must meet the following two requirements:
+ *
+ * * The buffer length must be a multiple of the maximum endpoint packet size.
+ *
+ * * The length must be less than or equal to the MAXIMUM_TRANSFER_SIZE value.
+ *
+ * This option should not be changed when any transfer is in progress on the
+ * specified endpoint.
+ *
+ * This option only affects the WinUSB backend. On other backends it is ignored
+ * and returns LIBUSB_OPTION_NOT_SUPPORTED, without modifying the value pointed
+ * to by max_transfer_size_ptr.
+ *
+ * Since version 1.0.27, \ref LIBUSB_API_VERSION >= 0x0100010A
+ */
+ LIBUSB_OPTION_WINUSB_RAW_IO = 3,
+
+ LIBUSB_OPTION_MAX = 4
};
/** \ingroup libusb_lib
diff --git a/libusb/os/windows_common.c b/libusb/os/windows_common.c
index 32887fb..308bbc8 100644
--- a/libusb/os/windows_common.c
+++ b/libusb/os/windows_common.c
@@ -607,8 +607,6 @@ static int windows_set_option(struct libusb_context *ctx, enum libusb_option opt
{
struct windows_context_priv *priv = usbi_get_context_priv(ctx);
- UNUSED(ap);
-
if (option == LIBUSB_OPTION_USE_USBDK) {
if (!usbdk_available) {
usbi_err(ctx, "UsbDk backend not available");
@@ -619,6 +617,10 @@ static int windows_set_option(struct libusb_context *ctx, enum libusb_option opt
return LIBUSB_SUCCESS;
}
+ if (priv->backend->set_option) {
+ return priv->backend->set_option(ctx, option, ap);
+ }
+
return LIBUSB_ERROR_NOT_SUPPORTED;
}
diff --git a/libusb/os/windows_common.h b/libusb/os/windows_common.h
index ff0a5b7..49c02ab 100644
--- a/libusb/os/windows_common.h
+++ b/libusb/os/windows_common.h
@@ -341,6 +341,7 @@ struct windows_backend {
int (*cancel_transfer)(struct usbi_transfer *itransfer);
void (*clear_transfer_priv)(struct usbi_transfer *itransfer);
enum libusb_transfer_status (*copy_transfer_data)(struct usbi_transfer *itransfer, DWORD length);
+ int (*set_option)(struct libusb_context *ctx, enum libusb_option option, va_list args);
};
struct windows_context_priv {
diff --git a/libusb/os/windows_usbdk.c b/libusb/os/windows_usbdk.c
index 9f52b48..4c08bcf 100644
--- a/libusb/os/windows_usbdk.c
+++ b/libusb/os/windows_usbdk.c
@@ -721,4 +721,5 @@ const struct windows_backend usbdk_backend = {
NULL, /* cancel_transfer */
usbdk_clear_transfer_priv,
usbdk_copy_transfer_data,
+ NULL /* usbdk_set_option */,
};
diff --git a/libusb/os/windows_winusb.c b/libusb/os/windows_winusb.c
index 4100eaa..99635f2 100644
--- a/libusb/os/windows_winusb.c
+++ b/libusb/os/windows_winusb.c
@@ -42,6 +42,8 @@
continue; \
}
+static int interface_by_endpoint(struct winusb_device_priv *priv,
+ struct winusb_device_handle_priv *handle_priv, uint8_t endpoint_address);
// WinUSB-like API prototypes
static bool winusbx_init(struct libusb_context *ctx);
static void winusbx_exit(void);
@@ -2167,6 +2169,111 @@ static enum libusb_transfer_status winusb_copy_transfer_data(struct usbi_transfe
return priv->apib->copy_transfer_data(SUB_API_NOTSET, itransfer, length);
}
+static int winusb_set_option(struct libusb_context *ctx, enum libusb_option option, va_list ap)
+{
+ struct libusb_device_handle *dev_handle;
+ unsigned int endpoint;
+ unsigned int enable;
+ unsigned int *max_transfer_size_ptr;
+ struct winusb_device_handle_priv *handle_priv;
+ struct winusb_device_priv *priv;
+ UCHAR policy;
+ int current_interface;
+ HANDLE winusb_handle;
+ int sub_api = SUB_API_NOTSET;
+ ULONG max_transfer_size = 0;
+
+ switch (option) {
+ case LIBUSB_OPTION_WINUSB_RAW_IO:
+ dev_handle = va_arg(ap, struct libusb_device_handle *);
+ endpoint = va_arg(ap, unsigned int);
+ enable = va_arg(ap, unsigned int);
+ max_transfer_size_ptr = va_arg(ap, unsigned int *);
+
+ policy = enable != 0;
+
+ if (dev_handle == NULL) {
+ usbi_err(ctx, "device handle passed for RAW_IO was NULL");
+ return LIBUSB_ERROR_INVALID_PARAM;
+ }
+
+ if (HANDLE_CTX(dev_handle) != ctx) {
+ usbi_err(ctx, "device handle passed for RAW_IO has wrong context");
+ return LIBUSB_ERROR_INVALID_PARAM;
+ }
+
+ if (endpoint & ~(LIBUSB_ENDPOINT_DIR_MASK | LIBUSB_ENDPOINT_ADDRESS_MASK)) {
+ usbi_err(ctx, "invalid endpoint %X passed for RAW_IO", endpoint);
+ return LIBUSB_ERROR_INVALID_PARAM;
+ }
+
+ if (!(endpoint & LIBUSB_ENDPOINT_DIR_MASK)) {
+ usbi_err(ctx, "endpoint %02X passed for RAW_IO is OUT, not IN", endpoint);
+ return LIBUSB_ERROR_INVALID_PARAM;
+ }
+
+ handle_priv = get_winusb_device_handle_priv(dev_handle);
+ priv = usbi_get_device_priv(dev_handle->dev);
+ current_interface = interface_by_endpoint(priv, handle_priv, (uint8_t) endpoint);
+
+ if (current_interface < 0) {
+ usbi_err(ctx, "unable to match endpoint to an open interface for RAW_IO");
+ return LIBUSB_ERROR_NOT_FOUND;
+ }
+
+ if (priv->usb_interface[current_interface].apib->id != USB_API_WINUSBX) {
+ usbi_err(ctx, "interface is not winusb when setting RAW_IO");
+ return LIBUSB_ERROR_NOT_SUPPORTED;
+ }
+
+ winusb_handle = handle_priv->interface_handle[current_interface].api_handle;
+
+ if (!HANDLE_VALID(winusb_handle)) {
+ usbi_err(HANDLE_CTX(dev_handle), "WinUSB handle not valid when setting RAW_IO");
+ return LIBUSB_ERROR_NOT_FOUND;
+ }
+
+ CHECK_WINUSBX_AVAILABLE(sub_api);
+
+ if (enable && max_transfer_size_ptr != NULL) {
+ ULONG size = sizeof(ULONG);
+ if (!WinUSBX[sub_api].GetPipePolicy(winusb_handle, (UCHAR) endpoint,
+ MAXIMUM_TRANSFER_SIZE, &size, &max_transfer_size)) {
+ usbi_err(ctx, "failed to get MAXIMUM_TRANSFER_SIZE for endpoint %02X", endpoint);
+ switch (GetLastError()) {
+ case ERROR_INVALID_HANDLE:
+ return LIBUSB_ERROR_INVALID_PARAM;
+ default:
+ return LIBUSB_ERROR_OTHER;
+ }
+ }
+ }
+
+ if (!WinUSBX[sub_api].SetPipePolicy(winusb_handle, (UCHAR) endpoint,
+ RAW_IO, sizeof(UCHAR), &policy)) {
+ usbi_err(ctx, "failed to change RAW_IO for endpoint %02X", endpoint);
+ switch (GetLastError()) {
+ case ERROR_INVALID_HANDLE:
+ case ERROR_INVALID_PARAMETER:
+ return LIBUSB_ERROR_INVALID_PARAM;
+ case ERROR_NOT_ENOUGH_MEMORY:
+ return LIBUSB_ERROR_NO_MEM;
+ default:
+ return LIBUSB_ERROR_OTHER;
+ }
+ }
+
+ usbi_dbg(ctx, "%s RAW_IO for endpoint %02X", enable ? "enabled" : "disabled", endpoint);
+
+ if (enable && max_transfer_size_ptr != NULL)
+ *max_transfer_size_ptr = max_transfer_size;
+
+ return LIBUSB_SUCCESS;
+ default:
+ return LIBUSB_ERROR_NOT_SUPPORTED;
+ }
+}
+
// NB: MSVC6 does not support named initializers.
const struct windows_backend winusb_backend = {
winusb_init,
@@ -2189,6 +2296,7 @@ const struct windows_backend winusb_backend = {
winusb_cancel_transfer,
winusb_clear_transfer_priv,
winusb_copy_transfer_data,
+ winusb_set_option,
};
/*
diff --git a/libusb/version_nano.h b/libusb/version_nano.h
index ade020c..1874d60 100644
--- a/libusb/version_nano.h
+++ b/libusb/version_nano.h
@@ -1 +1 @@
-#define LIBUSB_NANO 11770
+#define LIBUSB_NANO 11771