/*
* drivers/usb/driver.c - most of the driver model stuff for usb
*
* (C) Copyright 2005 Greg Kroah-Hartman <gregkh@suse.de>
*
* based on drivers/usb/usb.c which had the following copyrights:
* (C) Copyright Linus Torvalds 1999
* (C) Copyright Johannes Erdfelt 1999-2001
* (C) Copyright Andreas Gal 1999
* (C) Copyright Gregory P. Smith 1999
* (C) Copyright Deti Fliegl 1999 (new USB architecture)
* (C) Copyright Randy Dunlap 2000
* (C) Copyright David Brownell 2000-2004
* (C) Copyright Yggdrasil Computing, Inc. 2000
* (usb_device_id matching changes by Adam J. Richter)
* (C) Copyright Greg Kroah-Hartman 2002-2003
*
* NOTE! This is not actually a driver at all, rather this is
* just a collection of helper routines that implement the
* matching, probing, releasing, suspending and resuming for
* real drivers.
*
*/
#include <linux/device.h>
#include <linux/usb.h>
#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;
};
#ifdef CONFIG_HOTPLUG
/*
* 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)
{
struct usb_driver *usb_drv = to_usb_driver(driver);
struct usb_dynid *dynid;
u32 idVendor = 0;
u32 idProduct = 0;
int fields = 0;
fields = sscanf(buf, "%x %x", &idVendor, &idProduct);
if (fields < 2)
return -EINVAL;
dynid = kzalloc(sizeof(*dynid), GFP_KERNEL);
if (!dynid)
return -ENOMEM;
INIT_LIST_HEAD(&dynid->node);
dynid->id.idVendor = idVendor;
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);
if (get_driver(driver)) {
driver_attach(driver);
put_driver(driver);
}
return count;
}
static DRIVER_ATTR(new_id, S_IWUSR, NULL, store_new_id);
static int usb_create_newid_file(struct usb_driver *usb_drv)
{
int error = 0;
if (usb_drv->no_dynamic_id)
goto exit;
if (usb_drv->probe != NULL)
error = sysfs_create_file(&usb_drv->drvwrap.driver.kobj,
&driver_attr_new_id.attr);
exit:
return error;
}
static void usb_remove_newid_file(struct usb_driver *usb_drv)
{
if (usb_drv->no_dynamic_id)
return;
if (usb_drv->probe != NULL)
sysfs_remove_file(&usb_drv->drvwrap.driver.kobj,
&driver_attr_new_id.attr);
}
static void usb_free_dynids(struct usb_driver *usb_drv)
{
struct usb_dynid *dynid, *n;
spin_lock(&usb_drv->dynids.lock);
list_for_each_entry_safe(dynid, n, &usb_drv->dynids.list, node) {
list_del(&dynid->node);
kfree(dynid);
}
spin_unlock(&usb_drv->dynids.lock);
}
#else
static inline int usb_create_newid_file(struct usb_driver *usb_drv)
{
return 0;
}
static void usb_remove_newid_file(struct usb_driver *usb_drv)
{
}
static inline void usb_free_dynids(struct usb_driver *usb_drv)
{
}
#endif
static const struct usb_device_id *usb_match_dynamic_id(struct usb_interface *intf,
struct usb_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;
}
/* called from driver core with dev locked */
static int usb_probe_device(struct device *dev)
{
struct usb_device_driver *udriver = to_usb_device_driver(dev->driver);
struct usb_device *udev;
int error = -ENODEV;
dev_dbg(dev, "%s\n", __FUNCTION__);
if (!is_usb_device(dev)) /* Sanity check */
return error;
udev = to_usb_device(dev);
/* FIXME: resume a suspended device */
if (udev->state == USB_STATE_SUSPENDED)
return -EHOSTUNREACH;
/* TODO: Add real matching code */
error = udriver->probe(udev);
return error;
}
/* called from driver core with dev locked */
static int usb_unbind_device(struct device *dev)
{
struct usb_device_driver *udriver = to_usb_device_driver(