linux usb子系统(一):子系统架构,Linux usb子系统(一):子系统架构

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);

}

参考了很多大神的分析,非常感谢!

%5CUsers%5Cuser%5CDocuments%5CTencent%20Files%5C945627420%5CImage%5C%5D6OY%5DBEFZUFQU05ZTP%244T%V.jpg

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值