[PATCH v2 04/19] USB: Release interface claims refused during registration
From: Leon Romanovsky
Date: Mon Nov 09 2026 - 02:40:20 EST
From: Leon Romanovsky <leonro@xxxxxxxxxx>
An interface can be claimed by a sibling's probe before registration.
If its later binding fails, driver core clears the driver pointer while
USB retains the bound state and claimant data. Device registration still
succeeds, leaving the claimant unaware that its binding was refused.
Remember the claimant across device_add() and invoke USB unbinding when
driver core drops the claim. Hold the claimant's module while running its
disconnect callback, and clear the retained interface state.
Signed-off-by: Leon Romanovsky <leonro@xxxxxxxxxx>
---
drivers/usb/core/driver.c | 18 ++++++++++++++++++
drivers/usb/core/message.c | 8 ++++++++
drivers/usb/core/usb.h | 2 ++
3 files changed, 28 insertions(+)
diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c
index ad5d9d5fc7598..ee36eae34bdcc 100644
--- a/drivers/usb/core/driver.c
+++ b/drivers/usb/core/driver.c
@@ -652,6 +652,24 @@ void usb_driver_release_interface(struct usb_driver *driver,
}
EXPORT_SYMBOL_GPL(usb_driver_release_interface);
+/*
+ * The driver core refused to bind an interface that a sibling claimed before
+ * it was registered, and dropped the claim. Let the claimant release the
+ * interface instead of operating it with a bound interface but no driver.
+ */
+void usb_driver_release_refused_claim(struct usb_interface *iface,
+ struct device_driver *claimant)
+{
+ struct device *dev = &iface->dev;
+
+ device_lock(dev);
+ dev->driver = claimant;
+ usb_unbind_interface(dev);
+ dev_set_drvdata(dev, NULL);
+ dev->driver = NULL;
+ device_unlock(dev);
+}
+
/* returns 0 if no match, 1 if match */
int usb_match_device(struct usb_device *dev, const struct usb_device_id *id)
{
diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c
index a738d03c23eac..d35a7710c8ef4 100644
--- a/drivers/usb/core/message.c
+++ b/drivers/usb/core/message.c
@@ -2264,6 +2264,7 @@ int usb_set_configuration(struct usb_device *dev, int configuration)
*/
for (i = 0; i < nintf; ++i) {
struct usb_interface *intf = cp->interface[i];
+ struct device_driver *claimant;
if (intf->dev.of_node &&
!of_device_is_available(intf->dev.of_node)) {
@@ -2277,7 +2278,14 @@ int usb_set_configuration(struct usb_device *dev, int configuration)
dev_name(&intf->dev), configuration,
intf->cur_altsetting->desc.bInterfaceNumber);
device_enable_async_suspend(&intf->dev);
+ claimant = intf->dev.driver;
+ if (claimant && !try_module_get(claimant->owner))
+ claimant = NULL;
ret = device_add(&intf->dev);
+ if (!ret && claimant && !intf->dev.driver)
+ usb_driver_release_refused_claim(intf, claimant);
+ if (claimant)
+ module_put(claimant->owner);
if (ret != 0) {
dev_err(&dev->dev, "device_add(%s) --> %d\n",
dev_name(&intf->dev), ret);
diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h
index a9b37aeb515be..293340a48b6c5 100644
--- a/drivers/usb/core/usb.h
+++ b/drivers/usb/core/usb.h
@@ -49,6 +49,8 @@ extern int usb_set_isoch_delay(struct usb_device *dev);
extern int usb_get_bos_descriptor(struct usb_device *dev);
extern void usb_release_bos_descriptor(struct usb_device *dev);
extern int usb_set_configuration(struct usb_device *dev, int configuration);
+void usb_driver_release_refused_claim(struct usb_interface *iface,
+ struct device_driver *claimant);
extern int usb_choose_configuration(struct usb_device *udev);
extern int usb_generic_driver_probe(struct usb_device *udev);
extern void usb_generic_driver_disconnect(struct usb_device *udev);
--
2.56.0