Commit 0c7a2b72 authored by CHENG Renquan's avatar CHENG Renquan Committed by Greg Kroah-Hartman

USB: add remove_id sysfs attr for usb drivers

Accroding commit 0994375e, which is adding remove_id sysfs attr
for pci drivers, for management tools dynamically bind/unbind
a pci/usb devices to a specified drivers; with this patch,
the management tools can be simplied.

And the original code didn't handle the failure of
usb_create_newid_file, fixed in this patch.
Signed-off-by: default avatarCHENG Renquan <rqcheng@smu.edu.sg>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent 5791e103
...@@ -144,3 +144,16 @@ Description: ...@@ -144,3 +144,16 @@ Description:
Write a 1 to force the device to disconnect Write a 1 to force the device to disconnect
(equivalent to unplugging a wired USB device). (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
...@@ -83,6 +83,47 @@ static ssize_t store_new_id(struct device_driver *driver, ...@@ -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); 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) static int usb_create_newid_file(struct usb_driver *usb_drv)
{ {
int error = 0; int error = 0;
...@@ -107,6 +148,21 @@ static void usb_remove_newid_file(struct usb_driver *usb_drv) ...@@ -107,6 +148,21 @@ static void usb_remove_newid_file(struct usb_driver *usb_drv)
&driver_attr_new_id); &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) static void usb_free_dynids(struct usb_driver *usb_drv)
{ {
struct usb_dynid *dynid, *n; struct usb_dynid *dynid, *n;
...@@ -128,6 +184,16 @@ static void usb_remove_newid_file(struct usb_driver *usb_drv) ...@@ -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) 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, ...@@ -774,19 +840,34 @@ int usb_register_driver(struct usb_driver *new_driver, struct module *owner,
INIT_LIST_HEAD(&new_driver->dynids.list); INIT_LIST_HEAD(&new_driver->dynids.list);
retval = driver_register(&new_driver->drvwrap.driver); retval = driver_register(&new_driver->drvwrap.driver);
if (retval)
goto out;
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;
if (!retval) {
pr_info("%s: registered new interface driver %s\n", pr_info("%s: registered new interface driver %s\n",
usbcore_name, new_driver->name); usbcore_name, new_driver->name);
usbfs_update_special();
usb_create_newid_file(new_driver); out:
} else { 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 " printk(KERN_ERR "%s: error %d registering interface "
" driver %s\n", " driver %s\n",
usbcore_name, retval, new_driver->name); usbcore_name, retval, new_driver->name);
} goto out;
return retval;
} }
EXPORT_SYMBOL_GPL(usb_register_driver); EXPORT_SYMBOL_GPL(usb_register_driver);
...@@ -806,6 +887,7 @@ void usb_deregister(struct usb_driver *driver) ...@@ -806,6 +887,7 @@ void usb_deregister(struct usb_driver *driver)
pr_info("%s: deregistering interface driver %s\n", pr_info("%s: deregistering interface driver %s\n",
usbcore_name, driver->name); usbcore_name, driver->name);
usb_remove_removeid_file(driver);
usb_remove_newid_file(driver); usb_remove_newid_file(driver);
usb_free_dynids(driver); usb_free_dynids(driver);
driver_unregister(&driver->drvwrap.driver); driver_unregister(&driver->drvwrap.driver);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment