[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20251018172156.69e93897.michal.pecio@gmail.com>
Date: Sat, 18 Oct 2025 17:21:56 +0200
From: Michal Pecio <michal.pecio@...il.com>
To: Alan Stern <stern@...land.harvard.edu>
Cc: yicongsrfy@....com, andrew+netdev@...n.ch, davem@...emloft.net,
edumazet@...gle.com, kuba@...nel.org, linux-usb@...r.kernel.org,
netdev@...r.kernel.org, oliver@...kum.org, pabeni@...hat.com
Subject: Re: [PATCH net v5 2/3] net: usb: ax88179_178a: add USB device
driver for config selection
On Fri, 17 Oct 2025 22:27:35 -0400, Alan Stern wrote:
> Without a reasonable clear and quick criterion for deciding when to
> favor vendor-specific configs in the USB core, there's little I can do.
> Having a quirks flag should help remove some of the indecision, since
> such flags are set by hand rather than by an automated procedure. But
> I'd still want to have a better idea of exactly what to do when the
> quirk flag is set.
Existing r8152-cfgselector and the planned ax88179-cfgselector
implement the following logic:
IF a device has particular IDs
(same id_table as in the vendor interface driver)
IF the vendor interface driver is loaded
(ensured by loading it together with cfgselector)
IF the vendor driver supports this device
(calls internal vendor driver code)
THEN select the vendor configuration
It was a PITA, but I have a working proof of concept for r8152.
Still missing is automatic reevaluation of configuration choice when
the vendor driver is loaded after device connection (e.g. by udev).
Those cfgselectors can do it because it seems that registering a new
device (but not interface) driver forces reevaluation.
---
drivers/net/usb/r8152.c | 13 ++++++-------
drivers/usb/core/driver.c | 23 +++++++++++++++++++++++
drivers/usb/core/generic.c | 17 +++++++++++++++--
include/linux/usb.h | 6 ++++++
4 files changed, 50 insertions(+), 9 deletions(-)
diff --git a/drivers/net/usb/r8152.c b/drivers/net/usb/r8152.c
index a22d4bb2cf3b..1b016dd81949 100644
--- a/drivers/net/usb/r8152.c
+++ b/drivers/net/usb/r8152.c
@@ -10020,6 +10020,11 @@ static void rtl8152_disconnect(struct usb_interface *intf)
}
}
+static bool rtl8152_preferred(struct usb_device *udev)
+{
+ return __rtl_get_hw_ver(udev) != RTL_VER_UNKNOWN;
+}
+
/* table of devices that work with this driver */
static const struct usb_device_id rtl8152_table[] = {
/* Realtek */
@@ -10067,6 +10072,7 @@ static struct usb_driver rtl8152_driver = {
.name = MODULENAME,
.id_table = rtl8152_table,
.probe = rtl8152_probe,
+ .preferred = rtl8152_preferred,
.disconnect = rtl8152_disconnect,
.suspend = rtl8152_suspend,
.resume = rtl8152_resume,
@@ -10119,13 +10125,7 @@ static int __init rtl8152_driver_init(void)
{
int ret;
- ret = usb_register_device_driver(&rtl8152_cfgselector_driver, THIS_MODULE);
- if (ret)
- return ret;
-
ret = usb_register(&rtl8152_driver);
- if (ret)
- usb_deregister_device_driver(&rtl8152_cfgselector_driver);
return ret;
}
@@ -10133,7 +10133,6 @@ static int __init rtl8152_driver_init(void)
static void __exit rtl8152_driver_exit(void)
{
usb_deregister(&rtl8152_driver);
- usb_deregister_device_driver(&rtl8152_cfgselector_driver);
}
module_init(rtl8152_driver_init);
diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c
index d29edc7c616a..eaf21c30eac1 100644
--- a/drivers/usb/core/driver.c
+++ b/drivers/usb/core/driver.c
@@ -1119,6 +1119,29 @@ void usb_deregister(struct usb_driver *driver)
}
EXPORT_SYMBOL_GPL(usb_deregister);
+/**
+ * usb_driver_preferred - check if this is a preferred interface driver
+ * @drv: interface driver to check (device drivers are ignored)
+ * @udev: the device we are looking up a driver for
+ * Context: must be able to sleep
+ *
+ * TODO locking?
+ */
+bool usb_driver_preferred(struct device_driver *drv, struct usb_device *udev)
+{
+ struct usb_driver *usb_drv;
+
+ if (is_usb_device_driver(drv))
+ return false;
+
+ usb_drv = to_usb_driver(drv);
+
+ return usb_drv->preferred &&
+ usb_device_match_id(udev, usb_drv->id_table) &&
+ usb_drv->preferred(udev);
+}
+EXPORT_SYMBOL_GPL(usb_driver_preferred);
+
/* Forced unbinding of a USB interface driver, either because
* it doesn't support pre_reset/post_reset/reset_resume or
* because it doesn't support suspend/resume.
diff --git a/drivers/usb/core/generic.c b/drivers/usb/core/generic.c
index a48994e11ef3..1923e6f4923b 100644
--- a/drivers/usb/core/generic.c
+++ b/drivers/usb/core/generic.c
@@ -49,11 +49,17 @@ static bool is_uac3_config(struct usb_interface_descriptor *desc)
return desc->bInterfaceProtocol == UAC_VERSION_3;
}
+static int prefer_vendor(struct device_driver *drv, void *data)
+{
+ return usb_driver_preferred(drv, data);
+}
+
int usb_choose_configuration(struct usb_device *udev)
{
int i;
int num_configs;
int insufficient_power = 0;
+ bool class_found = false;
struct usb_host_config *c, *best;
struct usb_device_driver *udriver;
@@ -169,6 +175,12 @@ int usb_choose_configuration(struct usb_device *udev)
#endif
}
+ /* Check if we have a preferred vendor driver for this config */
+ else if (bus_for_each_drv(&usb_bus_type, NULL, (void *) udev, prefer_vendor)) {
+ best = c;
+ break;
+ }
+
/* 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
@@ -177,8 +189,9 @@ int usb_choose_configuration(struct usb_device *udev)
USB_CLASS_VENDOR_SPEC &&
(desc && desc->bInterfaceClass !=
USB_CLASS_VENDOR_SPEC)) {
- best = c;
- break;
+ if (!class_found)
+ best = c;
+ class_found = true;
}
/* If all the remaining configs are vendor-specific,
diff --git a/include/linux/usb.h b/include/linux/usb.h
index e85105939af8..1d2c5ebc81ab 100644
--- a/include/linux/usb.h
+++ b/include/linux/usb.h
@@ -1202,6 +1202,8 @@ extern ssize_t usb_show_dynids(struct usb_dynids *dynids, char *buf);
* @post_reset: Called by usb_reset_device() after the device
* has been reset
* @shutdown: Called at shut-down time to quiesce the device.
+ * @preferred: Check if this driver is preferred over generic class drivers
+ * applicable to the device. May probe device with control transfers.
* @id_table: USB drivers use ID table to support hotplugging.
* Export this with MODULE_DEVICE_TABLE(usb,...). This must be set
* or your driver's probe function will never get called.
@@ -1255,6 +1257,8 @@ struct usb_driver {
void (*shutdown)(struct usb_interface *intf);
+ bool (*preferred)(struct usb_device *udev);
+
const struct usb_device_id *id_table;
const struct attribute_group **dev_groups;
@@ -1267,6 +1271,8 @@ struct usb_driver {
};
#define to_usb_driver(d) container_of_const(d, struct usb_driver, driver)
+extern bool usb_driver_preferred(struct device_driver *drv, struct usb_device *udev);
+
/**
* struct usb_device_driver - identifies USB device driver to usbcore
* @name: The driver name should be unique among USB drivers,
--
2.48.1
Powered by blists - more mailing lists