From foo@baz.org Thu Dec 15 13:04:03 2005 Date: Thu, 15 Dec 2005 12:58:42 -0800 From: Greg Kroah-Hartman Subject: USB serial: add dynamic id support to usb-serial core 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 drivers/usb/core/driver.c | 37 ++++++++++++++++------------- drivers/usb/serial/bus.c | 50 ++++++++++++++++++++++++++++++++++++++++ drivers/usb/serial/usb-serial.c | 43 +++++++++++++++++++++++++++++----- drivers/usb/serial/usb-serial.h | 2 + include/linux/usb.h | 13 ++++++++++ 5 files changed, 122 insertions(+), 23 deletions(-) --- --- gregkh-2.6.orig/drivers/usb/core/driver.c +++ gregkh-2.6/drivers/usb/core/driver.c @@ -26,15 +26,6 @@ #include "hcd.h" #include "usb.h" -static int usb_match_one_id(struct usb_interface *interface, - const struct usb_device_id *id); - -struct usb_dynid { - struct list_head node; - struct usb_device_id id; -}; - - static int generic_probe(struct device *dev) { return 0; @@ -72,10 +63,10 @@ int usb_generic_driver_data; * Adds a new dynamic USBdevice ID to this driver, * and cause the driver to probe for all devices again. */ -static ssize_t store_new_id(struct device_driver *driver, - const char *buf, size_t count) +ssize_t usb_store_new_id(struct usb_dynids *dynids, + struct device_driver *driver, + const char *buf, size_t count) { - struct usb_driver *usb_drv = to_usb_driver(driver); struct usb_dynid *dynid; u32 idVendor = 0; u32 idProduct = 0; @@ -84,6 +75,7 @@ static ssize_t store_new_id(struct devic fields = sscanf(buf, "%x %x", &idVendor, &idProduct); if (fields < 2) return -EINVAL; + printk("GREG: idVendor=%x idProduct=%x\n", idVendor, idProduct); dynid = kzalloc(sizeof(*dynid), GFP_KERNEL); if (!dynid) @@ -94,9 +86,9 @@ static ssize_t store_new_id(struct devic dynid->id.idProduct = idProduct; dynid->id.match_flags = USB_DEVICE_ID_MATCH_DEVICE; - spin_lock(&usb_drv->dynids.lock); - list_add_tail(&usb_drv->dynids.list, &dynid->node); - spin_unlock(&usb_drv->dynids.lock); + spin_lock(&dynids->lock); + list_add_tail(&dynids->list, &dynid->node); + spin_unlock(&dynids->lock); if (get_driver(driver)) { driver_attach(driver); @@ -105,6 +97,15 @@ static ssize_t store_new_id(struct devic return count; } +EXPORT_SYMBOL_GPL(usb_store_new_id); + +static ssize_t store_new_id(struct device_driver *driver, + const char *buf, size_t count) +{ + struct usb_driver *usb_drv = to_usb_driver(driver); + + return usb_store_new_id(&usb_drv->dynids, driver, buf, count); +} static DRIVER_ATTR(new_id, S_IWUSR, NULL, store_new_id); static int usb_create_newid_file(struct usb_driver *usb_drv) @@ -239,8 +240,8 @@ static int usb_unbind_interface(struct d } /* returns 0 if no match, 1 if match */ -static int usb_match_one_id(struct usb_interface *interface, - const struct usb_device_id *id) +int usb_match_one_id(struct usb_interface *interface, + const struct usb_device_id *id) { struct usb_host_interface *intf; struct usb_device *dev; @@ -296,6 +297,8 @@ static int usb_match_one_id(struct usb_i return 1; } +EXPORT_SYMBOL(usb_match_one_id); + /** * usb_match_id - find first usb_device_id matching device or interface * @interface: the interface of interest --- gregkh-2.6.orig/drivers/usb/serial/bus.c +++ gregkh-2.6/drivers/usb/serial/bus.c @@ -103,11 +103,57 @@ exit: return retval; } +#ifdef CONFIG_HOTPLUG +static ssize_t store_new_id(struct device_driver *driver, + const char *buf, size_t count) +{ + struct usb_serial_driver *usb_drv = to_usb_serial_driver(driver); + ssize_t retval = usb_store_new_id(&usb_drv->dynids, driver, buf, count); + + if (retval >= 0) { + /* kick the usb-serial main driver */ + struct device_driver *driver = &usb_serial_driver.driver; + if (get_driver(driver)) { + driver_attach(driver); + put_driver(driver); + } + } + return retval; +} + +static struct driver_attribute drv_attrs[] = { + __ATTR(new_id, S_IWUSR, NULL, store_new_id), + __ATTR_NULL, +}; + +static void free_dynids(struct usb_serial_driver *drv) +{ + struct usb_dynid *dynid, *n; + + printk("GREG: %s\n", __FUNCTION__); + spin_lock(&drv->dynids.lock); + list_for_each_entry_safe(dynid, n, &drv->dynids.list, node) { + list_del(&dynid->node); + kfree(dynid); + } + spin_unlock(&drv->dynids.lock); +} + +#else +static struct driver_attribute drv_attrs[] = { + __ATTR_NULL, +}; +static inline void free_dynids(struct usb_driver *drv) +{ +} +#endif + struct bus_type usb_serial_bus_type = { .name = "usb-serial", .match = usb_serial_device_match, .probe = usb_serial_device_probe, .remove = usb_serial_device_remove, + .drv_attrs = drv_attrs, }; int usb_serial_bus_register(struct usb_serial_driver *driver) @@ -115,6 +161,9 @@ int usb_serial_bus_register(struct usb_s int retval; driver->driver.bus = &usb_serial_bus_type; + spin_lock_init(&driver->dynids.lock); + INIT_LIST_HEAD(&driver->dynids.list); + retval = driver_register(&driver->driver); return retval; @@ -122,6 +171,7 @@ int usb_serial_bus_register(struct usb_s void usb_serial_bus_deregister(struct usb_serial_driver *driver) { + free_dynids(driver); driver_unregister(&driver->driver); } --- gregkh-2.6.orig/drivers/usb/serial/usb-serial.c +++ gregkh-2.6/drivers/usb/serial/usb-serial.c @@ -43,7 +43,7 @@ static void port_free(struct usb_serial_port *port); /* Driver structure we register with the USB core */ -static struct usb_driver usb_serial_driver = { +struct usb_driver usb_serial_driver = { .name = "usbserial", .probe = usb_serial_probe, .disconnect = usb_serial_disconnect, @@ -593,6 +593,39 @@ static struct usb_serial * create_serial return serial; } +static const struct usb_device_id *match_dynamic_id(struct usb_interface *intf, + struct usb_serial_driver *drv) +{ + struct usb_dynid *dynid; + + spin_lock(&drv->dynids.lock); + list_for_each_entry(dynid, &drv->dynids.list, node) { + if (usb_match_one_id(intf, &dynid->id)) { + spin_unlock(&drv->dynids.lock); + return &dynid->id; + } + } + spin_unlock(&drv->dynids.lock); + return NULL; +} + +static const struct usb_device_id *get_iface_id(struct usb_serial_driver *drv, + struct usb_interface *intf) +{ + const struct usb_device_id *id; + + id = usb_match_id(intf, drv->id_table); + if (id) { + dbg("static descriptor matches"); + goto exit; + } + id = match_dynamic_id(intf, drv); + if (id) + dbg("dynamic descriptor matches"); +exit: + return id; +} + static struct usb_serial_driver *search_serial_device(struct usb_interface *iface) { struct list_head *p; @@ -602,11 +635,9 @@ static struct usb_serial_driver *search_ /* Check if the usb id matches a known device */ list_for_each(p, &usb_serial_driver_list) { t = list_entry(p, struct usb_serial_driver, driver_list); - id = usb_match_id(iface, t->id_table); - if (id != NULL) { - dbg("descriptor matches"); + id = get_iface_id(t, iface); + if (id) return t; - } } return NULL; @@ -658,7 +689,7 @@ int usb_serial_probe(struct usb_interfac return -EIO; } - id = usb_match_id(interface, type->id_table); + id = get_iface_id(type, interface); retval = type->probe(serial, id); module_put(type->driver.owner); --- gregkh-2.6.orig/drivers/usb/serial/usb-serial.h +++ gregkh-2.6/drivers/usb/serial/usb-serial.h @@ -202,6 +202,7 @@ struct usb_serial_driver { struct list_head driver_list; struct device_driver driver; + struct usb_dynids dynids; int (*probe) (struct usb_serial *serial, const struct usb_device_id *id); int (*attach) (struct usb_serial *serial); @@ -272,6 +273,7 @@ extern int usb_serial_bus_register (stru extern void usb_serial_bus_deregister (struct usb_serial_driver *device); extern struct usb_serial_driver usb_serial_generic_device; +extern struct usb_driver usb_serial_driver; extern struct bus_type usb_serial_bus_type; extern struct tty_driver *usb_serial_tty_driver; --- gregkh-2.6.orig/include/linux/usb.h +++ gregkh-2.6/include/linux/usb.h @@ -421,6 +421,8 @@ extern void usb_driver_release_interface struct usb_interface *iface); const struct usb_device_id *usb_match_id(struct usb_interface *interface, const struct usb_device_id *id); +extern int usb_match_one_id(struct usb_interface *interface, + const struct usb_device_id *id); extern struct usb_interface *usb_find_interface(struct usb_driver *drv, int minor); @@ -535,11 +537,21 @@ static inline int usb_make_path (struct /* ----------------------------------------------------------------------- */ +/* Stuff for dynamic usb ids */ struct usb_dynids { spinlock_t lock; struct list_head list; }; +struct usb_dynid { + struct list_head node; + struct usb_device_id id; +}; + +extern ssize_t usb_store_new_id(struct usb_dynids *dynids, + struct device_driver *driver, + const char *buf, size_t count); + /** * struct usb_driver - identifies USB driver to usbcore * @name: The driver name should be unique among USB drivers, @@ -1203,6 +1215,7 @@ usb_maxpacket(struct usb_device *udev, i extern void usb_register_notify(struct notifier_block *nb); extern void usb_unregister_notify(struct notifier_block *nb); + #ifdef DEBUG #define dbg(format, arg...) printk(KERN_DEBUG "%s: " format "\n" , \ __FILE__ , ## arg)