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()。