diff options
-rw-r--r-- | drivers/usb/core/driver.c | 35 | ||||
-rw-r--r-- | drivers/usb/serial/bus.c | 45 | ||||
-rw-r--r-- | drivers/usb/serial/usb-serial.c | 41 | ||||
-rw-r--r-- | include/linux/usb.h | 12 | ||||
-rw-r--r-- | include/linux/usb/serial.h | 5 |
5 files changed, 117 insertions, 21 deletions
diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index d6eb5ce1dd1d..0c0c03a4e031 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c | |||
@@ -28,24 +28,16 @@ | |||
28 | #include "hcd.h" | 28 | #include "hcd.h" |
29 | #include "usb.h" | 29 | #include "usb.h" |
30 | 30 | ||
31 | static int usb_match_one_id(struct usb_interface *interface, | ||
32 | const struct usb_device_id *id); | ||
33 | |||
34 | struct usb_dynid { | ||
35 | struct list_head node; | ||
36 | struct usb_device_id id; | ||
37 | }; | ||
38 | |||
39 | #ifdef CONFIG_HOTPLUG | 31 | #ifdef CONFIG_HOTPLUG |
40 | 32 | ||
41 | /* | 33 | /* |
42 | * Adds a new dynamic USBdevice ID to this driver, | 34 | * Adds a new dynamic USBdevice ID to this driver, |
43 | * and cause the driver to probe for all devices again. | 35 | * and cause the driver to probe for all devices again. |
44 | */ | 36 | */ |
45 | static ssize_t store_new_id(struct device_driver *driver, | 37 | ssize_t usb_store_new_id(struct usb_dynids *dynids, |
46 | const char *buf, size_t count) | 38 | struct device_driver *driver, |
39 | const char *buf, size_t count) | ||
47 | { | 40 | { |
48 | struct usb_driver *usb_drv = to_usb_driver(driver); | ||
49 | struct usb_dynid *dynid; | 41 | struct usb_dynid *dynid; |
50 | u32 idVendor = 0; | 42 | u32 idVendor = 0; |
51 | u32 idProduct = 0; | 43 | u32 idProduct = 0; |
@@ -65,9 +57,9 @@ static ssize_t store_new_id(struct device_driver *driver, | |||
65 | dynid->id.idProduct = idProduct; | 57 | dynid->id.idProduct = idProduct; |
66 | dynid->id.match_flags = USB_DEVICE_ID_MATCH_DEVICE; | 58 | dynid->id.match_flags = USB_DEVICE_ID_MATCH_DEVICE; |
67 | 59 | ||
68 | spin_lock(&usb_drv->dynids.lock); | 60 | spin_lock(&dynids->lock); |
69 | list_add_tail(&usb_drv->dynids.list, &dynid->node); | 61 | list_add_tail(&dynids->list, &dynid->node); |
70 | spin_unlock(&usb_drv->dynids.lock); | 62 | spin_unlock(&dynids->lock); |
71 | 63 | ||
72 | if (get_driver(driver)) { | 64 | if (get_driver(driver)) { |
73 | retval = driver_attach(driver); | 65 | retval = driver_attach(driver); |
@@ -78,6 +70,15 @@ static ssize_t store_new_id(struct device_driver *driver, | |||
78 | return retval; | 70 | return retval; |
79 | return count; | 71 | return count; |
80 | } | 72 | } |
73 | EXPORT_SYMBOL_GPL(usb_store_new_id); | ||
74 | |||
75 | static ssize_t store_new_id(struct device_driver *driver, | ||
76 | const char *buf, size_t count) | ||
77 | { | ||
78 | struct usb_driver *usb_drv = to_usb_driver(driver); | ||
79 | |||
80 | return usb_store_new_id(&usb_drv->dynids, driver, buf, count); | ||
81 | } | ||
81 | static DRIVER_ATTR(new_id, S_IWUSR, NULL, store_new_id); | 82 | static DRIVER_ATTR(new_id, S_IWUSR, NULL, store_new_id); |
82 | 83 | ||
83 | static int usb_create_newid_file(struct usb_driver *usb_drv) | 84 | static int usb_create_newid_file(struct usb_driver *usb_drv) |
@@ -365,8 +366,8 @@ void usb_driver_release_interface(struct usb_driver *driver, | |||
365 | EXPORT_SYMBOL(usb_driver_release_interface); | 366 | EXPORT_SYMBOL(usb_driver_release_interface); |
366 | 367 | ||
367 | /* returns 0 if no match, 1 if match */ | 368 | /* returns 0 if no match, 1 if match */ |
368 | static int usb_match_one_id(struct usb_interface *interface, | 369 | int usb_match_one_id(struct usb_interface *interface, |
369 | const struct usb_device_id *id) | 370 | const struct usb_device_id *id) |
370 | { | 371 | { |
371 | struct usb_host_interface *intf; | 372 | struct usb_host_interface *intf; |
372 | struct usb_device *dev; | 373 | struct usb_device *dev; |
@@ -432,6 +433,8 @@ static int usb_match_one_id(struct usb_interface *interface, | |||
432 | 433 | ||
433 | return 1; | 434 | return 1; |
434 | } | 435 | } |
436 | EXPORT_SYMBOL_GPL(usb_match_one_id); | ||
437 | |||
435 | /** | 438 | /** |
436 | * usb_match_id - find first usb_device_id matching device or interface | 439 | * usb_match_id - find first usb_device_id matching device or interface |
437 | * @interface: the interface of interest | 440 | * @interface: the interface of interest |
diff --git a/drivers/usb/serial/bus.c b/drivers/usb/serial/bus.c index 6542f220468f..c08a38402b93 100644 --- a/drivers/usb/serial/bus.c +++ b/drivers/usb/serial/bus.c | |||
@@ -103,11 +103,52 @@ exit: | |||
103 | return retval; | 103 | return retval; |
104 | } | 104 | } |
105 | 105 | ||
106 | #ifdef CONFIG_HOTPLUG | ||
107 | static ssize_t store_new_id(struct device_driver *driver, | ||
108 | const char *buf, size_t count) | ||
109 | { | ||
110 | struct usb_serial_driver *usb_drv = to_usb_serial_driver(driver); | ||
111 | ssize_t retval = usb_store_new_id(&usb_drv->dynids, driver, buf, count); | ||
112 | |||
113 | if (retval >= 0 && usb_drv->usb_driver != NULL) | ||
114 | retval = usb_store_new_id(&usb_drv->usb_driver->dynids, | ||
115 | &usb_drv->usb_driver->drvwrap.driver, | ||
116 | buf, count); | ||
117 | return retval; | ||
118 | } | ||
119 | |||
120 | static struct driver_attribute drv_attrs[] = { | ||
121 | __ATTR(new_id, S_IWUSR, NULL, store_new_id), | ||
122 | __ATTR_NULL, | ||
123 | }; | ||
124 | |||
125 | static void free_dynids(struct usb_serial_driver *drv) | ||
126 | { | ||
127 | struct usb_dynid *dynid, *n; | ||
128 | |||
129 | spin_lock(&drv->dynids.lock); | ||
130 | list_for_each_entry_safe(dynid, n, &drv->dynids.list, node) { | ||
131 | list_del(&dynid->node); | ||
132 | kfree(dynid); | ||
133 | } | ||
134 | spin_unlock(&drv->dynids.lock); | ||
135 | } | ||
136 | |||
137 | #else | ||
138 | static struct driver_attribute drv_attrs[] = { | ||
139 | __ATTR_NULL, | ||
140 | }; | ||
141 | static inline void free_dynids(struct usb_driver *drv) | ||
142 | { | ||
143 | } | ||
144 | #endif | ||
145 | |||
106 | struct bus_type usb_serial_bus_type = { | 146 | struct bus_type usb_serial_bus_type = { |
107 | .name = "usb-serial", | 147 | .name = "usb-serial", |
108 | .match = usb_serial_device_match, | 148 | .match = usb_serial_device_match, |
109 | .probe = usb_serial_device_probe, | 149 | .probe = usb_serial_device_probe, |
110 | .remove = usb_serial_device_remove, | 150 | .remove = usb_serial_device_remove, |
151 | .drv_attrs = drv_attrs, | ||
111 | }; | 152 | }; |
112 | 153 | ||
113 | int usb_serial_bus_register(struct usb_serial_driver *driver) | 154 | int usb_serial_bus_register(struct usb_serial_driver *driver) |
@@ -115,6 +156,9 @@ int usb_serial_bus_register(struct usb_serial_driver *driver) | |||
115 | int retval; | 156 | int retval; |
116 | 157 | ||
117 | driver->driver.bus = &usb_serial_bus_type; | 158 | driver->driver.bus = &usb_serial_bus_type; |
159 | spin_lock_init(&driver->dynids.lock); | ||
160 | INIT_LIST_HEAD(&driver->dynids.list); | ||
161 | |||
118 | retval = driver_register(&driver->driver); | 162 | retval = driver_register(&driver->driver); |
119 | 163 | ||
120 | return retval; | 164 | return retval; |
@@ -122,6 +166,7 @@ int usb_serial_bus_register(struct usb_serial_driver *driver) | |||
122 | 166 | ||
123 | void usb_serial_bus_deregister(struct usb_serial_driver *driver) | 167 | void usb_serial_bus_deregister(struct usb_serial_driver *driver) |
124 | { | 168 | { |
169 | free_dynids(driver); | ||
125 | driver_unregister(&driver->driver); | 170 | driver_unregister(&driver->driver); |
126 | } | 171 | } |
127 | 172 | ||
diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index 716f6806cc89..90beb5c50e59 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c | |||
@@ -596,6 +596,39 @@ static struct usb_serial * create_serial (struct usb_device *dev, | |||
596 | return serial; | 596 | return serial; |
597 | } | 597 | } |
598 | 598 | ||
599 | static const struct usb_device_id *match_dynamic_id(struct usb_interface *intf, | ||
600 | struct usb_serial_driver *drv) | ||
601 | { | ||
602 | struct usb_dynid *dynid; | ||
603 | |||
604 | spin_lock(&drv->dynids.lock); | ||
605 | list_for_each_entry(dynid, &drv->dynids.list, node) { | ||
606 | if (usb_match_one_id(intf, &dynid->id)) { | ||
607 | spin_unlock(&drv->dynids.lock); | ||
608 | return &dynid->id; | ||
609 | } | ||
610 | } | ||
611 | spin_unlock(&drv->dynids.lock); | ||
612 | return NULL; | ||
613 | } | ||
614 | |||
615 | static const struct usb_device_id *get_iface_id(struct usb_serial_driver *drv, | ||
616 | struct usb_interface *intf) | ||
617 | { | ||
618 | const struct usb_device_id *id; | ||
619 | |||
620 | id = usb_match_id(intf, drv->id_table); | ||
621 | if (id) { | ||
622 | dbg("static descriptor matches"); | ||
623 | goto exit; | ||
624 | } | ||
625 | id = match_dynamic_id(intf, drv); | ||
626 | if (id) | ||
627 | dbg("dynamic descriptor matches"); | ||
628 | exit: | ||
629 | return id; | ||
630 | } | ||
631 | |||
599 | static struct usb_serial_driver *search_serial_device(struct usb_interface *iface) | 632 | static struct usb_serial_driver *search_serial_device(struct usb_interface *iface) |
600 | { | 633 | { |
601 | struct list_head *p; | 634 | struct list_head *p; |
@@ -605,11 +638,9 @@ static struct usb_serial_driver *search_serial_device(struct usb_interface *ifac | |||
605 | /* Check if the usb id matches a known device */ | 638 | /* Check if the usb id matches a known device */ |
606 | list_for_each(p, &usb_serial_driver_list) { | 639 | list_for_each(p, &usb_serial_driver_list) { |
607 | t = list_entry(p, struct usb_serial_driver, driver_list); | 640 | t = list_entry(p, struct usb_serial_driver, driver_list); |
608 | id = usb_match_id(iface, t->id_table); | 641 | id = get_iface_id(t, iface); |
609 | if (id != NULL) { | 642 | if (id) |
610 | dbg("descriptor matches"); | ||
611 | return t; | 643 | return t; |
612 | } | ||
613 | } | 644 | } |
614 | 645 | ||
615 | return NULL; | 646 | return NULL; |
@@ -661,7 +692,7 @@ int usb_serial_probe(struct usb_interface *interface, | |||
661 | return -EIO; | 692 | return -EIO; |
662 | } | 693 | } |
663 | 694 | ||
664 | id = usb_match_id(interface, type->id_table); | 695 | id = get_iface_id(type, interface); |
665 | retval = type->probe(serial, id); | 696 | retval = type->probe(serial, id); |
666 | module_put(type->driver.owner); | 697 | module_put(type->driver.owner); |
667 | 698 | ||
diff --git a/include/linux/usb.h b/include/linux/usb.h index f3b21636c9df..3cb9285df2d1 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h | |||
@@ -476,6 +476,8 @@ extern void usb_driver_release_interface(struct usb_driver *driver, | |||
476 | struct usb_interface *iface); | 476 | struct usb_interface *iface); |
477 | const struct usb_device_id *usb_match_id(struct usb_interface *interface, | 477 | const struct usb_device_id *usb_match_id(struct usb_interface *interface, |
478 | const struct usb_device_id *id); | 478 | const struct usb_device_id *id); |
479 | extern int usb_match_one_id(struct usb_interface *interface, | ||
480 | const struct usb_device_id *id); | ||
479 | 481 | ||
480 | extern struct usb_interface *usb_find_interface(struct usb_driver *drv, | 482 | extern struct usb_interface *usb_find_interface(struct usb_driver *drv, |
481 | int minor); | 483 | int minor); |
@@ -724,11 +726,21 @@ static inline int usb_endpoint_is_isoc_out(const struct usb_endpoint_descriptor | |||
724 | 726 | ||
725 | /* ----------------------------------------------------------------------- */ | 727 | /* ----------------------------------------------------------------------- */ |
726 | 728 | ||
729 | /* Stuff for dynamic usb ids */ | ||
727 | struct usb_dynids { | 730 | struct usb_dynids { |
728 | spinlock_t lock; | 731 | spinlock_t lock; |
729 | struct list_head list; | 732 | struct list_head list; |
730 | }; | 733 | }; |
731 | 734 | ||
735 | struct usb_dynid { | ||
736 | struct list_head node; | ||
737 | struct usb_device_id id; | ||
738 | }; | ||
739 | |||
740 | extern ssize_t usb_store_new_id(struct usb_dynids *dynids, | ||
741 | struct device_driver *driver, | ||
742 | const char *buf, size_t count); | ||
743 | |||
732 | /** | 744 | /** |
733 | * struct usbdrv_wrap - wrapper for driver-model structure | 745 | * struct usbdrv_wrap - wrapper for driver-model structure |
734 | * @driver: The driver-model core driver structure. | 746 | * @driver: The driver-model core driver structure. |
diff --git a/include/linux/usb/serial.h b/include/linux/usb/serial.h index 10f99e5f1a97..33dcd8576696 100644 --- a/include/linux/usb/serial.h +++ b/include/linux/usb/serial.h | |||
@@ -179,6 +179,9 @@ static inline void usb_set_serial_data (struct usb_serial *serial, void *data) | |||
179 | * memory structure allocation at this point in time. | 179 | * memory structure allocation at this point in time. |
180 | * @shutdown: pointer to the driver's shutdown function. This will be | 180 | * @shutdown: pointer to the driver's shutdown function. This will be |
181 | * called when the device is removed from the system. | 181 | * called when the device is removed from the system. |
182 | * @usb_driver: pointer to the struct usb_driver that controls this | ||
183 | * device. This is necessary to allow dynamic ids to be added to | ||
184 | * the driver from sysfs. | ||
182 | * | 185 | * |
183 | * This structure is defines a USB Serial driver. It provides all of | 186 | * This structure is defines a USB Serial driver. It provides all of |
184 | * the information that the USB serial core code needs. If the function | 187 | * the information that the USB serial core code needs. If the function |
@@ -202,6 +205,8 @@ struct usb_serial_driver { | |||
202 | 205 | ||
203 | struct list_head driver_list; | 206 | struct list_head driver_list; |
204 | struct device_driver driver; | 207 | struct device_driver driver; |
208 | struct usb_driver *usb_driver; | ||
209 | struct usb_dynids dynids; | ||
205 | 210 | ||
206 | int (*probe) (struct usb_serial *serial, const struct usb_device_id *id); | 211 | int (*probe) (struct usb_serial *serial, const struct usb_device_id *id); |
207 | int (*attach) (struct usb_serial *serial); | 212 | int (*attach) (struct usb_serial *serial); |