diff mbox series

usb: quirks: Add quirk to prefer vendor-specific configuration

Message ID 20250410024626.981215-1-ivan.hu@canonical.com
State New
Headers show
Series usb: quirks: Add quirk to prefer vendor-specific configuration | expand

Commit Message

ivanhu April 10, 2025, 2:46 a.m. UTC
Some USB devices with multiple configurations expose a vendor-specific
interface class that should be preferred by default. However, the generic
usb_choose_configuration() logic selects the first configuration whose
first interface uses a non-vendor-specific class, which can lead to
incomplete or limited functionality.

Introduce a new quirk, USB_QUIRK_CHOOSE_VENDOR_SPEC_CFG, which
instructs the USB core to prefer a configuration that contains a
vendor-specific interface class when multiple configurations are present.

Apply this quirk to the ASIX AX88179 USB Ethernet adapter
(0x0b95:0x1790), which requires selecting its vendor-specific
configuration for full functionality, instead of falling back to
cdc_ncm.

Signed-off-by: Ivan Hu <ivan.hu@canonical.com>
---
 drivers/usb/core/generic.c | 15 +++++++++++++++
 drivers/usb/core/quirks.c  |  3 +++
 include/linux/usb/quirks.h |  3 +++
 3 files changed, 21 insertions(+)

Comments

Greg KH April 10, 2025, 6:58 a.m. UTC | #1
On Thu, Apr 10, 2025 at 10:46:26AM +0800, Ivan Hu wrote:
> Some USB devices with multiple configurations expose a vendor-specific
> interface class that should be preferred by default. However, the generic
> usb_choose_configuration() logic selects the first configuration whose
> first interface uses a non-vendor-specific class, which can lead to
> incomplete or limited functionality.
> 
> Introduce a new quirk, USB_QUIRK_CHOOSE_VENDOR_SPEC_CFG, which
> instructs the USB core to prefer a configuration that contains a
> vendor-specific interface class when multiple configurations are present.
> 
> Apply this quirk to the ASIX AX88179 USB Ethernet adapter
> (0x0b95:0x1790), which requires selecting its vendor-specific
> configuration for full functionality, instead of falling back to
> cdc_ncm.

Shouldn't this be done in userspace instead?  And how does other
operating systems handle this, the "first" configuration is usually the
default for them as well, do they have some built-in quirk to handle
this or do they rely on a vendor-provided driver?

thanks,

greg k-h
ivanhu April 11, 2025, 5:25 a.m. UTC | #2
On 2025/4/10 14:58, Greg KH wrote:
> On Thu, Apr 10, 2025 at 10:46:26AM +0800, Ivan Hu wrote:
>> Some USB devices with multiple configurations expose a vendor-specific
>> interface class that should be preferred by default. However, the generic
>> usb_choose_configuration() logic selects the first configuration whose
>> first interface uses a non-vendor-specific class, which can lead to
>> incomplete or limited functionality.
>>
>> Introduce a new quirk, USB_QUIRK_CHOOSE_VENDOR_SPEC_CFG, which
>> instructs the USB core to prefer a configuration that contains a
>> vendor-specific interface class when multiple configurations are present.
>>
>> Apply this quirk to the ASIX AX88179 USB Ethernet adapter
>> (0x0b95:0x1790), which requires selecting its vendor-specific
>> configuration for full functionality, instead of falling back to
>> cdc_ncm.
> 
> Shouldn't this be done in userspace instead?  And how does other
> operating systems handle this, the "first" configuration is usually the
> default for them as well, do they have some built-in quirk to handle
> this or do they rely on a vendor-provided driver?
> 
> thanks,
> 
> greg k-h
> 

Hi Greg,

Thanks for the feedback.

In this case, the device advertises three configuration descriptors. The first is vendor-specific, the second is CDC-NCM, and the third is CDC-Ether:

Device Descriptor:
   idVendor           0x0b95 ASIX Electronics Corp.
   idProduct          0x1790 AX88179 Gigabit Ethernet
   bNumConfigurations      3

Configuration 1:
   Interface 0:
     bInterfaceClass       255 Vendor Specific Class

Configuration 2:
   Interface 0:
     bInterfaceClass         2 Communications
     bInterfaceSubClass     13 (CDC-NCM)

Configuration 3:
   Interface 0:
     bInterfaceClass         2 Communications
     bInterfaceSubClass      6 (CDC-Ether)

In drivers/usb/core/generic.c, the logic currently prefers the first configuration whose first interface is non-vendor-specific,
based on the assumption that Linux is more likely to have a generic class driver than a vendor-specific one:
		/* From the remaining configs, choose the first one whose
		 * first interface is for a non-vendor-specific class.
		 * Reason: Linux is more likely to have a class driver
		 * than a vendor-specific driver. */
This results in the CDC-NCM configuration being selected by default, even though the kernel already supports the vendor-specific driver ax88179_178a,
which provides the correct and full functionality.

Of course, this could be handled in userspace, but due to security restrictions on certain systems, such as Ubuntu Core, modifying configuration selection in userspace becomes significantly more complex and less straightforward.
And although I don’t have insight into the exact design in Windows, but during testing on a standard Windows install, the device is correctly initialized with the vendor driver without any additional configuration or modification.
Given that this quirk is device-specific and not a general change in policy, the proposed quirk simply allows the kernel to prefer the correct vendor-specific configuration when it's known to be required.

Cheers,
Ivan
diff mbox series

Patch

diff --git a/drivers/usb/core/generic.c b/drivers/usb/core/generic.c
index 9c6ae5e1198b..19bf35ede5a0 100644
--- a/drivers/usb/core/generic.c
+++ b/drivers/usb/core/generic.c
@@ -23,6 +23,7 @@ 
 #include <linux/usb/hcd.h>
 #include <linux/string_choices.h>
 #include <uapi/linux/usb/audio.h>
+#include <linux/usb/quirks.h>
 #include "usb.h"
 
 static int is_rndis(struct usb_interface_descriptor *desc)
@@ -155,6 +156,20 @@  int usb_choose_configuration(struct usb_device *udev)
 			continue;
 		}
 
+		/* This quirk forces the selection of a vendor-specific
+		 * interface class configuration when multiple configurations
+		 * are present.
+		 */
+		if (num_configs > 1 &&
+			udev->descriptor.bDeviceClass ==
+						USB_CLASS_PER_INTERFACE &&
+				udev->quirks & USB_QUIRK_CHOOSE_VENDOR_SPEC_CFG
+				&& (desc && desc->bInterfaceClass ==
+						USB_CLASS_VENDOR_SPEC)) {
+			best = c;
+			break;
+		}
+
 		/* When the first config's first interface is one of Microsoft's
 		 * pet nonstandard Ethernet-over-USB protocols, ignore it unless
 		 * this kernel has enabled the necessary host side driver.
diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c
index 8efbacc5bc34..1e0e05cb29df 100644
--- a/drivers/usb/core/quirks.c
+++ b/drivers/usb/core/quirks.c
@@ -565,6 +565,9 @@  static const struct usb_device_id usb_quirk_list[] = {
 	/* INTEL VALUE SSD */
 	{ USB_DEVICE(0x8086, 0xf1a5), .driver_info = USB_QUIRK_RESET_RESUME },
 
+	/* ASIX AS88179 */
+	{ USB_DEVICE(0x0b95, 0x1790), .driver_info = USB_QUIRK_CHOOSE_VENDOR_SPEC_CFG },
+
 	{ }  /* terminating entry must be last */
 };
 
diff --git a/include/linux/usb/quirks.h b/include/linux/usb/quirks.h
index 59409c1fc3de..1f73bfc191f0 100644
--- a/include/linux/usb/quirks.h
+++ b/include/linux/usb/quirks.h
@@ -75,4 +75,7 @@ 
 /* short SET_ADDRESS request timeout */
 #define USB_QUIRK_SHORT_SET_ADDRESS_REQ_TIMEOUT	BIT(16)
 
+/* force selection of vendor-specific configuration*/
+#define USB_QUIRK_CHOOSE_VENDOR_SPEC_CFG	BIT(17)
+
 #endif /* __LINUX_USB_QUIRKS_H */