Skip to content

Commit

Permalink
USB: add remove_id sysfs attr for usb drivers
Browse files Browse the repository at this point in the history
Accroding commit 0994375, 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: CHENG Renquan <rqcheng@smu.edu.sg>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
  • Loading branch information
crquan authored and gregkh committed Dec 11, 2009
1 parent 5791e10 commit 0c7a2b7
Show file tree
Hide file tree
Showing 2 changed files with 104 additions and 9 deletions.
13 changes: 13 additions & 0 deletions Documentation/ABI/testing/sysfs-bus-usb
Original file line number Diff line number Diff line change
Expand Up @@ -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
100 changes: 91 additions & 9 deletions drivers/usb/core/driver.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -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)
{
}
Expand Down Expand Up @@ -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);

Expand All @@ -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);
Expand Down

0 comments on commit 0c7a2b7

Please sign in to comment.