动态加载驱动之热拔插之usb驱动

1.USB主机端的USB集线器监听它的每个端口电压信号的变化。若usb插入主机后,引起信号线的电平信号发生变化,此时主机就知道有新设备插入了。USB
主机检查到设备插入后:
1)首先会重新启动这个设备,接着主机发出Get_Port_Status请求来验证设备是否已经重启,设备重启后主机通过检测根信号线的电平状态判断设备的速度。
2)主机会向设备的控制端点发送Get_Descriptor来了解很多信息,包括:设备通讯终端0的最大包的大小,设备支持的配置号以及有关这个设备的其它信息,主机通过对这些信息的分析以确定接下来的通信动作。
3)主机指定一个地址,发送Set_Address标准请求设置设备的地址。
4)主机使用新的地址,多次发送Get_Descriptor获取各种描述符。
以上都是总线驱动进行数据的交换。
至如主机如何检测端口是否插入:是usb初始化时在usb_init函数中调用usb_hub_init函数,就进入了hub的初始化。
在usb_hub_init函数中完成了注册hub驱动,并且利用函数kthread_run创建一个内核线程。该线程用来管理监视hub的状态,所有的情况都通过该线程来报告。
hub_thread()->hub_events()->hub_port_connect_change()->usb_new_device()->device_add()->kobject_uevent();

以上是插入usb至枚举结束过程。
参考:http://www.baiheee.com/OpenSource/Easy%20USB%2051%20Programer/Easy%20USB%2051%20Programer_txgc.htm
http://blog.csdn.net/YAOZHENGUO2006/article/details/7748896
在枚举结束后,已经获得了硬件设备的一些信息,如各种描述符。
接下来会调用device_add()函数。也是设备热插拔的uevent事件最终的源头。
热拔插章节

热拔插的主要实现函数是在kset中:
kset中包含kset_uevent_ops,里面主要定义了三个函数

      int (*filter)(struct kset *kset, struct kobject *kobj);
       const char *(*name)(struct kset *kset, struct kobject *kobj);
       int (*uevent)(struct kset *kset, struct kobject *kobj, struct kobj_uevent_env *env);

这三个函数都与uevent相关。filter用于判断uevent是否要发出去。name用于得到subsystem的名字。uevent用于填充env变量。
从device_add()函数说起:

 <drivers/base/core.c>
    int device_add(struct device *dev)
    {
          ...
          kobject_uevent(&dev->kobj, KOBJ_ADD);
          ...
    }

热拔插在device_add中核心实现就是kobject_uevent函数的调用。device_add中涉及的热拔插action是KOBJ_ADD,那么移除设备自然对应KOBJ_REMOVE了。kobject_uevent函数最终调用的是kobject_uevent_env。
热拔插的真正实现就是在kobject_uevent_env函数中。
下面给出kobject_uevent_env函数的核心框架:

int kobject_uevent_env(struct kobject *kobj, enum kobject_action action,
                           char *envp_ext[])
    {
            ...
    #if defined(CONFIG_NET)
            /* send netlink message */
            ...
    #endif

            /* call uevent_helper, usually only enabled during early boot */
            if (uevent_helper[0] && !kobj_usermode_filter(kobj)) {
                    char *argv [3];

                    argv [0] = uevent_helper;
                    argv [1] = (char *)subsystem;
                    argv [2] = NULL;
                    retval = add_uevent_var(env, "HOME=/");
                    if (retval)
                            goto exit;
                    retval = add_uevent_var(env,
                                            "PATH=/sbin:/bin:/usr/sbin:/usr/bin");
                    if (retval)
                            goto exit;

                    retval = call_usermodehelper(argv[0], argv,
                                                 env->envp, UMH_WAIT_EXEC);
            }

            ...
    }

这段代码主要作用如下:
kobject_uevent_env:
由kobject的parent向上查找,直到找到一个kobject包含kset。
如果kset中有filter函数,调用filter函数,看看是否需要过滤uevent消息。
如果kset中有name函数,调用name函数得到subsystem的名字;否则,subsystem的名字是kset中kobject的名字。
分配一个kobj_uevent_env,并开始填充env环境变量:
增加环境变量ACTION=< name>
增加环境变量DEVPATH=< path>
增加环境变量SUBSYSTEM=< name>
增加环境变量kobject_uevent_env中参数envp_ext指定的环境变量。
调用kset的uevent函数,这个函数会继续填充环境变量。
增加环境变量SEQNUM=,这里seq是静态变量,每次累加。
调用netlink发送uevent消息。
调用uevent_helper,最终转换成对用户空间sbin/mdev的调用。

1.底层netlink消息发送部分:与应用层Udevd消息接收相关部分。
参考:http://blog.chinaunix.net/uid-14753126-id-2978566.html,netlink socket提供了一组开发者熟悉的BSD风格的API函数。
如netlink_broadcast_filtered。通过这个函数实现netlink socket通讯,在内核和用户空间之间传递信息。
内核调用kobject_uevent函数发送netlink message给用户空间,Netlink socket作为一种内核与用户空间的通信方式,不仅仅用在hotplug机制中,同样还应用在其它很多真正和网络相关的内核子系统中。
Udevd通过标准的socket机制,创建socket连接来获取内核广播的uevent事件 并解析这些uevent事件。
udevd根据消息和环境变量,查询/sys的变化,在/dev目录下自动创建设备节点。
创建文件节点 Udevtrigger的工作机制
udevtrigger通过向/sysfs 文件系统下现有设备的uevent节点写”add”字符串,从而触发uevent事件,使得udevd能够接收到这些事件,并创建buildin的设备驱动的设备节点以及所有已经insmod的模块的设备节点。

udev通过netlink监听uevent消息,它能完成两个功能:
1.自动加载模块
2.根据uevent消息在dev目录下添加、删除设备节点。
另一个是mdev,mdev在busybox的代码包中能找到,它通过上节提到的uevent_helper函数被调用。

下面简要介绍udev的模块自动加载过程:规则是自动加载驱动?
etc目录下有一个uevent规则文件/etc/udev/rules.d/50-udev.rules
udev程序收到uevent消息后,在这个规则文件里匹配,如果匹配成功,则执行这个匹配定义的shell命令。例如,规则文件里有这么一行:
ACTION==”add”, SUBSYSTEM==”?“, ENV{MODALIAS}==”?“, RUN+=”/sbin/modprobe $env{MODALIAS}”
所以,当收到uevent的add事件后,shell能自动加载在MODALIAS中定义的模块。

下面简要介绍mdev的模块自动加载过程:规则是自动加载驱动?
它的配置文件在/etc/mdev.conf中。例如:
MODALIAS=.0:0660@modprobe" MODALIAS”
这条规则指的是:当收到的环境变量中含有MODALIAS,那么加载MODALIAS代表的模块。
mdev的详细说明在busybox的docs/mdev.txt中。
参考:http://blog.csdn.net/bingqingsuimeng/article/details/7924473

2.讨论后半段的if (uevent_helper[0] && !kobj_usermode_filter(kobj))代码
这里的核心调用是call_usermodehelper,这个函数最有意思的地方就在于在内核空间调用用户空间的程序,它的详细实现机制在书中已经讲得很多,这里就不再赘述了。call_usermodehelper在kobject_uevent_env函数中要调用的用户空间程序由uevent_helper[0]来指定,所以如果我们能控制这个uevent_helper[0],就能接收到设备加入系统移出系统等事件。那个if中的kobj_usermode_filter条件一般都会满足(除非这是个特别注意个人隐私的设备,那就不好说了,人家偷偷加入系统就是不想让你知道你也没有办法,但是udev还是能知道的)。
参考:http://blog.csdn.net/bingqingsuimeng/article/details/7924300

以上对于usb设备已经识别且驱动已经成功加载了。
还有以下参考:http://blog.chinaunix.net/uid-30016340-id-4633121.html

猜你喜欢

转载自blog.csdn.net/qq_28219531/article/details/76156815