Commit 93bacefc authored by Greg Kroah-Hartman's avatar Greg Kroah-Hartman

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: default avatarGreg Kroah-Hartman <gregkh@suse.de>
Signed-off-by: default avatarJohannes Hölzl <johannes.hoelzl@gmx.de>
parent 495a678f
...@@ -28,24 +28,16 @@ ...@@ -28,24 +28,16 @@
#include "hcd.h" #include "hcd.h"
#include "usb.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;
};
#ifdef CONFIG_HOTPLUG #ifdef CONFIG_HOTPLUG
/* /*
* Adds a new dynamic USBdevice ID to this driver, * Adds a new dynamic USBdevice ID to this driver,
* and cause the driver to probe for all devices again. * and cause the driver to probe for all devices again.
*/ */
static ssize_t store_new_id(struct device_driver *driver, ssize_t usb_store_new_id(struct usb_dynids *dynids,
struct device_driver *driver,
const char *buf, size_t count) const char *buf, size_t count)
{ {
struct usb_driver *usb_drv = to_usb_driver(driver);
struct usb_dynid *dynid; struct usb_dynid *dynid;
u32 idVendor = 0; u32 idVendor = 0;
u32 idProduct = 0; u32 idProduct = 0;
...@@ -65,9 +57,9 @@ static ssize_t store_new_id(struct device_driver *driver, ...@@ -65,9 +57,9 @@ static ssize_t store_new_id(struct device_driver *driver,
dynid->id.idProduct = idProduct; dynid->id.idProduct = idProduct;
dynid->id.match_flags = USB_DEVICE_ID_MATCH_DEVICE; dynid->id.match_flags = USB_DEVICE_ID_MATCH_DEVICE;
spin_lock(&usb_drv->dynids.lock); spin_lock(&dynids->lock);
list_add_tail(&usb_drv->dynids.list, &dynid->node); list_add_tail(&dynids->list, &dynid->node);
spin_unlock(&usb_drv->dynids.lock); spin_unlock(&dynids->lock);
if (get_driver(driver)) { if (get_driver(driver)) {
retval = driver_attach(driver); retval = driver_attach(driver);
...@@ -78,6 +70,15 @@ static ssize_t store_new_id(struct device_driver *driver, ...@@ -78,6 +70,15 @@ static ssize_t store_new_id(struct device_driver *driver,
return retval; return retval;
return count; 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 DRIVER_ATTR(new_id, S_IWUSR, NULL, store_new_id);
static int usb_create_newid_file(struct usb_driver *usb_drv) static int usb_create_newid_file(struct usb_driver *usb_drv)
...@@ -365,7 +366,7 @@ void usb_driver_release_interface(struct usb_driver *driver, ...@@ -365,7 +366,7 @@ void usb_driver_release_interface(struct usb_driver *driver,
EXPORT_SYMBOL(usb_driver_release_interface); EXPORT_SYMBOL(usb_driver_release_interface);
/* returns 0 if no match, 1 if match */ /* returns 0 if no match, 1 if match */
static int usb_match_one_id(struct usb_interface *interface, int usb_match_one_id(struct usb_interface *interface,
const struct usb_device_id *id) const struct usb_device_id *id)
{ {
struct usb_host_interface *intf; struct usb_host_interface *intf;
...@@ -432,6 +433,8 @@ static int usb_match_one_id(struct usb_interface *interface, ...@@ -432,6 +433,8 @@ static int usb_match_one_id(struct usb_interface *interface,
return 1; return 1;
} }
EXPORT_SYMBOL_GPL(usb_match_one_id);
/** /**
* usb_match_id - find first usb_device_id matching device or interface * usb_match_id - find first usb_device_id matching device or interface
* @interface: the interface of interest * @interface: the interface of interest
......
...@@ -103,11 +103,52 @@ static int usb_serial_device_remove (struct device *dev) ...@@ -103,11 +103,52 @@ static int usb_serial_device_remove (struct device *dev)
return retval; 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 && usb_drv->usb_driver != NULL)
retval = usb_store_new_id(&usb_drv->usb_driver->dynids,
&usb_drv->usb_driver->drvwrap.driver,
buf, count);
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;
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 = { struct bus_type usb_serial_bus_type = {
.name = "usb-serial", .name = "usb-serial",
.match = usb_serial_device_match, .match = usb_serial_device_match,
.probe = usb_serial_device_probe, .probe = usb_serial_device_probe,
.remove = usb_serial_device_remove, .remove = usb_serial_device_remove,
.drv_attrs = drv_attrs,
}; };
int usb_serial_bus_register(struct usb_serial_driver *driver) 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,6 +156,9 @@ int usb_serial_bus_register(struct usb_serial_driver *driver)
int retval; int retval;
driver->driver.bus = &usb_serial_bus_type; driver->driver.bus = &usb_serial_bus_type;
spin_lock_init(&driver->dynids.lock);
INIT_LIST_HEAD(&driver->dynids.list);
retval = driver_register(&driver->driver); retval = driver_register(&driver->driver);
return retval; return retval;
...@@ -122,6 +166,7 @@ int usb_serial_bus_register(struct usb_serial_driver *driver) ...@@ -122,6 +166,7 @@ int usb_serial_bus_register(struct usb_serial_driver *driver)
void usb_serial_bus_deregister(struct usb_serial_driver *driver) void usb_serial_bus_deregister(struct usb_serial_driver *driver)
{ {
free_dynids(driver);
driver_unregister(&driver->driver); driver_unregister(&driver->driver);
} }
...@@ -596,6 +596,39 @@ static struct usb_serial * create_serial (struct usb_device *dev, ...@@ -596,6 +596,39 @@ static struct usb_serial * create_serial (struct usb_device *dev,
return 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) static struct usb_serial_driver *search_serial_device(struct usb_interface *iface)
{ {
struct list_head *p; struct list_head *p;
...@@ -605,12 +638,10 @@ static struct usb_serial_driver *search_serial_device(struct usb_interface *ifac ...@@ -605,12 +638,10 @@ static struct usb_serial_driver *search_serial_device(struct usb_interface *ifac
/* Check if the usb id matches a known device */ /* Check if the usb id matches a known device */
list_for_each(p, &usb_serial_driver_list) { list_for_each(p, &usb_serial_driver_list) {
t = list_entry(p, struct usb_serial_driver, driver_list); t = list_entry(p, struct usb_serial_driver, driver_list);
id = usb_match_id(iface, t->id_table); id = get_iface_id(t, iface);
if (id != NULL) { if (id)
dbg("descriptor matches");
return t; return t;
} }
}
return NULL; return NULL;
} }
...@@ -661,7 +692,7 @@ int usb_serial_probe(struct usb_interface *interface, ...@@ -661,7 +692,7 @@ int usb_serial_probe(struct usb_interface *interface,
return -EIO; return -EIO;
} }
id = usb_match_id(interface, type->id_table); id = get_iface_id(type, interface);
retval = type->probe(serial, id); retval = type->probe(serial, id);
module_put(type->driver.owner); module_put(type->driver.owner);
......
...@@ -476,6 +476,8 @@ extern void usb_driver_release_interface(struct usb_driver *driver, ...@@ -476,6 +476,8 @@ extern void usb_driver_release_interface(struct usb_driver *driver,
struct usb_interface *iface); struct usb_interface *iface);
const struct usb_device_id *usb_match_id(struct usb_interface *interface, const struct usb_device_id *usb_match_id(struct usb_interface *interface,
const struct usb_device_id *id); 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, extern struct usb_interface *usb_find_interface(struct usb_driver *drv,
int minor); int minor);
...@@ -724,11 +726,21 @@ static inline int usb_endpoint_is_isoc_out(const struct usb_endpoint_descriptor ...@@ -724,11 +726,21 @@ static inline int usb_endpoint_is_isoc_out(const struct usb_endpoint_descriptor
/* ----------------------------------------------------------------------- */ /* ----------------------------------------------------------------------- */
/* Stuff for dynamic usb ids */
struct usb_dynids { struct usb_dynids {
spinlock_t lock; spinlock_t lock;
struct list_head list; 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 usbdrv_wrap - wrapper for driver-model structure * struct usbdrv_wrap - wrapper for driver-model structure
* @driver: The driver-model core driver structure. * @driver: The driver-model core driver structure.
......
...@@ -179,6 +179,9 @@ static inline void usb_set_serial_data (struct usb_serial *serial, void *data) ...@@ -179,6 +179,9 @@ static inline void usb_set_serial_data (struct usb_serial *serial, void *data)
* memory structure allocation at this point in time. * memory structure allocation at this point in time.
* @shutdown: pointer to the driver's shutdown function. This will be * @shutdown: pointer to the driver's shutdown function. This will be
* called when the device is removed from the system. * called when the device is removed from the system.
* @usb_driver: pointer to the struct usb_driver that controls this
* device. This is necessary to allow dynamic ids to be added to
* the driver from sysfs.
* *
* This structure is defines a USB Serial driver. It provides all of * This structure is defines a USB Serial driver. It provides all of
* the information that the USB serial core code needs. If the function * the information that the USB serial core code needs. If the function
...@@ -202,6 +205,8 @@ struct usb_serial_driver { ...@@ -202,6 +205,8 @@ struct usb_serial_driver {
struct list_head driver_list; struct list_head driver_list;
struct device_driver driver; struct device_driver driver;
struct usb_driver *usb_driver;
struct usb_dynids dynids;
int (*probe) (struct usb_serial *serial, const struct usb_device_id *id); int (*probe) (struct usb_serial *serial, const struct usb_device_id *id);
int (*attach) (struct usb_serial *serial); int (*attach) (struct usb_serial *serial);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment