usb_generic_driver函数

usb_generic_driver在自己的生命线里,以一己之力将设备的各个接口送给了linux的设备模型,让usb总线的match函数,也就是usb_device_match,在自己的那条驱动链表里为它们寻找一个合适的接口驱动程序。
下面分析usb_generic_driver函数:

struct usb_device_driver usb_generic_driver = {
    .name =    "usb",
    .probe = generic_probe,
    .disconnect = generic_disconnect,
#ifdef    CONFIG_PM
    .suspend = generic_suspend,
    .resume = generic_resume,
#endif
    .supports_autosuspend = 1,
};

当usb设备(只有设备先被注册之后才会分析接口,才会注册接口),被探测并被注册到系统之后(用device_add),会调用usb_bus_type.match()(只要是usb设备,都会跟usb_generic_driver匹配上),之后会调用usb_probe_device(),从而引发usb_generic_deiver的probe()调用,也就是generic_probe。
下面将会对generic_probe函数进行分析:

static int generic_probe(struct usb_device *udev)
{
    int err, c;

    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_notify_add_device(udev);
    return 0;
}

usb_generic_driver中的generic_probe函数,这个函数是一个usb设备的第一个匹配的driver。Generic通用,只要是个usb设备就得先跟他来一段,usb设备驱动界的老大。他的probe干啥了呢?很简单!找个合适的配置,配置一下。从此usb设备就进入配置的时代了。(前期的工作谁做的呢,到这都已经设置完地址了,当然是hub了,hub发现设备后,会进行前期的枚举过程,获得配置,最终调用device_add将该usb设备添加到总线上。这个过程可以专门来一大段,是hub的主要工作,所以需要把hub单独作为一个家族来对待,人家可是走在第一线的默默无闻的工作者,默默的将设备枚举完成后,将这个设备添加到usb总线上,多伟大)。

注意:设备setconfig时参数只能为0或者合理的配置值,0就代表不配置,仍然是寻址态。不过有些设备就是拿配置0作为配置值得。

usb_choose_configuration从设备可能的众多配置(udev->descriptor.bNumConfigurations)选择一个合适的配置(struct usb_host_config),并返回该配置的索引值。

//为usb device选择一个合适的配置
int usb_choose_configuration(struct usb_device *udev)
{
    int i;
    int num_configs;
    int insufficient_power = 0;
    struct usb_host_config *c, *best;
 
    best = NULL;
    //udev->config,其实是一个数组,存放设备的配置.usb_dev->config[m]-> interface[n]表示第m个配置的第n个接口的intercace结构.(m,n不是配置序号和接口序号).
    c = udev->config;
    //config项数
    num_configs = udev->descriptor.bNumConfigurations;
    //遍历所有配置项
    for (i = 0; i < num_configs; (i++, c++)) {
        struct usb_interface_descriptor *desc = NULL;
 
        //配置项的接口数目
        //取配置项的第一个接口
        if (c->desc.bNumInterfaces > 0)
            desc = &c->intf_cache[0]->altsetting->desc;
 
        ... ...

        //电源不足.配置描述符中的电力是所需电力的1/2
        if (c->desc.bMaxPower * 2 > udev->bus_mA) {
            insufficient_power++;
            continue;
        }
   
         //非标准Ethernet-over-USB协议
   if (i == 0 && num_configs > 1 && desc &&
            (is_rndis(desc) || is_activesync(desc))){
        
      ... ...

     }
        //选择一个不是USB_CLASS_VENDOR_SPEC的配置
     else if (udev->descriptor.bDeviceClass !=
                        USB_CLASS_VENDOR_SPEC &&
                (!desc || desc->bInterfaceClass !=
                        USB_CLASS_VENDOR_SPEC)) {
            best = c;
            break;
        }
 
        /*如果所有剩下的配置是特殊的vendor,选择第一个*/
        else if (!best)
            best = c;
    }
 
    ... ...

    //如果选择好了配置,返回配置的序号,否则,返回-1
    if (best) {
        i = best->desc.bConfigurationValue;
        dev_info(&udev->dev,
            "configuration #%d chosen from %d choice%s\n",
            i, num_configs, plural(num_configs));
    } else {
        i = -1;
        dev_warn(&udev->dev,
            "no configuration chosen from %d choice%s\n",
            num_configs, plural(num_configs));
    }

    return i;
}    

例如:我机器上的的 usb 驱动加载时,输出:usb 1-1: configuration #1 chosen from 3 choices

表示:此设备有3个配置,而驱动最终选择了索引号为1的配置,至于选择策略是怎样的,请看usb_choose_configuration()函数。 
generic_probe函数中的usb_set_configuration函数里有很重要的动作,不是简单的设置个配置,当我们选择了某一个配置后,需要将这个配置的所有接口取出来,初始化接口作为驱动对应的一种”设备”的参数,如总线类型、设备类型等,调用device_add将该接口设备添加到设备模型中。

int usb_set_configuration(struct usb_device *dev, int configuration)
{
    ... ...

    if (cp && configuration == 0)
        dev_warn(&dev->dev, "config 0 descriptor??\n");

  /*首先,根据选择好的配置号找到相应的配置,在这里要注意了, dev->config[]数组中的配置并不是按照配置的序号来存放的,而是按照遍历到顺序来排序的.因为有些设备在发送配置描述符的时候,并不是按照配置序号来发送的,例如,配置2可能在第一次GET_CONFIGURATION就被发送了,而配置1可能是在第二次GET_CONFIGURATION才能发送.
    取得配置描述信息之后,要对它进行有效性判断,注意一下本段代码的最后几行代码:usb2.0 spec上规定,0号配置是无效配置,但是可能有些厂商的设备并末按照这一约定,所以在linux中,遇到这种情况只是打印出警告信息,然后尝试使用这一配置.*/
    n = nintf = 0;
    if (cp) {
        //接口总数
        nintf = cp->desc.bNumInterfaces;
        //在这里, 注要是为new_interfaces分配空间,要这意的是, new_interfaces是一个二级指针,它的最终指向是struct usb_interface结构.特别的,如果总电流数要小于配置所需电流,则打印出警告消息.实际上,这种情况在usb_choose_configuration()中已经进行了过滤.
        new_interfaces = kmalloc(nintf * sizeof(*new_interfaces),
                GFP_KERNEL);
        ... ... 
 
        for (; n < nintf; ++n) {
            new_interfaces[n] = kzalloc(
                    sizeof(struct usb_interface),
                    GFP_KERNEL);
             ... ...
        }
 
        //如果总电源小于所需电流,打印警告信息
        i = dev->bus_mA - cp->desc.bMaxPower * 2;
        ... ...
    }
 
    //要对设备进行配置了,先唤醒它
    ret = usb_autoresume_device(dev);
    if (ret)
        goto free_interfaces;
 
     //不是处于ADDRESS状态,先清除设备的状态
    if (dev->state != USB_STATE_ADDRESS)
        usb_disable_device(dev, 1); /* Skip ep0 */
 
    //确定我们有足够带宽提供这个配置
    ret = usb_hcd_alloc_bandwidth(dev, cp, NULL, NULL);
    ... ...

    //发送控制消息,选取配置
    ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
                  USB_REQ_SET_CONFIGURATION, 0, configuration, 0,
                  NULL, 0, USB_CTRL_SET_TIMEOUT);
     ... ...
    }
 
    //dev->actconfig存放的是当前设备选取的配置
    dev->actconfig = cp;
     ... ...
    //将状态设为CONFIGURED
    usb_set_device_state(dev, USB_STATE_CONFIGURED);
    /*接下来,就要对设备进行配置了,首先,将设备唤醒.只有在ADDRESS状态才能转入到CONFIG状态.(SUSPEND状态除外). 所以,如果设备当前不是处于ADDRESS状态,就需要将设备的状态初始化
    接着,发送SET_CONFIGURATION的Control消息给设备,用来选择配置最后,将dev->actconfig指向选定的配置,将设备状态设为CONFIG*/
 
     //遍历所有的接口
    for (i = 0; i < nintf; ++i) {
        struct usb_interface_cache *intfc;
        struct usb_interface *intf;
        struct usb_host_interface *alt;
      /*之前初始化的new_interfaces在这里终于要派上用场了.初始化各接口,从上面的初始化过程中,我们可以看出:
    Intf->altsetting,表示接口的各种设置
    Intf->num_altsetting:表示接口的设置数目
    Intf->intf_assoc:接口的关联接口(定义于minor usb 2.0 spec)
    Intf->cur_altsetting:接口的当前设置.*/
        cp->interface[i] = intf = new_interfaces[i];
        intfc = cp->intf_cache[i];
        intf->altsetting = intfc->altsetting;
        intf->num_altsetting = intfc->num_altsetting;
        //是否关联的接口描述符,定义在minor usb 2.0 spec中
        intf->intf_assoc = find_iad(dev, cp, i);
        kref_get(&intfc->ref);
 
        //选择0号设置
        alt = usb_altnum_to_altsetting(intf, 0);

        //如果0号设置不存在,选排在第一个设置
        if (!alt)
            alt = &intf->altsetting[0];
 
        //当前的配置
        intf->cur_altsetting = alt;
     //用来启用接口,也就是启用接口中的每一个endpoint.
        usb_enable_interface(dev, intf);
        //注意这个地方对intf内嵌的struct devcie结构赋值,它的type被赋值为了usb_if_device_type.bus还是usb_bus_type.可能你已经反应过来了,要和这个device匹配的设备是interface的驱动.
        intf->dev.parent = &dev->dev;
        intf->dev.driver = NULL;
        intf->dev.bus = &usb_bus_type;
        intf->dev.type = &usb_if_device_type;
        intf->dev.dma_mask = dev->dev.dma_mask;
        device_initialize(&intf->dev);//device 初始化
        mark_quiesced(intf);
     /*
       device的命名:
      dev指的是这个接口所属的usb_dev,结合我们之前在UHCI中关于usb设备命名方式的描述.可得出它的命令方式如下:
      USB总线号-设备路径:配置号.接口号.
      例如,在我的虚拟机上:/sys/bus/usb/devices
      1-0:1.0 usb1
      可以得知,系统只有一个usb control.
      1-0:1.0:表示,第一个usb control下的root hub的1号配置的0号接口.
        */
        sprintf(&intf->dev.bus_id[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);

    //注册每一个接口?
    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);//增加device
        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状态.

到此为止usb_generic_driver凭借自己的博爱的胸襟将所有设备的各个接口添加到了linux的设备模型中。

usb设备首先以设备的身份与usb_generic_driver匹配,成功之后,会分裂出接口,当对接口调用device_add()后,会引起接口和接口驱动的匹配,这个匹配还是用usb_bus_type.mach()函数。因为接口的device->bus=& usb_bus_type, 这跟usb设备是一样的,所以,都会调用到usb_bus_type.mach()。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值