aboutsummaryrefslogtreecommitdiffstats
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:
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
148What: /sys/bus/usb/drivers/.../remove_id
149Date: November 2009
150Contact: CHENG Renquan <rqcheng@smu.edu.sg>
151Description:
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}
84static DRIVER_ATTR(new_id, S_IWUSR, NULL, store_new_id); 84static 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 */
94static ssize_t
95store_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}
125static DRIVER_ATTR(remove_id, S_IWUSR, NULL, store_remove_id);
126
86static int usb_create_newid_file(struct usb_driver *usb_drv) 127static 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
151static int
152usb_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
161static void usb_remove_removeid_file(struct usb_driver *drv)
162{
163 driver_remove_file(&drv->drvwrap.driver, &driver_attr_remove_id);
164}
165
110static void usb_free_dynids(struct usb_driver *usb_drv) 166static 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
187static int
188usb_create_removeid_file(struct usb_driver *drv)
189{
190 return 0;
191}
192
193static void usb_remove_removeid_file(struct usb_driver *drv)
194{
195}
196
131static inline void usb_free_dynids(struct usb_driver *usb_drv) 197static 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
859out:
789 return retval; 860 return retval;
861
862out_removeid:
863 usb_remove_newid_file(new_driver);
864out_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}
791EXPORT_SYMBOL_GPL(usb_register_driver); 872EXPORT_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);