forked from luck/tmp_suning_uos_patched
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>
This commit is contained in:
parent
495a678fc6
commit
93bacefc4c
@ -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,
|
||||||
const char *buf, size_t count)
|
struct device_driver *driver,
|
||||||
|
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,
|
|||||||
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,
|
|||||||
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,8 +366,8 @@ 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;
|
||||||
struct usb_device *dev;
|
struct usb_device *dev;
|
||||||
@ -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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
|
|
||||||
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,
|
|||||||
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,11 +638,9 @@ 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,
|
|||||||
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,
|
|||||||
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
|
|||||||
|
|
||||||
/* ----------------------------------------------------------------------- */
|
/* ----------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
/* 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)
|
|||||||
* 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 {
|
|||||||
|
|
||||||
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);
|
||||||
|
Loading…
Reference in New Issue
Block a user