diff options
author | CHENG Renquan <rqcheng@smu.edu.sg> | 2009-11-21 12:28:52 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2009-12-11 14:55:26 -0500 |
commit | 0c7a2b72746a96f999fd2728520d03d94879be69 (patch) | |
tree | 30c4d09a7d142c46177ced1c8600a7d7f6bcde25 | |
parent | 5791e10341f8bf284bd16eb0949cbeed91c9dac8 (diff) |
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: CHENG Renquan <rqcheng@smu.edu.sg>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
-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: | |||
144 | 144 | ||
145 | Write a 1 to force the device to disconnect | 145 | Write a 1 to force the device to disconnect |
146 | (equivalent to unplugging a wired USB device). | 146 | (equivalent to unplugging a wired USB device). |
147 | |||
148 | What: /sys/bus/usb/drivers/.../remove_id | ||
149 | Date: November 2009 | ||
150 | Contact: CHENG Renquan <rqcheng@smu.edu.sg> | ||
151 | Description: | ||
152 | Writing a device ID to this file will remove an ID | ||
153 | that was dynamically added via the new_id sysfs entry. | ||
154 | The format for the device ID is: | ||
155 | idVendor idProduct. After successfully | ||
156 | removing an ID, the driver will no longer support the | ||
157 | device. This is useful to ensure auto probing won't | ||
158 | match the driver to the device. For example: | ||
159 | # 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, | |||
83 | } | 83 | } |
84 | static DRIVER_ATTR(new_id, S_IWUSR, NULL, store_new_id); | 84 | static DRIVER_ATTR(new_id, S_IWUSR, NULL, store_new_id); |
85 | 85 | ||
86 | /** | ||
87 | * store_remove_id - remove a USB device ID from this driver | ||
88 | * @driver: target device driver | ||
89 | * @buf: buffer for scanning device ID data | ||
90 | * @count: input size | ||
91 | * | ||
92 | * Removes a dynamic usb device ID from this driver. | ||
93 | */ | ||
94 | static ssize_t | ||
95 | store_remove_id(struct device_driver *driver, const char *buf, size_t count) | ||
96 | { | ||
97 | struct usb_dynid *dynid, *n; | ||
98 | struct usb_driver *usb_driver = to_usb_driver(driver); | ||
99 | u32 idVendor = 0; | ||
100 | u32 idProduct = 0; | ||
101 | int fields = 0; | ||
102 | int retval = 0; | ||
103 | |||
104 | fields = sscanf(buf, "%x %x", &idVendor, &idProduct); | ||
105 | if (fields < 2) | ||
106 | return -EINVAL; | ||
107 | |||
108 | spin_lock(&usb_driver->dynids.lock); | ||
109 | list_for_each_entry_safe(dynid, n, &usb_driver->dynids.list, node) { | ||
110 | struct usb_device_id *id = &dynid->id; | ||
111 | if ((id->idVendor == idVendor) && | ||
112 | (id->idProduct == idProduct)) { | ||
113 | list_del(&dynid->node); | ||
114 | kfree(dynid); | ||
115 | retval = 0; | ||
116 | break; | ||
117 | } | ||
118 | } | ||
119 | spin_unlock(&usb_driver->dynids.lock); | ||
120 | |||
121 | if (retval) | ||
122 | return retval; | ||
123 | return count; | ||
124 | } | ||
125 | static DRIVER_ATTR(remove_id, S_IWUSR, NULL, store_remove_id); | ||
126 | |||
86 | static int usb_create_newid_file(struct usb_driver *usb_drv) | 127 | static int usb_create_newid_file(struct usb_driver *usb_drv) |
87 | { | 128 | { |
88 | int error = 0; | 129 | int error = 0; |
@@ -107,6 +148,21 @@ static void usb_remove_newid_file(struct usb_driver *usb_drv) | |||
107 | &driver_attr_new_id); | 148 | &driver_attr_new_id); |
108 | } | 149 | } |
109 | 150 | ||
151 | static int | ||
152 | usb_create_removeid_file(struct usb_driver *drv) | ||
153 | { | ||
154 | int error = 0; | ||
155 | if (drv->probe != NULL) | ||
156 | error = driver_create_file(&drv->drvwrap.driver, | ||
157 | &driver_attr_remove_id); | ||
158 | return error; | ||
159 | } | ||
160 | |||
161 | static void usb_remove_removeid_file(struct usb_driver *drv) | ||
162 | { | ||
163 | driver_remove_file(&drv->drvwrap.driver, &driver_attr_remove_id); | ||
164 | } | ||
165 | |||
110 | static void usb_free_dynids(struct usb_driver *usb_drv) | 166 | static void usb_free_dynids(struct usb_driver *usb_drv) |
111 | { | 167 | { |
112 | struct usb_dynid *dynid, *n; | 168 | struct usb_dynid *dynid, *n; |
@@ -128,6 +184,16 @@ static void usb_remove_newid_file(struct usb_driver *usb_drv) | |||
128 | { | 184 | { |
129 | } | 185 | } |
130 | 186 | ||
187 | static int | ||
188 | usb_create_removeid_file(struct usb_driver *drv) | ||
189 | { | ||
190 | return 0; | ||
191 | } | ||
192 | |||
193 | static void usb_remove_removeid_file(struct usb_driver *drv) | ||
194 | { | ||
195 | } | ||
196 | |||
131 | static inline void usb_free_dynids(struct usb_driver *usb_drv) | 197 | static inline void usb_free_dynids(struct usb_driver *usb_drv) |
132 | { | 198 | { |
133 | } | 199 | } |
@@ -774,19 +840,34 @@ int usb_register_driver(struct usb_driver *new_driver, struct module *owner, | |||
774 | INIT_LIST_HEAD(&new_driver->dynids.list); | 840 | INIT_LIST_HEAD(&new_driver->dynids.list); |
775 | 841 | ||
776 | retval = driver_register(&new_driver->drvwrap.driver); | 842 | retval = driver_register(&new_driver->drvwrap.driver); |
843 | if (retval) | ||
844 | goto out; | ||
777 | 845 | ||
778 | if (!retval) { | 846 | usbfs_update_special(); |
779 | pr_info("%s: registered new interface driver %s\n", | 847 | |
848 | retval = usb_create_newid_file(new_driver); | ||
849 | if (retval) | ||
850 | goto out_newid; | ||
851 | |||
852 | retval = usb_create_removeid_file(new_driver); | ||
853 | if (retval) | ||
854 | goto out_removeid; | ||
855 | |||
856 | pr_info("%s: registered new interface driver %s\n", | ||
780 | usbcore_name, new_driver->name); | 857 | usbcore_name, new_driver->name); |
781 | usbfs_update_special(); | ||
782 | usb_create_newid_file(new_driver); | ||
783 | } else { | ||
784 | printk(KERN_ERR "%s: error %d registering interface " | ||
785 | " driver %s\n", | ||
786 | usbcore_name, retval, new_driver->name); | ||
787 | } | ||
788 | 858 | ||
859 | out: | ||
789 | return retval; | 860 | return retval; |
861 | |||
862 | out_removeid: | ||
863 | usb_remove_newid_file(new_driver); | ||
864 | out_newid: | ||
865 | driver_unregister(&new_driver->drvwrap.driver); | ||
866 | |||
867 | printk(KERN_ERR "%s: error %d registering interface " | ||
868 | " driver %s\n", | ||
869 | usbcore_name, retval, new_driver->name); | ||
870 | goto out; | ||
790 | } | 871 | } |
791 | EXPORT_SYMBOL_GPL(usb_register_driver); | 872 | EXPORT_SYMBOL_GPL(usb_register_driver); |
792 | 873 | ||
@@ -806,6 +887,7 @@ void usb_deregister(struct usb_driver *driver) | |||
806 | pr_info("%s: deregistering interface driver %s\n", | 887 | pr_info("%s: deregistering interface driver %s\n", |
807 | usbcore_name, driver->name); | 888 | usbcore_name, driver->name); |
808 | 889 | ||
890 | usb_remove_removeid_file(driver); | ||
809 | usb_remove_newid_file(driver); | 891 | usb_remove_newid_file(driver); |
810 | usb_free_dynids(driver); | 892 | usb_free_dynids(driver); |
811 | driver_unregister(&driver->drvwrap.driver); | 893 | driver_unregister(&driver->drvwrap.driver); |