一.前言
对于usb的普通驱动,我们了解了不少,但是对于usb的真正核心还是不是太理解。该文中对于usb的初始化进行一定的学习,如有不对之处,请各位多多指教。
二.usb子系统初始化。
话说在linux启动之初,就会将usb子系统初始化完成,亦如input子系统和V4L2一样。usb_init就完成了初始化以及启动usb hub守护进程。那来看usb_init中的各个函数的实现。
2.1 ksuspend_usb_init
static int ksuspend_usb_init(void)
{
ksuspend_usb_wq= create_freezeable_workqueue("ksuspend_usbd");
if(!ksuspend_usb_wq)
return-ENOMEM;
return 0;
}
创建一个名为ksuspend_usbd的工作队列。不过在该函数说明中也说明了,该函数在多CPU中并不能很好的工作。也许大家对这个函数会很奇怪,不知道该工作队列的作用。
这个工作队列如何使用的,将在看usbsuspend和resume时再具体看这个东东具体有什么用。
2.2 bus_register
在usb_init中第二个调用的函数是bus_register(&usb_bus_type);这个应该很熟悉吧,注册一个usb总线,其会在/sys/bus下创建一个usb的文件夹,在其中会创建bus属性文件和drivers和devices文件夹,以后所有的usb设备和usb驱动都会在这两个文件夹下。bus总线需要指明device和driver匹配的规则,例如platform bus以name进行匹配,而usb则以id进行匹配。对于bus_register在前文中讲述多,这里不多此一举了。那主要来看usb bus的特征。
struct bus_type usb_bus_type = {
.name = "usb", --定义bus的name,这个也会是在/sys/bus下看见的文件夹得名称。
.match = usb_device_match, --定义bus的device和driver的匹配规则
.uevent = usb_uevent, --usb的触发事件处理。
};
下面来看usb的匹配规则函数。不过有一点需要说明的是一般usb的驱动为usb的interface的驱动,而不是usb设备的驱动。为了方便描述,并没有加以说明,要注意了。
static int usb_device_match(structdevice *dev, struct device_driver *drv)
{
if(is_usb_device(dev)) { --判断是否为usbdevice
if (!is_usb_device_driver(drv)) --判断是否为为usb device的驱动。interface 驱动不能与device匹配。
return0;
return1; --这个不是主要的,下面才是真正的驱动和设备的匹配。
} else {
structusb_interface *intf;
structusb_driver *usb_drv;
conststruct usb_device_id *id;
if (is_usb_device_driver(drv)) --usb device driver不能驱动interface。
return0;
intf =to_usb_interface(dev); --取得设备的当前interface
usb_drv =to_usb_driver(drv); --取得驱动。
id = usb_match_id(intf, usb_drv->id_table); --真正的匹配规则。对于这个函数我们就不加描述。感兴趣的可以去看具体的实现,其实这个函数的额实现也很简单。就是对usb interface中的id如idVendor,idProduct, bDeviceClass等参数进行匹配。
if (id)
return1;
id =usb_match_dynamic_id(intf, usb_drv);
if (id)
return1;
}
return 0;
}
对于usb match比较好理解,而usb_uevent中,则定义了usb的热插拔的处理函数。这个的作用就是将设备的信息存储到env中,可以供用户空间读。对于设备的信息,我们可以通过在用户空间的程序中读取/proc/bus/usb/下的文件取得这些设备的信息。
static int usb_uevent(struct device*dev, struct kobj_uevent_env *env)
{
structusb_device *usb_dev;
if(is_usb_device(dev))
usb_dev= to_usb_device(dev);
else{
structusb_interface *intf = to_usb_interface(dev);
usb_dev= interface_to_usbdev(intf);
}
if(usb_dev->devnum < 0) {
return -ENODEV;
}
if(!usb_dev->bus) {
return -ENODEV;
}
#ifdef CONFIG_USB_DEVICEFS --添加设备名称
if(add_uevent_var(env, "DEVICE=/proc/bus/usb/%03d/%03d",
usb_dev->bus->busnum,usb_dev->devnum))
return-ENOMEM;
#endif
if(add_uevent_var(env, "PRODUCT=%x/%x/%x", --添加设备id
le16_to_cpu(usb_dev->descriptor.idVendor),
le16_to_cpu(usb_dev->descriptor.idProduct),
le16_to_cpu(usb_dev->descriptor.bcdDevice)))
return-ENOMEM;
if(add_uevent_var(env, "TYPE=%d/%d/%d", --添加设备类型。
usb_dev->descriptor.bDeviceClass,
usb_dev->descriptor.bDeviceSubClass,
usb_dev->descriptor.bDeviceProtocol))
return-ENOMEM;
return0;
}
对于uevent的理解可能比较难懂一点,不过你可以把uevent的操作看成将一些设备和驱动的信息写到/proc下对应的文件中,让usrspcae的用户程序可以读取从而获取设备的信息。
2.3 usb_host_init
也许从这个函数名上看不出这个函数的具体应用是什么,既然看函数名不知道干什么,那我们来看该函数的具体实现。你会发现这个函数好像挺简单的,就是创建一个usb host 类。
int usb_host_init(void)
{
intretval = 0;
usb_host_class= class_create(THIS_MODULE, "usb_host");
if(IS_ERR(usb_host_class))
retval= PTR_ERR(usb_host_class);
returnretval;
}
关于这个设备类有什么用,以后再说,咱们还是继续usb init的主线来看usb。
2.4 usb_major_init
注册usb major其实是注册一个以USB_MAJOR为主设备号的cdev。不过其还分配了256个从设备号。提供一个公共的open函数。
int usb_major_init(void)
{
interror;
error= register_chrdev(USB_MAJOR, "usb", &usb_fops);
if(error)
printk(KERN_ERR"Unable to get major %d for usb devices\n",
USB_MAJOR);
returnerror;
}
对于每一个usb设备的打开都会先调用主设备的open,而后根据不同的设备调用不同的open函数。下面就是cdev的ops操作集。
static const struct file_operationsusb_fops = {
.owner= THIS_MODULE,
.open= usb_open,
};
static int usb_open(struct inode *inode, struct file * file)
{
intminor = iminor(inode);
conststruct file_operations *c;
interr = -ENODEV;
conststruct file_operations *old_fops, *new_fops = NULL;
lock_kernel();
down_read(&minor_rwsem);
c= usb_minors[minor]; --关于这个全局变量在以后的usb设备注册时会看到,这里先有个底。
if(!c || !(new_fops = fops_get(c)))
gotodone;
old_fops= file->f_op;
file->f_op= new_fops;
/*Curiouser and curiouser... NULL ->open() as "no device" ? */
if(file->f_op->open)
err= file->f_op->open(inode,file); --调用真正的open
if(err) {
fops_put(file->f_op);
file->f_op= fops_get(old_fops);
}
fops_put(old_fops);
done:
up_read(&minor_rwsem);
unlock_kernel();
returnerr;
}
其实上面的公共的open就是提供一个对于所有的usb 设备的公共接口,真正的open其实是usb_minors中存储的fops->open.
2.5 usb_register
该函数是注册usbfs_driver驱动。不过注册该驱动不知是为什么。
2.6 usb_devio_init
对于这个函数就更加困惑了,这个同样是注册一个以USB_DEVICE_DEV为主设备号的cdev设备。
int __init usb_devio_init(void)
{
intretval;
retval= register_chrdev_region(USB_DEVICE_DEV, USB_DEVICE_MAX,
"usb_device");
if(retval) {
gotoout;
}
cdev_init(&usb_device_cdev,&usbdev_file_operations);
retval= cdev_add(&usb_device_cdev, USB_DEVICE_DEV, USB_DEVICE_MAX);
if(retval) {
gotoerror_cdev;
}
usb_classdev_class= class_create(THIS_MODULE, "usb_device");
if(IS_ERR(usb_classdev_class)) {
gotoout;
}
usb_classdev_class->dev_kobj= NULL;
usb_register_notify(&usbdev_nb);
returnretval;
}
关于这个函数具体有什么用,以后再看,先跳过。
2.7 usbfs_init
int __init usbfs_init(void)
{
intretval;
retval= register_filesystem(&usb_fs_type); --注册filesystem
if(retval)
returnretval;
usb_register_notify(&usbfs_nb);
usbdir= proc_mkdir("bus/usb", NULL); --为usbfs创建挂载点
return0;
}
2.8 usb_hub_init
int usb_hub_init(void)
{
if(usb_register(&hub_driver) < 0) { --注册hub驱动
printk(KERN_ERR"%s: can't register hub driver\n",
usbcore_name);
return-1;
}
khubd_task= kthread_run(hub_thread, NULL, "khubd"); --创建hub守护进程
if(!IS_ERR(khubd_task))
return0;
usb_deregister(&hub_driver);
return-1;
}
2.9 usb_register_device_driver
注册一个通用usb驱动。usb_register_device_driver(&usb_generic_driver,THIS_MODULE);
OK,到此usb init是看完了,不过这里面却有种种的迷雾,让人不解困惑。比如为什么要注册两个usb major,还注册了两个古怪的驱动,这些种种让人不解。不过这些都将会在后面的学习中来了解这些。
三.总结
对于大牛们而言,也许这些东西很简单,不过对于我这个菜鸟而言,却是那么的难弄。在此本文也仅仅是对自己学习的一个记录。