Commit 93bacefc4cc0b53e1cb6a336d43847154fdf6886
1 parent
495a678fc6
Exists in
master
and in
4 other branches
USB serial: add dynamic id support to usb-serial core
Thanks to Johannes Hölzl <johannes.hoelzl@gmx.de> for fixing a few things and getting it all working properly. This adds support for dynamic usb ids to the usb serial core. The file "new_id" will show up under the usb serial driver, not the usb driver associated with the usb-serial driver (yeah, it can be a bit confusing at first glance...) This patch also modifies the USB core to allow the usb-serial core to reuse much of the dynamic id logic. Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> Signed-off-by: Johannes Hölzl <johannes.hoelzl@gmx.de>
Showing 5 changed files with 117 additions and 21 deletions Side-by-side Diff
drivers/usb/core/driver.c
... | ... | @@ -28,24 +28,16 @@ |
28 | 28 | #include "hcd.h" |
29 | 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 | 31 | #ifdef CONFIG_HOTPLUG |
40 | 32 | |
41 | 33 | /* |
42 | 34 | * Adds a new dynamic USBdevice ID to this driver, |
43 | 35 | * and cause the driver to probe for all devices again. |
44 | 36 | */ |
45 | -static ssize_t store_new_id(struct device_driver *driver, | |
46 | - const char *buf, size_t count) | |
37 | +ssize_t usb_store_new_id(struct usb_dynids *dynids, | |
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 | 41 | struct usb_dynid *dynid; |
50 | 42 | u32 idVendor = 0; |
51 | 43 | u32 idProduct = 0; |
... | ... | @@ -65,9 +57,9 @@ |
65 | 57 | dynid->id.idProduct = idProduct; |
66 | 58 | dynid->id.match_flags = USB_DEVICE_ID_MATCH_DEVICE; |
67 | 59 | |
68 | - spin_lock(&usb_drv->dynids.lock); | |
69 | - list_add_tail(&usb_drv->dynids.list, &dynid->node); | |
70 | - spin_unlock(&usb_drv->dynids.lock); | |
60 | + spin_lock(&dynids->lock); | |
61 | + list_add_tail(&dynids->list, &dynid->node); | |
62 | + spin_unlock(&dynids->lock); | |
71 | 63 | |
72 | 64 | if (get_driver(driver)) { |
73 | 65 | retval = driver_attach(driver); |
... | ... | @@ -78,6 +70,15 @@ |
78 | 70 | return retval; |
79 | 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 | 82 | static DRIVER_ATTR(new_id, S_IWUSR, NULL, store_new_id); |
82 | 83 | |
83 | 84 | static int usb_create_newid_file(struct usb_driver *usb_drv) |
... | ... | @@ -365,8 +366,8 @@ |
365 | 366 | EXPORT_SYMBOL(usb_driver_release_interface); |
366 | 367 | |
367 | 368 | /* returns 0 if no match, 1 if match */ |
368 | -static int usb_match_one_id(struct usb_interface *interface, | |
369 | - const struct usb_device_id *id) | |
369 | +int usb_match_one_id(struct usb_interface *interface, | |
370 | + const struct usb_device_id *id) | |
370 | 371 | { |
371 | 372 | struct usb_host_interface *intf; |
372 | 373 | struct usb_device *dev; |
... | ... | @@ -432,6 +433,8 @@ |
432 | 433 | |
433 | 434 | return 1; |
434 | 435 | } |
436 | +EXPORT_SYMBOL_GPL(usb_match_one_id); | |
437 | + | |
435 | 438 | /** |
436 | 439 | * usb_match_id - find first usb_device_id matching device or interface |
437 | 440 | * @interface: the interface of interest |
drivers/usb/serial/bus.c
... | ... | @@ -103,11 +103,52 @@ |
103 | 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 | 146 | struct bus_type usb_serial_bus_type = { |
107 | 147 | .name = "usb-serial", |
108 | 148 | .match = usb_serial_device_match, |
109 | 149 | .probe = usb_serial_device_probe, |
110 | 150 | .remove = usb_serial_device_remove, |
151 | + .drv_attrs = drv_attrs, | |
111 | 152 | }; |
112 | 153 | |
113 | 154 | int usb_serial_bus_register(struct usb_serial_driver *driver) |
... | ... | @@ -115,6 +156,9 @@ |
115 | 156 | int retval; |
116 | 157 | |
117 | 158 | driver->driver.bus = &usb_serial_bus_type; |
159 | + spin_lock_init(&driver->dynids.lock); | |
160 | + INIT_LIST_HEAD(&driver->dynids.list); | |
161 | + | |
118 | 162 | retval = driver_register(&driver->driver); |
119 | 163 | |
120 | 164 | return retval; |
... | ... | @@ -122,6 +166,7 @@ |
122 | 166 | |
123 | 167 | void usb_serial_bus_deregister(struct usb_serial_driver *driver) |
124 | 168 | { |
169 | + free_dynids(driver); | |
125 | 170 | driver_unregister(&driver->driver); |
126 | 171 | } |
drivers/usb/serial/usb-serial.c
... | ... | @@ -596,6 +596,39 @@ |
596 | 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 | 632 | static struct usb_serial_driver *search_serial_device(struct usb_interface *iface) |
600 | 633 | { |
601 | 634 | struct list_head *p; |
602 | 635 | |
... | ... | @@ -605,11 +638,9 @@ |
605 | 638 | /* Check if the usb id matches a known device */ |
606 | 639 | list_for_each(p, &usb_serial_driver_list) { |
607 | 640 | t = list_entry(p, struct usb_serial_driver, driver_list); |
608 | - id = usb_match_id(iface, t->id_table); | |
609 | - if (id != NULL) { | |
610 | - dbg("descriptor matches"); | |
641 | + id = get_iface_id(t, iface); | |
642 | + if (id) | |
611 | 643 | return t; |
612 | - } | |
613 | 644 | } |
614 | 645 | |
615 | 646 | return NULL; |
... | ... | @@ -661,7 +692,7 @@ |
661 | 692 | return -EIO; |
662 | 693 | } |
663 | 694 | |
664 | - id = usb_match_id(interface, type->id_table); | |
695 | + id = get_iface_id(type, interface); | |
665 | 696 | retval = type->probe(serial, id); |
666 | 697 | module_put(type->driver.owner); |
667 | 698 |
include/linux/usb.h
... | ... | @@ -476,6 +476,8 @@ |
476 | 476 | struct usb_interface *iface); |
477 | 477 | const struct usb_device_id *usb_match_id(struct usb_interface *interface, |
478 | 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 | 482 | extern struct usb_interface *usb_find_interface(struct usb_driver *drv, |
481 | 483 | int minor); |
482 | 484 | |
... | ... | @@ -724,10 +726,20 @@ |
724 | 726 | |
725 | 727 | /* ----------------------------------------------------------------------- */ |
726 | 728 | |
729 | +/* Stuff for dynamic usb ids */ | |
727 | 730 | struct usb_dynids { |
728 | 731 | spinlock_t lock; |
729 | 732 | struct list_head list; |
730 | 733 | }; |
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); | |
731 | 743 | |
732 | 744 | /** |
733 | 745 | * struct usbdrv_wrap - wrapper for driver-model structure |
include/linux/usb/serial.h
... | ... | @@ -179,6 +179,9 @@ |
179 | 179 | * memory structure allocation at this point in time. |
180 | 180 | * @shutdown: pointer to the driver's shutdown function. This will be |
181 | 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 | 186 | * This structure is defines a USB Serial driver. It provides all of |
184 | 187 | * the information that the USB serial core code needs. If the function |
... | ... | @@ -202,6 +205,8 @@ |
202 | 205 | |
203 | 206 | struct list_head driver_list; |
204 | 207 | struct device_driver driver; |
208 | + struct usb_driver *usb_driver; | |
209 | + struct usb_dynids dynids; | |
205 | 210 | |
206 | 211 | int (*probe) (struct usb_serial *serial, const struct usb_device_id *id); |
207 | 212 | int (*attach) (struct usb_serial *serial); |