一定要让你彻底明白什么是USB子系统

这里写图片描述

usb_debugfs_init
/**
    这个函数主要做了一下事情:
                 在debugfs中创建了一个文件,并指定了操作他的函数。
*/
static int usb_debugfs_init(void)
{
    /**
        在debugfs文件系统中创建一个目录。
        "usb" : 目录的名称
        NULL  : 这个目录的父目录,如果时NULL,那么就放到debugfs目录下。(目录结构见图1)
    */
    usb_debug_root = debugfs_create_dir("usb", NULL);
    if (!usb_debug_root)
    {
        return -ENOENT;
    }
    /**
        在/sys/kernel/debug/usb目录下创建 devices文件节点。权限是 0444 ,

        向vfs层提供的接口函数是usbfs_devices_fops中定义的函数
        const struct file_operations usbfs_devices_fops = {
            .llseek =   usb_device_lseek,
            .read =     usb_device_read,
            .poll =     usb_device_poll,
        };
        后面详细分析这些函数
    */
    usb_debug_devices = debugfs_create_file("devices", 0444,usb_debug_root, NULL,&usbfs_devices_fops);
    if (!usb_debug_devices) 
    {
        debugfs_remove(usb_debug_root); //从debugfs中移除。
        usb_debug_root = NULL;
        return -ENOENT;
    }

    return 0;
}

图 1
这里写图片描述
这里写图片描述

bus_register
/**
    注册总线。总线也是一种设备。
    这个函数在以前博文中已经说过,请参考我以前的博文

    看一下usb总线,注册后会生成两个容器,分别盛放挂接再该总线下所有的驱动、设备。
    struct bus_type usb_bus_type = {
        .name =     "usb",
        .match =    usb_device_match
        .uevent =   usb_uevent,
    };

    下面自己注册一条总线,看一下注册总线时涉及到的重要函数。
    实验 见图2
*/
retval = bus_register(&usb_bus_type);
if (retval)
{
    goto bus_register_failed;
}       

这里写图片描述
这里写图片描述
这里写图片描述

bus_register_notifier内核通知链
/**
    usb总线通知链的注册。

    对于通知链,再上篇电源管理赏析中我们就见过了,再冻结APP、内核线程前会通知各个驱动 再启动app、内核线程之后也会通知各个驱动,

看下面:当设备加入、卸载的时候 也 会发通知。
    1 : 设备加入
        devic_add()
        {   
                if (dev->bus)
                {
                    blocking_notifier_call_chain(&dev->bus->p->bus_notifier,BUS_NOTIFY_ADD_DEVICE, dev);
                    {
                        最终调用:
                        ret = nb->notifier_call(nb, val, v);
                    }
                }
        }

2 : 设备卸载
        void device_del()
        {
            ...                 
            blocking_notifier_call_chain(&dev->bus->p->bus_notifier,BUS_NOTIFY_DEL_DEVICE, dev);
        }   

    下面有一个小实验,来验证,见图3、4
*/
int bus_register_notifier(struct bus_type *bus, struct notifier_block *nb)
{
    return blocking_notifier_chain_register(&bus->p->bus_notifier, nb);
}

图3:
这里写图片描述

int usb_major_init(void)
{
    int error;
    /**
        注册一个字符设备。看清楚,**这里是bus**,不是usb_device.一条总线也是一个设备。
        #define USB_MAJOR   180  主设备号 180
        向vfs层提供的操作函数集合是usb_fops
        看一下:
            static const struct file_operations usb_fops = {
                .owner =    THIS_MODULE,
                .open =     usb_open,
                .llseek =   noop_llseek,
            };
            这个和input子系统中提供的及其类似。甚至usb_open 和 input_open的逻辑也是极其类似的。
            只是usb_open中对临界区的保护机制使用的是读写锁,input_open使用的是互斥锁。

            内核字符设备对于open函数大部分都是这样设计。那么这样设计就是驱动分层的体现吧。
            由通用层转入具体设备提供的驱动层。

            input_open函数的赏析请见我以前的博文:
                             http://blog.csdn.net/leesagacious/article/details/50245937
    */
    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;
}
/**

    他做了哪些事情呢?很简单,字符设备的基本函数。

        1 : register_chrdev_region(USB_DEVICE_DEV, USB_DEVICE_MAX,"usb_device")
        2 : cdev_init(&usb_device_cdev, &usbdev_file_operations)
        3 : cdev_add(&usb_device_cdev, USB_DEVICE_DEV, USB_DEVICE_MAX)
        4 : usb_register_notify(&usbdev_nb);

        看一下 向vfs层提供的操作函数集
            const struct file_operations usbdev_file_operations = {
                    .owner =      THIS_MODULE,
                    .llseek =     usbdev_lseek,
                    .read =       usbdev_read,
                    .poll =       usbdev_poll,
                    .unlocked_ioctl = usbdev_ioctl,
                #ifdef CONFIG_COMPAT
                    .compat_ioctl =   usbdev_compat_ioctl,
                #endif
                    .open =       usbdev_open,
                    .release =    usbdev_release,
                };  
*/
retval = usb_devio_init();
if (retval)
{
    goto usb_devio_init_failed;
}   
/**
    这个函数请见上面的流程图。最终会调用bus_register()
    #define usb_register(driver) \
        usb_register_driver(driver, THIS_MODULE, KBUILD_MODNAME)

    bus_register()这个函数的赏析请见我以前的博文:http://blog.csdn.net/leesagacious/article/details/50200053
*/
retval = usb_register(&usbfs_driver);
if (retval)
{
    goto driver_register_failed;
}   
/**
    USB设备文件系统的初始化。
    现在你明白了usb设备文件系统是动态产生的了吧。和sys、proc一样都是动态产生的。
    看他的实现:
            int __init usbfs_init(void)
            {
                int retval;

                retval = register_filesystem(&usb_fs_type);//注册usb设备文件系统
                if (retval)
                {
                    return retval;
                }
                usb_register_notify(&usbfs_nb);        //usbfs_notify()函数会被调用。  

                usbdir = proc_mkdir("bus/usb", NULL); //创建挂载点。

                return 0;
            }
*/
retval = usbfs_init();
if (retval)
{
        goto fs_init_failed;
}       
/**
    hub的初始化。
      详细请看我以前的博文: 
                  http://blog.csdn.net/leesagacious/article/details/50506854

      主要是注册了一个hub驱动、创建了一个内核守护线程来监测hub端口的状态变化。             
*/
retval = usb_hub_init();
if (retval)
{
        goto hub_init_failed;
}       
/**
    终于再最后注册了usb_device的driver到usb的核心层了。
    注意 : 是 usb设备驱动: usb_device_driver,不是usb_driver(每个接口对应了一个特定的功能,应有专门的驱动来和它沟通)
    。
*/
retval = usb_register_device_driver(&usb_generic_driver, THIS_MODULE);
if (!retval)
{
    goto out;
}   
U盘驱动
/**
    这是一个内核线程。很重要。
    你可能会问:是谁创建了这个内核线程,他的作用是什么?请往下看。
*/
static int usb_stor_control_thread(void * __us)
{
    /**
        这个内核线程是再函数 : 
            usb_stor_acquire_resources
            {
                ....
                第二个参数是 : us_data

                要创建内核线程的信息被组织一个结构体 kthread_create_info  被挂接到 kthread_create_list链表上。
                而且 二号进程也被唤醒了,二号进程要从kthread_create_list链表上获取、创建他了。

                th = kthread_run(usb_stor_control_thread, us, "usb-storage");

            下面是 3.4内核的代码:   相比上面4.1的代码,4.1的一部到位。

                    下面的wake_up_process(th)激活了他。
                    th = kthread_create(usb_stor_control_thread, us, "usb-storage");
                    ....    
                    wake_up_process(th);
            }
        转换成 us_data,这个us_data太重要了,整个架构中都有他的身影。详细分析他每一个成员再框架中的生命历程 放到下面。 
    */
    struct us_data * us = (struct us_data *)__us;
    /**
        是把u盘模拟成了一个 SCSI设备。所以再配置内核的时候,要增加对U盘的支持,
        而且SCSI也要支持好。

        既然底层是依赖SCSI,那么内核已经做了下面的事情:
                1 : 为Scsi_host分配内存空间,使用函数 us_to_host()
                2 : 添加到SCSI核心层        使用函数 us_add_host()
                3 : Scsi_scan_host()      他会扫描添加的设备。

        SCSI封装的很好,上层只要一个api就搞定了。

        看一下 us_to_host()这个函数,他很重要,

        源码的注释是这样说的 : 
            Convert between us_data and the corresponding Scsi_Host

        就是再us_data和 Scsi_Host之间转换。这个函数让USB驱动与SCSI驱动关联了起来。
        由us_data 可以获取Scsi_host ,由Scsi_Host可以获取到us_data

        下面有一幅配置的图 可供参考。图5
        下面还有一幅图来验证这个内核线程创建的流程 图6
    */
    struct Scsi_Host *host = us_to_host(us);

    /**
        死循环。看他什么时候 能跳出来。
    */
    for (;;) {
        usb_stor_dbg(us, "*** thread sleeping\n");
        /**
            进程开始休眠。
            有可能会从这儿跳出死循环。

            看他的参数
                &us->cmnd_ready : 是 0。
                为什么?

                static int storage_probe(struct usb_interface *intf,const struct usb_device_id *id)
                {
                    int usb_stor_probe1(struct us_data **pus,
                        struct usb_interface *intf,
                        const struct usb_device_id *id,
                        struct us_unusual_dev *unusual_dev)
                    {
                        struct us_data *us;
                        ....
                        init_completion(&us->cmnd_ready); 看这里,被初始化了。
                        init_completion(&(us->notify));
                    }
                }

                老版本用的是 信号量,这里用了完成量,那么 谁会调用completion()呢?
                往下面看。图7实验验证。
                他直接就睡了。
        */
        if (wait_for_completion_interruptible(&us->cmnd_ready))
            break;
        /**
            被唤醒后执行的代码
        */
        usb_stor_dbg(us, "*** thread awakened\n");

        mutex_lock(&(us->dev_mutex));
}   

usb3.0的U盘。
这里写图片描述

下面是插入u盘后的log: 看 storage_probe()被调用了
这里写图片描述
这里写图片描述

  • 4
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 7
    评论
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值