Linux下的USB HUB驱动
[日期:2012-07-29]
来源:Linux社区
作者:zhengmeifu
[字体:大 中 小]
接下来,就要对设备进行配置了,首先,将设备唤醒.回忆一下我们在分析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状态.