分类: LINUX
2015-12-20 19:28:24
原文地址:linux usb初始化 作者:steven_miao
谨以此文纪念过往的岁月
一.前言
对于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_(struct device *dev, struct device_driver *drv)
{
if (is_usb_device(dev)) { --判断是否为usb device
if (!is_usb_device_driver(drv)) --判断是否为为usb device的驱动。interface 驱动不能与device匹配。
return 0;
return 1; --这个不是主要的,下面才是真正的驱动和设备的匹配。
} else {
struct usb_interface *intf;
struct usb_driver *usb_drv;
const struct usb_device_id *id;
if (is_usb_device_driver(drv)) --usb device driver不能驱动interface。
return 0;
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)
return 1;
id = usb_match_dynamic_id(intf, usb_drv);
if (id)
return 1;
}
return 0;
}
对于usb match比较好理解,而usb_uevent中,则定义了usb的热插拔的处理函数。这个的作用就是将设备的信息存储到env中,可以供用户空间读。对于设备的信息,我们可以通过在用户空间的程序中读取/proc/bus/usb/下的文件取得这些设备的信息。
static int usb_uevent(struct device *dev, struct kobj_uevent_env *env)
{
struct usb_device *usb_dev;
if (is_usb_device(dev))
usb_dev = to_usb_device(dev);
else {
struct usb_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;
return 0;
}
对于uevent的理解可能比较难懂一点,不过你可以把uevent的操作看成将一些设备和驱动的信息写到/proc下对应的文件中,让usrspcae的用户程序可以读取从而获取设备的信息。
2.3 usb_host_init
也许从这个函数名上看不出这个函数的具体应用是什么,既然看函数名不知道干什么,那我们来看该函数的具体实现。你会发现这个函数好像挺简单的,就是创建一个usb host 类。
int usb_host_init(void)
{
int retval = 0;
usb_host_class = class_create(THIS_MODULE, "usb_host");
if (IS_ERR(usb_host_class))
retval = PTR_ERR(usb_host_class);
return retval;
}
关于这个设备类有什么用,以后再说,咱们还是继续usb init的主线来看usb。
2.4 usb_major_init
注册usb major其实是注册一个以USB_MAJOR为主设备号的cdev。不过其还分配了256个从设备号。提供一个公共的open函数。
int (void)
{
int error;
error = register_chrdev(USB_MAJOR, "usb", &usb_fops);
if (error)
printk(KERN_ERR "Unable to get major %d for usb devices\n",
USB_MAJOR);
return error;
}
对于每一个usb设备的打开都会先调用主设备的open,而后根据不同的设备调用不同的open函数。下面就是cdev的ops操作集。
static const struct file_operations usb_fops = {
.owner = THIS_MODULE,
.open = usb_open,
};
static int usb_open(struct inode * inode, struct file * file)
{
int minor = iminor(inode);
const struct file_operations *c;
int err = -ENODEV;
const struct file_operations *old_fops, *new_fops = NULL;
lock_kernel();
down_read(&minor_rwsem);
c = [minor]; --关于这个全局变量在以后的usb设备注册时会看到,这里先有个底。
if (!c || !(new_fops = fops_get(c)))
goto done;
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();
return err;
}
其实上面的公共的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)
{
int retval;
retval = register_chrdev_region(, USB_DEVICE_MAX,
"usb_device");
if (retval) {
goto out;
}
cdev_init(&usb_device_cdev, &usbdev_file_operations);
retval = cdev_add(&usb_device_cdev, USB_DEVICE_DEV, USB_DEVICE_MAX);
if (retval) {
goto error_cdev;
}
usb_classdev_class = class_create(THIS_MODULE, "usb_device");
if (IS_ERR(usb_classdev_class)) {
goto out;
}
usb_classdev_class->dev_kobj = NULL;
usb_register_notify(&usbdev_nb);
return retval;
}
关于这个函数具体有什么用,以后再看,先跳过。
2.7 usbfs_init
int __init usbfs_init(void)
{
int retval;
retval = register_filesystem(&usb_fs_type); --注册filesystem
if (retval)
return retval;
usb_register_notify(&usbfs_nb);
usbdir = proc_mkdir("bus/usb", NULL); --为usbfs创建挂载点
return 0;
}
2.8 usb_hub_init
int (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))
return 0;
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,还注册了两个古怪的驱动,这些种种让人不解。不过这些都将会在后面的学习中来了解这些。
三.总结
对于大牛们而言,也许这些东西很简单,不过对于我这个菜鸟而言,却是那么的难弄。在此本文也仅仅是对自己学习的一个记录。