Linux设备驱动之USB hub驱动

------------------------------------------
本文系本站原创,欢迎转载!
转载请注明出处:http://ericxiao.cublog.cn/
------------------------------------------
一:前言
继UHCI的驱动之后,我们对USB Control的运作有了一定的了解.在接下来的分析中,我们对USB设备的驱动做一个全面的分析,我们先从HUB的驱动说起.关于HUB,usb2.0 spec上有详细的定义,基于这部份的代码位于linux-2.6.25/drivers/usb/core下,也就是说,这部份代码是位于core下,和具体设备是无关的,因为各厂商的hub都是按照spec的要求来设计的.
二:UHCI驱动中的root hub
记得在分析UHCI驱动的时候,曾详细分析过root hub的初始化操作.为了分析方便,将代码片段列出如下:
usb_add_hcd() à usb_alloc_dev():
struct usb_device *usb_alloc_dev(struct usb_device *parent,
               struct usb_bus *bus, unsigned port1)
{
    ……
    ……
    //usb_device,内嵌有struct device结构,对这个结构进行初始化
    device_initialize(&dev->dev);
    dev->dev.bus = &usb_bus_type;
    dev->dev.type = &usb_device_type;
    ……
    ……
}
一看到前面对dev的赋值,根据我们对设备模型的理解,一旦这个device进行注册,就会发生driver和device的匹配过程了.
不过,现在还不是分析这个过程的时候,我们先来看一下,USB子系统中的两种驱动.
 
三:USB子系统中的两种驱动
linux-2.6.25/drivers/usb/core/driver.c中,我们可以找到两种register driver的方式,分别为usb_register_driver()和usb_register_device_driver().分别来分析一下这两个接口.
 
usb_register_device_driver()接口的代码如下:
int usb_register_device_driver(struct usb_device_driver *new_udriver,
        struct module *owner)
{
    int retval = 0;
 
    if (usb_disabled())
        return -ENODEV;
 
    new_udriver->drvwrap.for_devices = 1;
    new_udriver->drvwrap.driver.name = (char *) new_udriver->name;
    new_udriver->drvwrap.driver.bus = &usb_bus_type;
    new_udriver->drvwrap.driver.probe = usb_probe_device;
    new_udriver->drvwrap.driver.remove = usb_unbind_device;
    new_udriver->drvwrap.driver.owner = owner;
 
    retval = driver_register(&new_udriver->drvwrap.driver);
 
    if (!retval) {
        pr_info("%s: registered new device driver %s/n",
            usbcore_name, new_udriver->name);
        usbfs_update_special();
    } else {
        printk(KERN_ERR "%s: error %d registering device "
            "   driver %s/n",
            usbcore_name, retval, new_udriver->name);
    }
 
    return retval;
}
首先,通过usb_disabled()来判断一下usb是否被禁用,如果被禁用,当然就不必执行下面的流程了,直接退出即可.
从上面的代码,很明显可以看到, struct usb_device_driver 对struct device_driver进行了一次封装,我们注意一下这里的赋值操作:new_udriver->drvwrap.for_devices = 1.等等.这些在后面都是用派上用场的.
 
usb_register_driver()的代码如下:
int usb_register_driver(struct usb_driver *new_driver, struct module *owner,
            const char *mod_name)
{
    int retval = 0;
 
    if (usb_disabled())
        return -ENODEV;
 
    new_driver->drvwrap.for_devices = 0;
    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) {
        pr_info("%s: registered new interface driver %s/n",
            usbcore_name, new_driver->name);
        usbfs_update_special();
        usb_create_newid_file(new_driver);
    } else {
        printk(KERN_ERR "%s: error %d registering interface "
            "   driver %s/n",
            usbcore_name, retval, new_driver->name);
    }
 
    return retval;
}
很明显,在这里接口里,将new_driver->drvwrap.for_devices设为了0.而且两个接口的porbe()函数也不一样.
其实,对于usb_register_driver()可以看作是usb设备中的接口驱动,而usb_register_device_driver()是一个单纯的USB设备驱动.
 
四: hub的驱动分析
4.1: usb_bus_type->match()的匹配过程
usb_bus_type->match()用来判断驱动和设备是否匹配,它的代码如下:
static int usb_device_match(struct device *dev, struct device_driver *drv)
{
    /* devices and interfaces are handled separately */
 
    //usb device的情况
    if (is_usb_device(dev)) {
 
        /* interface drivers never match devices */
        if (!is_usb_device_driver(drv))
            return 0;
 
        /* TODO: Add real matching code */
        return 1;
 
    }
    //interface的情况
    else {
        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 0;
 
        intf = to_usb_interface(dev);
        usb_drv = to_usb_driver(drv);
 
        id = usb_match_id(intf, usb_drv->id_table);
        if (id)
            return 1;
 
        id = usb_match_dynamic_id(intf, usb_drv);
        if (id)
            return 1;
    }
 
    return 0;
}
这里的match会区分上面所说的两种驱动,即设备的驱动和接口的驱动.
is_usb_device()的代码如下:
static inline int is_usb_device(const struct device *dev)
{
    return dev->type == &usb_device_type;
}
很明显,对于root hub来说,这个判断是肯定会满足的.
static inline int is_usb_device_driver(struct device_driver *drv)
{
    return container_of(drv, struct usbdrv_wrap, driver)->
            for_devices;
}
回忆一下,我们在分析usb_register_device_driver()的时候,不是将new_udriver->drvwrap.for_devices置为了1么?所以对于usb_register_device_driver()注册的驱动来说,这里也是会满足的.
因此,对应root hub的情况,从第一个if就会匹配到usb_register_device_driver()注册的驱动.
对于接口的驱动,我们等遇到的时候再来进行分析.
 
4.2:root hub的驱动入口
既然我们知道,root hub会匹配到usb_bus_type->match()的驱动,那这个驱动到底是什么呢?我们从usb子系统的初始化开始说起.
在linux-2.6.25/drivers/usb/core/usb.c中.有这样的一段代码:
subsys_initcall(usb_init);
对于subsys_initcall()我们已经不陌生了,在很多地方都会遇到它.在系统初始化的时候,会调用到它对应的函数.在这里,即为usb_init().
在usb_init()中,有这样的代码片段:
static int __init usb_init(void)
{
    ……
    ……
retval = usb_register_device_driver(&usb_generic_driver, THIS_MODULE);
    if (!retval)
        goto out;
    ……
}
在这里终于看到usb_register_device_driver()了. usb_generic_driver会匹配到所有usb 设备.定义如下:
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 = 1,
};
现在是到分析probe()的时候了.我们这里说的并不是usb_generic_driver中的probe,而是封装在struct usb_device_driver中的driver对应的probe函数.
在上面的分析, usb_register_device_driver()将封装的driver的probe()函数设置为了usb_probe_device().代码如下:
static int usb_probe_device(struct device *dev)
{
    struct usb_device_driver *udriver = to_usb_device_driver(dev->driver);
    struct usb_device *udev;
    int error = -ENODEV;
 
    dev_dbg(dev, "%s/n", __FUNCTION__);
 
    //再次判断dev是否是usb device
    if (!is_usb_device(dev))    /* Sanity check */
        return error;
 
    udev = to_usb_device(dev);
 
    /* TODO: Add real matching code */
 
    /* The device should always appear to be in use
     * unless the driver suports autosuspend.
     */
     //pm_usage_cnt: autosuspend计数.如果此计数为1,则不允许autosuspend
    udev->pm_usage_cnt = !(udriver->supports_autosuspend);
 
    error = udriver->probe(udev);
    return error;
}
首先,可以通过container_of()将封装的struct device, struct device_driver转换为struct usb_device和struct usb_device_driver.
然后,再执行一次安全检查,判断dev是否是属于一个usb device.
在这里,我们首次接触到了hub suspend.如果不支持suspend(udriver->supports_autosuspend为0),则udev->pm_usage_cnt被设为1,也就是说,它不允许设备suspend.否则,将其初始化为0.
最后,正如你所看到的,流程转入到了usb_device_driver->probe().
对应到root hub,流程会转入到generic_probe().代码如下:
 
static int generic_probe(struct usb_device *udev)
{
    int err, c;
 
    /* put device-specific files into sysfs */
    usb_create_sysfs_dev_files(udev);
 
    /* Choose and set the configuration.  This registers the interfaces
     * with the driver core and lets interface drivers bind to them.
     */
    if (udev->authorized == 0)
        dev_err(&udev->dev, "Device is not authorized for usage/n");
    else {
        //选择和设定一个配置
        c = usb_choose_configuration(udev);
        if (c >= 0) {
            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 device state == configured ... usable */
    usb_notify_add_device(udev);
 
    return 0;
}
usb_create_sysfs_dev_files()是在sysfs中显示几个属性文件,不进行详细分析,有兴趣的可以结合之前分析的<<linux设备模型详解>>来看下代码.
usb_notify_add_device()是有关notify链表的操作,这里也不做详细分析.
至于udev->authorized,在root hub的初始化中,是会将其初始化为1的.后面的逻辑就更简单了.为root hub 选择一个配置然后再设定这个配置.
还记得我们在分析root hub的时候,在usb_new_device()中,会将设备的所有配置都取出来,然后将它们放到了usb_device-> config.现在这些信息终于会派上用场了.不太熟悉的,可以看下本站之前有关usb控制器驱动的文档.
 
Usb2.0 spec上规定,对于hub设备,只能有一个config,一个interface,一个endpoint.实际上,在这里,对hub的选择约束不大,反正就一个配置,不管怎么样,选择和设定都是这个配置.
不过,为了方便以后的分析,我们还是跟进去看下usb_choose_configuration()和usb_set_configuration()的实现.
实际上,经过这两个函数之后,设备的probe()过程也就会结束了.
 
4.2.1:usb_choose_configuration()函数分析
usb_choose_configuration()的代码如下:
//为usb device选择一个合适的配置
int usb_choose_configuration(struct usb_device *udev)
{
    int i;
    int num_configs;
    int insufficient_power = 0;
    struct usb_host_config *c, *best;
 
    best = NULL;
    //config数组
    c = udev->config;
    //config项数
    num_configs = udev->descriptor.bNumConfigurations;
    //遍历所有配置项
    for (i = 0; i < num_configs; (i++, c++)) {
        struct usb_interface_descriptor *desc = NULL;
 
        /* It's possible that a config has no interfaces! */
        //配置项的接口数目
        //取配置项的第一个接口
        if (c->desc.bNumInterfaces > 0)
            desc = &c->intf_cache[0]->altsetting->desc;
 
        /*
         * HP's USB bus-powered keyboard has only one configuration
         * and it claims to be self-powered; other devices may have
         * similar errors in their descriptors.  If the next test
         * were allowed to execute, such configurations would always
         * be rejected and the devices would not work as expected.
         * In the meantime, we run the risk of selecting a config
         * that requires external power at a time when that power
         * isn't available.  It seems to be the lesser of two evils.
         *
         * Bugzilla #6448 reports a device that appears to crash
         * when it receives a GET_DEVICE_STATUS request!  We don't
         * have any other way to tell whether a device is self-powered,
         * but since we don't use that information anywhere but here,
         * the call has been removed.
         *
         * Maybe the GET_DEVICE_STATUS call and the test below can
         * be reinstated when device firmwares become more reliable.
         * Don't hold your breath.
         */
#if 0
        /* Rule out self-powered configs for a bus-powered device */
        if (bus_powered && (c->desc.bmAttributes &
                    USB_CONFIG_ATT_SELFPOWER))
            continue;
#endif
 
        /*
         * The next test may not be as effective as it should be.
         * Some hubs have errors in their descriptor, claiming
         * to be self-powered when they are really bus-powered.
         * We will overestimate the amount of current such hubs
         * make available for each port.
         *
         * This is a fairly benign sort of failure.  It won't
         * cause us to reject configurations that we should have
         * accepted.
         */
 
        /* Rule out configs that draw too much bus current */
        //电源不足.配置描述符中的电力是所需电力的1/2
        if (c->desc.bMaxPower * 2 > udev->bus_mA) {
            insufficient_power++;
            continue;
        }
 
        /* When the first config's first interface is one of Microsoft's
         * pet nonstandard Ethernet-over-USB protocols, ignore it unless
         * this kernel has enabled the necessary host side driver.
         */
        if (i == 0 && desc && (is_rndis(desc) || is_activesync(desc))) {
#if !defined(CONFIG_USB_NET_RNDIS_HOST) && !defined(CONFIG_USB_NET_RNDIS_HOST_MODULE)
            continue;
#else
            best = c;
#endif
        }
 
        /* From the remaining configs, choose the first one whose
         * first interface is for a non-vendor-specific class.
         * Reason: Linux is more likely to have a class driver
         * than a vendor-specific driver. */
         //选择一个不是USB_CLASS_VENDOR_SPEC的配置
        else if (udev->descriptor.bDeviceClass !=
                        USB_CLASS_VENDOR_SPEC &&
                (!desc || desc->bInterfaceClass !=
                        USB_CLASS_VENDOR_SPEC)) {
            best = c;
            break;
        }
 
        /* If all the remaining configs are vendor-specific,
         * choose the first one. */
        else if (!best)
            best = c;
    }
 
    if (insufficient_power > 0)
        dev_info(&udev->dev, "rejected %d configuration%s "
            "due to insufficient available bus power/n",
            insufficient_power, plural(insufficient_power));
    //如果选择好了配置,返回配置的序号,否则,返回-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 = -1;
        dev_warn(&udev->dev,
            "no configuration chosen from %d choice%s/n",
            num_configs, plural(num_configs));
    }
    return i;
}
Linux按照自己的喜好选择好了配置之后,返回配置的序号.不过对于HUB来说,它有且仅有一个配置.
 
4.2.2:usb_set_configuration()函数分析
既然已经选好配置了,那就告诉设备选好的配置,这个过程是在usb_set_configuration()中完成的.它的代码如下:
int usb_set_configuration(struct usb_device *dev, int configuration)
{
    int i, ret;
    struct usb_host_config *cp = NULL;
    struct usb_interface **new_interfaces = NULL;
    int n, nintf;
 
    if (dev->authorized == 0 || configuration == -1)
        configuration = 0;
    else {
        for (i = 0; i < dev->descriptor.bNumConfigurations; i++) {
            if (dev->config[i].desc.bConfigurationValue ==
                    configuration) {
                cp = &dev->config[i];
                break;
            }
        }
    }
 
    if ((!cp && configuration != 0))
        return -EINVAL;
 
    /* The USB spec says configuration 0 means unconfigured.
     * But if a device includes a configuration numbered 0,
     * we will accept it as a correctly configured state.
     * Use -1 if you really want to unconfigure the device.
     */
    if (cp && configuration == 0)
        dev_warn(&dev->dev, "config 0 descriptor??/n");
首先,根据选择好的配置号找到相应的配置,在这里要注意了, dev->config[]数组中的配置并不是按照配置的序号来存放的,而是按照遍历到顺序来排序的.因为有些设备在发送配置描述符的时候,并不是按照配置序号来发送的,例如,配置2可能在第一次GET_CONFIGURATION就被发送了,而配置1可能是在第二次GET_CONFIGURATION才能发送.
取得配置描述信息之后,要对它进行有效性判断,注意一下本段代码的最后几行代码:usb2.0 spec上规定,0号配置是无效配置,但是可能有些厂商的设备并末按照这一约定,所以在linux中,遇到这种情况只是打印出警告信息,然后尝试使用这一配置.
    /* Allocate memory for new interfaces before doing anything else,
     * so that if we run out then nothing will have changed. */
 
    n = nintf = 0;
    if (cp) {
        //接口总数
        nintf = cp->desc.bNumInterfaces;
        //interface指针数组,
        new_interfaces = kmalloc(nintf * sizeof(*new_interfaces),
                GFP_KERNEL);
        if (!new_interfaces) {
            dev_err(&dev->dev, "Out of memory/n");
            return -ENOMEM;
        }
 
        for (; n < nintf; ++n) {
            new_interfaces[n] = kzalloc(
                    sizeof(struct usb_interface),
                    GFP_KERNEL);
            if (!new_interfaces[n]) {
                dev_err(&dev->dev, "Out of memory/n");
                ret = -ENOMEM;
free_interfaces:
                while (--n >= 0)
                    kfree(new_interfaces[n]);
                kfree(new_interfaces);
                return ret;
            }
        }
 
        //如果总电源小于所需电流,打印警告信息
        i = dev->bus_mA - cp->desc.bMaxPower * 2;
        if (i < 0)
            dev_warn(&dev->dev, "new config #%d exceeds power "
                    "limit by %dmA/n",
                    configuration, -i);
    }
在这里,注要是为new_interfaces分配空间,要这意的是, new_interfaces是一个二级指针,它的最终指向是struct usb_interface结构.特别的,如果总电流数要小于配置所需电流,则打印出警告消息.实际上,这种情况在usb_choose_configuration()中已经进行了过滤.
 
    /* Wake up the device so we can send it the Set-Config request */
    //要对设备进行配置了,先唤醒它
    ret = usb_autoresume_device(dev);
    if (ret)
        goto free_interfaces;
 
    /* if it's already configured, clear out old state first.
     * getting rid of old interfaces means unbinding their drivers.
     */
     //不是处于ADDRESS状态,先清除设备的状态
    if (dev->state != USB_STATE_ADDRESS)
        usb_disable_device(dev, 1); /* Skip ep0 */
 
    //发送控制消息,选取配置
    ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
                  USB_REQ_SET_CONFIGURATION, 0, configuration, 0,
                  NULL, 0, USB_CTRL_SET_TIMEOUT);
    if (ret < 0) {
        /* All the old state is gone, so what else can we do?
         * The device is probably useless now anyway.
         */
        cp = NULL;
    }
 
    //dev->actconfig存放的是当前设备选取的配置
    dev->actconfig = cp;
    if (!cp) {
        usb_set_device_state(dev, USB_STATE_ADDRESS);
        usb_autosuspend_device(dev);
        goto free_interfaces;
    }
    //将状态设为CONFIGURED
    usb_set_device_state(dev, USB_STATE_CONFIGURED);
接下来,就要对设备进行配置了,首先,将设备唤醒.回忆一下我们在分析UHCI驱动时,列出来的设备状态图.只有在ADDRESS状态才能转入到CONFIG状态.(SUSPEND状态除外). 所以,如果设备当前不是处于ADDRESS状态,就需要将设备的状态初始化.usb_disable_device()函数是个比较重要的操作,在接下来再对它进行详细分析.
接着,发送SET_CONFIGURATION的Control消息给设备,用来选择配置
最后,将dev->actconfig指向选定的配置,将设备状态设为CONFIG
 
    /* Initialize the new interface structures and the
     * hc/hcd/usbcore interface/endpoint state.
     */
     //遍历所有的接口
    for (i = 0; i < nintf; ++i) {
        struct usb_interface_cache *intfc;
        struct usb_interface *intf;
        struct usb_host_interface *alt;
 
        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);
 
        /* No altsetting 0?  We'll assume the first altsetting.
         * We could use a GetInterface call, but if a device is
         * so non-compliant that it doesn't have altsetting 0
         * then I wouldn't trust its reply anyway.
         */
 
        //如果0号设置不存在,选排在第一个设置
        if (!alt)
            alt = &intf->altsetting[0];
 
        //当前的配置
        intf->cur_altsetting = alt;
        usb_enable_interface(dev, intf);
        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);
        mark_quiesced(intf);
        sprintf(&intf->dev.bus_id[0], "%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);
之前初始化的new_interfaces在这里终于要派上用场了.初始化各接口,从上面的初始化过程中,我们可以看出:
Intf->altsetting,表示接口的各种设置
Intf->num_altsetting:表示接口的设置数目
Intf->intf_assoc:接口的关联接口(定义于minor usb 2.0 spec)
Intf->cur_altsetting:接口的当前设置.
结合之前在UHCI中的分析,我们总结一下:
Usb_dev->config,其实是一个数组,存放设备的配置.usb_dev->config[m]-> interface[n]表示第m个配置的第n个接口的intercace结构.(m,n不是配置序号和接口序号 *^_^*).
注意这个地方对intf内嵌的struct devcie结构赋值,它的type被赋值为了usb_if_device_type.bus还是usb_bus_type.可能你已经反应过来了,要和这个device匹配的设备是interface的驱动.
特别的,这里的device的命名:
sprintf(&intf->dev.bus_id[0], "%d-%s:%d.%d",
            dev->bus->busnum, dev->devpath,
            configuration, alt->desc.bInterfaceNumber);
dev指的是这个接口所属的usb_dev,结合我们之前在UHCI中关于usb设备命名方式的描述.可得出它的命令方式如下:
USB总线号-设备路径:配置号.接口号.
例如,在我的虚拟机上:
[root@localhost devices]# pwd
/sys/bus/usb/devices
[root@localhost devices]# ls
1-0:1.0  usb1
[root@localhost devices]#
可以得知,系统只有一个usb control.
1-0:1.0:表示,第一个usb control下的root hub的1号配置的0号接口.
那在插入U盘之后,会发生什么变化呢?不着急,我们等着往下面看,待分析到的时候,我自然就会列出来了 ^_^
usb_enable_interface()用来启用接口,也就是启用接口中的每一个endpoint.这个接口比较简单,其中调用的子函数usb_enable_endpoint()在分析UHCI的时候已经详细分析过了.在这里就不再赘述了.
在这里,还涉及到一个很有意思的函数, mark_quiesced().从字面意思上看来,它是标记接口为停止状态.它的”反函数”是mark_active().两个函数如下示:
static inline void mark_active(struct usb_interface *f)
{
    f->is_active = 1;
    f->dev.power.power_state.event = PM_EVENT_ON;
}
 
static inline void mark_quiesced(struct usb_interface *f)
{
    f->is_active = 0;
    f->dev.power.power_state.event = PM_EVENT_SUSPEND;
}
从代码看来,它只是对接口的活动标志(is_active)进行了设置.
 
    /* Now that all the interfaces are set up, register them
     * to trigger binding of drivers to interfaces.  probe()
     * routines may install different altsettings and may
     * claim() any interfaces not yet bound.  Many class drivers
     * need that: CDC, audio, video, etc.
     */
 
    //注册每一个接口?
    for (i = 0; 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);
        if (ret != 0) {
            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 0;
}
最后,注册intf内嵌的device结构.设备配置完成了,为了省电,可以将设备置为SUSPEND状态.
 
这个函数中还有几个比较重要的子函数,依次分析如下:
1: usb_disable_device()函数.
顾名思义,这个函数是将设备disable掉.代码如下:
void usb_disable_device(struct usb_device *dev, int skip_ep0)
{
    int i;
 
    dev_dbg(&dev->dev, "%s nuking %s URBs/n", __FUNCTION__,
        skip_ep0 ? "non-ep0" : "all");
    for (i = skip_ep0; i < 16; ++i) {
        usb_disable_endpoint(dev, i);
        usb_disable_endpoint(dev, i + USB_DIR_IN);
    }
    dev->toggle[0] = dev->toggle[1] = 0;
 
    /* getting rid of interfaces will disconnect
     * any drivers bound to them (a key side effect)
     */
    if (dev->actconfig) {
        for (i = 0; i < dev->actconfig->desc.bNumInterfaces; i++) {
            struct usb_interface    *interface;
 
            /* remove this interface if it has been registered */
            interface = dev->actconfig->interface[i];
            if (!device_is_registered(&interface->dev))
                continue;
            dev_dbg(&dev->dev, "unregistering interface %s/n",
                interface->dev.bus_id);
            usb_remove_sysfs_intf_files(interface);
            device_del(&interface->dev);
        }
 
        /* Now that the interfaces are unbound, nobody should
         * try to access them.
         */
        for (i = 0; i < dev->actconfig->desc.bNumInterfaces; i++) {
            put_device(&dev->actconfig->interface[i]->dev);
            dev->actconfig->interface[i] = NULL;
        }
        dev->actconfig = NULL;
        if (dev->state == USB_STATE_CONFIGURED)
            usb_set_device_state(dev, USB_STATE_ADDRESS);
    }
}
第二个参数是skip_ep0.是表示是否跳过ep0.为1表示跳过,为0表示清除掉设备中的所有endpoint.
这个函数可以分为两个部份,一部份是对usb_dev中的endpoint进行操作,一方面是释放usb_dev的选定配置项.
对于第一部份:
从代码中可能看到,如果skip_ep0为1,那就是从1开始循环,所以,就跳过了ep0.另外,一个端点号对应了两个端点,一个IN,一个OUT.IN端点比OUT端点要大USB_DIR_IN.
另外,既然设备都已经被禁用了,那toggle也应该回归原位了.因些将两个方向的toggle都设为0. usb_disable_endpoint()是一个很有意思的处理.它的代码如下:
void usb_disable_endpoint(struct usb_device *dev, unsigned int epaddr)
{
    unsigned int epnum = epaddr & USB_ENDPOINT_NUMBER_MASK;
    struct usb_host_endpoint *ep;
 
    if (!dev)
        return;
    //在dev->ep_out和dev->ep_in删除endpoint
    if (usb_endpoint_out(epaddr)) {
        ep = dev->ep_out[epnum];
        dev->ep_out[epnum] = NULL;
    } else {
        ep = dev->ep_in[epnum];
        dev->ep_in[epnum] = NULL;
    }
    //禁用掉此ep.包括删除ep上提交的urb 和ep上的QH
    if (ep) {
        ep->enabled = 0;
        usb_hcd_flush_endpoint(dev, ep);
        usb_hcd_disable_endpoint(dev, ep);
    }
}
在dev->ep_in[]/dev->ep_out[]中删除endpoint,这点很好理解.比较难以理解的是后面的两个操作,即usb_hcd_flush_endpoint()和usb_hcd_disable_endpoint().根据之前分析的UHCI的驱动,我们得知,对于每个endpoint都有一个传输的qh,这个qh上又挂上了要传输的urb.因此,这两个函数一个是删除urb,一个是删除qh.
usb_hcd_flush_endpoint()的代码如下:
void usb_hcd_flush_endpoint(struct usb_device *udev,
        struct usb_host_endpoint *ep)
{
    struct usb_hcd      *hcd;
    struct urb      *urb;
 
    if (!ep)
        return;
    might_sleep();
    hcd = bus_to_hcd(udev->bus);
 
    /* No more submits can occur */
    //在提交urb时,将urb加到ep->urb_list上的时候要持锁
    //因此,这里持锁的话,无法发生中断和提交urb
    spin_lock_irq(&hcd_urb_list_lock);
rescan:
//将挂在ep->urb_list上的所有urb unlink.注意这里unlink一般只会设置urb->unlinked的
//值,不会将urb从ep->urb_list上删除.只有在UHCI的中断处理的时候,才会调用
//uhci_giveback_urb()将其从ep->urb_list中删除
    list_for_each_entry (urb, &ep->urb_list, urb_list) {
        int is_in;
 
        if (urb->unlinked)
            continue;
        usb_get_urb (urb);
        is_in = usb_urb_dir_in(urb);
        spin_unlock(&hcd_urb_list_lock);
 
        /* kick hcd */
        unlink1(hcd, urb, -ESHUTDOWN);
        dev_dbg (hcd->self.controller,
            "shutdown urb %p ep%d%s%s/n",
            urb, usb_endpoint_num(&ep->desc),
            is_in ? "in" : "out",
            ({  char *s;
 
                 switch (usb_endpoint_type(&ep->desc)) {
                 case USB_ENDPOINT_XFER_CONTROL:
                    s = ""; break;
                 case USB_ENDPOINT_XFER_BULK:
                    s = "-bulk"; break;
                 case USB_ENDPOINT_XFER_INT:
                    s = "-intr"; break;
                 default:
                    s = "-iso"; break;
                };
                s;
            }));
        usb_put_urb (urb);
 
        /* list contents may have changed */
        //在这里解开锁了,对应ep->urb_list上又可以提交urb.
        //这里释放释的话,主要是为了能够产生中断
        spin_lock(&hcd_urb_list_lock);
        goto rescan;
    }
    spin_unlock_irq(&hcd_urb_list_lock);
 
    /* Wait until the endpoint queue is completely empty */
 
    //等待urb被调度完
    while (!list_empty (&ep->urb_list)) {
        spin_lock_irq(&hcd_urb_list_lock);
 
        /* The list may have changed while we acquired the spinlock */
        urb = NULL;
        if (!list_empty (&ep->urb_list)) {
            urb = list_entry (ep->urb_list.prev, struct urb,
                    urb_list);
            usb_get_urb (urb);
        }
        spin_unlock_irq(&hcd_urb_list_lock);
 
        if (urb) {
            usb_kill_urb (urb);
            usb_put_urb (urb);
        }
    }
}
仔细体会这里的代码,为什么在前一个循环中,要使用goto rescan重新开始这个循环呢?这是因为在后面已经将自旋锁释放了,因此,就会有这样的可能,在函数中操作的urb,可能已经被调度完释放了.因此,再对这个urb操作就会产生错误.所以,需要重新开始这个循环.
那后一个循环又是干什么的呢?后一个循环就是等待urb被调度完.有人就会有这样的疑问了,这里一边等待,然后endpoint一边还提交urb,那这个函数岂不是要耗掉很长时间?
在这里,不要忘记了前面的操作,在调这个函数之前, usb_disable_endpoint()已经将这个endpoint禁用了,也就是说该endpoint不会产生新的urb.因为,在后一个循环中,只需要等待那些被unlink的urb调度完即可.在usb_kill_urb()中,会一直等待,直到这个urb被调度完成为止.
可能有人又会有这样的疑问:
Usb_kill_urb()中也有unlink urb的操作,为什么这里要分做两个循环呢?
 
 
另外的一个函数是usb_hcd_disable_endpoint().代码如下:
void usb_hcd_disable_endpoint(struct usb_device *udev,
        struct usb_host_endpoint *ep)
{
    struct usb_hcd      *hcd;
 
    might_sleep();
    hcd = bus_to_hcd(udev->bus);
    if (hcd->driver->endpoint_disable)
        hcd->driver->endpoint_disable(hcd, ep);
}
从上面的代码可以看到,操作转向了hcd->driver的endpoint_disable()接口.
以UHCI为例.在UHCI中,对应的接口为:
static void uhci_hcd_endpoint_disable(struct usb_hcd *hcd,
        struct usb_host_endpoint *hep)
{
    struct uhci_hcd *uhci = hcd_to_uhci(hcd);
    struct uhci_qh *qh;
 
    spin_lock_irq(&uhci->lock);
    qh = (struct uhci_qh *) hep->hcpriv;
    if (qh == NULL)
        goto done;
 
    while (qh->state != QH_STATE_IDLE) {
        ++uhci->num_waiting;
        spin_unlock_irq(&uhci->lock);
        wait_event_interruptible(uhci->waitqh,
                qh->state == QH_STATE_IDLE);
        spin_lock_irq(&uhci->lock);
        --uhci->num_waiting;
    }
 
    uhci_free_qh(uhci, qh);
done:
    spin_unlock_irq(&uhci->lock);
}
这个函数没啥好说的,就是在uhci->waitqh上等待队列状态变为QH_STATE_IDLE.来回忆一下,qh在什么情况下才会变为QH_STATE_IDLE呢? 是在qh没有待传输的urb的时候.
然后,将qh释放.
 
现在我们来接着看usb_disable_device()的第二个部份.
第二部份主要是针对dev->actconfig进行的操作, dev->actconfig存放的是设备当前的配置,现在要将设备设回Address状态.就些东西当然是用不了上的了.释放dev->actconfig->interface[]中的各元素,注意不要将dev->actconfig->interface[]所指向的信息释放了,它都是指向dev->config[]-> intf_cache[]中的东西,这些东西一释放,usb device在Get_ Configure所获得的信息就会部丢失了.
就这样, usb_disable_device()函数也走到了尾声.
 
2: usb_cache_string()函数
这个函数我们在分析UHCI的时候已经接触过,但末做详细的分析.
首先了解一下这个函数的作用,有时候,为了形象的说明,会提供一个字符串形式的说明.例如,对于配置描述符来说,它的iConfiguration就表示一个字符串索引,然后用Get_String就可以取得这个索引所对应的字串了.不过,事情并不是这么简单.因为字符串对应不同的编码,所以这里还会对应有编码的处理.来看具体的代码:
char *usb_cache_string(struct usb_device *udev, int index)
{
    char *buf;
    char *smallbuf = NULL;
    int len;
 
    if (index <= 0)
        return NULL;
 
    //不知道字符到底有多长,就按最长256字节处理
    buf = kmalloc(256, GFP_KERNEL);
    if (buf) {
        len = usb_string(udev, index, buf, 256);
        //取到字符了,分配合适的长度
        if (len > 0) {
            smallbuf = kmalloc(++len, GFP_KERNEL);
            if (!smallbuf)
                return buf;
            //将字符copy过去
            memcpy(smallbuf, buf, len);
        }
        //释放旧空间
        kfree(buf);
    }
    return smallbuf;
}
这个函数没啥好说的,流程转入到usb_string中.代码如下:
int usb_string(struct usb_device *dev, int index, char *buf, size_t size)
{
    unsigned char *tbuf;
    int err;
    unsigned int u, idx;
 
    if (dev->state == USB_STATE_SUSPENDED)
        return -EHOSTUNREACH;
    if (size <= 0 || !buf || !index)
        return -EINVAL;
    buf[0] = 0;
    tbuf = kmalloc(256, GFP_KERNEL);
    if (!tbuf)
        return -ENOMEM;
 
    /* get langid for strings if it's not yet known */
    //先取得设备支持的编码ID
    if (!dev->have_langid) {
        //以0号序号和编码0,Get_String就可得到设备所支持的编码列表
        err = usb_string_sub(dev, 0, 0, tbuf);
        //如果发生了错误,或者是取得的数据超短(最短为4字节)
        if (err < 0) {
            dev_err(&dev->dev,
                "string descriptor 0 read error: %d/n",
                err);
            goto errout;
        } else if (err < 4) {
            dev_err(&dev->dev, "string descriptor 0 too short/n");
            err = -EINVAL;
            goto errout;
        }
        //取设备支持的第一个编码
        else {
            dev->have_langid = 1;
            dev->string_langid = tbuf[2] | (tbuf[3] << 8);
            /* always use the first langid listed */
            dev_dbg(&dev->dev, "default language 0x%04x/n",
                dev->string_langid);
        }
    }
 
    //以编码ID和序号Index作为参数Get_String取得序号对应的字串
    err = usb_string_sub(dev, dev->string_langid, index, tbuf);
    if (err < 0)
        goto errout;
 
    //空一个字符来用来存放结束符
    size--;     /* leave room for trailing NULL char in output buffer */
    //两字节一组,(Unicode编码的)
    for (idx = 0, u = 2; u < err; u += 2) {
        if (idx >= size)
            break;
        //如果高字节有值,说明它不是ISO-8859-1编码的,将它置为?
        //否则,就将低位的值存放到buf中
        if (tbuf[u+1])          /* high byte */
            buf[idx++] = '?';  /* non ISO-8859-1 character */
        else
            buf[idx++] = tbuf[u];
    }
    //在最后一位赋0,字串结尾
    buf[idx] = 0;
    //返回字串的长度,(算上了最后的结尾字符)
    err = idx;
    //如果该描述符不是STRING描述符,打印出错误提示
    if (tbuf[1] != USB_DT_STRING)
        dev_dbg(&dev->dev,
            "wrong descriptor type %02x for string %d (/"%s/")/n",
            tbuf[1], index, buf);
 
 errout:
    //释放空间,返回长度
    kfree(tbuf);
    return err;
}
结合代码中的注释,就很容易理解这一函数了,在此不对这一函数做详细分析.
跟踪进usb_string_sub().代码如下:
static int usb_string_sub(struct usb_device *dev, unsigned int langid,
              unsigned int index, unsigned char *buf)
{
    int rc;
 
    /* Try to read the string descriptor by asking for the maximum
     * possible number of bytes */
     //如果设备不需要Fixup 就发出Get_String
    if (dev->quirks & USB_QUIRK_STRING_FETCH_255)
        rc = -EIO;
    else
        rc = usb_get_string(dev, langid, index, buf, 255);
 
    /* If that failed try to read the descriptor length, then
     * ask for just that many bytes */
     //如果Get_String失败或者取得长度有问题.就先取字符描述符的头部
     //再以实际的长度和参数,再次Get_String
    if (rc < 2) {
        rc = usb_get_string(dev, langid, index, buf, 2);
        if (rc == 2)
            rc = usb_get_string(dev, langid, index, buf, buf[0]);
    }
 
    //如果成功
    if (rc >= 2) {
        //如果前两个字节为空.则需要找到数据的有效起始位置
        if (!buf[0] && !buf[1])
            usb_try_string_workarounds(buf, &rc);
 
        /* There might be extra junk at the end of the descriptor */
        //整调一下描述符的长度
        if (buf[0] < rc)
            rc = buf[0];
        //将rc置为了一个偶数.
        rc = rc - (rc & 1); /* force a multiple of two */
    }
 
    //长度最终小于2.返回错误值
    if (rc < 2)
        rc = (rc < 0 ? rc : -EINVAL);
 
    return rc;
}
在这个地方,有个错误处理,可能有的设备你一次用255的长度去取它对字符串会返回一个错误,所以,在用255长度返回错误的时候,先以2为长度取它的描述符头度,再以描述符的实际长度去取字符串描述符串.
另外,在描述符的前两个字节都为空的情况下,就需要计算它的有效长度.在代码中,这一工作是由usb_try_string_workarounds()完成的.
static void usb_try_string_workarounds(unsigned char *buf, int *length)
{
    int newlength, oldlength = *length;
    //前两个字节是描述符头部,所以从2开始循环.
    //Unicode编码用两个字节来表示一个字符. 所以每次循环完了之后要+2
    for (newlength = 2; newlength + 1 < oldlength; newlength += 2)
        //低字节是不可打印字符,或者高字节不为空(不是ISO-8859-1), 就退出循环.
        if (!isprint(buf[newlength]) || buf[newlength + 1])
            break;
 
    //修正字符串描述符的实际长度.
    //如果newlength 等于2.说明字符中描述符没有带字串
    if (newlength > 2) {
        buf[0] = newlength;
        *length = newlength;
    }
}
这个函数涉及到编码方面的东东,建议参阅fudan_abc的<< Linux那些事儿之我是USB core >>,上面的较详细的描述.
至此, usb_cache_string()完分析完了.
到这里,usb device driver的probe过程也就完成了.
 
五:hub接口驱动分析
5.1:接口驱动架构
是时候来分析接口驱动的架构了.
我们在上面看到了接口设备的注册.在开篇的时候分析了接口驱动的注册.我们首先来分析接口驱备和接口驱动的匹配.
代码还是在usb_bus_type->match().只不过是对应另外的一种情况了.将相关代码列出:
static int usb_device_match(struct device *dev, struct device_driver *drv)
{
    ……
    if (is_usb_device(dev)) {
        ……
}
    //interface的情况
    else {
        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 0;
 
        intf = to_usb_interface(dev);
        usb_drv = to_usb_driver(drv);
 
        id = usb_match_id(intf, usb_drv->id_table);
        if (id)
            return 1;
 
        id = usb_match_dynamic_id(intf, usb_drv);
        if (id)
            return 1;
    }
 
    return 0;
}
经过前面的分析,因为在注册接口设备的时候,是将type设为usb_if_device_type,因此,这个函数第一个if是不会满足的.
首先,将struct device和struct device_driver转换为被封装的struct usb_interface和struct usb_driver.紧接着,我们看到了两个匹配,一个是usb_match_id().另外一个是usb_match_dynamic_id().后者只有在前者没有匹配成功的情况下才能调用.我们也可以看到, struct usb_driver中一个struct usb_device_id类型的数组(id_table字段)和一个dynids链表.哪id和dyname_id有什么区别呢?
一般来说,id_table是usb 接口驱动静态定义的可以和此驱动匹配设备的一些参数.而dyname_id是可以动态调整的.
你可以通过写/sys/bus/usb/drivers/XXX/new_id来设置驱动的dyname_id.其中,XXX表示驱动的名称.
这两个函数的逻辑都差不多,我们以usb_match_id()为例进行分析.代码如下:
const struct usb_device_id *usb_match_id(struct usb_interface *interface,
                     const struct usb_device_id *id)
{
    /* proc_connectinfo in devio.c may call us with id == NULL. */
    //如果driver中没有定义符合条件的id_talbe.则不能静态匹配所有设备
    if (id == NULL)
        return NULL;
 
    /* It is important to check that id->driver_info is nonzero,
       since an entry that is all zeroes except for a nonzero
       id->driver_info is the way to create an entry that
       indicates that the driver want to examine every
       device and interface. */
       //如果id_talbe中,有定义的匹配项,则调用usb_match_one_id()进行匹配测试
       //如果成功,则返回匹配成功的id_talbe 项数.否则,继续下一项
    for (; id->idVendor || id->idProduct || id->bDeviceClass ||
           id->bInterfaceClass || id->driver_info; id++) {
        if (usb_match_one_id(interface, id))
            return id;
    }
 
    //如果没有匹配成功,则返回NULL
    return NULL;
}
Id_talbe所属的结构如下示:
struct usb_device_id {
    /* which fields to match against? */
    __u16       match_flags;
 
    /* Used for product specific matches; range is inclusive */
    __u16       idVendor;
    __u16       idProduct;
    __u16       bcdDevice_lo;
    __u16       bcdDevice_hi;
 
    /* Used for device class matches */
    __u8        bDeviceClass;
    __u8        bDeviceSubClass;
    __u8        bDeviceProtocol;
 
    /* Used for interface class matches */
    __u8        bInterfaceClass;
    __u8        bInterfaceSubClass;
    __u8        bInterfaceProtocol;
 
    /* not matched against */
    kernel_ulong_t  driver_info;
};
match_flags是一个匹配标志,表示要匹配哪一项.而后面的成员,例如idVendor, idProduct.表示具体要匹配项的值.
usb_match_one_id()的代码如下:
int usb_match_one_id(struct usb_interface *interface,
             const struct usb_device_id *id)
{
    struct usb_host_interface *intf;
    struct usb_device *dev;
 
    /* proc_connectinfo in devio.c may call us with id == NULL. */
    //再次判断id是否为空
    if (id == NULL)
        return 0;
 
    //接口听当前匹配
    intf = interface->cur_altsetting;
    //转换为所属的usb_dev
    dev = interface_to_usbdev(interface);
 
    //关于设备参数的匹配.只有在设备参数符合的情况下,才会进行接口
    //参数的匹配.
    if (!usb_match_device(dev, id))
        return 0;
   
    //下面是关于接口参数的匹配
    /* The interface class, subclass, and protocol should never be
     * checked for a match if the device class is Vendor Specific,
     * unless the match record specifies the Vendor ID. */
 //当设备是厂商自定义类型时(Vendor Specific).除非定义了//USB_DEVICE_ID_MATCH_VENDOR
     //否则是不需要匹配的
    if (dev->descriptor.bDeviceClass == USB_CLASS_VENDOR_SPEC &&
            !(id->match_flags & USB_DEVICE_ID_MATCH_VENDOR) &&
            (id->match_flags & (USB_DEVICE_ID_MATCH_INT_CLASS |
                USB_DEVICE_ID_MATCH_INT_SUBCLASS |
                USB_DEVICE_ID_MATCH_INT_PROTOCOL)))
        return 0;
    //如果要匹配interface class
    if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_CLASS) &&
        (id->bInterfaceClass != intf->desc.bInterfaceClass))
        return 0;
    //如果要匹配interface subclass
    if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_SUBCLASS) &&
        (id->bInterfaceSubClass != intf->desc.bInterfaceSubClass))
        return 0;
    //如果要匹配interface protocol
    if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_PROTOCOL) &&
        (id->bInterfaceProtocol != intf->desc.bInterfaceProtocol))
        return 0;
 
    return 1;
}
这个函数逻辑比较简单.它先检测所属设备参数的匹配项.再检查接口参数的匹配项.由于这个函数比较简单,就不对它多费舌去解释了.
简单的说一下关于Vendor Specific class的设备,这个类型在USB Class Codes被定义如下:
This base class is defined for vendors to use as they please.  These class codes can be used in both Device and Interface Descriptors.
 
 
也就是说,这个类型是被厂商自定义的.它主要是靠SubClass和protocol来区分设备的.这些都是非标准定义的.
 
如果被这个match匹配成功的话,就会转入probe()了,由于bus中没有probe()函数,因此,流程转入到了与之匹配的驱动的probe函数.
记得在开篇的时候,分析过接口驱动,它的probe函数被设置为了usb_cache_string().代码如下:
static int usb_probe_interface(struct device *dev)
{
    struct usb_driver *driver = to_usb_driver(dev->driver);
    struct usb_interface *intf;
    struct usb_device *udev;
    const struct usb_device_id *id;
    int error = -ENODEV;
 
    dev_dbg(dev, "%s/n", __FUNCTION__);
    //如果是一个usb 设备,退出
    if (is_usb_device(dev))     /* Sanity check */
        return error;
 
    intf = to_usb_interface(dev);
    udev = interface_to_usbdev(intf);
 
    //如果所属设备的authorized 为0.错误,退出
    if (udev->authorized == 0) {
        dev_err(&intf->dev, "Device is not authorized for usage/n");
        return -ENODEV;
    }
 
    //再确认一下设备和驱动是否匹配
    id = usb_match_id(intf, driver->id_table);
    if (!id)
        id = usb_match_dynamic_id(intf, driver);
    //如果匹配
    if (id) {
        dev_dbg(dev, "%s - got id/n", __FUNCTION__);
 
        //使设备resume
        error = usb_autoresume_device(udev);
        if (error)
            return error;
 
        /* Interface "power state" doesn't correspond to any hardware
         * state whatsoever.  We use it to record when it's bound to
         * a driver that may start I/0:  it's not frozen/quiesced.
         */
         //将接口标志为active
        mark_active(intf);
        //intf->condition:接口的状态
        //USB_INTERFACE_BINDING:正在绑定
        intf->condition = USB_INTERFACE_BINDING;
 
        /* The interface should always appear to be in use
         * unless the driver suports autosuspend.
         */
         //intf->pm_usage_cnt:如果为1,接口不会autosuspend
        intf->pm_usage_cnt = !(driver->supports_autosuspend);
        //调用挂装结构的probe
        error = driver->probe(intf, id);
        //如果发生了错误,将接口标记成不活动的,接口状态设为USB_INTERFACE_UNBOUND
        if (error) {
            mark_quiesced(intf);
            intf->needs_remote_wakeup = 0;
            intf->condition = USB_INTERFACE_UNBOUND;
        }
        //如果成功,将接口状态设为USB_INTERFACE_BOUND
        else
            intf->condition = USB_INTERFACE_BOUND;
 
        //使设备suspend
        usb_autosuspend_device(udev);
    }
 
    return error;
}
至此为至,我们遇到了大量的usb_autoresume_device()/usb_autosuspend_device().先将它们放到一边,等以后再来详细分析他们的操作.
还记得,在usb_set_configuration()中,为每一个接口调用了mark_quiesced().所以在这个probe里,将正确匹配的接口调用mark_active(),将其标记为Active.
另外,我们来看一下,intf-> condition的几种情况,在代码中, condition属于enum usb_interface_condition结构.如下示:
enum usb_interface_condition {
    USB_INTERFACE_UNBOUND = 0,
    USB_INTERFACE_BINDING,
    USB_INTERFACE_BOUND,
    USB_INTERFACE_UNBINDING,
};
分别表示,没有绑定,正在绑定,已经绑定,正在解除绑定.
从上面的代码中可以看出.最后的流程会回溯到usb_driver的probe.
 
5.2:hub的接口驱动
经过上面的分析,我们知道,接口驱动和接口的匹配主要是根据接口的一些信息来判断的.那对于hub的接口,它的标志信息是什么呢?
在usb2.0 spec上,规定了hub的设备class和接口class都为0x9.也就是代码中定义的USB_CLASS_HUB.
同时,我们注意到,在usb_init()àusb_hub_init()中:
int usb_hub_init(void)
{
    if (usb_register(&hub_driver) < 0) {
        printk(KERN_ERR "%s: can't register hub driver/n",
            usbcore_name);
        return -1;
    }
 
    khubd_task = kthread_run(hub_thread, NULL, "khubd");
    if (!IS_ERR(khubd_task))
        return 0;
 
    /* Fall through if kernel_thread failed */
    usb_deregister(&hub_driver);
    printk(KERN_ERR "%s: can't start khubd/n", usbcore_name);
 
    return -1;
}
这个函数注册了hub_driver对应的接口驱动,还启动了一个内核线程.在这里,我们注意到, hub_driver的定义如下:
static struct usb_driver hub_driver = {
    .name =     "hub",
    .probe =    hub_probe,
    .disconnect =   hub_disconnect,
    .suspend =  hub_suspend,
    .resume =   hub_resume,
    .reset_resume = hub_reset_resume,
    .pre_reset =    hub_pre_reset,
    .post_reset =   hub_post_reset,
    .ioctl =    hub_ioctl,
    .id_table = hub_id_table,
    .supports_autosuspend = 1,
};
Hub_id_talbe被定义为:
static struct usb_device_id hub_id_table [] = {
    { .match_flags = USB_DEVICE_ID_MATCH_DEV_CLASS,
      .bDeviceClass = USB_CLASS_HUB},
    { .match_flags = USB_DEVICE_ID_MATCH_INT_CLASS,
      .bInterfaceClass = USB_CLASS_HUB},
    { }                      /* Terminating entry */
};
看到了没,这些信息就是hub 的接口class信息.
也就是说, hub_driver就是为们要找的hub 接口的驱动.
根据上面的分析,流程最终会到hub_driver->probe中,对应的接口为hub_probe().
 
5.2.1:接口驱动的probe过程
Hub_probe()的代码如下:
static int hub_probe(struct usb_interface *intf, const struct usb_device_id *id)
{
    struct usb_host_interface *desc;
    struct usb_endpoint_descriptor *endpoint;
    struct usb_device *hdev;
    struct usb_hub *hub;
 
    desc = intf->cur_altsetting;
    hdev = interface_to_usbdev(intf);
 
#ifdef  CONFIG_USB_OTG_BLACKLIST_HUB
    if (hdev->parent) {
        dev_warn(&intf->dev, "ignoring external hub/n");
        return -ENODEV;
    }
#endif
 
    /* Some hubs have a subclass of 1, which AFAICT according to the */
    /*  specs is not defined, but it works */
    //spec上规定接口的subclass为0.但为1时,也能工作
    if ((desc->desc.bInterfaceSubClass != 0) &&
        (desc->desc.bInterfaceSubClass != 1)) {
descriptor_error:
        dev_err (&intf->dev, "bad descriptor, ignoring hub/n");
        return -EIO;
    }
 
    /* Multiple endpoints? What kind of mutant ninja-hub is this? */
    //spec上规定hub interface的endpoint数目为1,这里的数目没有包括ep0
    if (desc->desc.bNumEndpoints != 1)
        goto descriptor_error;
    //端点描述符
    endpoint = &desc->endpoint[0].desc;
 
    /* If it's not an interrupt in endpoint, we'd better punt! */
    //不是IN方向的中断传输端点,有错误
    if (!usb_endpoint_is_int_in(endpoint))
        goto descriptor_error;
 
    /* We found a hub */
    dev_info (&intf->dev, "USB hub found/n");
 
    //初始化一个struct usb_hub结构
    hub = kzalloc(sizeof(*hub), GFP_KERNEL);
    if (!hub) {
        dev_dbg (&intf->dev, "couldn't kmalloc hub struct/n");
        return -ENOMEM;
    }
    //初始化引用计数
    kref_init(&hub->kref);
    //初始化event_list
    INIT_LIST_HEAD(&hub->event_list);
    //hub->intfdev:指向接口封装的dev 
    hub->intfdev = &intf->dev;
    //hub->hdev:hub所属的usb_dev
    hub->hdev = hdev;
    //初始化一个延时工作队列,用来管理hub LED的
    INIT_DELAYED_WORK(&hub->leds, led_work);
    //增加intf->dev的引用计数
    usb_get_intf(intf);
    //intf->dev的私有结构指和hub
    usb_set_intfdata (intf, hub);
    //接口需要远程唤醒
    intf->needs_remote_wakeup = 1;
 
    //如果是一个高速设备,增加highspeed_hubs  计数
    if (hdev->speed == USB_SPEED_HIGH)
        highspeed_hubs++;
 
    //配置hub
    if (hub_configure(hub, endpoint) >= 0)
        return 0;
 
    hub_disconnect (intf);
    return -ENODEV;
}
这个函数前一部份进行一些有效性检查,后半部份分配并初始化一个struct usb_hub结构.然后流程就转入了hub_configure().
hub_configure()函数是一个很重要的操作,它的代码如下:
static int hub_configure(struct usb_hub *hub,
    struct usb_endpoint_descriptor *endpoint)
{
    struct usb_device *hdev = hub->hdev;
    struct device *hub_dev = hub->intfdev;
    u16 hubstatus, hubchange;
    u16 wHubCharacteristics;
    unsigned int pipe;
    int maxp, ret;
    char *message;
 
    //为buffer分配空间,DMA分配,其物理地址存放在hub->buffer_dma中
    //buffer是一个指向数组的指针
    hub->buffer = usb_buffer_alloc(hdev, sizeof(*hub->buffer), GFP_KERNEL,
            &hub->buffer_dma);
    if (!hub->buffer) {
        message = "can't allocate hub irq buffer";
        ret = -ENOMEM;
        goto fail;
    }
 
    //为status分配空间
    hub->status = kmalloc(sizeof(*hub->status), GFP_KERNEL);
    if (!hub->status) {
        message = "can't kmalloc hub status buffer";
        ret = -ENOMEM;
        goto fail;
    }
    mutex_init(&hub->status_mutex);
 
    //为hub->descriptor分配空间
    hub->descriptor = kmalloc(sizeof(*hub->descriptor), GFP_KERNEL);
    if (!hub->descriptor) {
        message = "can't kmalloc hub descriptor";
        ret = -ENOMEM;
        goto fail;
    }
 
    /* Request the entire hub descriptor.
     * hub->descriptor can handle USB_MAXCHILDREN ports,
     * but the hub can/will return fewer bytes here.
     */
     //取得hub描述符
    ret = get_hub_descriptor(hdev, hub->descriptor,
            sizeof(*hub->descriptor));
    //如果Get_Descriptor失败或者hub端口超多
    if (ret < 0) {
        message = "can't read hub descriptor";
        goto fail;
    }
    //协议上规定hub最多有255个接口,但Linux认为31个接口已经够多了
    else if (hub->descriptor->bNbrPorts > USB_MAXCHILDREN) {
        message = "hub has too many ports!";
        ret = -ENODEV;
        goto fail;
    }
 
    //hdev->maxchild: hub的端口数目
    hdev->maxchild = hub->descriptor->bNbrPorts;
    dev_info (hub_dev, "%d port%s detected/n", hdev->maxchild,
        (hdev->maxchild == 1) ? "" : "s");
 
    //wHubCharacteristics字段
    wHubCharacteristics = le16_to_cpu(hub->descriptor->wHubCharacteristics);
 
    //是否是一个复合设备
    if (wHubCharacteristics & HUB_CHAR_COMPOUND) {
        int i;
        char    portstr [USB_MAXCHILDREN + 1];
 
        for (i = 0; i < hdev->maxchild; i++)
            //找到数组中的所在项和数组项中的位置
            portstr[i] = hub->descriptor->DeviceRemovable
                    [((i + 1) / 8)] & (1 << ((i + 1) % 8))
                ? 'F' : 'R';
        //以/0 结尾
        portstr[hdev->maxchild] = 0;
        dev_dbg(hub_dev, "compound device; port removable status: %s/n", portstr);
    } else
        dev_dbg(hub_dev, "standalone hub/n");
 
    //电源开关模式
    switch (wHubCharacteristics & HUB_CHAR_LPSM) {
        //ganged 电源开关,所有连接端口同时开机
        case 0x00:
            dev_dbg(hub_dev, "ganged power switching/n");
            break;
        //端口有单独的电源开关
        case 0x01:
            dev_dbg(hub_dev, "individual port power switching/n");
            break;
        //1X:保留, 表示没有电源开关   
        case 0x02:
        case 0x03:
            dev_dbg(hub_dev, "no power switching (usb 1.0)/n");
            break;
    }
 
    //过电流保护模式
    switch (wHubCharacteristics & HUB_CHAR_OCPM) {
        //全部电流保护
        case 0x00:
            dev_dbg(hub_dev, "global over-current protection/n");
            break;
        //个别连接端口电源保护
        case 0x08:
            dev_dbg(hub_dev, "individual port over-current protection/n");
            break;
        //没有过电流保护
        case 0x10:
        case 0x18:
            dev_dbg(hub_dev, "no over-current protection/n");
                        break;
    }
 
    //初始化TT相关的东西
    spin_lock_init (&hub->tt.lock);
    INIT_LIST_HEAD (&hub->tt.clear_list);
    INIT_WORK (&hub->tt.kevent, hub_tt_kevent);
    //设备描述符的bDeviceProtocol 字段
    switch (hdev->descriptor.bDeviceProtocol) {
        //HUB是一个低速/全速设备
        case 0:
            break;
        //只有一个TT
        case 1:
            dev_dbg(hub_dev, "Single TT/n");
            hub->tt.hub = hdev;
            break;
        //多个TT   
        case 2:
            //为接口选取1号设置
            ret = usb_set_interface(hdev, 0, 1);
            if (ret == 0) {
                dev_dbg(hub_dev, "TT per port/n");
                hub->tt.multi = 1;
            } else
                dev_err(hub_dev, "Using single TT (err %d)/n",
                    ret);
            hub->tt.hub = hdev;
            break;
        default:
            dev_dbg(hub_dev, "Unrecognized hub protocol %d/n",
                hdev->descriptor.bDeviceProtocol);
            break;
    }
 
    /* Note 8 FS bit times == (8 bits / 12000000 bps) ~= 666ns */
    //TT think time,
    switch (wHubCharacteristics & HUB_CHAR_TTTT) {
        case HUB_TTTT_8_BITS:
            if (hdev->descriptor.bDeviceProtocol != 0) {
                hub->tt.think_time = 666;
                dev_dbg(hub_dev, "TT requires at most %d "
                        "FS bit times (%d ns)/n",
                    8, hub->tt.think_time);
            }
            break;
        case HUB_TTTT_16_BITS:
            hub->tt.think_time = 666 * 2;
            dev_dbg(hub_dev, "TT requires at most %d "
                    "FS bit times (%d ns)/n",
                16, hub->tt.think_time);
            break;
        case HUB_TTTT_24_BITS:
            hub->tt.think_time = 666 * 3;
            dev_dbg(hub_dev, "TT requires at most %d "
                    "FS bit times (%d ns)/n",
                24, hub->tt.think_time);
            break;
        case HUB_TTTT_32_BITS:
            hub->tt.think_time = 666 * 4;
            dev_dbg(hub_dev, "TT requires at most %d "
                    "FS bit times (%d ns)/n",
                32, hub->tt.think_time);
            break;
    }
 
    /* probe() zeroes hub->indicator[] */
    //是否支持连接端口LED
    if (wHubCharacteristics & HUB_CHAR_PORTIND) {
        hub->has_indicators = 1;
        dev_dbg(hub_dev, "Port indicators are supported/n");
    }
    //bPwrOn2PwrGood字段表示连接端口从开机到准备好的时间
    dev_dbg(hub_dev, "power on to power good time: %dms/n",
        hub->descriptor->bPwrOn2PwrGood * 2);
 
    /* power budgeting mostly matters with bus-powered hubs,
     * and battery-powered root hubs (may provide just 8 mA).
     */
     //Get_Status 设备
    ret = usb_get_status(hdev, USB_RECIP_DEVICE, 0, &hubstatus);
    //Get_Status失败
    if (ret < 2) {
        message = "can't get hub status";
        goto fail;
    }
    //设备的status包含两个部份:bit 0 表示使用自身电源bit 1是远程唤醒字段
    le16_to_cpus(&hubstatus);
    //如果是root hub
    if (hdev == hdev->bus->root_hub) {
        //如果电流没有限制
        if (hdev->bus_mA == 0 || hdev->bus_mA >= 500)
            hub->mA_per_port = 500;
        //有限制的情况下
        else {
            hub->mA_per_port = hdev->bus_mA;
            hub->limited_power = 1;
        }
    }
    //如果使用总线电流
    else if ((hubstatus & (1 << USB_DEVICE_SELF_POWERED)) == 0) {
        dev_dbg(hub_dev, "hub controller current requirement: %dmA/n",
            hub->descriptor->bHubContrCurrent);
        hub->limited_power = 1;
        if (hdev->maxchild > 0) {
            int remaining = hdev->bus_mA -
                    hub->descriptor->bHubContrCurrent;
 
            if (remaining < hdev->maxchild * 100)
                dev_warn(hub_dev,
                    "insufficient power available "
                    "to use all downstream ports/n");
            hub->mA_per_port = 100;     /* 7.2.1.1 */
        }
    }
    // 如果使用本身电流
    else {  /* Self-powered external hub */
        /* FIXME: What about battery-powered external hubs that
         * provide less current per port? */
        hub->mA_per_port = 500;
    }
    if (hub->mA_per_port < 500)
        dev_dbg(hub_dev, "%umA bus power budget for each child/n",
                hub->mA_per_port);
    //Get hub status
    //hub 状态位和改变位
    ret = hub_hub_status(hub, &hubstatus, &hubchange);
    if (ret < 0) {
        message = "can't get hub status";
        goto fail;
    }
 
    /* local power status reports aren't always correct */
    //自身供电
    if (hdev->actconfig->desc.bmAttributes & USB_CONFIG_ATT_SELFPOWER)
        dev_dbg(hub_dev, "local power source is %s/n",
            (hubstatus & HUB_STATUS_LOCAL_POWER)
            ? "lost (inactive)" : "good");
    //不支持过电流保护
    if ((wHubCharacteristics & HUB_CHAR_OCPM) == 0)
        dev_dbg(hub_dev, "%sover-current condition exists/n",
            (hubstatus & HUB_STATUS_OVERCURRENT) ? "" : "no ");
 
    /* set up the interrupt endpoint
     * We use the EP's maxpacket size instead of (PORTS+1+7)/8
     * bytes as USB2.0[11.12.3] says because some hubs are known
     * to send more data (and thus cause overflow). For root hubs,
     * maxpktsize is defined in hcd.c's fake endpoint descriptors
     * to be big enough for at least USB_MAXCHILDREN ports. */
     
     //设备中断控制传输的urb
     //通道和最大包长度
    pipe = usb_rcvintpipe(hdev, endpoint->bEndpointAddress);
    maxp = usb_maxpacket(hdev, pipe, usb_pipeout(pipe));
 
    if (maxp > sizeof(*hub->buffer))
        maxp = sizeof(*hub->buffer);
    //分配urb
    hub->urb = usb_alloc_urb(0, GFP_KERNEL);
    if (!hub->urb) {
        message = "couldn't allocate interrupt urb";
        ret = -ENOMEM;
        goto fail;
    }
    //填充urb
    usb_fill_int_urb(hub->urb, hdev, pipe, *hub->buffer, maxp, hub_irq,
        hub, endpoint->bInterval);
    hub->urb->transfer_dma = hub->buffer_dma;
    hub->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
 
    /* maybe cycle the hub leds */
    if (hub->has_indicators && blinkenlights)
        hub->indicator [0] = INDICATOR_CYCLE;
    //驱动hub
    hub_power_on(hub);
    //激活hub
    hub_activate(hub);
    return 0;
 
fail:
    dev_err (hub_dev, "config failed, %s (err %d)/n",
            message, ret);
    /* hub_disconnect() frees urb and descriptor */
    return ret;
}
这个函数先初始化struct usb_hub中的几个指针,为之分配空间,然后,取得hub的描述符,再根据取得的描述符信息再初始化和显示一些调试信息.其中的一些成员赋值等我们用到的时候再来进行分析.这个函数的后面关于urb部份和后面调用的两个子函数才是我们要分析的重点.
在这里,顺带提一下HUB的指示灯问题.
Hub描述符的wHubCharacteristics的bit7来描述设备是否支持显示灯.为1表示在下游的连接端口上支持显示灯,为0则不支持.
如果Hub支持指示灯,则将hub->has_indicators置为1.另外,HUB的指示灯是否起作用,还由一个参数决定,在代码中,大家也看到,这个参数是blinkenlights.这个参数定义如下:
static int blinkenlights = 0;
module_param (blinkenlights, bool, S_IRUGO);
MODULE_PARM_DESC (blinkenlights, "true to cycle leds on hubs");
这是一个可调的模块参数.如果要显示灯起作用,必须要将其置为1才可以.我们可以用下面两种方法来设置这个参数:
1:如果模块没有编译进kernel,可以在插入模块的时候,加上这个参数:
    modprobe usbcore blinkenlights=1
2:如果模块已经编进kernel,那在kernel的启动参数上加上如下参数:
    usbcore.blinkenlights=1
在usb2.0 spec中,对不同情况下的灯颜色都做了定义.
在代码中,灯的显示交给了一延迟的工作队列进行处理,初始化如下所示:
INIT_DELAYED_WORK(&hub->leds, led_work); (在hub_probe()函数中)
在这里,并不打算详细分析这一部份,有兴趣的可以跟踪下去看下.
 
那函数后面初始化的URB是用来干什么的呢?我们将这个URB的初始化部份单独列出如下:
pipe = usb_rcvintpipe(hdev, endpoint->bEndpointAddress);
    maxp = usb_maxpacket(hdev, pipe, usb_pipeout(pipe));
    if (maxp > sizeof(*hub->buffer))
        maxp = sizeof(*hub->buffer);
我们从此可以看出,这个URB是作用于ep0之外的另一个端点,而且传输数据的长度最大为sizeof(*hub->buffer)
Hub->buffer被定义如下:
struct usb_hub {
    ……
    char            (*buffer)[8];
    ……
}
由此可以看出buffer是指向一个8元素字符数组的指针,sizeof(*hub->buffer)等于0.
关于传输的数据长度,代码中有一段注释,这段注释说,spec上规定的长度是(PORTS+1+7)/8.而linux中,对每个hub上挂有认为最多的端口进行处理,因些,就是(31+1+7)/8 = 5
为什么这里要是8呢?
因为usb2.0 spec上规定,ep0的最大发包长度,可能为8.16.32.64.512.所以选择比5要大的最小值8.
另外,我们注意到,URB完成之后,所要调用的函数是hub_irq().如下所示:
usb_fill_int_urb(hub->urb, hdev, pipe, *hub->buffer, maxp, hub_irq,
        hub, endpoint->bInterval);
UHCI必须要知道HUB的端口的一些连接状态,因此,需要HUB周期性的上报它的端口连接状态.这个URB就是用来做这个用途的.UHCI周期性的发送IN方向中断传输传输给HUB.HUB就会通过这个URB将端口信息发送给HUB.
那这个轮询周期是多长呢?根据我们之前分析的UHCI的知识,它的调度周期是由endpoint的bInterval 字段所决定的.
 
现在,我们慢慢来接触到hub的一些核心处理了.整理一下心情,继续看代码.^_^
接下来,我们要看到的第一个函数是hub_power_on().代码如下:
static void hub_power_on(struct usb_hub *hub)
{
    int port1;
    //从连接端口从开机到准备好的时间
    unsigned pgood_delay = hub->descriptor->bPwrOn2PwrGood * 2;
    //wHubCharacteristics字段
    u16 wHubCharacteristics =
            le16_to_cpu(hub->descriptor->wHubCharacteristics);
 
    /* Enable power on each port.  Some hubs have reserved values
     * of LPSM (> 2) in their descriptors, even though they are
     * USB 2.0 hubs.  Some hubs do not implement port-power switching
     * but only emulate it.  In all cases, the ports won't work
     * unless we send these messages to the hub.
     */
     //开关模式
    if ((wHubCharacteristics & HUB_CHAR_LPSM) < 2)
        dev_dbg(hub->intfdev, "enabling power on all ports/n");
    else
        dev_dbg(hub->intfdev, "trying to enable port power on "
                "non-switchable hub/n");
    //给每一个端口发送PORT_POWER的Set_Feature消息
    for (port1 = 1; port1 <= hub->descriptor->bNbrPorts; port1++)
        set_port_feature(hub->hdev, port1, USB_PORT_FEAT_POWER);
 
    /* Wait at least 100 msec for power to become stable */
    //等待端口可以正常工作,至少100 ms
    msleep(max(pgood_delay, (unsigned) 100));
}
这里就是给每个接口发送PORT_POWER的Set_Feature消息,告之可起来工作了,然后等待端口可以工作.
 
另外要分析的函数是hub_activate().代码如下:
static void hub_activate(struct usb_hub *hub)
{
    int status;
 
    hub->quiescing = 0;
    hub->activating = 1;
 
    //提交urb
    status = usb_submit_urb(hub->urb, GFP_NOIO);
    if (status < 0)
        dev_err(hub->intfdev, "activate --> %d/n", status);
    if (hub->has_indicators && blinkenlights)
        schedule_delayed_work(&hub->leds, LED_CYCLE_PERIOD);
 
    /* scan all ports ASAP */
    kick_khubd(hub);
}
首先 ,是在hub的两个字段进行赋值操作,hub-> quiescing 和 hub->activating表示分别表示hub处理暂停和活跃状态.注意,在这里,不要和接口的mark_active()设置的intf->is_active相混淆.
然后,将hub->urb提交.开始调度Led的工作队列.
最后,流程转入kick_khubd().代码如下:
static void kick_khubd(struct usb_hub *hub)
{
    unsigned long   flags;
 
    /* Suppress autosuspend until khubd runs */
    //pm_usage_cnt设置为1,防止autosuspend
    to_usb_interface(hub->intfdev)->pm_usage_cnt = 1;
 
    spin_lock_irqsave(&hub_event_lock, flags);
    if (!hub->disconnected && list_empty(&hub->event_list)) {
        list_add_tail(&hub->event_list, &hub_event_list);
        wake_up(&khubd_wait);
    }
    spin_unlock_irqrestore(&hub_event_lock, flags);
}
在这个函数中,先将接口的pm_usage_cnt置1,此后,该接口就不能SUSPEND.
然后,将该hub添加进hub_event_list链表,并唤醒Khubd_wait等待队列.
hub_event_list和Khubd_wait到底代表着什么呢?它后面的参数又是什么呢?接着往下看…
5.2.2:接口驱动中的hub_thread()函数
我们之前在分析usb_hub_init()的代码的时候,忽略掉了一部份.
代码片段如下所示:
int usb_hub_init(void)
{
   ……
    khubd_task = kthread_run(hub_thread, NULL, "khubd");
    ……
}
Kthread_run()是kernel中用来启动一个新kernel线程的接口,它所要执行的函数就是后面跟的第一个参数.在这里,也就是hub_thread().另外,顺带提一句,要终止kthread_run()创建的线程,可以调用kthread_stop().
 
Hub_thread()的代码如下:
static int hub_thread(void *__unused)
{
    set_freezable();
    do {
        hub_events();
        wait_event_freezable(khubd_wait,
                !list_empty(&hub_event_list) ||
                kthread_should_stop());
    } while (!kthread_should_stop() || !list_empty(&hub_event_list));
 
    pr_debug("%s: khubd exiting/n", usbcore_name);
    return 0;
}
在上面的代码中, kthread_should_stop()用来判断是否有kthread_stop()将其终止.
在这里,我们终止看到,我们在前面要唤醒的等待队列khubd_wait,也就是在这个地方了.
这个函数的核心处理是hub_events().分段分析代码,如下:
static void hub_events(void)
{
    struct list_head *tmp;
    struct usb_device *hdev;
    struct usb_interface *intf;
    struct usb_hub *hub;
    struct device *hub_dev;
    u16 hubstatus;
    u16 hubchange;
    u16 portstatus;
    u16 portchange;
    int i, ret;
    int connect_change;
 
    /*
     *  We restart the list every time to avoid a deadlock with
     * deleting hubs downstream from this one. This should be
     * safe since we delete the hub from the event list.
     * Not the most efficient, but avoids deadlocks.
     */
    while (1) {
 
        /* Grab the first entry at the beginning of the list */
        //如果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);
 
        hub = list_entry(tmp, struct usb_hub, event_list);
        kref_get(&hub->kref);
        spin_unlock_irq(&hub_event_lock);
 
        hdev = hub->hdev;
        hub_dev = hub->intfdev;
        intf = to_usb_interface(hub_dev);
        dev_dbg(hub_dev, "state %d ports %d chg %04x evt %04x/n",
                hdev->state, hub->descriptor
                    ? hub->descriptor->bNbrPorts
                    : 0,
                /* NOTE: expects max 15 ports... */
                (u16) hub->change_bits[0],
                (u16) hub->event_bits[0]);
 
        /* Lock the device, then check to see if we were
         * disconnected while waiting for the lock to succeed. */
        usb_lock_device(hdev);
        //如果hub断开了,继续hub_event_list中的下一个
        if (unlikely(hub->disconnected))
            goto loop;
 
        /* If the hub has died, clean up after it */
        //设备没有连接上
        if (hdev->state == USB_STATE_NOTATTACHED) {
            hub->error = -ENODEV;
            //将下面的子设备全部disable
            hub_pre_reset(intf);
            goto loop;
        }
 
        /* Autoresume */
        ret = usb_autopm_get_interface(intf);
        if (ret) {
            dev_dbg(hub_dev, "Can't autoresume: %d/n", ret);
            goto loop;
        }
 
        /* If this is an inactive hub, do nothing */
        //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 = 0;
            hub->error = 0;
        }
首先,从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()中完成的.
 
        /* deal with port status changes */
        //遍历hub中的每一个port
        for (i = 1; i <= hub->descriptor->bNbrPorts; i++) {
{
            if (test_bit(i, hub->busy_bits))
                continue;
            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 < 0)
                continue;
 
            //如果对应端口没有在设备树上,且端口显示已经连接上
            //将connect_change置为1
            if (hub->activating && !hdev->children[i-1] &&
                    (portstatus &
                        USB_PORT_STAT_CONNECTION))
                connect_change = 1;
            //端口的连接状态发生了改变.需要发送Clear_Feature
            if (portchange & USB_PORT_STAT_C_CONNECTION) {
                clear_port_feature(hdev, i,
                    USB_PORT_FEAT_C_CONNECTION);
                connect_change = 1;
            }
 
            //端口的状态从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);
 
                /*
                 * EM interference sometimes causes badly
                 * shielded USB devices to be shutdown by
                 * the hub, this hack enables them again.
                 * Works at least with mouse driver.
                 */
                 //端口已经被停止了,且端口已经被连在设备树中.
                 //需要重启一下此端口
                if (!(portstatus & USB_PORT_STAT_ENABLE)
                    && !connect_change
                    && hdev->children[i-1]) {
                    dev_err (hub_dev,
                        "port %i "
                        "disabled by hub (EMI?), "
                        "re-enabling.../n",
                        i);
                    connect_change = 1;
                }
            }
 
            //Resume完成   
            if (portchange & USB_PORT_STAT_C_SUSPEND) {
                clear_port_feature(hdev, i,
                    USB_PORT_FEAT_C_SUSPEND);
                //如果端口连接了设备,就将设备唤醒
                if (hdev->children[i-1]) {
                    ret = remote_wakeup(hdev->
                            children[i-1]);
                    if (ret < 0)
                        connect_change = 1;
                }
                //如果端口没有连接设备,就将端口禁用
                else {
                    ret = -ENODEV;
                    hub_port_disable(hub, i, 1);
                }
                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);
            }
 
            if (connect_change)
                hub_port_connect_change(hub, i,
                        portstatus, portchange);
        }
这段代码就是最核心的操作了,首先要说明的是,在struct usb_dev中,有一个struct usb_device *children[USB_MAXCHILDREN]的成员,它是表示对应端口序号上所连接的usb设备.
在这里,它遍历hub上的每一个端口,如果端口的连接会生了改变(connect_change等于1)的情况,就会调用hub_port_connect_change().我们来看一下,什么情况下, 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()函数我们放在后面再来讨论
 
                //对HUB的处理
        /* deal with hub status changes */
        //如果hub状态末变化,不需要做任何处理
        if (test_and_clear_bit(0, hub->event_bits) == 0)
            ;   /* do nothing */
        //Get_hub_status 失败?
        else if (hub_hub_status(hub, &hubstatus, &hubchange) < 0)
            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 = 1;
                //如果本电源不供电
                else
                    hub->limited_power = 0;
            }
            //如果hub 发生过电源保护,需要对hub power on
            if (hubchange & HUB_CHANGE_OVERCURRENT) {
                dev_dbg (hub_dev, "overcurrent change/n");
                msleep(500);    /* Cool down */
                clear_hub_feature(hdev, C_HUB_OVER_CURRENT);
                           hub_power_on(hub);
            }
        }
 
        hub->activating = 0;
 
        /* 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[0])
            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之后,就要来处理hub本身的状态改变了,结合代码中的注释应该很容易看懂,在这里主要是清除hub的对应Feature.
之后,将  hub->activating设为了0,如果hub是root hub,需要重新打开root hub的中断.
这个函数到这里就完成了.不过,其中的几个子函数,涉及到的操作很重要,现分析如下:
1: hub_pre_reset()函数.
该函数在设备断开连接的时候,将其下挂载的所有子设备全部注销掉,代码如下所示:
static int hub_pre_reset(struct usb_interface *intf)
{
    struct usb_hub *hub = usb_get_intfdata(intf);
    struct usb_device *hdev = hub->hdev;
    int i;
 
    /* Disconnect all the children */
    for (i = 0; i < hdev->maxchild; ++i) {
        if (hdev->children[i])
            usb_disconnect(&hdev->children[i]);
    }
    hub_quiesce(hub);
    return 0;
}
它将设备上所挂载的所有设备全部都调用usb_disconnect()来断开联接.之后,再对hub调用hub_quiesce().
hub_quiesce()是和hub_activate()相对应的一个函数, hub_activate()在前面已经分析过了,现在来对hub_quiesce()进行分析.
代码如下:
static void hub_quiesce(struct usb_hub *hub)
{
    /* (nonblocking) khubd and related activity won't re-trigger */
    hub->quiescing = 1;
    hub->activating = 0;
 
    /* (blocking) stop khubd and related activity */
    usb_kill_urb(hub->urb);
    if (hub->has_indicators)
        cancel_delayed_work_sync(&hub->leds);
    if (hub->tt.hub)
        cancel_work_sync(&hub->tt.kevent);
}
首先,它调hub->quiescing置为1,而activating置为0.这和hub_activate()刚好是相反的动作.之后,取消hub的中断传输出URB.取得TT和LED的工作队列.
我们在后面分析的HUB中断URB传输,可以知道,如果将这个URB禁用,那么,就不会将hub->event_list添加到hub_event_list.因此,也不会进入到hub_events()函数.
 
usb_disconnect()用来断开某个设备,代码如下:
void usb_disconnect(struct usb_device **pdev)
{
    struct usb_device   *udev = *pdev;
    int         i;
 
    if (!udev) {
        pr_debug ("%s nodev/n", __FUNCTION__);
        return;
    }
 
    /* mark the device as inactive, so any further urb submissions for
     * this device (and any of its children) will fail immediately.
     * this quiesces everyting except pending urbs.
     */
    usb_set_device_state(udev, USB_STATE_NOTATTACHED);
    dev_info (&udev->dev, "USB disconnect, address %d/n", udev->devnum);
 
    usb_lock_device(udev);
 
    /* Free up all the children before we remove this device */
    for (i = 0; i < USB_MAXCHILDREN; i++) {
        if (udev->children[i])
            usb_disconnect(&udev->children[i]);
    }
 
    /* deallocate hcd/hardware state ... nuking all pending urbs and
     * cleaning up all state associated with the current configuration
     * so that the hardware is now fully quiesced.
     */
    dev_dbg (&udev->dev, "unregistering device/n");
    usb_disable_device(udev, 0);
 
    usb_unlock_device(udev);
 
    /* Unregister the device.  The device driver is responsible
     * for removing the device files from usbfs and sysfs and for
     * de-configuring the device.
     */
    device_del(&udev->dev);
 
    /* Free the device number and delete the parent's children[]
     * (or root_hub) pointer.
     */
    release_address(udev);
 
    /* Avoid races with recursively_mark_NOTATTACHED() */
    spin_lock_irq(&device_state_lock);
    *pdev = NULL;
    spin_unlock_irq(&device_state_lock);
 
    usb_stop_pm(udev);
 
    put_device(&udev->dev);
}
很容易看出.这个函数采用深度遍历算法,它依次遍历udev->children[]下的子设备,然后依然调用usb_disconnect().
这个函数中的另外几个子函数有的在前面已经分析过,有的是设备模型中的基础函数.很有是跟PM相关的,在这里就不做详细分析,来看一下release_address()函数,顾名思意,它用来释放设备的地址,如下示:
static void release_address(struct usb_device *udev)
{
    if (udev->devnum > 0) {
        clear_bit(udev->devnum, udev->bus->devmap.devicemap);
        udev->devnum = -1;
    }
}
我们在分析UHCI中,有关root hub的初始化时说明,设各号都是保存在bus->devmap数组中的.在这里,只需要将该设备号在数组中的某位清了即可.
hub_pre_reset()函数就分析到这里了.
注意到这里调用的put_device(&udev->dev)没.根据Linux设备模型的分析,这时它会调用跟它绑定的driver的remove()接口,对应的,这个函数会将操作回溯到usb_driver-> disconnect().可以自行查阅这个过程.
或许,有人的疑问又来了?要是这个usb_dev没有跟usb_driver绑定怎么办呢?
不要忘记我们之前的分析了,对于usb_generic_driver这个驱动是会适用所有的usb_dev的.^_^,也是说,无论如何,usb_dev都会绑定到usb_generic_driver.
 
2: hub_port_connect_change()函数
这个函数是一个很核心的操作,它的代码如下:
static void hub_port_connect_change(struct usb_hub *hub, int port1,
                    u16 portstatus, u16 portchange)
{
    struct usb_device *hdev = hub->hdev;
    struct device *hub_dev = hub->intfdev;
    struct usb_hcd *hcd = bus_to_hcd(hdev->bus);
    u16 wHubCharacteristics = le16_to_cpu(hub->descriptor->wHubCharacteristics);
    int status, i;
 
    dev_dbg (hub_dev,
        "port %d, status %04x, change %04x, %s/n",
        port1, portstatus, portchange, portspeed (portstatus));
 
    //HUB LED
    if (hub->has_indicators) {
        set_port_led(hub, port1, HUB_LED_AUTO);
        hub->indicator[port1-1] = INDICATOR_AUTO;
    }
 
    /* Disconnect any existing devices under this port */
    //如果对应端口已经有设备连接,先将其断开
    if (hdev->children[port1-1])
        usb_disconnect(&hdev->children[port1-1]);
    //将hub_change_bits中的对应位清零,以免下次进来的时候,还会检测到
    //hub_port_logical_disconnect()对该值的设置
    clear_bit(port1, hub->change_bits);
 
#ifdef  CONFIG_USB_OTG
    /* during HNP, don't repeat the debounce */
    if (hdev->bus->is_b_host)
        portchange &= ~USB_PORT_STAT_C_CONNECTION;
#endif
 
    //连接发生改变
    //连接反弹的处理,实际上就是除抖动
    if (portchange & USB_PORT_STAT_C_CONNECTION) {
        status = hub_port_debounce(hub, port1);
        if (status < 0) {
            if (printk_ratelimit())
                dev_err (hub_dev, "connect-debounce failed, "
                        "port %d disabled/n", port1);
            goto done;
        }
        portstatus = status;
    }
在这里,我们忽略掉HUB LED灯的操作,然后,将HUB对应端口下面挂载的设备断开.经过前面的分析,进入到这个函数的可能有多种情况(在hub_events()中分析的五种情况).可以分为三大类:
一类是之前有连接之后没联接的,在这里,将hub 对应端口下的设备全部断开是无可非议的.
第二类是之前没有,之后有连接的,在这里,if(hdev->children[port-1])的判断是不会满足的.
第三类是需要重置的端口,在这里先将设备断开,然后再将它联连上去好了.
 
接下来,将hub->change_bits的对应位清掉,该位是在函数hub_port_logical_disconnect()中被置的,在这里将其清除,免得下次在进入hub_events()的时候,再次检测到这个位发生改变.
 
忽略掉CONFIG_USB_OTG的处理,这个宏我们在前面分析过很多次了,这里不再赘述.
如果该端口的连接发生改变(从有连接到无接接,或者从无连接到有连接),就有一个除抖动的过程,usb2.0 spec上规定,除抖动的时间为100ms.
也许有人会有这样的想法: 那检测到移除了一个设备,但它在100ms又插上去了,这里适不适合这里的抖动检测的情况呢?
我们先从代码的流程看,检测到连接发生改变,进入到hub_port_connect_change(),它首先就会将端口上的设备移除.这样,就算你在100ms上连接上去了,也得要再次建立.
从usb2.0的协议看来,设备移除后,usb设备里保存的信息(例如选择的配置,给它分配的地址)全部都丢失了,必须要重新进行配置过程才能够使用.
在这里,顺便将hub_port_debounce()列出,来看一下具体的除抖过程是怎么样实现的.
static int hub_port_debounce(struct usb_hub *hub, int port1)
{
    int ret;
    int total_time, stable_time = 0;
    u16 portchange, portstatus;
    unsigned connection = 0xffff;
 
    for (total_time = 0; ; total_time += HUB_DEBOUNCE_STEP) {
        ret = hub_port_status(hub, port1, &portstatus, &portchange);
        if (ret < 0)
            return ret;
 
        if (!(portchange & USB_PORT_STAT_C_CONNECTION) &&
             (portstatus & USB_PORT_STAT_CONNECTION) == connection) {
            stable_time += HUB_DEBOUNCE_STEP;
            if (stable_time >= HUB_DEBOUNCE_STABLE)
                break;
        } else {
            stable_time = 0;
            connection = portstatus & USB_PORT_STAT_CONNECTION;
        }
 
        if (portchange & USB_PORT_STAT_C_CONNECTION) {
            clear_port_feature(hub->hdev, port1,
                    USB_PORT_FEAT_C_CONNECTION);
        }
 
        if (total_time >= HUB_DEBOUNCE_TIMEOUT)
            break;
        msleep(HUB_DEBOUNCE_STEP);
    }
 
    dev_dbg (hub->intfdev,
        "debounce: port %d: total %dms stable %dms status 0x%x/n",
        port1, total_time, stable_time, portstatus);
 
    if (stable_time < HUB_DEBOUNCE_STABLE)
        return -ETIMEDOUT;
    return portstatus;
}
函数中的stable_time表示隐定的时间.在hub_events()的代码分析时,我们看到了,在检测到连接状态发生改变的时候,会发送Clear_Feature.因此,如果在这里检测到有USB_PORT_STAT_C_CONNECTION,就说明之后又有一次连接状态发生改变了.
分析这个函数的时候,要注意有这样的情况,端口的连接状态,一直在波动,即时有连接,时末有连接.
还有注意, connection的初始值是0xffff, 所以(portstatus & USB_PORT_STAT_CONNECTION) == connection这个判断是肯定不会满足的,因为hub_port_status()取得的portstatus里面还有一些保留位.所以,在第一次进入这个循环的时候,就会进入到else中,就会将stable_time置0,而connection也保存了这一次的连接信息.
如果端口维持前一个状态,那循环中的流程就会满足第一个if,在这个if的操作里,会增加stable_time的值.
如果端口的状态发生了改变,那循环中的流程就会满足else,又将stable_time和connection初始化了.另外,要记得在状态发生改变的时候,要发送Clear_Feature,将状态清除.
在函数里,定义的测试时间是1500ms.如果在这个时间内,端口还末处于稳定状态,就会返回-ETIMEDOUT.
如果已经处于稳定状态了,就会返回稳定状态下的portstatus.
 
/* Return now if nothing is connected */
    //如果接口上没有连接了,可以直接退出了
    if (!(portstatus & USB_PORT_STAT_CONNECTION)) {
 
        /* maybe switch power back on (e.g. root hub was reset) */
        if ((wHubCharacteristics & HUB_CHAR_LPSM) < 2
                && !(portstatus & (1 << USB_PORT_FEAT_POWER)))
            set_port_feature(hdev, port1, USB_PORT_FEAT_POWER);
 
        if (portstatus & USB_PORT_STAT_ENABLE)
            goto done;
        return;
    }
经过去抖后,端口稳定的处于断开连接状态.说明端口已经没有设备了.然后,再判断hub是否有电源开关((wHubCharacteristics & HUB_CHAR_LPSM) < 2),portstatus 的 USB_PORT_FEAT_POWER位是否被设置,如果没有被设置,则说明该端口断电了.如果hub有电源开关,且端口没有上电,则需要发送POWER的Set_Feature来为之上电.
如果端口依然处理enable状态,就会跳转到标号done处,就端口disalbe.
 
    //如果接口上面有了联接,需要为联接在端口上设备建立连接
    for (i = 0; i < SET_CONFIG_TRIES; i++) {
        struct usb_device *udev;
 
        /* reallocate for each attempt, since references
         * to the previous one can escape in various ways
         */
        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_set_device_state(udev, USB_STATE_POWERED);
        udev->speed = USB_SPEED_UNKNOWN;
        udev->bus_mA = hub->mA_per_port;
        udev->level = hdev->level + 1;
 
        /* set the address */
        choose_address(udev);
        if (udev->devnum <= 0) {
            status = -ENOTCONN; /* Don't retry */
            goto loop;
        }
 
        /* reset and get descriptor */
        status = hub_port_init(hub, udev, port1, i);
        if (status < 0)
            goto loop;
 
        /* 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 <= 100) {
            u16 devstat;
 
            status = usb_get_status(udev, USB_RECIP_DEVICE, 0,
                    &devstat);
            if (status < 2) {
                dev_dbg(&udev->dev, "get status %d ?/n", status);
                goto loop_disable;
            }
            le16_to_cpus(&devstat);
            if ((devstat & (1 << USB_DEVICE_SELF_POWERED)) == 0) {
                dev_err(&udev->dev,
                    "can't connect bus-powered hub "
                    "to this port/n");
                if (hub->has_indicators) {
                    hub->indicator[port1-1] =
                        INDICATOR_AMBER_BLINK;
                    schedule_delayed_work (&hub->leds, 0);
                }
                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 != 0)
            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 = 0;
 
        /* We mustn't add new devices if the parent hub has
         * been disconnected; we would race with the
         * recursively_mark_NOTATTACHED() routine.
         */
        spin_lock_irq(&device_state_lock);
        if (hdev->state == USB_STATE_NOTATTACHED)
            status = -ENOTCONN;
        else
            hdev->children[port1-1] = udev;
        spin_unlock_irq(&device_state_lock);
 
        /* Run it through the hoops (find a driver, etc) */
        if (!status) {
            status = usb_new_device(udev);
            if (status) {
                spin_lock_irq(&device_state_lock);
                hdev->children[port1-1] = 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, 1);
loop:
        ep0_reinit(udev);
        release_address(udev);
        usb_put_dev(udev);
        if ((status == -ENOTCONN) || (status == -ENOTSUPP))
            break;
    }
如果端口隐定处于连接状态,那就需要连接端口下的设备了.首先看到的是一个for循环,是用来配置设备的两种方式.我们知道,在配置设备的时候,首先要去取设备的描述符,这个过程是在ep0上完成的.而这个ep0支持的最大传输出数据又是在设备描述符的bMaxPacketSize0中所定义的.
因此就对应有两种处理方式:
第一种是传输8个字节,取得描述符的前面一部份,从而就可以取得bMaxPacketSize0.此后再reset设备,再根据这个bMaxPacketSize0的长度去取它的设备描述符.
第二种是一次传输64字节,取得设备描述符的bMaxPacketSize0字段
关于这两种方式的描述,详见fudan_abc的<< Linux那些事儿之我是Hub>>.
有关这个for循环的作用就解释到这里.
在这段代码里,它首先分配一个usb_dev的结构,然后将其置为USB_STATE_POWERED状态.接着,为设备指定一个地址.
然后就调用hub_port_init()对这个usb_dev结构进行一系的初始化,在这个函数中会处理:Get_Description,Set_address.等操作,这个函数接下来我们再详细分析.
接着,将分配的struct usb_dev结构跟他的父结构关联起来,也就是说添加到它的父结构的usb_dev-> children[]数组.
最后再调用usb_new_device()来取这个设备的配置项.这个函数我们在分析UHCI的时候已经分析过了.
中间是关于一些电流的判断处理,这部份比较简单,自行查看就可以看懂,这里不再分析.
注意,这里在分配usb_dev结构的时候,跟root hub是不相同的,如下示:
    udev = usb_alloc_dev(hdev, hdev->bus, port1)
在为root hub分配struct usb_dev的时候,它的第一个参数,也就是它的父结点是为NULL.
我们来观察一下它在sysfs中的命名方式:
如下所示:
在没有插入U盘之前:
[root@localhost devices]# pwd
/sys/bus/usb/devices
[root@localhost devices]# ls
1-0:1.0  usb1
[root@localhost devices]#
插入U盘之后:
[root@localhost devices]# ls
1-0:1.0  1-1  1-1:1.0  usb1
增加的两个目是:
1-1和1-1:1.0
表示,U盘对应的设备目录是1-1.结合之前UHCI分析中,对usb_alloc_dev()应该很容易理解.
1-1:1.0 :只有这样的目录,表示该U盘只有一个接口,当前选取的是第0号设置项.
 
done:
    hub_port_disable(hub, port1, 1);
    if (hcd->driver->relinquish_port && !hub->hdev->parent)
        hcd->driver->relinquish_port(hcd, port1);
}
Done标号是对应上述处理失败的处理,它禁用掉该端口(因为该端口没有连接设备或者是端口上的设备配置失败),如果是root hub,且USB控制器器驱动中又定义了relinquish_port.调用它.
 
照例,还是分析一下这个函数中涉及到的重要的子函数.
第一个要分析的函数是choose_address()
该函数用来为设备选择一个地址,代码如下所示:
static void choose_address(struct usb_device *udev)
{
    int     devnum;
    struct usb_bus  *bus = udev->bus;
 
    /* If khubd ever becomes multithreaded, this will need a lock */
 
    /* Try to allocate the next devnum beginning at bus->devnum_next. */
//从bus->devnum_next开始找到一个末被使用的位
    devnum = find_next_zero_bit(bus->devmap.devicemap, 128,
            bus->devnum_next);
    //如果搜索到了最末尾,(128是不能被占用的),则从1起开始搜索
    if (devnum >= 128)
        devnum = find_next_zero_bit(bus->devmap.devicemap, 128, 1);
    //更新bus->devnum_next
    bus->devnum_next = ( devnum >= 127 ? 1 : devnum + 1);
    //如果找到了合适位,将该位设为占用,然后更新udev->devnum为找到的设备号
    if (devnum < 128) {
        set_bit(devnum, bus->devmap.devicemap);
        udev->devnum = devnum;
    }
}
这个函数的原理我们在之前说过了多次,它是到所属的usb bus的bus->devmap中找到没有使用的那一位.在这里设置bus->devnum_next项是一个搜索的优化,它不必每次都从第1位起开始搜索.最后将找到的值存放在udev->devnum中.
 
第二个要分析的函数是hub_port_disable().
这个函数将hub对应的端口禁用,代码如下:
static int hub_port_disable(struct usb_hub *hub, int port1, int set_state)
{
    struct usb_device *hdev = hub->hdev;
    int ret = 0;
    //将接在该端口下的设备设为末连接
    if (hdev->children[port1-1] && set_state)
        usb_set_device_state(hdev->children[port1-1],
                USB_STATE_NOTATTACHED);
    //发送enable 的Clear_Feature请求.
    if (!hub->error)
        ret = clear_port_feature(hdev, port1, USB_PORT_FEAT_ENABLE);
    if (ret)
        dev_err(hub->intfdev, "cannot disable port %d (err = %d)/n",
                port1, ret);
    return ret;
}
该函数的逻辑很简单,就是该端点下的联接设备断开,如果端口有设备连接的话.然后清除端口的enable.
 
第三个要分析的函数是hub_port_init().
将它列到最后,并不是因为它最轻微,而是因为它太复杂.^_^
代码分段分析如下:
static int
hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
        int retry_counter)
{
    static DEFINE_MUTEX(usb_address0_mutex);
 
    struct usb_device   *hdev = hub->hdev;
    int         i, j, retval;
    unsigned        delay = HUB_SHORT_RESET_TIME;
    enum usb_device_speed   oldspeed = udev->speed;
    char            *speed, *type;
    int         devnum = udev->devnum;
 
    /* root hub ports have a slightly longer reset period
     * (from USB 2.0 spec, section 7.1.7.5)
     */
     //设置port 的重置等待时间
    if (!hdev->parent) {
        delay = HUB_ROOT_RESET_TIME;
        if (port1 == hdev->bus->otg_port)
            hdev->bus->b_hnp_enable = 0;
    }
 
    /* Some low speed devices have problems with the quick delay, so */
    /*  be a bit pessimistic with those devices. RHbug #23670 */
    if (oldspeed == USB_SPEED_LOW)
        delay = HUB_LONG_RESET_TIME;
 
    mutex_lock(&usb_address0_mutex);
 
    /* Reset the device; full speed may morph to high speed */
    //将port reset
    retval = hub_port_reset(hub, port1, udev, delay);
    if (retval < 0)     /* error or disconnect */
        goto fail;
                /* success, speed is known */
    retval = -ENODEV;
 
    //在设备之前的设速已经确定的情况下
    //如果设备的速度发生了改变,肯定是发生了错误
    if (oldspeed != USB_SPEED_UNKNOWN && oldspeed != udev->speed) {
        dev_dbg(&udev->dev, "device reset changed speed!/n");
        goto fail;
    }
    oldspeed = udev->speed;
首先为端口重置选择一个合适的延时,即在这个延时过后,端口的Reset应该完成了.usb2.0 spec上规定,root hub的延时值是50ms,高速设备是10ms,而低速设备是100ms.从代码上看,这个延时都是从udev参数中来的,这个参数就是表示在端口上连接的设备.其实,所谓的Reset端口,就是Reset端口上连接的设备.
由于我们现在要对这个设备进行配置,因此,先将它复原成初始值.
另外,如果重置之后,设备的speed发生了变化,这肯定是错误的.
 
    /* USB 2.0 section 5.5.3 talks about ep0 maxpacket ...
     * it's fixed size except for full speed devices.
     * For Wireless USB devices, ep0 max packet is always 512 (tho
     * reported as 0xff in the device descriptor). WUSB1.0[4.8.1].
     */
    switch (udev->speed) {
    case USB_SPEED_VARIABLE:    /* fixed at 512 */
        udev->ep0.desc.wMaxPacketSize = __constant_cpu_to_le16(512);
        break;
    case USB_SPEED_HIGH:        /* fixed at 64 */
        udev->ep0.desc.wMaxPacketSize = __constant_cpu_to_le16(64);
        break;
    case USB_SPEED_FULL:        /* 8, 16, 32, or 64 */
        /* to determine the ep0 maxpacket size, try to read
         * the device descriptor to get bMaxPacketSize0 and
         * then correct our initial guess.
         */
        udev->ep0.desc.wMaxPacketSize = __constant_cpu_to_le16(64);
        break;
    case USB_SPEED_LOW:     /* fixed at 8 */
        udev->ep0.desc.wMaxPacketSize = __constant_cpu_to_le16(8);
        break;
    default:
        goto fail;
    }
根据设备的speed来设定ep0的MaxPacketSize.这个只是spec上规定的值.另外对于Full Speed的设来说,它的MaxPacketSize有四种情况,即8.16.32和64实际的值要在设备描述符的bMaxPacketSize0字段才能知道.
 
    type = "";
    switch (udev->speed) {
    case USB_SPEED_LOW: speed = "low";  break;
    case USB_SPEED_FULL:    speed = "full"; break;
    case USB_SPEED_HIGH:    speed = "high"; break;
    case USB_SPEED_VARIABLE:
                speed = "variable";
                type = "Wireless ";
                break;
    default:        speed = "?";    break;
    }
    dev_info (&udev->dev,
          "%s %s speed %sUSB device using %s and address %d/n",
          (udev->config) ? "reset" : "new", speed, type,
          udev->bus->controller->driver->name, devnum);
这段代码无关紧要,只是打印出了一个Debug信息,
 
    /* Set up TT records, if needed  */
    if (hdev->tt) {
        udev->tt = hdev->tt;
        udev->ttport = hdev->ttport;
    } else if (udev->speed != USB_SPEED_HIGH
            && hdev->speed == USB_SPEED_HIGH) {
        udev->tt = &hub->tt;
        udev->ttport = port1;
    }
 
    /* Why interleave GET_DESCRIPTOR and SET_ADDRESS this way?
     * Because device hardware and firmware is sometimes buggy in
     * this area, and this is how Linux has done it for ages.
     * Change it cautiously.
     *
     * NOTE:  If USE_NEW_SCHEME() is true we will start by issuing
     * a 64-byte GET_DESCRIPTOR request.  This is what Windows does,
     * so it may help with some non-standards-compliant devices.
     * Otherwise we start with SET_ADDRESS and then try to read the
     * first 8 bytes of the device descriptor to get the ep0 maxpacket
     * value.
     */
    for (i = 0; i < GET_DESCRIPTOR_TRIES; (++i, msleep(100))) {
        if (USE_NEW_SCHEME(retry_counter)) {
            struct usb_device_descriptor *buf;
            int r = 0;
 
#define GET_DESCRIPTOR_BUFSIZE  64
            buf = kmalloc(GET_DESCRIPTOR_BUFSIZE, GFP_NOIO);
            if (!buf) {
                retval = -ENOMEM;
                continue;
            }
 
            /* Retry on all errors; some devices are flakey.
             * 255 is for WUSB devices, we actually need to use
             * 512 (WUSB1.0[4.8.1]).
             */
            for (j = 0; j < 3; ++j) {
                buf->bMaxPacketSize0 = 0;
                r = usb_control_msg(udev, usb_rcvaddr0pipe(),
                    USB_REQ_GET_DESCRIPTOR, USB_DIR_IN,
                    USB_DT_DEVICE << 8, 0,
                    buf, GET_DESCRIPTOR_BUFSIZE,
                    USB_CTRL_GET_TIMEOUT);
                switch (buf->bMaxPacketSize0) {
                case 8: case 16: case 32: case 64: case 255:
                    if (buf->bDescriptorType ==
                            USB_DT_DEVICE) {
                        r = 0;
                        break;
                    }
                    /* FALL THROUGH */
                default:
                    if (r == 0)
                        r = -EPROTO;
                    break;
                }
                if (r == 0)
                    break;
            }
            udev->descriptor.bMaxPacketSize0 =
                    buf->bMaxPacketSize0;
            kfree(buf);
 
            retval = hub_port_reset(hub, port1, udev, delay);
            if (retval < 0)     /* error or disconnect */
                goto fail;
            if (oldspeed != udev->speed) {
                dev_dbg(&udev->dev,
                    "device reset changed speed!/n");
                retval = -ENODEV;
                goto fail;
            }
            if (r) {
                dev_err(&udev->dev, "device descriptor "
                        "read/%s, error %d/n",
                        "64", r);
                retval = -EMSGSIZE;
                continue;
            }
#undef GET_DESCRIPTOR_BUFSIZE
        }
 
        for (j = 0; j < SET_ADDRESS_TRIES; ++j) {
            retval = hub_set_address(udev, devnum);
            if (retval >= 0)
                break;
            msleep(200);
        }
        if (retval < 0) {
            dev_err(&udev->dev,
                "device not accepting address %d, error %d/n",
                devnum, retval);
            goto fail;
        }
 
        /* cope with hardware quirkiness:
         *  - let SET_ADDRESS settle, some device hardware wants it
         *  - read ep0 maxpacket even for high and low speed,
         */
        msleep(10);
        if (USE_NEW_SCHEME(retry_counter))
            break;
 
        retval = usb_get_device_descriptor(udev, 8);
        if (retval < 8) {
            dev_err(&udev->dev, "device descriptor "
                    "read/%s, error %d/n",
                    "8", retval);
            if (retval >= 0)
                retval = -EMSGSIZE;
        } else {
            retval = 0;
            break;
        }
    }
这个for循环是一个很重要的操作,首先,我们来看一下USE_NEW_SCHEME宏的定义.如下示:
((i) / 2 == old_scheme_first), old_scheme_first默认为0,也就是说,当i为0,1的时候,这个宏会返回1.那就是说,对于之前分析的两种机制,每种机制尝试两次.
区分一下这两种机制的不同:
对于第一种机制,它先用64的buffer去取设备描述符.而第二种机制,是以长度8的缓存区,取设备描述符的前半部份.
另外,第一种机制,去取设备描述符之前没有设置设备的地址,因此使用地址0来表示设备的地址,在代码中,用usb_rcvaddr0pipe()表示.而在第二种机制中,它在取设备描述符之前已经设置了设备的地址.
疑问:可能有人就有这样的疑问,既然地址0可以表示没有设置地址的设备地址,那如果有多个没有set address的设备,这个地址0到底是表示那个设备呢?
实际上,从代码上看,Linux是每打开一个hub的端口就初始连在这个端口上的设备.之后这连接上的设备设置好地址之后再打开hub的另外的端口进行配置,因此,在同一条usb bus上,不会出现多个末配置的活动设备.
 
    if (retval)
        goto fail;
 
    i = udev->descriptor.bMaxPacketSize0 == 0xff?
        512 : udev->descriptor.bMaxPacketSize0;
    if (le16_to_cpu(udev->ep0.desc.wMaxPacketSize) != i) {
        if (udev->speed != USB_SPEED_FULL ||
                !(i == 8 || i == 16 || i == 32 || i == 64)) {
            dev_err(&udev->dev, "ep0 maxpacket = %d/n", i);
            retval = -EMSGSIZE;
            goto fail;
        }
        dev_dbg(&udev->dev, "ep0 maxpacket = %d/n", i);
        udev->ep0.desc.wMaxPacketSize = cpu_to_le16(i);
        ep0_reinit(udev);
    }
    retval = usb_get_device_descriptor(udev, USB_DT_DEVICE_SIZE);
    if (retval < (signed)sizeof(udev->descriptor)) {
        dev_err(&udev->dev, "device descriptor read/%s, error %d/n",
            "all", retval);
        if (retval >= 0)
            retval = -ENOMSG;
        goto fail;
    }
 
    retval = 0;
 
fail:
    if (retval) {
        hub_port_disable(hub, port1, 0);
        udev->devnum = devnum;  /* for disconnect processing */
    }
    mutex_unlock(&usb_address0_mutex);
    return retval;
}
在上面获得的设备描述符的bMaxPacketSize0字段,也就是ep0的MaxPacketSize.但如果这个值不和我们之前根据spec为ep0设定的MaxPacketSize值相等,且不是Full speed的话,就会有错误了.因为只有Full Speed的设备的ep0 的MaxPacketSize在spec上并没有一个明确的定义值.
有了确定的ep0 的MaxPacketSize值,就可以取得完整的设备描述符了.
 
第四个要分析的函数是hub_port_reset().
这个函数将端口重置并等待端口重置完成.代码如下:
static int hub_port_reset(struct usb_hub *hub, int port1,
                struct usb_device *udev, unsigned int delay)
{
    int i, status;
 
    /* Block EHCI CF initialization during the port reset.
     * Some companion controllers don't like it when they mix.
     */
    down_read(&ehci_cf_port_reset_rwsem);
 
    /* Reset the port */
    //尝试5次
    for (i = 0; i < PORT_RESET_TRIES; i++) {
        //发送Reset 的Set_Feature
        status = set_port_feature(hub->hdev,
                port1, USB_PORT_FEAT_RESET);
        //发送错误
        if (status)
            dev_err(hub->intfdev,
                    "cannot reset port %d (err = %d)/n",
                    port1, status);
        else {
            //发送Clear_Feature成功,等待端口重置完成
            status = hub_port_wait_reset(hub, port1, udev, delay);
            if (status && status != -ENOTCONN)
                dev_dbg(hub->intfdev,
                        "port_wait_reset: err = %d/n",
                        status);
        }
 
        /* return on disconnect or reset */
        switch (status) {
        //成功  
        case 0:
            /* TRSTRCY = 10 ms; plus some extra */
            msleep(10 + 40);
            udev->devnum = 0;   /* Device now at address 0 */
            /* FALL THROUGH */
        //端口没有连接   
        case -ENOTCONN:
        //要发送的设备不存在  
        case -ENODEV:
            clear_port_feature(hub->hdev,
                port1, USB_PORT_FEAT_C_RESET);
            /* FIXME need disconnect() for NOTATTACHED device */
            usb_set_device_state(udev, status
                    ? USB_STATE_NOTATTACHED
                    : USB_STATE_DEFAULT);
            goto done;
        }
 
        dev_dbg (hub->intfdev,
            "port %d not enabled, trying reset again.../n",
            port1);
        //将延迟设至最长,再试一次
        delay = HUB_LONG_RESET_TIME;
    }
 
    dev_err (hub->intfdev,
        "Cannot enable port %i.  Maybe the USB cable is bad?/n",
        port1);
 
 done:
    up_read(&ehci_cf_port_reset_rwsem);
    return status;
}
这个函数的代码看清淅,首先将端口重置,然后等待端口重置完成.在成功返回或者是发错致命错误的时候就会在清除掉RESET Feature,设置设备状态之后返回.这个所谓的致命包括:
1:发送Clear_Feature时,返回-ENODEV,表示设备不存在
2:在hub_port_wait_reset()后返回的-ENOTCONN,表示端口上末连接设备.
另外,在这里哆嗦的重复一句,只有在设备有这个Feature的时候,才能Clear_Feature.在上面的代码中,只有代码中,如果Reset不成功,是不需要Clear USB_PORT_FEAT_C_RESET 这个Feature的.只有在已经设置成功的情况,才能将其Clear(-ENODEV的情况,无所谓,这个错误在submit urb前期就能测检出来,不会跟硬件交互,而-ENOTCONN则表示端口Reset完成,但尚末检测到连接设备,这种情况下,也是需要Clear_Feature的).
另外,里面还调用了一个子函数, hub_port_wait_reset().代码如下:
static int hub_port_wait_reset(struct usb_hub *hub, int port1,
                struct usb_device *udev, unsigned int delay)
{
    int delay_time, ret;
    u16 portstatus;
    u16 portchange;
 
    //最长等待时间是500
    for (delay_time = 0;
            delay_time < HUB_RESET_TIMEOUT;
            delay_time += delay) {
        /* wait to give the device a chance to reset */
        msleep(delay);
 
        /* read and decode port status */
        ret = hub_port_status(hub, port1, &portstatus, &portchange);
        if (ret < 0)
            return ret;
 
        /* Device went away? */
        //端口已经没有连接了,说明连接的设备在某个时刻被拨下来了
        if (!(portstatus & USB_PORT_STAT_CONNECTION))
            return -ENOTCONN;
 
        /* bomb out completely if the connection bounced */
        //连接状态发生了改变,则说明连接状态不稳定.因为断开之后,再联上是需要重新配置的
        //退出
        if ((portchange & USB_PORT_STAT_C_CONNECTION))
            return -ENOTCONN;
 
        /* if we`ve finished resetting, then break out of the loop */
        //如果Reset已经完成,且端口处于enable状态,设置speed成员就可以返回了
        if (!(portstatus & USB_PORT_STAT_RESET) &&
            (portstatus & USB_PORT_STAT_ENABLE)) {
            if (hub_is_wusb(hub))
                udev->speed = USB_SPEED_VARIABLE;
            else if (portstatus & USB_PORT_STAT_HIGH_SPEED)
                udev->speed = USB_SPEED_HIGH;
            else if (portstatus & USB_PORT_STAT_LOW_SPEED)
                udev->speed = USB_SPEED_LOW;
            else
                udev->speed = USB_SPEED_FULL;
            return 0;
        }
 
        /* switch to the long delay after two short delay failures */
        //失败两次,将延时时间设为最长的时间
        if (delay_time >= 2 * HUB_SHORT_RESET_TIME)
            delay = HUB_LONG_RESET_TIME;
 
        dev_dbg (hub->intfdev,
            "port %d not reset yet, waiting %dms/n",
            port1, delay);
    }
 
    return -EBUSY;
}
注意到在上面为speed成员赋值的时候,出现了一个hub_is_wusb().该宏用来判断hcd是否是一个无线的USB主机控制器.如果hcd 是一个无线的,那其下的所有设备的speed均为USB_SPEED_VARIABLE.这个是属于usb2.5 spec里面定义的.
到这里,hub_thread()函数已经分析完了.它已经将hub下连接的所有新设备都初始化并添加进了设备模型.
 
5.2.3:HUB中断URB传输完成的处理
在之前分析中断URB初始化的时候,曾分析到,如果中断URB传输完成,就会调用hub_irq().在分析这个函数之前,我们先从spec上了解一下,对于hub的中断传输到底会传些什么样的东西:
如下图所示:
 
Bit0表示hub的连接状态发生了改变,而bit1~bitN表示的是各端口连接状态的改变.如果1表示改变,为0表示末改变.
现在可以看该函数的代码了,如下:
static void hub_irq(struct urb *urb)
{
    struct usb_hub *hub = urb->context;
    int status = urb->status;
    int i;
    unsigned long bits;
 
    switch (status) {
    case -ENOENT:       /* synchronous unlink */
    case -ECONNRESET:   /* async unlink */
    case -ESHUTDOWN:    /* hardware going away */
        return;
 
    default:        /* presumably an error */
        /* Cause a hub reset after 10 consecutive errors */
        dev_dbg (hub->intfdev, "transfer --> %d/n", status);
        if ((++hub->nerrors < 10) || hub->error)
            goto resubmit;
        hub->error = status;
        /* FALL THROUGH */
 
    /* let khubd handle things */
    case 0:         /* we got data:  port status changed */
        bits = 0;
        for (i = 0; i < urb->actual_length; ++i)
            bits |= ((unsigned long) ((*hub->buffer)[i]))
                    << (i*8);
        hub->event_bits[0] = bits;
        break;
    }
 
    hub->nerrors = 0;
 
    /* Something happened, let khubd figure it out */
    kick_khubd(hub);
 
resubmit:
    if (hub->quiescing)
        return;
 
    if ((status = usb_submit_urb (hub->urb, GFP_ATOMIC)) != 0
            && status != -ENODEV && status != -EPERM)
        dev_err (hub->intfdev, "resubmit --> %d/n", status);
}
从上面的代码可以看出,就将是设HUB中断传输的信息保存在hub->event_bits中,然后又将此URB再次提交,再次提交的结果是,可以轮询获得hub的状态,另外,还会调用kick_khubd().这样, hub_events()就又会调用,又可以处理HUB端口的状态改变.
 
六:小结
在本小结里,对HUB的处理过程做了一个详尽的分析,在这一节里,也了解到了USB的驱动架构以及USB设备的枚举过程.
在下一节里,我们以特定的USB设备分例,来分析USB驱动程序的架构.
  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值