USB总线和USB设备使用软件进行抽象描述起来是非常复杂的,一方面是协议使然,一方面也是因为它们使用太广泛了,抽象时考虑很太多情况。幸运的是,内核开发者们抽象出来的内核USB 子系统把很多复杂性都隐藏了。
针对上面这幅图,为了理解什么是USB子系统,我们要做以下说明:
a) USB 驱动都是夸kernel子系统的,因为最终USB设备是要通过BLCOCK 或CHAR设备的方式呈现给我们的,所以USB Driver之上还有一层。
b) USB driver利用USB Core提供的API来简单优雅的完成驱动工作,这里USB Core抽象了复杂的USB协议。
c) 主机控制器驱动位于USB软件的最下层,提供主机控制器硬件的抽象,隐藏硬件的细节,在主机控制器之下是物理的USB及所有与之连接的USB设备。主机控制器驱动只和USB Core进行关联,USB Core将用户的请求映射到相关的主机控制器驱动,从而使用户无需去访问主机控制器。
d) USB Core和USB主机控制器驱动就构成了我们的USB子系统,USB Core负责实现一些核心的功能,例如协议之类,提供一个用于访问和控制USB硬件的接口,使设备驱动不用去考虑系统当前使用哪种主机控制器。自从有了USB子系统,写USB驱动的时候,只需要调用USB Core export的接口,就几乎能完成所有工作。
e) USB总线将USB设备和USB驱动关联起来。
USB子系统初始化
usb初始化函数定义在内核源码(2.6.37)drivers/usb/core/usb.c:
/*
* Init
*/
static int __init usb_init(void)
{
int retval;
if (nousb) {
pr_info("%s: USB support disabled\n", usbcore_name);
return ;
}
retval = usb_debugfs_init();
if (retval)
goto out;
retval = bus_register(&usb_bus_type);
if (retval)
goto bus_register_failed;
retval = bus_register_notifier(&usb_bus_type, &usb_bus_nb);
if (retval)
goto bus_notifier_failed;
retval = usb_major_init();
if (retval)
goto major_init_failed;
retval = usb_register(&usbfs_driver);
if (retval)
goto driver_register_failed;
retval = usb_devio_init();
if (retval)
goto usb_devio_init_failed;
retval = usbfs_init();
if (retval)
goto fs_init_failed;
retval =usb_hub_init();
if (retval)
goto hub_init_failed;
retval = usb_register_device_driver(&usb_generic_driver, THIS_MODULE);
if (!retval)
goto out;
usb_hub_cleanup();
hub_init_failed:
usbfs_cleanup();
fs_init_failed:
usb_devio_cleanup();
usb_devio_init_failed:
usb_deregister(&usbfs_driver);
driver_register_failed:
usb_major_cleanup();
major_init_failed:
bus_unregister_notifier(&usb_bus_type, &usb_bus_nb);
bus_notifier_failed:
bus_unregister(&usb_bus_type);
bus_register_failed:
usb_debugfs_cleanup();
out:
return retval;
}
subsys_initcall(usb_init);
usb_debugfs_init():
DebugFS,顾名思义,是一种用于内核调试的虚拟文件系统,内核开发者通过debugfs和用户空间交换数据。类似的虚拟文件系统还有procfs和sysfs等,这几种虚拟文件系统都并不实际存储在硬盘上,而是Linux内核运行起来后,执行mount -t debugfs none /media/mmcblk0p2/ 才建立起来。在/media/mmcblk0p2/目录下创建usb目录并在下面创建devices文件。
当我们执行cat devices会调用usbfs_devices_fops->read(usb_device_read)函数去搜寻usb_bus_list链表下的usb设备信息,也就是所有总线下的设备。
bus_register:
是将usb总线注册到系统中,总线可是linux设备模型中的领导者,不管是多大的领导,也是领导,如PCI、USB、I2C,即使他们在物理上有从属关系,但是在模型的世界里,都是总线,拥有一样的待遇,所以任何一个子系统只要管理自己的设备和驱动,就需要向内核注册一个总线,注册报到。
bus_register_notifier:
大多数内核子系统都是相互独立的,因此某个子系统可能对其它子系统产生的事件感兴趣。为了满足这个需求,也即是让某个子系统在发生某个事件时通知其它的子系统,Linux内核提供了通知链的机制。通知链表只能够在内核的子系统之间使用,而不能够在内核与用户空间之间进行事件的通知。
通知链表是一个函数链表,链表上的每一个节点都注册了一个函数。当某个事情发生时,链表上所有节点对应的函数就会被执行。所以对于通知链表来说有一个通知方与一个接收方。在通知这个事件时所运行的函数由被通知方决定,实际上也即是被通知方注册了某个函数,在发生某个事件时这些函数就得到执行。其实和系统调用signal的思想差不多。
bus_register->BLOCKING_INIT_NOTIFIER_HEAD(&priv->bus_notifier),已经初始化了usb_bus_type->p->bus_notifier通过blocking_notifier_chain_register函数注册到通知链表。
那什么时候usb总线收到通知呢?
当总线发现新的设备调用device_add->blocking_notifier_call_chain(&dev->bus->p->bus_notifier, BUS_NOTIFY_ADD_DEVICE, dev)
当总线卸载设备时调用device_del->blocking_notifier_call_chain(&dev->bus->p->bus_notifier,BUS_NOTIFY_DEL_DEVICE, dev);
则调用usb_bus_nb的回调成员函数notifier_call(usb_bus_notify),函数定义如下:
/*
* Notifications of device and interface registration
*/
static int usb_bus_notify(struct notifier_block *nb, unsigned long action,
void *data)
{
struct device *dev = data;
switch (action) {
case BUS_NOTIFY_ADD_DEVICE:
if (dev->type == &usb_device_type)//usb 设备
(void) usb_create_sysfs_dev_files(to_usb_device(dev)); //创建descriptors文件
else if (dev->type == &usb_if_device_type) //usb接口
(void) usb_create_sysfs_intf_files(
to_usb_interface(dev));//创建interface文件
break;
case BUS_NOTIFY_DEL_DEVICE:
if (dev->type == &usb_device_type)//usb设备
usb_remove_sysfs_dev_files(to_usb_device(dev));//删除descriptors文件
else if (dev->type == &usb_if_device_type)//usb接口
usb_remove_sysfs_intf_files(to_usb_interface(dev));//删除interface文件
break;
}
return ;
}
usb_major_init:注册字符设备,主设备号180。
usb_register(&usbfs_driver):
struct usb_driver usbfs_driver = {
.name = "usbfs",
.probe = driver_probe,
.disconnect = driver_disconnect,
.suspend = driver_suspend,
.resume = driver_resume,
};
usb_register->usb_register_driver():
/**
* usb_register_driver - register a USB interface driver
* @new_driver: USB operations for the interface driver
* @owner: module owner of this driver.
* @mod_name: module name string
*
* Registers a USB interface driver with the USB core. The list of
* unattached interfaces will be rescanned whenever a new driver is
* added, allowing the new driver to attach to any recognized interfaces.
* Returns a negative error code on failure and 0 on success.
*
* NOTE: if you want your driver to use the USB major number, you must call
* usb_register_dev() to enable that functionality. This function no longer
* takes care of that.
*/
int usb_register_driver(struct usb_driver *new_driver, struct module *owner,
const char *mod_name)
{
int retval = ;
if (usb_disabled())
return -ENODEV;
new_driver->drvwrap.for_devices = ;
new_driver->drvwrap.driver.name = (char *) new_driver->name;
new_driver->drvwrap.driver.bus = &usb_bus_type;
new_driver->drvwrap.driver.probe = usb_probe_interface;
new_driver->drvwrap.driver.remove = usb_unbind_interface;
new_driver->drvwrap.driver.owner = owner;
new_driver->drvwrap.driver.mod_name = mod_name;
spin_lock_init(&new_driver->dynids.lock);
INIT_LIST_HEAD(&new_driver->dynids.list);
retval = driver_register(&new_driver->drvwrap.driver);
if (retval)
goto out;
usbfs_update_special();
retval = usb_create_newid_file(new_driver);
if (retval)
goto out_newid;
retval = usb_create_removeid_file(new_driver);
if (retval)
goto out_removeid;
pr_info("%s: registered new interface driver %s\n",
usbcore_name, new_driver->name);
out:
return retval;
out_removeid:
usb_remove_newid_file(new_driver);
out_newid:
driver_unregister(&new_driver->drvwrap.driver);
printk(KERN_ERR "%s: error %d registering interface "
" driver %s\n",
usbcore_name, retval, new_driver->name);
goto out;
}
EXPORT_SYMBOL_GPL(usb_register_driver);
其余功能如下:
1> driver_register实现。后面会详细分析。
2> usbfs_update_special(): 跟usb文件系统相关,看下面的usbfs_init分析。
3> usb_create_newid_file(): 创建newid属性文件,在/sys/bus/usb/drivers/usbfs/下面可以看到此文件。根据传入的ID值,增加一个新的动态usb设备到驱动(这里是usbfs),引起驱动重新探测所有的设备。
4> usb_create_removeid_file():创建removeid属性文件,在/sys/bus/usb/drivers/usbfs/下面可以看到此文件。根据传入的ID值,删除驱动(这里是usbfs)里的一个usb设备。
5> 输出信息:usbcore: registered new interface driver usbfs
现在分析driver_register功能:
1> 首先判断,些驱动所属bus的subsys_private结构有没有初始化。如果没有,报bug信息。
2> 判断需要注册的driver和driver所属的bus是否都有probe, remove, shutdown函数。如有,打印kernel warning信息。
3> 判断此driver已经在driver所属的bus上面注册过了。如果注册过了,打印错误信息,并返回。
4> 调用bus_add_driver来注册driver。
5> 调用driver_add_groups来添加组属性。
最后对bus_add_driver进行分析。
/**
* bus_add_driver - Add a driver to the bus.
* @drv: driver.
*/
int bus_add_driver(struct device_driver *drv)
{
struct bus_type *bus;
struct driver_private *priv;
int error = ;
bus = bus_get(drv->bus);
if (!bus)
return -EINVAL;
pr_debug("bus: '%s': add driver %s\n", bus->name, drv->name);
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
if (!priv) {
error = -ENOMEM;
goto out_put_bus;
}
klist_init(&priv->klist_devices, NULL, NULL);
priv->driver = drv;
drv->p = priv;
priv->kobj.kset = bus->p->drivers_kset;
error = kobject_init_and_add(&priv->kobj, &driver_ktype, NULL,
"%s", drv->name);
if (error)
goto out_unregister;
if (drv->bus->p->drivers_autoprobe) {
error = driver_attach(drv);
if (error)
goto out_unregister;
}
klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers);
module_add_driver(drv->owner, drv);
error = driver_create_file(drv, &driver_attr_uevent);
if (error) {
printk(KERN_ERR "%s: uevent attr (%s) failed\n",
__func__, drv->name);
}
error = driver_add_attrs(bus, drv);
if (error) {
/* How the hell do we get out of this pickle? Give up */
printk(KERN_ERR "%s: driver_add_attrs(%s) failed\n",
__func__, drv->name);
}
if (!drv->suppress_bind_attrs) {
error = add_bind_files(drv);
if (error) {
/* Ditto */
printk(KERN_ERR "%s: add_bind_files(%s) failed\n",
__func__, drv->name);
}
}
kobject_uevent(&priv->kobj, KOBJ_ADD);
return ;
out_unregister:
kobject_put(&priv->kobj);
kfree(drv->p);
drv->p = NULL;
out_put_bus:
bus_put(bus);
return error;
}
其功能是向bus中添加一个driver。
1> bus_get():bus的计数加1;
2> kzalloc,分配driver_private内存空间。
3> 初始化此driver的klist_devices链表。
4> kobject_init_and_add():在/sys/bus/usb/drivers/下面创建usbfs文件夹。
5> 如果总线支持drivers_autoprobe,调用driver_attach。(USB 总线支持)
6> driver_create_file: 在/sys/bus/usb/drivers/usbfs下面创建uevent属性文件。
7> driver_add_attrs():将总线的属性也加到/sys/bus/usb/drivers/usbfs
8> add_bind_files():在/sys/bus/usb/drivers/usbfs创建bind和unbind属性文件。
9> kobject_uevent():发送一个KOBJ_ADD的事件。
在/sys/bus/usb/drivers/usbfs下面的文件:
bind module new_id remove_id uevent unbind
usb_devio_init:注册字符设备,主设备189。
usbfs_init:
int __init usbfs_init(void)
{
int retval;
retval = register_filesystem(&usb_fs_type);
if (retval)
return retval;
usb_register_notify(&usbfs_nb);
/* create mount point for usbfs */
usbdir = proc_mkdir("bus/usb", NULL);
return ;
}
函数功能:
1> register_filesystem注册usbfs文件系统,当应用程序执行mount命令的时候,挂载文件系统到相应的目录。
2> usb_register_notify函数注册到内核通知链表,当收到其他子系统通知,调用notifier_call回调函数usbfs_notify:
static int usbfs_notify(struct notifier_block *self, unsigned long action, void *dev)
{
switch (action) {
case USB_DEVICE_ADD:
usbfs_add_device(dev);//在bus号创建的目录下,根据设备号创建设备文件
break;
case USB_DEVICE_REMOVE:
usbfs_remove_device(dev);//删除bus号创建的目录下的设备文件
break;
case USB_BUS_ADD:
usbfs_add_bus(dev);//根据bus号创建目录
break;
case USB_BUS_REMOVE:
usbfs_remove_bus(dev);//删除bus号创建的目录
}
usbfs_update_special();//更新文件系统节点
usbfs_conn_disc_event();
return NOTIFY_OK;
}
static BLOCKING_NOTIFIER_HEAD(usb_notifier_list);usb_notifier_list通知链表初始化
usb_register_notify->blocking_notifier_chain_register(&usb_notifier_list, nb):向usb_notifier_list通知链表注册
blocking_notifier_call_chain(&usb_notifier_list, USB_DEVICE_ADD, udev):通知有usb设备增加
blocking_notifier_call_chain(&usb_notifier_list,USB_DEVICE_REMOVE, udev):通知有usb设备移除
blocking_notifier_call_chain(&usb_notifier_list, USB_BUS_ADD, ubus):通知有usb总线增加
blocking_notifier_call_chain(&usb_notifier_list, USB_BUS_REMOVE, ubus):通知有usb总线移除
3> proc_mkdir在/proc/bus/目录下创建usb目录。
usb_register_device_driver:
在了解usb_generic_driver驱动前,先分析usb总线的match函数:
static int usb_device_match(struct device *dev, struct device_driver *drv)
{
/* devices and interfaces are handled separately */
if (is_usb_device(dev)) {
/* interface drivers never match devices */
if (!is_usb_device_driver(drv))
return ;
/* TODO: Add real matching code */
return ;
} else if (is_usb_interface(dev)) {
struct usb_interface *intf;
struct usb_driver *usb_drv;
const struct usb_device_id *id;
/* device drivers never match interfaces */
if (is_usb_device_driver(drv))
return ;
intf = to_usb_interface(dev);
usb_drv = to_usb_driver(drv);
id = usb_match_id(intf, usb_drv->id_table);
if (id)
return ;
id = usb_match_dynamic_id(intf, usb_drv);
if (id)
return ;
}
return ;
}
函数中我们分成两类判断:
is_usb_device(),根据设备类型dev->type == &usb_device_type 来判断是否是usb设备,然后在通过for_devices(usb_register_device_driver函数注册的时候设置为1) 判断驱动是否是usb设备设备驱动,如果成功,则设备和设备驱动匹配,调用相应的驱动的probe函数(因为usb总线没有probe成员函数)。
is_usb_interface(),根据设备类型dev->type == &usb_if_device_type 来判断是否是接口,然后在通过for_devices(usb_register函数注册的时候设置为0) 判断驱动是否是接口驱动,如果是接口驱动(所以调用usb_register都是注册的接口驱动,因为一个设备可以有多个接口,每个接口必须独立驱动),接着usb_match_id这个函数就是用来判断这个接口是否在id table中得到了match,一旦得到,就进入了具体接口驱动的probe函数了。。
到这里我们不禁要思索驱动找到了注册的地方,那设备来自哪里?这里也有两个函数要分析:
usb_alloc_dev():dev->dev.type = &usb_device_type,这里就表示了是usb设备,这个函数主要有两个地方调用。一个就是usb_init->usb_hub_init->hub_thread->hub_events->hub_port_connect_change,这个会在下面进行详细的分析;另外一个musb_probe->musb_init_controller->usb_add_hcd,DM8168芯片注册主控器的时候用到(或者其他芯片主控器注册)。
usb_set_configuration(): intf->dev.type = &usb_if_device_type,这里就表示了是接口。
这里我们知道usb_register和 usb_register_device_driver,一个是设备驱动的注册,一个是接口驱动的注册,match的时候通过for_devices来区分。接口指的就是一种具体的功能。
上面我们提过每种类型的总线都有一套自己的驱动函数,看来在usb的世界里更特殊一些,usb总线下的设备驱动有一套,接口驱动也有一套:usb_probe_interface。
不管是设备还是接口都是挂在总线上的,一个总线只有一个match函数,usb_device_match。
在这个usb的match函数里,首先是对usb设备的match,设备的match很简单的,只要是个usb设备就认为match了,因为现在进来的usb设备统统都认为是usb_generic_driver的,都和他match。上面我们提到过这个,所有的usb设备首先都会经过筛选这一关,处理之后,才有重生的机会。接口就不一样了,如果进来的dev不是设备,就认为是个接口,然后判断drv是否为接口驱动,如果是,那么就继续判断,这个判断机制就是usb特有的了:Id。每个接口驱动注册的时候都会有一个id 的,加到了id table表中。
看了上面分析,usb match函数中涉及到的设备和接口驱动两条判断路线,在usb的世界里,真正的驱动是针对接口的,针对设备的其实是刚开始没有配置之前,一个通用的usb设备驱动,用来处理所有的usb设备,将其进入配置态,获取该配置下的各种接口,并将接口作为一种特殊的usb设备(接口设备)添加到设备模型中。
下面我们分析usb_generic_driver:
struct usb_device_driver usb_generic_driver = {
.name = "usb",
.probe = generic_probe,
.disconnect = generic_disconnect,
#ifdef CONFIG_PM
.suspend = generic_suspend,
.resume = generic_resume,
#endif
.supports_autosuspend = ,
};
当USB设备(只有设备先被注册之后才会分析接口,才会注册接口) 被探测并被注册到系统后(用device_add),会调用usb_bus_type.mach()(只要是usb设备,都会跟usb_generic_driver匹配上),之后会调用usb_probe_device(),从而引发usb_generic_driver的 probe()调用,也就是generic_probe函数。
下面将会对generic_probe函数进行分析:
static int generic_probe(struct usb_device *udev)
{
int err, c;
if (udev->authorized == )
dev_err(&udev->dev, "Device is not authorized for usage\n");
else {
c = usb_choose_configuration(udev);
if (c >= ) {
err = usb_set_configuration(udev, c);
if (err) {
dev_err(&udev->dev, "can't set config #%d, error %d\n",
c, err);
/* This need not be fatal. The user can try to
* set other configurations. */
}
}
}
usb_notify_add_device(udev);
return ;
}
usb_generic_driver中的generic_probe函数,这个函数是一个usb设备的第一个匹配的driver。Generic通用,只要是个usb设备就得先跟他来一段,usb设备驱动界的老大。他的probe干啥了呢?很简单!找个合适的配置,配置一下。从此usb设备就进入配置的时代了。(前期的工作谁做的呢,到这都已经设置完地址了,当然是hub了,hub发现设备后,会进行前期的枚举过程,获得配置,最终调用device_add将该usb设备添加到总线上。这个过程可以专门来一大段,是hub的主要工作,所以需要把hub单独作为一个家族来对待,人家可是走在第一线的默默无闻的工作者,默默的将设备枚举完成后,将这个设备添加到usb总线上,多伟大)。
注意:设备setconfig时参数只能为0或者合理的配置值,0就代表不配置,仍然是寻址态。不过有些设备就是拿配置0作为配置值得。
usb_choose_configuration从设备可能的众多配置(udev->descriptor.bNumConfigurations)选择一个合适的配置(struct usb_host_config),并返回该配置的索引值。
//为usb device选择一个合适的配置
int usb_choose_configuration(struct usb_device *udev)
{
int i;
int num_configs;
int insufficient_power = ;
struct usb_host_config *c, *best;
best = NULL;
//udev->config,其实是一个数组,存放设备的配置.usb_dev->config[m]-> interface[n]表示第m个配置的第n个接口的intercace结构.(m,n不是配置序号和接口序号).
c = udev->config;
//config项数
num_configs = udev->descriptor.bNumConfigurations;
//遍历所有配置项
for (i = ; i < num_configs; (i++, c++)) {
struct usb_interface_descriptor *desc = NULL;
//配置项的接口数目
//取配置项的第一个接口
if (c->desc.bNumInterfaces > )
desc = &c->intf_cache[]->altsetting->desc;
... ...
//电源不足.配置描述符中的电力是所需电力的1/2
if (c->desc.bMaxPower * > udev->bus_mA) {
insufficient_power++;
continue;
}
//非标准Ethernet-over-USB协议
if (i == && num_configs > && desc &&
(is_rndis(desc) || is_activesync(desc))){
... ...
}
//选择一个不是USB_CLASS_VENDOR_SPEC的配置
else if (udev->descriptor.bDeviceClass !=
USB_CLASS_VENDOR_SPEC &&
(!desc || desc->bInterfaceClass !=
USB_CLASS_VENDOR_SPEC)) {
best = c;
break;
}
/*如果所有剩下的配置是特殊的vendor,选择第一个*/
else if (!best)
best = c;
}
... ...
//如果选择好了配置,返回配置的序号,否则,返回-1
if (best) {
i = best->desc.bConfigurationValue;
dev_info(&udev->dev,
"configuration #%d chosen from %d choice%s\n",
i, num_configs, plural(num_configs));
} else {
i = -;
dev_warn(&udev->dev,
"no configuration chosen from %d choice%s\n",
num_configs, plural(num_configs));
}
return i;
}
例如:我机器上的的 usb 驱动加载时,输出:usb 1-1: configuration #1 chosen from 3 choices
表示:此设备有3个配置,而驱动最终选择了索引号为1的配置,至于选择策略是怎样的,请看usb_choose_configuration()函数。
generic_probe函数中的usb_set_configuration函数里有很重要的动作,不是简单的设置个配置,当我们选择了某一个配置后,需要将这个配置的所有接口取出来,初始化接口作为驱动对应的一种”设备”的参数,如总线类型、设备类型等,调用device_add将该接口设备添加到设备模型中。
int usb_set_configuration(struct usb_device *dev, int configuration)
{
... ...
if (cp && configuration == )
dev_warn(&dev->dev, "config 0 descriptor??\n");
/*首先,根据选择好的配置号找到相应的配置,在这里要注意了, dev->config[]数组中的配置并不是按照配置的序号来存放的,而是按照遍历到顺序来排序的.因为有些设备在发送配置描述符的时候,并不是按照配置序号来发送的,例如,配置2可能在第一次GET_CONFIGURATION就被发送了,而配置1可能是在第二次GET_CONFIGURATION才能发送.
取得配置描述信息之后,要对它进行有效性判断,注意一下本段代码的最后几行代码:usb2.0 spec上规定,0号配置是无效配置,但是可能有些厂商的设备并末按照这一约定,所以在linux中,遇到这种情况只是打印出警告信息,然后尝试使用这一配置.*/
n = nintf = ;
if (cp) {
//接口总数
nintf = cp->desc.bNumInterfaces;
//在这里, 注要是为new_interfaces分配空间,要这意的是, new_interfaces是一个二级指针,它的最终指向是struct usb_interface结构.特别的,如果总电流数要小于配置所需电流,则打印出警告消息.实际上,这种情况在usb_choose_configuration()中已经进行了过滤.
new_interfaces = kmalloc(nintf * sizeof(*new_interfaces),
GFP_KERNEL);
... ...
for (; n < nintf; ++n) {
new_interfaces[n] = kzalloc(
sizeof(struct usb_interface),
GFP_KERNEL);
... ...
}
//如果总电源小于所需电流,打印警告信息
i = dev->bus_mA - cp->desc.bMaxPower * ;
... ...
}
//要对设备进行配置了,先唤醒它
ret = usb_autoresume_device(dev);
if (ret)
goto free_interfaces;
//不是处于ADDRESS状态,先清除设备的状态
if (dev->state != USB_STATE_ADDRESS)
usb_disable_device(dev, ); /* Skip ep0 */
//确定我们有足够带宽提供这个配置
ret = usb_hcd_alloc_bandwidth(dev, cp, NULL, NULL);
... ...
//发送控制消息,选取配置
ret = usb_control_msg(dev, usb_sndctrlpipe(dev, ),
USB_REQ_SET_CONFIGURATION, , configuration, ,
NULL, , USB_CTRL_SET_TIMEOUT);
... ...
}
//dev->actconfig存放的是当前设备选取的配置
dev->actconfig = cp;
... ...
//将状态设为CONFIGURED
usb_set_device_state(dev, USB_STATE_CONFIGURED);
/*接下来,就要对设备进行配置了,首先,将设备唤醒.只有在ADDRESS状态才能转入到CONFIG状态.(SUSPEND状态除外). 所以,如果设备当前不是处于ADDRESS状态,就需要将设备的状态初始化
接着,发送SET_CONFIGURATION的Control消息给设备,用来选择配置最后,将dev->actconfig指向选定的配置,将设备状态设为CONFIG*/
//遍历所有的接口
for (i = ; i < nintf; ++i) {
struct usb_interface_cache *intfc;
struct usb_interface *intf;
struct usb_host_interface *alt;
/*之前初始化的new_interfaces在这里终于要派上用场了.初始化各接口,从上面的初始化过程中,我们可以看出:
Intf->altsetting,表示接口的各种设置
Intf->num_altsetting:表示接口的设置数目
Intf->intf_assoc:接口的关联接口(定义于minor usb 2.0 spec)
Intf->cur_altsetting:接口的当前设置.*/
cp->interface[i] = intf = new_interfaces[i];
intfc = cp->intf_cache[i];
intf->altsetting = intfc->altsetting;
intf->num_altsetting = intfc->num_altsetting;
//是否关联的接口描述符,定义在minor usb 2.0 spec中
intf->intf_assoc = find_iad(dev, cp, i);
kref_get(&intfc->ref);
//选择0号设置
alt = usb_altnum_to_altsetting(intf, );
//如果0号设置不存在,选排在第一个设置
if (!alt)
alt = &intf->altsetting[];
//当前的配置
intf->cur_altsetting = alt;
//用来启用接口,也就是启用接口中的每一个endpoint.
usb_enable_interface(dev, intf);
//注意这个地方对intf内嵌的struct devcie结构赋值,它的type被赋值为了usb_if_device_type.bus还是usb_bus_type.可能你已经反应过来了,要和这个device匹配的设备是interface的驱动.
intf->dev.parent = &dev->dev;
intf->dev.driver = NULL;
intf->dev.bus = &usb_bus_type;
intf->dev.type = &usb_if_device_type;
intf->dev.dma_mask = dev->dev.dma_mask;
device_initialize(&intf->dev);//device 初始化
mark_quiesced(intf);
/*
device的命名:
dev指的是这个接口所属的usb_dev,结合我们之前在UHCI中关于usb设备命名方式的描述.可得出它的命令方式如下:
USB总线号-设备路径:配置号.接口号.
例如,在我的虚拟机上:/sys/bus/usb/devices
1-0:1.0 usb1
可以得知,系统只有一个usb control.
1-0:1.0:表示,第一个usb control下的root hub的1号配置的0号接口.
*/
sprintf(&intf->dev.bus_id[], "%d-%s:%d.%d",
dev->bus->busnum, dev->devpath,
configuration, alt->desc.bInterfaceNumber);
}
kfree(new_interfaces);
if (cp->string == NULL)
cp->string = usb_cache_string(dev, cp->desc.iConfiguration);
//注册每一个接口?
for (i = ; i < nintf; ++i) {
struct usb_interface *intf = cp->interface[i];
dev_dbg(&dev->dev,
"adding %s (config #%d, interface %d)\n",
intf->dev.bus_id, configuration,
intf->cur_altsetting->desc.bInterfaceNumber);
ret = device_add(&intf->dev);//增加device
if (ret != ) {
dev_err(&dev->dev, "device_add(%s) --> %d\n",
intf->dev.bus_id, ret);
continue;
}
usb_create_sysfs_intf_files(intf);
}
//使设备suspend
usb_autosuspend_device(dev);
return ;
}
最后,注册intf内嵌的device结构.设备配置完成了,为了省电,可以将设备置为SUSPEND状态.
到此为止usb_generic_driver凭借自己的博爱的胸襟将所有设备的各个接口添加到了linux的设备模型中。
usb设备首先以设备的身份与usb_generic_driver匹配,成功之后,会分裂出接口,当对接口调用device_add()后,会引起接口和接口驱动的匹配,这个匹配还是用usb_bus_type.mach()函数。因为接口的device->bus=& usb_bus_type, 这跟usb设备是一样的,所以,都会调用到usb_bus_type.mach(),但设备和接口的处理流程是不一样的(前面已经分析过)。
usb_hub_init:
int usb_hub_init(void)
{
if (usb_register(&hub_driver) < ) {
printk(KERN_ERR "%s: can't register hub driver\n",
usbcore_name);
return -;
}
khubd_task = kthread_run(hub_thread, NULL, "khubd");
if (!IS_ERR(khubd_task))
return ;
/* Fall through if kernel_thread failed */
usb_deregister(&hub_driver);
printk(KERN_ERR "%s: can't start khubd\n", usbcore_name);
return -;
}
这个函数主要有两个功能:
在系统初始化的时候在usb_init函数中调用usb_hub_init函数,就进入了hub的初始化。
对于usb_register()可以看作是usb设备中的接口驱动,而usb_register_device_driver()是一个单纯的USB设备驱动。
在usb_hub_init函数中完成了注册hub驱动,并且利用函数kthread_run创建一个内核线程。该线程用来管理监视hub的状态,所有的情况都通过该线程来报告。
当加载主控器的时候,在自身的platform驱动的probe函数里,调用usb_add_hcd->register_root_hub向usb总线注册root hub设备, usb总线match成功后,由usb_generic_driver驱动的probe函数,配置interface设备,然后向usb总线注册interface, usb总线再一次match, 不过这次是匹配了interface,通过ID值和hub驱动配置,因此调用hub驱动的probe函数(hub_probe),hub_probe函数中调用hub_configure函数来配置hub,在这个函数中主要是利用函数usb_alloc_urb函数来分配一个urb,利用usb_fill_int_urb来初始化这个urb结构,包括hub的中断服务程序hub_irq的,查询的周期等。
每当有设备连接到USB接口时,USB总线在查询hub状态信息的时候会触发hub的中断服务程序hub_irq,在该函数中利用kick_khubd将hub结构通过event_list添加到khubd的队列hub_event_list,然后唤醒khubd。进入hub_events函数,该函数用来处理khubd事件队列,从khubd的hub_event_list中的每个usb_hub数据结构。该函数中首先判断hub是否出错,然后通过一个for循环来检测每个端口的状态信息。利用usb_port_status获取端口信息,如果发生变化就调用hub_port_connect_change函数来配置端口等。
static void hub_events(void)
{
... ...
while () {
//如果hub_event_list为空,退出
spin_lock_irq(&hub_event_lock);
if (list_empty(&hub_event_list)) {
spin_unlock_irq(&hub_event_lock);
break;
}
//取hub_event_list中的后一个元素,并将其断链
tmp = hub_event_list.next;
list_del_init(tmp);
//根据tmp获取hub
hub = list_entry(tmp, struct usb_hub, event_list);
//增加hub计数
kref_get(&hub->kref);
//解锁
spin_unlock_irq(&hub_event_lock);
hdev = hub->hdev;
hub_dev = hub->intfdev;
intf = to_usb_interface(hub_dev);
... ...
usb_lock_device(hdev);
//如果hub断开了,继续hub_event_list中的下一个
if (unlikely(hub->disconnected))
goto loop;
//设备没有连接上
if (hdev->state == USB_STATE_NOTATTACHED) {
hub->error = -ENODEV;
//将下面的子设备全部disable
hub_pre_reset(intf);
goto loop;
}
/* 自动恢复 */
ret = usb_autopm_get_interface(intf);
if (ret) {
dev_dbg(hub_dev, "Can't autoresume: %d\n", ret);
goto loop;
}
//hub 暂停
if (hub->quiescing)
goto loop_autopm;
//hub 有错误发生?
if (hub->error) {
dev_dbg (hub_dev, "resetting for error %d\n",
hub->error);
ret = usb_reset_composite_device(hdev, intf);
if (ret) {
dev_dbg (hub_dev,
"error resetting hub: %d\n", ret);
goto loop_autopm;
}
hub->nerrors = ;
hub->error = ;
}
/*首先,从hub_event_list摘下第一个元素,根据我们之前在接口驱动probe过程的kick_khubd()函数分析中,有将hub-> event_list添加到hub_event_list.因此,就可以顺藤摸瓜找到hub,再根据hub结构,找到接口结构和所属的usb 设备结构.
然后,进行第一个重要的判断.如果hub被断开了,则,断开hub下面所连接的所有端口,这是在hub_pre_reset()中完成的.
最后,进行第二个重要的判断,如果hub发生了错误,则reset它下面的所有端口,这是在usb_reset_composite_device()中完成的.*/
//在这里,它遍历hub上的每一个端口,如果端口的连接会生了改变(connect_change等于1)的情况,就会调用hub_port_connect_change()
for (i = ; i <= hub->descriptor->bNbrPorts; i++) {
//检测端口是否忙
if (test_bit(i, hub->busy_bits))
continue;
//change_bits会在hub 第一次初始化时被赋值。而event_bits则在hub_irq中改变
connect_change = test_bit(i, hub->change_bits);
//如果都没有改变,继续测试下一个端口。
if (!test_and_clear_bit(i, hub->event_bits) &&
!connect_change && !hub->activating)
continue;
//Get_Port_Status:取得端口状态.
//会取得port的改变值和状态值
ret = hub_port_status(hub, i,
&portstatus, &portchange);
if (ret < )
continue;
//在struct usb_dev中,有一个struct usb_device *children[USB_MAXCHILDREN]的成员,它是表示对应端口序号上所连接的usb设备.
//如果对应端口没有在设备树上,且端口显示已经连接上
//将connect_change置为1
if (hub->activating && !hdev->children[i-] &&
(portstatus &
USB_PORT_STAT_CONNECTION))
connect_change = ;
//端口的连接状态发生了改变.需要发送Clear_Feature
if (portchange & USB_PORT_STAT_C_CONNECTION) {
clear_port_feature(hdev, i,
USB_PORT_FEAT_C_CONNECTION);
connect_change = ;
}
//端口的状态从enable 变为了disable
if (portchange & USB_PORT_STAT_C_ENABLE) {
if (!connect_change)
dev_dbg (hub_dev,
"port %d enable change, "
"status %08x\n",
i, portstatus);
clear_port_feature(hdev, i,
USB_PORT_FEAT_C_ENABLE);
//端口已经被停止了,且端口已经被连在设备树中.
//需要重启一下此端口
if (!(portstatus & USB_PORT_STAT_ENABLE)
&& !connect_change
&& hdev->children[i-]) {
dev_err (hub_dev,
"port %i "
"disabled by hub (EMI?), "
"re-enabling...\n",
i);
connect_change = ;
}
}
//Resume完成
if (portchange & USB_PORT_STAT_C_SUSPEND) {
clear_port_feature(hdev, i,
USB_PORT_FEAT_C_SUSPEND);
//如果端口连接了设备,就将设备唤醒
if (hdev->children[i-]) {
ret = remote_wakeup(hdev->
children[i-]);
if (ret < )
connect_change = ;
}
//如果端口没有连接设备,就将端口禁用
else {
ret = -ENODEV;
hub_port_disable(hub, i, );
}
dev_dbg (hub_dev,
"resume on port %d, status %d\n",
i, ret);
}
//有过流保护,需要对hub power on
if (portchange & USB_PORT_STAT_C_OVERCURRENT) {
dev_err (hub_dev,
"over-current change on port %d\n",
i);
clear_port_feature(hdev, i,
USB_PORT_FEAT_C_OVER_CURRENT);
hub_power_on(hub);
}
//Reset状态已经完成了
if (portchange & USB_PORT_STAT_C_RESET) {
dev_dbg (hub_dev,
"reset change on port %d\n",
i);
clear_port_feature(hdev, i,
USB_PORT_FEAT_C_RESET);
}
/*什么情况下, hub_port_connect_change才会被设为1.
1:端口在hub->change_bits中被置位.搜索整个代码树,发生在设置hub->change_bits的地方,只有在hub_port_logical_disconnect()中手动将端口禁用,会将对应位置1.
2:hub上没有这个设备树上没有这个端口上的设备.但显示端口已经连上了设备
3:hub这个端口上的连接发生了改变,从端口有设备连接变为无设备连接,或者从无设备连接变为有设备连接.
4:hub的端口变为了disable,此时这个端口上连接了设备,但被显示该端口已经变禁用,需要将connect_change设为1.
5:端口状态从SUSPEND变成了RESUME,远程唤醒端口上的设备失败,就需要将connect_change设为1.
另外hub_port_connect_change()函数我们放在后面再来讨论*/
if (connect_change)
hub_port_connect_change(hub, i,
portstatus, portchange);
}
//对HUB的处理
//如果hub状态末变化,不需要做任何处理
if (test_and_clear_bit(, hub->event_bits) == )
; /* do nothing */
//Get_hub_status 失败?
else if (hub_hub_status(hub, &hubstatus, &hubchange) < )
dev_err (hub_dev, "get_hub_status failed\n");
else {
//这里是对应hub 状态发生了改变,且Get_hub_status正常返回的情况
//如果hub的本地电源供电发生了改变
if (hubchange & HUB_CHANGE_LOCAL_POWER) {
dev_dbg (hub_dev, "power change\n");
clear_hub_feature(hdev, C_HUB_LOCAL_POWER);
//如果是本地电源供电
if (hubstatus & HUB_STATUS_LOCAL_POWER)
/* FIXME: Is this always true? */
hub->limited_power = ;
//如果本电源不供电
else
hub->limited_power = ;
}
//如果hub 发生过电源保护,需要对hub power on
if (hubchange & HUB_CHANGE_OVERCURRENT) {
dev_dbg (hub_dev, "overcurrent change\n");
msleep(); /* Cool down */
clear_hub_feature(hdev, C_HUB_OVER_CURRENT);
hub_power_on(hub);
}
}
hub->activating = ;
/* If this is a root hub, tell the HCD it's okay to
* re-enable port-change interrupts now. */
if (!hdev->parent && !hub->busy_bits[])
usb_enable_root_hub_irq(hdev->bus);
loop_autopm:
/* Allow autosuspend if we're not going to run again */
if (list_empty(&hub->event_list))
usb_autopm_enable(intf);
loop:
usb_unlock_device(hdev);
kref_put(&hub->kref, hub_release);
} /* end while (1) */
}
hub_port_connect_change()函数分析:
static void hub_port_connect_change(struct usb_hub *hub, int port1,
u16 portstatus, u16 portchange)
{
... ...
//hub led
if (hub->has_indicators) {
set_port_led(hub, port1, HUB_LED_AUTO);
hub->indicator[port1-] = INDICATOR_AUTO;
}
//忽略掉CONFIG_USB_OTG的处理
#ifdef CONFIG_USB_OTG
/* during HNP, don't repeat the debounce */
if (hdev->bus->is_b_host)
portchange &= ~(USB_PORT_STAT_C_CONNECTION |
USB_PORT_STAT_C_ENABLE);
#endif
//尝试唤醒一个存在的设备
udev = hdev->children[port1-];
if ((portstatus & USB_PORT_STAT_CONNECTION) && udev &&
udev->state != USB_STATE_NOTATTACHED) {
usb_lock_device(udev);
if (portstatus & USB_PORT_STAT_ENABLE) {
status = ; /* Nothing to do */
#ifdef CONFIG_USB_SUSPEND
} else if (udev->state == USB_STATE_SUSPENDED &&
udev->persist_enabled) {
/* For a suspended device, treat this as a
* remote wakeup event.
*/
status = usb_remote_wakeup(udev);
#endif
} else {
status = -ENODEV; /* Don't resuscitate */
}
usb_unlock_device(udev);
if (status == ) {
clear_bit(port1, hub->change_bits);
return;
}
}
//如果对应端口已经有设备连接,先将其断开
if (udev)
usb_disconnect(&hdev->children[port1-]);
//接下来,将hub->change_bits的对应位清掉,该位是在函数hub_port_logical_disconnect()中被置的,在这里将其清除,免得下次在进入hub_events()的时候,再次检测到这个位发生改变.
clear_bit(port1, hub->change_bits);
//如果发生物理断开或者连接状态改变,我们可能忘记移除设备
if (!(portstatus & USB_PORT_STAT_CONNECTION) ||
(portchange & USB_PORT_STAT_C_CONNECTION))
clear_bit(port1, hub->removed_bits);
//连接发生改变
//连接反弹的处理,实际上就是除抖动
if (portchange & (USB_PORT_STAT_C_CONNECTION |
USB_PORT_STAT_C_ENABLE)) {
//如果该端口的连接发生改变(从有连接到无接接,或者从无连接到有连接),就有一个除抖动的过程,usb2.0 spec上规定,除抖动的时间为100ms.
//在函数里,定义的测试时间是1500ms.如果在这个时间内,端口还末处于稳定状态,就会返回-ETIMEDOUT
//如果已经处于稳定状态了,就会返回稳定状态下的portstatus
status = hub_port_debounce(hub, port1);
if (status < ) {
if (printk_ratelimit())
dev_err(hub_dev, "connect-debounce failed, "
"port %d disabled\n", port1);
portstatus &= ~USB_PORT_STAT_CONNECTION;
} else {
portstatus = status;
}
}
//如果接口上没有连接了,可以直接退出了
if (!(portstatus & USB_PORT_STAT_CONNECTION) ||
test_bit(port1, hub->removed_bits)) {
/*经过去抖后,端口稳定的处于断开连接状态.说明端口已经没有设备了.然后,再判断hub是否有电源开关((wHubCharacteristics & HUB_CHAR_LPSM) < 2),portstatus 的 USB_PORT_FEAT_POWER位是否被设置,如果没有被设置,则说明该端口断电了.
如果hub有电源开关,且端口没有上电,则需要发送POWER的Set_Feature来为之上电*/
if ((wHubCharacteristics & HUB_CHAR_LPSM) <
&& !(portstatus & USB_PORT_STAT_POWER))
set_port_feature(hdev, port1, USB_PORT_FEAT_POWER);
//如果端口依然处理enable状态,就会跳转到标号done处,就端口disalbe.
if (portstatus & USB_PORT_STAT_ENABLE)
goto done;
return;
}
/*如果端口隐定处于连接状态,那就需要连接端口下的设备了.首先看到的是一个for循环,是用来配置设备的两种方式.我们知道,在配置设备的时候,首先要去取设备的描述符,这个过程是在ep0上完成的.而这个ep0支持的最大传输出数据又是在设备描述符的bMaxPacketSize0中所 定义的.因此就对应有两种处理方式:
第一种是传输8个字节,取得描述符的前面一部份,从而就可以取得bMaxPacketSize0.此后再reset设备,再根据这个bMaxPacketSize0的长度去取它的设备描述符.
第二种是一次传输64字节,取得设备描述符的bMaxPacketSize0字段*/
for (i = ; i < SET_CONFIG_TRIES; i++) {
//为探测到的usb设备(包括普通hub,u盘等)分配并初始化udev
// 在为root hub分配struct usb_dev的时候,它的第一个参数,也就是它的父结点是为NULL.
/*我们来观察一下它在sysfs中的命名方式
在没有插入U盘之前:/sys/bus/usb/devices
1-0:1.0 usb1
插入U盘之后:
1-0:1.0 1-1 1-1:1.0 usb1
增加的两个目是:
1-1和1-1:1.0
1-1:1.0 :只有这样的目录,表示该U盘只有一个接口,当前选取的是第0号设置项.*/
udev = usb_alloc_dev(hdev, hdev->bus, port1);
if (!udev) {
dev_err (hub_dev,
"couldn't allocate port %d usb_device\n",
port1);
goto done;
}
//置为USB_STATE_POWERED状态
usb_set_device_state(udev, USB_STATE_POWERED);
udev->bus_mA = hub->mA_per_port;
udev->level = hdev->level + ;
udev->wusb = hub_is_wusb(hub);
/*
* USB 3.0 devices are reset automatically before the connect
* port status change appears, and the root hub port status
* shows the correct speed. We also get port change
* notifications for USB 3.0 devices from the USB 3.0 portion of
* an external USB 3.0 hub, but this isn't handled correctly yet
* FIXME.
*/
if (!(hcd->driver->flags & HCD_USB3))
udev->speed = USB_SPEED_UNKNOWN;
else if ((hdev->parent == NULL) &&
(portstatus & USB_PORT_STAT_SUPER_SPEED))
udev->speed = USB_SPEED_SUPER;
else
udev->speed = USB_SPEED_UNKNOWN;
/*为设备指定一个地址,是到所属的usb bus的bus->devmap中找到没有使用的那一位,先进行两次新的策略(i=0和=1时),如果不行就再进行两次旧的策略(i=2和i=3时).所有这一切只有一个目的,就是为了获得设备的描述符,
设置了udev->tt、udev->ttport和udev->ep 0.desc.wMaxPacketSize,设置udev->status= USB_STATE_ADDRESS。*/
choose_address(udev);
if (udev->devnum <= ) {
status = -ENOTCONN; /* Don't retry */
goto loop;
}
//hub_port_init()对这个usb_dev结构进行一系的初始化,在这个函数中会处理:Get_Description,Set_address.等操作
status = hub_port_init(hub, udev, port1, i);
if (status < )
goto loop;
usb_detect_quirks(udev);
if (udev->quirks & USB_QUIRK_DELAY_INIT)
msleep();
/* consecutive bus-powered hubs aren't reliable; they can
* violate the voltage drop budget. if the new child has
* a "powered" LED, users should notice we didn't enable it
* (without reading syslog), even without per-port LEDs
* on the parent.
*/
if (udev->descriptor.bDeviceClass == USB_CLASS_HUB
&& udev->bus_mA <= ) {
u16 devstat;
status = usb_get_status(udev, USB_RECIP_DEVICE, ,
&devstat);
if (status < ) {
dev_dbg(&udev->dev, "get status %d ?\n", status);
goto loop_disable;
}
le16_to_cpus(&devstat);
if ((devstat & ( << USB_DEVICE_SELF_POWERED)) == ) {
dev_err(&udev->dev,
"can't connect bus-powered hub "
"to this port\n");
if (hub->has_indicators) {
hub->indicator[port1-] =
INDICATOR_AMBER_BLINK;
schedule_delayed_work (&hub->leds, );
}
status = -ENOTCONN; /* Don't retry */
goto loop_disable;
}
}
/* check for devices running slower than they could */
if (le16_to_cpu(udev->descriptor.bcdUSB) >= 0x0200
&& udev->speed == USB_SPEED_FULL
&& highspeed_hubs != )
check_highspeed (hub, udev, port1);
/* Store the parent's children[] pointer. At this point
* udev becomes globally accessible, although presumably
* no one will look at it until hdev is unlocked.
*/
status = ;
// 将分配的struct usb_dev结构跟他的父结构关联起来,也就是说添加到它的父结构的usb_dev-> children[]数组.
spin_lock_irq(&device_state_lock);
if (hdev->state == USB_STATE_NOTATTACHED)
status = -ENOTCONN;
else
hdev->children[port1-] = udev;
spin_unlock_irq(&device_state_lock);
if (!status) {
/*usb_configure_device(): 得到设备的描述符(包括设备描述符、配置描述符、接口描述符等),分析以上描述符信息,提取出配置、接口等,并赋值给udev结构里相应的字段。
device_add() :将usb设备注册到系统里,这个动作将触发驱动的匹配,由于这是个usb设备,所以万能usb驱动usb_generic_driver会匹配上,从而generic_probe会得到执行,从上面可以看出来,这一次hub_events()调用是由于主控制器初始化调用了
hub_probe,从而引发hub_events调用。那root hub初始化完成以后hub_events会如何触发呢?答案是通过中断!而这个中断的服务函数就是hub_irq,也即是说,凡是真正的有端口变化事件发生,hub_irq就会被调用,而hub_irq()最终会调用kick_khubd(), 触发hub的event_list,于是再次调用hub_events().*/
status = usb_new_device(udev);
if (status) {
spin_lock_irq(&device_state_lock);
hdev->children[port1-] = NULL;
spin_unlock_irq(&device_state_lock);
}
}
if (status)
goto loop_disable;
status = hub_power_remaining(hub);
if (status)
dev_dbg(hub_dev, "%dmA power budget left\n", status);
return;
loop_disable:
hub_port_disable(hub, port1, );
loop:
usb_ep0_reinit(udev);
release_address(udev);
hub_free_dev(udev);
usb_put_dev(udev);
if ((status == -ENOTCONN) || (status == -ENOTSUPP))
break;
}
if (hub->hdev->parent ||
!hcd->driver->port_handed_over ||
!(hcd->driver->port_handed_over)(hcd, port1))
dev_err(hub_dev, "unable to enumerate USB device on port %d\n",
port1);
// Done标号是对应上述处理失败的处理,它禁用掉该端口(因为该端口没有连接设备或者是端口上的设备配置失败),如果是root hub,且USB控制器器驱动中又定义了relinquish_port.调用它.
done:
hub_port_disable(hub, port1, );
if (hcd->driver->relinquish_port && !hub->hdev->parent)
hcd->driver->relinquish_port(hcd, port1);
}
参考了很多大神的分析,非常感谢!