Chinaunix首页 | 论坛 | 博客
  • 博客访问: 15193
  • 博文数量: 14
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 10
  • 用 户 组: 普通用户
  • 注册时间: 2015-12-13 16:51
文章分类
文章存档

2016年(2)

2015年(12)

我的朋友
最近访客

分类: LINUX

2015-12-20 19:28:24

原文地址:linux usb初始化 作者:steven_miao

linux usb初始化

谨以此文纪念过往的岁月

 一.前言

对于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中并不能很好的工作。也许大家对这个函数会很奇怪,不知道该工作队列的作用。

这个工作队列如何使用的,将在看usbsuspendresume时再具体看这个东东具体有什么用。

2.2 bus_register

usb_init中第二个调用的函数是bus_register(&usb_bus_type);这个应该很熟悉吧,注册一个usb总线,其会在/sys/bus下创建一个usb的文件夹,在其中会创建bus属性文件和driversdevices文件夹,以后所有的usb设备和usb驱动都会在这两个文件夹下。bus总线需要指明devicedriver匹配的规则,例如platform busname进行匹配,而usb则以id进行匹配。对于bus_register在前文中讲述多,这里不多此一举了。那主要来看usb bus的特征。

struct bus_type usb_bus_type = {

.name =      "usb",              --定义busname,这个也会是在/sys/bus下看见的文件夹得名称。

    .match =    usb_device_match,   --定义busdevicedriver的匹配规则

    .uevent =   usb_uevent,         --usb的触发事件处理。

};

下面来看usb的匹配规则函数。不过有一点需要说明的是一般usb的驱动为usbinterface的驱动,而不是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中的ididVendor idProductbDeviceClass等参数进行匹配。

    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函数。下面就是cdevops操作集。

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,还注册了两个古怪的驱动,这些种种让人不解。不过这些都将会在后面的学习中来了解这些。

.总结

对于大牛们而言,也许这些东西很简单,不过对于我这个菜鸟而言,却是那么的难弄。在此本文也仅仅是对自己学习的一个记录。

 

 

阅读(401) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~