提交 36e56a34 编写于 作者: A Alan Stern 提交者: Greg Kroah-Hartman

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: NAlan Stern <stern@rowland.harvard.edu>
Signed-off-by: NGreg Kroah-Hartman <gregkh@suse.de>
上级 140d8f68
......@@ -4,7 +4,7 @@
usbcore-objs := usb.o hub.o hcd.o urb.o message.o driver.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)
usbcore-objs += hcd-pci.o
......
......@@ -17,7 +17,8 @@
*
* NOTE! This is not actually a driver at all, rather this is
* 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;
};
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
/*
......@@ -238,6 +207,89 @@ static int usb_unbind_interface(struct device *dev)
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 */
static int usb_match_one_id(struct usb_interface *interface,
const struct usb_device_id *id)
......@@ -402,6 +454,120 @@ int usb_device_match(struct device *dev, struct device_driver *drv)
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
* @new_driver: USB operations for the driver
......@@ -469,3 +635,119 @@ void usb_deregister(struct usb_driver *driver)
usbfs_update_special();
}
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
};
/*
* 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;
}
/**
* 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 {
int minor;
struct usb_interface *interface;
......@@ -234,120 +153,6 @@ struct usb_interface *usb_find_interface(struct usb_driver *drv, int minor)
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.
* @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);
}
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 */
__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_driver_claim_interface);
EXPORT_SYMBOL(usb_driver_release_interface);
EXPORT_SYMBOL(usb_find_interface);
EXPORT_SYMBOL(usb_ifnum_to_if);
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_resume(struct usb_device *dev);
extern struct bus_type usb_bus_type;
extern struct device_driver usb_generic_driver;
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 */
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册