summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/ABI/testing/sysfs-bus-usb13
-rw-r--r--drivers/usb/core/driver.c100
2 files changed, 104 insertions, 9 deletions
diff --git a/Documentation/ABI/testing/sysfs-bus-usb b/Documentation/ABI/testing/sysfs-bus-usb
index 7772928ee48f..deb6b489e4e5 100644
--- a/Documentation/ABI/testing/sysfs-bus-usb
+++ b/Documentation/ABI/testing/sysfs-bus-usb
@@ -144,3 +144,16 @@ Description:
Write a 1 to force the device to disconnect
(equivalent to unplugging a wired USB device).
+
+What: /sys/bus/usb/drivers/.../remove_id
+Date: November 2009
+Contact: CHENG Renquan <rqcheng@smu.edu.sg>
+Description:
+ Writing a device ID to this file will remove an ID
+ that was dynamically added via the new_id sysfs entry.
+ The format for the device ID is:
+ idVendor idProduct. After successfully
+ removing an ID, the driver will no longer support the
+ device. This is useful to ensure auto probing won't
+ match the driver to the device. For example:
+ # echo "046d c315" > /sys/bus/usb/drivers/foo/remove_id
diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c
index 7a05bab73960..60a45f1e3a67 100644
--- a/drivers/usb/core/driver.c
+++ b/drivers/usb/core/driver.c
@@ -83,6 +83,47 @@ static ssize_t store_new_id(struct device_driver *driver,
}
static DRIVER_ATTR(new_id, S_IWUSR, NULL, store_new_id);
+/**
+ * store_remove_id - remove a USB device ID from this driver
+ * @driver: target device driver
+ * @buf: buffer for scanning device ID data
+ * @count: input size
+ *
+ * Removes a dynamic usb device ID from this driver.
+ */
+static ssize_t
+store_remove_id(struct device_driver *driver, const char *buf, size_t count)
+{
+ struct usb_dynid *dynid, *n;
+ struct usb_driver *usb_driver = to_usb_driver(driver);
+ u32 idVendor = 0;
+ u32 idProduct = 0;
+ int fields = 0;
+ int retval = 0;
+
+ fields = sscanf(buf, "%x %x", &idVendor, &idProduct);
+ if (fields < 2)
+ return -EINVAL;
+
+ spin_lock(&usb_driver->dynids.lock);
+ list_for_each_entry_safe(dynid, n, &usb_driver->dynids.list, node) {
+ struct usb_device_id *id = &dynid->id;
+ if ((id->idVendor == idVendor) &&
+ (id->idProduct == idProduct)) {
+ list_del(&dynid->node);
+ kfree(dynid);
+ retval = 0;
+ break;
+ }
+ }
+ spin_unlock(&usb_driver->dynids.lock);
+
+ if (retval)
+ return retval;
+ return count;
+}
+static DRIVER_ATTR(remove_id, S_IWUSR, NULL, store_remove_id);
+
static int usb_create_newid_file(struct usb_driver *usb_drv)
{
int error = 0;
@@ -107,6 +148,21 @@ static void usb_remove_newid_file(struct usb_driver *usb_drv)
&driver_attr_new_id);
}
+static int
+usb_create_removeid_file(struct usb_driver *drv)
+{
+ int error = 0;
+ if (drv->probe != NULL)
+ error = driver_create_file(&drv->drvwrap.driver,
+ &driver_attr_remove_id);
+ return error;
+}
+
+static void usb_remove_removeid_file(struct usb_driver *drv)
+{
+ driver_remove_file(&drv->drvwrap.driver, &driver_attr_remove_id);
+}
+
static void usb_free_dynids(struct usb_driver *usb_drv)
{
struct usb_dynid *dynid, *n;
@@ -128,6 +184,16 @@ static void usb_remove_newid_file(struct usb_driver *usb_drv)
{
}
+static int
+usb_create_removeid_file(struct usb_driver *drv)
+{
+ return 0;
+}
+
+static void usb_remove_removeid_file(struct usb_driver *drv)
+{
+}
+
static inline void usb_free_dynids(struct usb_driver *usb_drv)
{
}
@@ -774,19 +840,34 @@ int usb_register_driver(struct usb_driver *new_driver, struct module *owner,
INIT_LIST_HEAD(&new_driver->dynids.list);
retval = driver_register(&new_driver->drvwrap.driver);
+ if (retval)
+ goto out;
- if (!retval) {
- pr_info("%s: registered new interface driver %s\n",
+ usbfs_update_special();
+
+ retval = usb_create_newid_file(new_driver);
+ if (retval)
+ goto out_newid;
+
+ retval = usb_create_removeid_file(new_driver);
+ if (retval)
+ goto out_removeid;
+
+ pr_info("%s: registered new interface driver %s\n",
usbcore_name, new_driver->name);
- usbfs_update_special();
- usb_create_newid_file(new_driver);
- } else {
- printk(KERN_ERR "%s: error %d registering interface "
- " driver %s\n",
- usbcore_name, retval, new_driver->name);
- }
+out:
return retval;
+
+out_removeid:
+ usb_remove_newid_file(new_driver);
+out_newid:
+ driver_unregister(&new_driver->drvwrap.driver);
+
+ printk(KERN_ERR "%s: error %d registering interface "
+ " driver %s\n",
+ usbcore_name, retval, new_driver->name);
+ goto out;
}
EXPORT_SYMBOL_GPL(usb_register_driver);
@@ -806,6 +887,7 @@ void usb_deregister(struct usb_driver *driver)
pr_info("%s: deregistering interface driver %s\n",
usbcore_name, driver->name);
+ usb_remove_removeid_file(driver);
usb_remove_newid_file(driver);
usb_free_dynids(driver);
driver_unregister(&driver->drvwrap.driver);