diff options
-rw-r--r-- | Documentation/ABI/testing/sysfs-bus-usb | 13 | ||||
-rw-r--r-- | drivers/usb/core/driver.c | 100 |
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); |