usbcore: move code among source files
This revised patch (as713b) moves a few routines among source files in usbcore. Some driver-related code in usb.c (claiming interfaces and matching IDs) is moved to driver.c, where it belongs. Also the usb_generic stuff in driver.c is moved to a new source file: generic.c. (That's the reason for revising the patch.) Although not very big now, it will get bigger in a later patch. None of the code has been changed; it has only been re-arranged. Signed-off-by: Alan Stern <stern@rowland.harvard.edu> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
This commit is contained in:
Родитель
140d8f6874
Коммит
36e56a3458
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
usbcore-objs := usb.o hub.o hcd.o urb.o message.o driver.o \
|
usbcore-objs := usb.o hub.o hcd.o urb.o message.o driver.o \
|
||||||
config.o file.o buffer.o sysfs.o endpoint.o \
|
config.o file.o buffer.o sysfs.o endpoint.o \
|
||||||
devio.o notify.o
|
devio.o notify.o generic.o
|
||||||
|
|
||||||
ifeq ($(CONFIG_PCI),y)
|
ifeq ($(CONFIG_PCI),y)
|
||||||
usbcore-objs += hcd-pci.o
|
usbcore-objs += hcd-pci.o
|
||||||
|
|
|
@ -17,7 +17,8 @@
|
||||||
*
|
*
|
||||||
* NOTE! This is not actually a driver at all, rather this is
|
* NOTE! This is not actually a driver at all, rather this is
|
||||||
* just a collection of helper routines that implement the
|
* just a collection of helper routines that implement the
|
||||||
* generic USB things that the real drivers can use..
|
* matching, probing, releasing, suspending and resuming for
|
||||||
|
* real drivers.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -34,38 +35,6 @@ struct usb_dynid {
|
||||||
struct usb_device_id id;
|
struct usb_device_id id;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
static int generic_probe(struct device *dev)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
static int generic_remove(struct device *dev)
|
|
||||||
{
|
|
||||||
struct usb_device *udev = to_usb_device(dev);
|
|
||||||
|
|
||||||
/* if this is only an unbind, not a physical disconnect, then
|
|
||||||
* unconfigure the device */
|
|
||||||
if (udev->state == USB_STATE_CONFIGURED)
|
|
||||||
usb_set_configuration(udev, 0);
|
|
||||||
|
|
||||||
/* in case the call failed or the device was suspended */
|
|
||||||
if (udev->state >= USB_STATE_CONFIGURED)
|
|
||||||
usb_disable_device(udev, 0);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct device_driver usb_generic_driver = {
|
|
||||||
.owner = THIS_MODULE,
|
|
||||||
.name = "usb",
|
|
||||||
.bus = &usb_bus_type,
|
|
||||||
.probe = generic_probe,
|
|
||||||
.remove = generic_remove,
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Fun hack to determine if the struct device is a
|
|
||||||
* usb device or a usb interface. */
|
|
||||||
int usb_generic_driver_data;
|
|
||||||
|
|
||||||
#ifdef CONFIG_HOTPLUG
|
#ifdef CONFIG_HOTPLUG
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -238,6 +207,89 @@ static int usb_unbind_interface(struct device *dev)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* usb_driver_claim_interface - bind a driver to an interface
|
||||||
|
* @driver: the driver to be bound
|
||||||
|
* @iface: the interface to which it will be bound; must be in the
|
||||||
|
* usb device's active configuration
|
||||||
|
* @priv: driver data associated with that interface
|
||||||
|
*
|
||||||
|
* This is used by usb device drivers that need to claim more than one
|
||||||
|
* interface on a device when probing (audio and acm are current examples).
|
||||||
|
* No device driver should directly modify internal usb_interface or
|
||||||
|
* usb_device structure members.
|
||||||
|
*
|
||||||
|
* Few drivers should need to use this routine, since the most natural
|
||||||
|
* way to bind to an interface is to return the private data from
|
||||||
|
* the driver's probe() method.
|
||||||
|
*
|
||||||
|
* Callers must own the device lock and the driver model's usb_bus_type.subsys
|
||||||
|
* writelock. So driver probe() entries don't need extra locking,
|
||||||
|
* but other call contexts may need to explicitly claim those locks.
|
||||||
|
*/
|
||||||
|
int usb_driver_claim_interface(struct usb_driver *driver,
|
||||||
|
struct usb_interface *iface, void* priv)
|
||||||
|
{
|
||||||
|
struct device *dev = &iface->dev;
|
||||||
|
|
||||||
|
if (dev->driver)
|
||||||
|
return -EBUSY;
|
||||||
|
|
||||||
|
dev->driver = &driver->driver;
|
||||||
|
usb_set_intfdata(iface, priv);
|
||||||
|
iface->condition = USB_INTERFACE_BOUND;
|
||||||
|
mark_active(iface);
|
||||||
|
|
||||||
|
/* if interface was already added, bind now; else let
|
||||||
|
* the future device_add() bind it, bypassing probe()
|
||||||
|
*/
|
||||||
|
if (device_is_registered(dev))
|
||||||
|
device_bind_driver(dev);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(usb_driver_claim_interface);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* usb_driver_release_interface - unbind a driver from an interface
|
||||||
|
* @driver: the driver to be unbound
|
||||||
|
* @iface: the interface from which it will be unbound
|
||||||
|
*
|
||||||
|
* This can be used by drivers to release an interface without waiting
|
||||||
|
* for their disconnect() methods to be called. In typical cases this
|
||||||
|
* also causes the driver disconnect() method to be called.
|
||||||
|
*
|
||||||
|
* This call is synchronous, and may not be used in an interrupt context.
|
||||||
|
* Callers must own the device lock and the driver model's usb_bus_type.subsys
|
||||||
|
* writelock. So driver disconnect() entries don't need extra locking,
|
||||||
|
* but other call contexts may need to explicitly claim those locks.
|
||||||
|
*/
|
||||||
|
void usb_driver_release_interface(struct usb_driver *driver,
|
||||||
|
struct usb_interface *iface)
|
||||||
|
{
|
||||||
|
struct device *dev = &iface->dev;
|
||||||
|
|
||||||
|
/* this should never happen, don't release something that's not ours */
|
||||||
|
if (!dev->driver || dev->driver != &driver->driver)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* don't release from within disconnect() */
|
||||||
|
if (iface->condition != USB_INTERFACE_BOUND)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* don't release if the interface hasn't been added yet */
|
||||||
|
if (device_is_registered(dev)) {
|
||||||
|
iface->condition = USB_INTERFACE_UNBINDING;
|
||||||
|
device_release_driver(dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
dev->driver = NULL;
|
||||||
|
usb_set_intfdata(iface, NULL);
|
||||||
|
iface->condition = USB_INTERFACE_UNBOUND;
|
||||||
|
mark_quiesced(iface);
|
||||||
|
}
|
||||||
|
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,
|
static int usb_match_one_id(struct usb_interface *interface,
|
||||||
const struct usb_device_id *id)
|
const struct usb_device_id *id)
|
||||||
|
@ -402,6 +454,120 @@ int usb_device_match(struct device *dev, struct device_driver *drv)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_HOTPLUG
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This sends an uevent to userspace, typically helping to load driver
|
||||||
|
* or other modules, configure the device, and more. Drivers can provide
|
||||||
|
* a MODULE_DEVICE_TABLE to help with module loading subtasks.
|
||||||
|
*
|
||||||
|
* We're called either from khubd (the typical case) or from root hub
|
||||||
|
* (init, kapmd, modprobe, rmmod, etc), but the agents need to handle
|
||||||
|
* delays in event delivery. Use sysfs (and DEVPATH) to make sure the
|
||||||
|
* device (and this configuration!) are still present.
|
||||||
|
*/
|
||||||
|
static int usb_uevent(struct device *dev, char **envp, int num_envp,
|
||||||
|
char *buffer, int buffer_size)
|
||||||
|
{
|
||||||
|
struct usb_interface *intf;
|
||||||
|
struct usb_device *usb_dev;
|
||||||
|
struct usb_host_interface *alt;
|
||||||
|
int i = 0;
|
||||||
|
int length = 0;
|
||||||
|
|
||||||
|
if (!dev)
|
||||||
|
return -ENODEV;
|
||||||
|
|
||||||
|
/* driver is often null here; dev_dbg() would oops */
|
||||||
|
pr_debug ("usb %s: uevent\n", dev->bus_id);
|
||||||
|
|
||||||
|
/* Must check driver_data here, as on remove driver is always NULL */
|
||||||
|
if ((dev->driver == &usb_generic_driver) ||
|
||||||
|
(dev->driver_data == &usb_generic_driver_data))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
intf = to_usb_interface(dev);
|
||||||
|
usb_dev = interface_to_usbdev (intf);
|
||||||
|
alt = intf->cur_altsetting;
|
||||||
|
|
||||||
|
if (usb_dev->devnum < 0) {
|
||||||
|
pr_debug ("usb %s: already deleted?\n", dev->bus_id);
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
if (!usb_dev->bus) {
|
||||||
|
pr_debug ("usb %s: bus removed?\n", dev->bus_id);
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_USB_DEVICEFS
|
||||||
|
/* If this is available, userspace programs can directly read
|
||||||
|
* all the device descriptors we don't tell them about. Or
|
||||||
|
* even act as usermode drivers.
|
||||||
|
*
|
||||||
|
* FIXME reduce hardwired intelligence here
|
||||||
|
*/
|
||||||
|
if (add_uevent_var(envp, num_envp, &i,
|
||||||
|
buffer, buffer_size, &length,
|
||||||
|
"DEVICE=/proc/bus/usb/%03d/%03d",
|
||||||
|
usb_dev->bus->busnum, usb_dev->devnum))
|
||||||
|
return -ENOMEM;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* per-device configurations are common */
|
||||||
|
if (add_uevent_var(envp, num_envp, &i,
|
||||||
|
buffer, buffer_size, &length,
|
||||||
|
"PRODUCT=%x/%x/%x",
|
||||||
|
le16_to_cpu(usb_dev->descriptor.idVendor),
|
||||||
|
le16_to_cpu(usb_dev->descriptor.idProduct),
|
||||||
|
le16_to_cpu(usb_dev->descriptor.bcdDevice)))
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
/* class-based driver binding models */
|
||||||
|
if (add_uevent_var(envp, num_envp, &i,
|
||||||
|
buffer, buffer_size, &length,
|
||||||
|
"TYPE=%d/%d/%d",
|
||||||
|
usb_dev->descriptor.bDeviceClass,
|
||||||
|
usb_dev->descriptor.bDeviceSubClass,
|
||||||
|
usb_dev->descriptor.bDeviceProtocol))
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
if (add_uevent_var(envp, num_envp, &i,
|
||||||
|
buffer, buffer_size, &length,
|
||||||
|
"INTERFACE=%d/%d/%d",
|
||||||
|
alt->desc.bInterfaceClass,
|
||||||
|
alt->desc.bInterfaceSubClass,
|
||||||
|
alt->desc.bInterfaceProtocol))
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
if (add_uevent_var(envp, num_envp, &i,
|
||||||
|
buffer, buffer_size, &length,
|
||||||
|
"MODALIAS=usb:v%04Xp%04Xd%04Xdc%02Xdsc%02Xdp%02Xic%02Xisc%02Xip%02X",
|
||||||
|
le16_to_cpu(usb_dev->descriptor.idVendor),
|
||||||
|
le16_to_cpu(usb_dev->descriptor.idProduct),
|
||||||
|
le16_to_cpu(usb_dev->descriptor.bcdDevice),
|
||||||
|
usb_dev->descriptor.bDeviceClass,
|
||||||
|
usb_dev->descriptor.bDeviceSubClass,
|
||||||
|
usb_dev->descriptor.bDeviceProtocol,
|
||||||
|
alt->desc.bInterfaceClass,
|
||||||
|
alt->desc.bInterfaceSubClass,
|
||||||
|
alt->desc.bInterfaceProtocol))
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
envp[i] = NULL;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
static int usb_uevent(struct device *dev, char **envp,
|
||||||
|
int num_envp, char *buffer, int buffer_size)
|
||||||
|
{
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* CONFIG_HOTPLUG */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* usb_register_driver - register a USB driver
|
* usb_register_driver - register a USB driver
|
||||||
* @new_driver: USB operations for the driver
|
* @new_driver: USB operations for the driver
|
||||||
|
@ -469,3 +635,119 @@ void usb_deregister(struct usb_driver *driver)
|
||||||
usbfs_update_special();
|
usbfs_update_special();
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL_FUTURE(usb_deregister);
|
EXPORT_SYMBOL_GPL_FUTURE(usb_deregister);
|
||||||
|
|
||||||
|
#ifdef CONFIG_PM
|
||||||
|
|
||||||
|
static int verify_suspended(struct device *dev, void *unused)
|
||||||
|
{
|
||||||
|
if (dev->driver == NULL)
|
||||||
|
return 0;
|
||||||
|
return (dev->power.power_state.event == PM_EVENT_ON) ? -EBUSY : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int usb_generic_suspend(struct device *dev, pm_message_t message)
|
||||||
|
{
|
||||||
|
struct usb_interface *intf;
|
||||||
|
struct usb_driver *driver;
|
||||||
|
int status;
|
||||||
|
|
||||||
|
/* USB devices enter SUSPEND state through their hubs, but can be
|
||||||
|
* marked for FREEZE as soon as their children are already idled.
|
||||||
|
* But those semantics are useless, so we equate the two (sigh).
|
||||||
|
*/
|
||||||
|
if (dev->driver == &usb_generic_driver) {
|
||||||
|
if (dev->power.power_state.event == message.event)
|
||||||
|
return 0;
|
||||||
|
/* we need to rule out bogus requests through sysfs */
|
||||||
|
status = device_for_each_child(dev, NULL, verify_suspended);
|
||||||
|
if (status)
|
||||||
|
return status;
|
||||||
|
return usb_port_suspend(to_usb_device(dev));
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((dev->driver == NULL) ||
|
||||||
|
(dev->driver_data == &usb_generic_driver_data))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
intf = to_usb_interface(dev);
|
||||||
|
driver = to_usb_driver(dev->driver);
|
||||||
|
|
||||||
|
/* with no hardware, USB interfaces only use FREEZE and ON states */
|
||||||
|
if (!is_active(intf))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (driver->suspend && driver->resume) {
|
||||||
|
status = driver->suspend(intf, message);
|
||||||
|
if (status)
|
||||||
|
dev_err(dev, "%s error %d\n", "suspend", status);
|
||||||
|
else
|
||||||
|
mark_quiesced(intf);
|
||||||
|
} else {
|
||||||
|
// FIXME else if there's no suspend method, disconnect...
|
||||||
|
dev_warn(dev, "no suspend for driver %s?\n", driver->name);
|
||||||
|
mark_quiesced(intf);
|
||||||
|
status = 0;
|
||||||
|
}
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int usb_generic_resume(struct device *dev)
|
||||||
|
{
|
||||||
|
struct usb_interface *intf;
|
||||||
|
struct usb_driver *driver;
|
||||||
|
struct usb_device *udev;
|
||||||
|
int status;
|
||||||
|
|
||||||
|
if (dev->power.power_state.event == PM_EVENT_ON)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* mark things as "on" immediately, no matter what errors crop up */
|
||||||
|
dev->power.power_state.event = PM_EVENT_ON;
|
||||||
|
|
||||||
|
/* devices resume through their hubs */
|
||||||
|
if (dev->driver == &usb_generic_driver) {
|
||||||
|
udev = to_usb_device(dev);
|
||||||
|
if (udev->state == USB_STATE_NOTATTACHED)
|
||||||
|
return 0;
|
||||||
|
return usb_port_resume(udev);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((dev->driver == NULL) ||
|
||||||
|
(dev->driver_data == &usb_generic_driver_data)) {
|
||||||
|
dev->power.power_state.event = PM_EVENT_FREEZE;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
intf = to_usb_interface(dev);
|
||||||
|
driver = to_usb_driver(dev->driver);
|
||||||
|
|
||||||
|
udev = interface_to_usbdev(intf);
|
||||||
|
if (udev->state == USB_STATE_NOTATTACHED)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* if driver was suspended, it has a resume method;
|
||||||
|
* however, sysfs can wrongly mark things as suspended
|
||||||
|
* (on the "no suspend method" FIXME path above)
|
||||||
|
*/
|
||||||
|
if (driver->resume) {
|
||||||
|
status = driver->resume(intf);
|
||||||
|
if (status) {
|
||||||
|
dev_err(dev, "%s error %d\n", "resume", status);
|
||||||
|
mark_quiesced(intf);
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
dev_warn(dev, "no resume for driver %s?\n", driver->name);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* CONFIG_PM */
|
||||||
|
|
||||||
|
struct bus_type usb_bus_type = {
|
||||||
|
.name = "usb",
|
||||||
|
.match = usb_device_match,
|
||||||
|
.uevent = usb_uevent,
|
||||||
|
#ifdef CONFIG_PM
|
||||||
|
.suspend = usb_generic_suspend,
|
||||||
|
.resume = usb_generic_resume,
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
|
@ -0,0 +1,53 @@
|
||||||
|
/*
|
||||||
|
* drivers/usb/generic.c - generic driver for USB devices (not interfaces)
|
||||||
|
*
|
||||||
|
* (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
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/config.h>
|
||||||
|
#include <linux/usb.h>
|
||||||
|
#include "usb.h"
|
||||||
|
|
||||||
|
static int generic_probe(struct device *dev)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
static int generic_remove(struct device *dev)
|
||||||
|
{
|
||||||
|
struct usb_device *udev = to_usb_device(dev);
|
||||||
|
|
||||||
|
/* if this is only an unbind, not a physical disconnect, then
|
||||||
|
* unconfigure the device */
|
||||||
|
if (udev->state == USB_STATE_CONFIGURED)
|
||||||
|
usb_set_configuration(udev, 0);
|
||||||
|
|
||||||
|
/* in case the call failed or the device was suspended */
|
||||||
|
if (udev->state >= USB_STATE_CONFIGURED)
|
||||||
|
usb_disable_device(udev, 0);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct device_driver usb_generic_driver = {
|
||||||
|
.owner = THIS_MODULE,
|
||||||
|
.name = "usb",
|
||||||
|
.bus = &usb_bus_type,
|
||||||
|
.probe = generic_probe,
|
||||||
|
.remove = generic_remove,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Fun hack to determine if the struct device is a
|
||||||
|
* usb device or a usb interface. */
|
||||||
|
int usb_generic_driver_data;
|
|
@ -112,87 +112,6 @@ struct usb_host_interface *usb_altnum_to_altsetting(struct usb_interface *intf,
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* usb_driver_claim_interface - bind a driver to an interface
|
|
||||||
* @driver: the driver to be bound
|
|
||||||
* @iface: the interface to which it will be bound; must be in the
|
|
||||||
* usb device's active configuration
|
|
||||||
* @priv: driver data associated with that interface
|
|
||||||
*
|
|
||||||
* This is used by usb device drivers that need to claim more than one
|
|
||||||
* interface on a device when probing (audio and acm are current examples).
|
|
||||||
* No device driver should directly modify internal usb_interface or
|
|
||||||
* usb_device structure members.
|
|
||||||
*
|
|
||||||
* Few drivers should need to use this routine, since the most natural
|
|
||||||
* way to bind to an interface is to return the private data from
|
|
||||||
* the driver's probe() method.
|
|
||||||
*
|
|
||||||
* Callers must own the device lock and the driver model's usb_bus_type.subsys
|
|
||||||
* writelock. So driver probe() entries don't need extra locking,
|
|
||||||
* but other call contexts may need to explicitly claim those locks.
|
|
||||||
*/
|
|
||||||
int usb_driver_claim_interface(struct usb_driver *driver,
|
|
||||||
struct usb_interface *iface, void* priv)
|
|
||||||
{
|
|
||||||
struct device *dev = &iface->dev;
|
|
||||||
|
|
||||||
if (dev->driver)
|
|
||||||
return -EBUSY;
|
|
||||||
|
|
||||||
dev->driver = &driver->driver;
|
|
||||||
usb_set_intfdata(iface, priv);
|
|
||||||
iface->condition = USB_INTERFACE_BOUND;
|
|
||||||
mark_active(iface);
|
|
||||||
|
|
||||||
/* if interface was already added, bind now; else let
|
|
||||||
* the future device_add() bind it, bypassing probe()
|
|
||||||
*/
|
|
||||||
if (device_is_registered(dev))
|
|
||||||
device_bind_driver(dev);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* usb_driver_release_interface - unbind a driver from an interface
|
|
||||||
* @driver: the driver to be unbound
|
|
||||||
* @iface: the interface from which it will be unbound
|
|
||||||
*
|
|
||||||
* This can be used by drivers to release an interface without waiting
|
|
||||||
* for their disconnect() methods to be called. In typical cases this
|
|
||||||
* also causes the driver disconnect() method to be called.
|
|
||||||
*
|
|
||||||
* This call is synchronous, and may not be used in an interrupt context.
|
|
||||||
* Callers must own the device lock and the driver model's usb_bus_type.subsys
|
|
||||||
* writelock. So driver disconnect() entries don't need extra locking,
|
|
||||||
* but other call contexts may need to explicitly claim those locks.
|
|
||||||
*/
|
|
||||||
void usb_driver_release_interface(struct usb_driver *driver,
|
|
||||||
struct usb_interface *iface)
|
|
||||||
{
|
|
||||||
struct device *dev = &iface->dev;
|
|
||||||
|
|
||||||
/* this should never happen, don't release something that's not ours */
|
|
||||||
if (!dev->driver || dev->driver != &driver->driver)
|
|
||||||
return;
|
|
||||||
|
|
||||||
/* don't release from within disconnect() */
|
|
||||||
if (iface->condition != USB_INTERFACE_BOUND)
|
|
||||||
return;
|
|
||||||
|
|
||||||
/* don't release if the interface hasn't been added yet */
|
|
||||||
if (device_is_registered(dev)) {
|
|
||||||
iface->condition = USB_INTERFACE_UNBINDING;
|
|
||||||
device_release_driver(dev);
|
|
||||||
}
|
|
||||||
|
|
||||||
dev->driver = NULL;
|
|
||||||
usb_set_intfdata(iface, NULL);
|
|
||||||
iface->condition = USB_INTERFACE_UNBOUND;
|
|
||||||
mark_quiesced(iface);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct find_interface_arg {
|
struct find_interface_arg {
|
||||||
int minor;
|
int minor;
|
||||||
struct usb_interface *interface;
|
struct usb_interface *interface;
|
||||||
|
@ -234,120 +153,6 @@ struct usb_interface *usb_find_interface(struct usb_driver *drv, int minor)
|
||||||
return argb.interface;
|
return argb.interface;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_HOTPLUG
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This sends an uevent to userspace, typically helping to load driver
|
|
||||||
* or other modules, configure the device, and more. Drivers can provide
|
|
||||||
* a MODULE_DEVICE_TABLE to help with module loading subtasks.
|
|
||||||
*
|
|
||||||
* We're called either from khubd (the typical case) or from root hub
|
|
||||||
* (init, kapmd, modprobe, rmmod, etc), but the agents need to handle
|
|
||||||
* delays in event delivery. Use sysfs (and DEVPATH) to make sure the
|
|
||||||
* device (and this configuration!) are still present.
|
|
||||||
*/
|
|
||||||
static int usb_uevent(struct device *dev, char **envp, int num_envp,
|
|
||||||
char *buffer, int buffer_size)
|
|
||||||
{
|
|
||||||
struct usb_interface *intf;
|
|
||||||
struct usb_device *usb_dev;
|
|
||||||
struct usb_host_interface *alt;
|
|
||||||
int i = 0;
|
|
||||||
int length = 0;
|
|
||||||
|
|
||||||
if (!dev)
|
|
||||||
return -ENODEV;
|
|
||||||
|
|
||||||
/* driver is often null here; dev_dbg() would oops */
|
|
||||||
pr_debug ("usb %s: uevent\n", dev->bus_id);
|
|
||||||
|
|
||||||
/* Must check driver_data here, as on remove driver is always NULL */
|
|
||||||
if ((dev->driver == &usb_generic_driver) ||
|
|
||||||
(dev->driver_data == &usb_generic_driver_data))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
intf = to_usb_interface(dev);
|
|
||||||
usb_dev = interface_to_usbdev (intf);
|
|
||||||
alt = intf->cur_altsetting;
|
|
||||||
|
|
||||||
if (usb_dev->devnum < 0) {
|
|
||||||
pr_debug ("usb %s: already deleted?\n", dev->bus_id);
|
|
||||||
return -ENODEV;
|
|
||||||
}
|
|
||||||
if (!usb_dev->bus) {
|
|
||||||
pr_debug ("usb %s: bus removed?\n", dev->bus_id);
|
|
||||||
return -ENODEV;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef CONFIG_USB_DEVICEFS
|
|
||||||
/* If this is available, userspace programs can directly read
|
|
||||||
* all the device descriptors we don't tell them about. Or
|
|
||||||
* even act as usermode drivers.
|
|
||||||
*
|
|
||||||
* FIXME reduce hardwired intelligence here
|
|
||||||
*/
|
|
||||||
if (add_uevent_var(envp, num_envp, &i,
|
|
||||||
buffer, buffer_size, &length,
|
|
||||||
"DEVICE=/proc/bus/usb/%03d/%03d",
|
|
||||||
usb_dev->bus->busnum, usb_dev->devnum))
|
|
||||||
return -ENOMEM;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* per-device configurations are common */
|
|
||||||
if (add_uevent_var(envp, num_envp, &i,
|
|
||||||
buffer, buffer_size, &length,
|
|
||||||
"PRODUCT=%x/%x/%x",
|
|
||||||
le16_to_cpu(usb_dev->descriptor.idVendor),
|
|
||||||
le16_to_cpu(usb_dev->descriptor.idProduct),
|
|
||||||
le16_to_cpu(usb_dev->descriptor.bcdDevice)))
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
/* class-based driver binding models */
|
|
||||||
if (add_uevent_var(envp, num_envp, &i,
|
|
||||||
buffer, buffer_size, &length,
|
|
||||||
"TYPE=%d/%d/%d",
|
|
||||||
usb_dev->descriptor.bDeviceClass,
|
|
||||||
usb_dev->descriptor.bDeviceSubClass,
|
|
||||||
usb_dev->descriptor.bDeviceProtocol))
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
if (add_uevent_var(envp, num_envp, &i,
|
|
||||||
buffer, buffer_size, &length,
|
|
||||||
"INTERFACE=%d/%d/%d",
|
|
||||||
alt->desc.bInterfaceClass,
|
|
||||||
alt->desc.bInterfaceSubClass,
|
|
||||||
alt->desc.bInterfaceProtocol))
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
if (add_uevent_var(envp, num_envp, &i,
|
|
||||||
buffer, buffer_size, &length,
|
|
||||||
"MODALIAS=usb:v%04Xp%04Xd%04Xdc%02Xdsc%02Xdp%02Xic%02Xisc%02Xip%02X",
|
|
||||||
le16_to_cpu(usb_dev->descriptor.idVendor),
|
|
||||||
le16_to_cpu(usb_dev->descriptor.idProduct),
|
|
||||||
le16_to_cpu(usb_dev->descriptor.bcdDevice),
|
|
||||||
usb_dev->descriptor.bDeviceClass,
|
|
||||||
usb_dev->descriptor.bDeviceSubClass,
|
|
||||||
usb_dev->descriptor.bDeviceProtocol,
|
|
||||||
alt->desc.bInterfaceClass,
|
|
||||||
alt->desc.bInterfaceSubClass,
|
|
||||||
alt->desc.bInterfaceProtocol))
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
envp[i] = NULL;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#else
|
|
||||||
|
|
||||||
static int usb_uevent(struct device *dev, char **envp,
|
|
||||||
int num_envp, char *buffer, int buffer_size)
|
|
||||||
{
|
|
||||||
return -ENODEV;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif /* CONFIG_HOTPLUG */
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* usb_release_dev - free a usb device structure when all users of it are finished.
|
* usb_release_dev - free a usb device structure when all users of it are finished.
|
||||||
* @dev: device that's been disconnected
|
* @dev: device that's been disconnected
|
||||||
|
@ -990,116 +795,6 @@ void usb_buffer_unmap_sg (struct usb_device *dev, unsigned pipe,
|
||||||
usb_pipein (pipe) ? DMA_FROM_DEVICE : DMA_TO_DEVICE);
|
usb_pipein (pipe) ? DMA_FROM_DEVICE : DMA_TO_DEVICE);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int verify_suspended(struct device *dev, void *unused)
|
|
||||||
{
|
|
||||||
if (dev->driver == NULL)
|
|
||||||
return 0;
|
|
||||||
return (dev->power.power_state.event == PM_EVENT_ON) ? -EBUSY : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int usb_generic_suspend(struct device *dev, pm_message_t message)
|
|
||||||
{
|
|
||||||
struct usb_interface *intf;
|
|
||||||
struct usb_driver *driver;
|
|
||||||
int status;
|
|
||||||
|
|
||||||
/* USB devices enter SUSPEND state through their hubs, but can be
|
|
||||||
* marked for FREEZE as soon as their children are already idled.
|
|
||||||
* But those semantics are useless, so we equate the two (sigh).
|
|
||||||
*/
|
|
||||||
if (dev->driver == &usb_generic_driver) {
|
|
||||||
if (dev->power.power_state.event == message.event)
|
|
||||||
return 0;
|
|
||||||
/* we need to rule out bogus requests through sysfs */
|
|
||||||
status = device_for_each_child(dev, NULL, verify_suspended);
|
|
||||||
if (status)
|
|
||||||
return status;
|
|
||||||
return usb_port_suspend(to_usb_device(dev));
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((dev->driver == NULL) ||
|
|
||||||
(dev->driver_data == &usb_generic_driver_data))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
intf = to_usb_interface(dev);
|
|
||||||
driver = to_usb_driver(dev->driver);
|
|
||||||
|
|
||||||
/* with no hardware, USB interfaces only use FREEZE and ON states */
|
|
||||||
if (!is_active(intf))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
if (driver->suspend && driver->resume) {
|
|
||||||
status = driver->suspend(intf, message);
|
|
||||||
if (status)
|
|
||||||
dev_err(dev, "%s error %d\n", "suspend", status);
|
|
||||||
else
|
|
||||||
mark_quiesced(intf);
|
|
||||||
} else {
|
|
||||||
// FIXME else if there's no suspend method, disconnect...
|
|
||||||
dev_warn(dev, "no suspend for driver %s?\n", driver->name);
|
|
||||||
mark_quiesced(intf);
|
|
||||||
status = 0;
|
|
||||||
}
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int usb_generic_resume(struct device *dev)
|
|
||||||
{
|
|
||||||
struct usb_interface *intf;
|
|
||||||
struct usb_driver *driver;
|
|
||||||
struct usb_device *udev;
|
|
||||||
int status;
|
|
||||||
|
|
||||||
if (dev->power.power_state.event == PM_EVENT_ON)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
/* mark things as "on" immediately, no matter what errors crop up */
|
|
||||||
dev->power.power_state.event = PM_EVENT_ON;
|
|
||||||
|
|
||||||
/* devices resume through their hubs */
|
|
||||||
if (dev->driver == &usb_generic_driver) {
|
|
||||||
udev = to_usb_device(dev);
|
|
||||||
if (udev->state == USB_STATE_NOTATTACHED)
|
|
||||||
return 0;
|
|
||||||
return usb_port_resume(udev);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((dev->driver == NULL) ||
|
|
||||||
(dev->driver_data == &usb_generic_driver_data)) {
|
|
||||||
dev->power.power_state.event = PM_EVENT_FREEZE;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
intf = to_usb_interface(dev);
|
|
||||||
driver = to_usb_driver(dev->driver);
|
|
||||||
|
|
||||||
udev = interface_to_usbdev(intf);
|
|
||||||
if (udev->state == USB_STATE_NOTATTACHED)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
/* if driver was suspended, it has a resume method;
|
|
||||||
* however, sysfs can wrongly mark things as suspended
|
|
||||||
* (on the "no suspend method" FIXME path above)
|
|
||||||
*/
|
|
||||||
if (driver->resume) {
|
|
||||||
status = driver->resume(intf);
|
|
||||||
if (status) {
|
|
||||||
dev_err(dev, "%s error %d\n", "resume", status);
|
|
||||||
mark_quiesced(intf);
|
|
||||||
}
|
|
||||||
} else
|
|
||||||
dev_warn(dev, "no resume for driver %s?\n", driver->name);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct bus_type usb_bus_type = {
|
|
||||||
.name = "usb",
|
|
||||||
.match = usb_device_match,
|
|
||||||
.uevent = usb_uevent,
|
|
||||||
.suspend = usb_generic_suspend,
|
|
||||||
.resume = usb_generic_resume,
|
|
||||||
};
|
|
||||||
|
|
||||||
/* format to disable USB on kernel command line is: nousb */
|
/* format to disable USB on kernel command line is: nousb */
|
||||||
__module_param_call("", nousb, param_set_bool, param_get_bool, &nousb, 0444);
|
__module_param_call("", nousb, param_set_bool, param_get_bool, &nousb, 0444);
|
||||||
|
|
||||||
|
@ -1203,8 +898,6 @@ EXPORT_SYMBOL(usb_hub_tt_clear_buffer);
|
||||||
|
|
||||||
EXPORT_SYMBOL(usb_lock_device_for_reset);
|
EXPORT_SYMBOL(usb_lock_device_for_reset);
|
||||||
|
|
||||||
EXPORT_SYMBOL(usb_driver_claim_interface);
|
|
||||||
EXPORT_SYMBOL(usb_driver_release_interface);
|
|
||||||
EXPORT_SYMBOL(usb_find_interface);
|
EXPORT_SYMBOL(usb_find_interface);
|
||||||
EXPORT_SYMBOL(usb_ifnum_to_if);
|
EXPORT_SYMBOL(usb_ifnum_to_if);
|
||||||
EXPORT_SYMBOL(usb_altnum_to_altsetting);
|
EXPORT_SYMBOL(usb_altnum_to_altsetting);
|
||||||
|
|
|
@ -33,9 +33,9 @@ extern void usb_host_cleanup(void);
|
||||||
extern int usb_port_suspend(struct usb_device *dev);
|
extern int usb_port_suspend(struct usb_device *dev);
|
||||||
extern int usb_port_resume(struct usb_device *dev);
|
extern int usb_port_resume(struct usb_device *dev);
|
||||||
|
|
||||||
|
extern struct bus_type usb_bus_type;
|
||||||
extern struct device_driver usb_generic_driver;
|
extern struct device_driver usb_generic_driver;
|
||||||
extern int usb_generic_driver_data;
|
extern int usb_generic_driver_data;
|
||||||
extern int usb_device_match(struct device *dev, struct device_driver *drv);
|
|
||||||
|
|
||||||
/* Interfaces and their "power state" are owned by usbcore */
|
/* Interfaces and their "power state" are owned by usbcore */
|
||||||
|
|
||||||
|
|
Загрузка…
Ссылка в новой задаче