summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRyan Metcalfe <ryan.metcalfe@novanta.com>2021-07-26 16:16:54 -0400
committerTormod Volden <debian.tormod@gmail.com>2023-01-19 15:02:11 +0100
commit5ec2821fdc491b470306d3cbb7175e53388d46fd (patch)
treede5bd9b242d17f420d318bda13d856f8f89a6ff9
parent809a6df614b87be780df9b0c97ba6c0bb6172aab (diff)
downloadlibusb-5ec2821fdc491b470306d3cbb7175e53388d46fd.tar.gz
windows: Allow claiming all interfaces for a WINUSBX composite function
On Windows 10, when a USB composite device is using usbccgp to manage the parent device, there can be problems when an application tries to claim multiple interfaces for a WINSBX-enabled subfunction. For example, a common scenario is that an application is able to claim the first interface of a given child function, but not the others. This commit attempts to resolve this issue by making use of (potentially) available interface association descriptors (IADs). Within set_composite_interface(), which is normally called for the first interface of a composite child function (Some MI_XX device, where XX is the first interface), the set of IADs for the current configuration is retrieved and, if the current interface matches the 'bFirstInterface' for one of the returned IADs then the API backend, sub_api is also set up for the other 'associated' interfaces. Subsequent calls to libusb_claim_interface for any of these 'associated' interfaces will get properly routed to winusbx_claim_interface. Two fields have been added to winusb_device_priv.usb_interface: num_associated_interfaces first_associated_interface These are made use of by winusbx_claim_interface() to decide whether Initialize() or GetAssociatedInterface() function calls are needed, as well as within winusbx_close() to perform proper cleanup for groups of associated functions. About composite functions in Windows (by Maciej T. Nowak): (1) Non-composite device: Single device is created with all interfaces grouped (associated) together. WinUSB_Initialize gives access to first interface and WinUSB_GetAssociatedInteface can be used to get access to remaining interfaces. (2) Composite device without IAD is presented by set of devices each containing single interface. As they are separate devices, separate WinUSB_Initialize calls are required to gain access to each interface. As there are no more interfaces in each device, WinUSB_GetAssociatedInterface has no use in this case. In other words: Each interface is grouped (associated) with itself. (3) When we add IAD to a composite device, interfaces specified in each IAD are grouped (associated) into single devices where WinUSB_Initialize is required to open the first interface in each association and WinUSB_GetAssociatedInterface for the remaining interfaces in each group. Effectively this case is a mix of (1) and (2). Closes #965 Signed-off-by: Ryan Metcalfe <ryan.metcalfe@novanta.com> [Tormod: Removed redundant is_associated_interface member] Signed-off-by: Tormod Volden <debian.tormod@gmail.com>
-rw-r--r--libusb/os/windows_common.h7
-rw-r--r--libusb/os/windows_winusb.c100
-rw-r--r--libusb/version_nano.h2
3 files changed, 91 insertions, 18 deletions
diff --git a/libusb/os/windows_common.h b/libusb/os/windows_common.h
index 4582ce4..ff0a5b7 100644
--- a/libusb/os/windows_common.h
+++ b/libusb/os/windows_common.h
@@ -257,6 +257,13 @@ struct winusb_device_priv {
int current_altsetting;
bool restricted_functionality; // indicates if the interface functionality is restricted
// by Windows (eg. HID keyboards or mice cannot do R/W)
+ uint8_t num_associated_interfaces; // If non-zero, the interface is part of a grouped
+ // set of associated interfaces (defined by an IAD)
+ // and this is the number of interfaces within the
+ // associated group (bInterfaceCount in IAD).
+ uint8_t first_associated_interface; // For associated interfaces, this is the index of
+ // the first interface (bFirstInterface in IAD) for
+ // the grouped set of associated interfaces.
} usb_interface[USB_MAXINTERFACES];
struct hid_device_priv *hid;
PUSB_CONFIGURATION_DESCRIPTOR *config_descriptor; // list of pointers to the cached config descriptors
diff --git a/libusb/os/windows_winusb.c b/libusb/os/windows_winusb.c
index 3e3f0b4..885b4e4 100644
--- a/libusb/os/windows_winusb.c
+++ b/libusb/os/windows_winusb.c
@@ -1377,6 +1377,9 @@ static int set_composite_interface(struct libusb_context *ctx, struct libusb_dev
struct winusb_device_priv *priv = usbi_get_device_priv(dev);
int interface_number;
const char *mi_str;
+ int iadi, iadintfi;
+ struct libusb_interface_association_descriptor_array *iad_array;
+ const struct libusb_interface_association_descriptor *iad;
// Because MI_## are not necessarily in sequential order (some composite
// devices will have only MI_00 & MI_03 for instance), we retrieve the actual
@@ -1415,6 +1418,27 @@ static int set_composite_interface(struct libusb_context *ctx, struct libusb_dev
return LIBUSB_ERROR_NO_MEM;
}
+ // For WinUSBX, set up associations for interfaces grouped by an IAD
+ if ((api == USB_API_WINUSBX) && !libusb_get_active_interface_association_descriptors(dev, &iad_array)) {
+ for (iadi = 0; iadi < iad_array->length; iadi++) {
+ iad = &iad_array->iad[iadi];
+ if (iad->bFirstInterface == interface_number) {
+ priv->usb_interface[interface_number].num_associated_interfaces = iad->bInterfaceCount;
+ priv->usb_interface[interface_number].first_associated_interface = iad->bFirstInterface;
+ for (iadintfi = 1; iadintfi < iad->bInterfaceCount; iadintfi++) {
+ usbi_dbg(ctx, "interface[%d] is associated with interface[%d]",
+ interface_number + iadintfi, interface_number);
+ priv->usb_interface[interface_number + iadintfi].apib = &usb_api_backend[api];
+ priv->usb_interface[interface_number + iadintfi].sub_api = sub_api;
+ priv->usb_interface[interface_number + iadintfi].num_associated_interfaces = iad->bInterfaceCount;
+ priv->usb_interface[interface_number + iadintfi].first_associated_interface = iad->bFirstInterface;
+ }
+ break;
+ }
+ }
+ libusb_free_interface_association_descriptors(iad_array);
+ }
+
return LIBUSB_SUCCESS;
}
@@ -2482,7 +2506,7 @@ static void winusbx_close(int sub_api, struct libusb_device_handle *dev_handle)
struct winusb_device_handle_priv *handle_priv = get_winusb_device_handle_priv(dev_handle);
struct winusb_device_priv *priv = usbi_get_device_priv(dev_handle->dev);
HANDLE handle;
- int i;
+ int i, ai;
if (sub_api == SUB_API_NOTSET)
sub_api = priv->sub_api;
@@ -2491,17 +2515,41 @@ static void winusbx_close(int sub_api, struct libusb_device_handle *dev_handle)
return;
if (priv->apib->id == USB_API_COMPOSITE) {
- // If this is a composite device, just free and close all WinUSB-like
- // interfaces directly (each is independent and not associated with another)
+ // If this is a composite device, just free and close any WinUSB-like
+ // interfaces that are not part of an associated group
+ // (each is independent and not associated with another).
+ // For associated interface groupings, free interfaces that
+ // are NOT the first within that group (i.e. not bFirstInterface),
+ // then free & close bFirstInterface last.
for (i = 0; i < USB_MAXINTERFACES; i++) {
if (priv->usb_interface[i].apib->id == USB_API_WINUSBX) {
- handle = handle_priv->interface_handle[i].api_handle;
- if (HANDLE_VALID(handle))
- WinUSBX[sub_api].Free(handle);
+ if (priv->usb_interface[i].num_associated_interfaces == 0) {
+ handle = handle_priv->interface_handle[i].api_handle;
+ if (HANDLE_VALID(handle))
+ WinUSBX[sub_api].Free(handle);
+
+ handle = handle_priv->interface_handle[i].dev_handle;
+ if (HANDLE_VALID(handle))
+ CloseHandle(handle);
+ } else {
+ if (i==priv->usb_interface[i].first_associated_interface) {
+ //first free all handles for all *other* associated interfaces
+ for (ai = 1; ai < priv->usb_interface[i].num_associated_interfaces; ai++) {
+ handle = handle_priv->interface_handle[i + ai].api_handle;
+ if (HANDLE_VALID(handle))
+ WinUSBX[sub_api].Free(handle);
+ }
- handle = handle_priv->interface_handle[i].dev_handle;
- if (HANDLE_VALID(handle))
- CloseHandle(handle);
+ //free & close bFirstInterface
+ handle = handle_priv->interface_handle[i].api_handle;
+ if (HANDLE_VALID(handle))
+ WinUSBX[sub_api].Free(handle);
+
+ handle = handle_priv->interface_handle[i].dev_handle;
+ if (HANDLE_VALID(handle))
+ CloseHandle(handle);
+ }
+ }
}
}
} else {
@@ -2582,6 +2630,7 @@ static int winusbx_claim_interface(int sub_api, struct libusb_device_handle *dev
struct winusb_device_handle_priv *handle_priv = get_winusb_device_handle_priv(dev_handle);
struct winusb_device_priv *priv = usbi_get_device_priv(dev_handle->dev);
bool is_using_usbccgp = (priv->apib->id == USB_API_COMPOSITE);
+ bool is_associated_interface = (priv->usb_interface[iface].num_associated_interfaces != 0);
HDEVINFO dev_info;
char *dev_interface_path = NULL;
char *dev_interface_path_guid_start;
@@ -2590,12 +2639,18 @@ static int winusbx_claim_interface(int sub_api, struct libusb_device_handle *dev
HANDLE file_handle, winusb_handle;
DWORD err, _index;
int r;
+ uint8_t initialized_iface;
CHECK_WINUSBX_AVAILABLE(sub_api);
// If the device is composite, but using the default Windows composite parent driver (usbccgp)
// or if it's the first WinUSB-like interface, we get a handle through Initialize().
- if ((is_using_usbccgp) || (iface == 0)) {
+ // If it's an associated interface, and is the first one (iface==bFirstInterface), we also
+ // want to get the handle through Initialize(). If it's an associated interface, and NOT
+ // the first one, we want to direct control to the 'else' where the handle will be obtained
+ // via GetAssociatedInterface().
+ if (((is_using_usbccgp) || (iface == 0)) &&
+ (!is_associated_interface || (iface==priv->usb_interface[iface].first_associated_interface))) {
// composite device (independent interfaces) or interface 0
file_handle = handle_priv->interface_handle[iface].dev_handle;
if (!HANDLE_VALID(file_handle))
@@ -2656,21 +2711,32 @@ static int winusbx_claim_interface(int sub_api, struct libusb_device_handle *dev
}
handle_priv->interface_handle[iface].api_handle = winusb_handle;
} else {
+ if (is_associated_interface) {
+ initialized_iface = priv->usb_interface[iface].first_associated_interface;
+ if (iface <= initialized_iface) {
+ usbi_err(ctx, "invalid associated index. iface=%u, initialized iface=%u", iface, initialized_iface);
+ return LIBUSB_ERROR_NOT_FOUND;
+ }
+ } else {
+ initialized_iface = 0;
+ }
+
// For all other interfaces, use GetAssociatedInterface()
- winusb_handle = handle_priv->interface_handle[0].api_handle;
+ winusb_handle = handle_priv->interface_handle[initialized_iface].api_handle;
// It is a requirement for multiple interface devices on Windows that, to you
// must first claim the first interface before you claim the others
if (!HANDLE_VALID(winusb_handle)) {
- file_handle = handle_priv->interface_handle[0].dev_handle;
+ file_handle = handle_priv->interface_handle[initialized_iface].dev_handle;
if (WinUSBX[sub_api].Initialize(file_handle, &winusb_handle)) {
- handle_priv->interface_handle[0].api_handle = winusb_handle;
- usbi_warn(ctx, "auto-claimed interface 0 (required to claim %u with WinUSB)", iface);
+ handle_priv->interface_handle[initialized_iface].api_handle = winusb_handle;
+ usbi_warn(ctx, "auto-claimed interface %u (required to claim %u with WinUSB)", initialized_iface, iface);
} else {
- usbi_warn(ctx, "failed to auto-claim interface 0 (required to claim %u with WinUSB): %s", iface, windows_error_str(0));
+ usbi_warn(ctx, "failed to auto-claim interface %u (required to claim %u with WinUSB): %s",
+ initialized_iface, iface, windows_error_str(0));
return LIBUSB_ERROR_ACCESS;
}
}
- if (!WinUSBX[sub_api].GetAssociatedInterface(winusb_handle, (UCHAR)(iface - 1),
+ if (!WinUSBX[sub_api].GetAssociatedInterface(winusb_handle, (UCHAR)(iface - 1 - initialized_iface),
&handle_priv->interface_handle[iface].api_handle)) {
handle_priv->interface_handle[iface].api_handle = INVALID_HANDLE_VALUE;
switch (GetLastError()) {
@@ -2685,7 +2751,7 @@ static int winusbx_claim_interface(int sub_api, struct libusb_device_handle *dev
return LIBUSB_ERROR_ACCESS;
}
}
- handle_priv->interface_handle[iface].dev_handle = handle_priv->interface_handle[0].dev_handle;
+ handle_priv->interface_handle[iface].dev_handle = handle_priv->interface_handle[initialized_iface].dev_handle;
}
usbi_dbg(ctx, "claimed interface %u", iface);
handle_priv->active_interface = iface;
diff --git a/libusb/version_nano.h b/libusb/version_nano.h
index 04b0954..6cb280e 100644
--- a/libusb/version_nano.h
+++ b/libusb/version_nano.h
@@ -1 +1 @@
-#define LIBUSB_NANO 11764
+#define LIBUSB_NANO 11765