感谢支持
我们一直在努力

Linux Driver 编写思考

Linux 下面对 driver 的定义:


struct device_driver {


const char              * name;


struct bus_type              * bus;


struct completion    unloaded;


struct kobject         kobj;


struct klist             klist_devices;


struct klist_node     knode_bus;


struct module         * owner;


const char             * mod_name;  /* used for built-in modules */


struct module_kobject    * mkobj;


int   (*probe)  (struct device * dev);


int   (*remove)      (struct device * dev);


void (*shutdown)   (struct device * dev);


int   (*suspend)      (struct device * dev, pm_message_t state);


int   (*resume)       (struct device * dev);


unsigned int multithread_probe:1;


};


我们一个个来看,


Name :就是这个 driver 的名字;


Bus :就是这个 driver 是挂在上面 bus 上面的;


Unloaded :这个以后再讨论;


Kobj :这个可以理解为 driver 结构的父亲,如果从面向对象的角度来看的话;


Klist_devices: 这个就是由此 driver 驱动的设备列表;


Knode_bus: 这个就是用来挂在它说属的 bus 链表上的节点,顺着这个链表就可以找到所有的挂在这个 bus 上的所有的 driver ;


Owner :这个 driver 所属的模块;


Mod_name: 模块名字;


mkobj: 模块的顶层描述;


probe :这个是很关键的函数,用来初始化此 driver 驱动的硬件,还有其它能够正常为应用层提供服务说需要提前做的事都需要在这里做;


remove :这个就是移除的时候做的事情;


suspend :这个应该是睡眠的时候做的事情,也就是说上层通过这个函数实现对硬件的电源策略控制;


resume :这个就是从睡眠中醒来需要对硬件所做的事都需要在这里做;


multithread_probe :是否启用多线程 probe ;


分析完了 driver 的结构,我们看看为什么需要这样的设计,也就是说,如果我们自己需要实现一套架构用来实现同样的功能,我们需要做什么呢?


Driver 应该提供的功能

driver 应该提供哪些功能呢?


1,  为应用层使用 driver 提供接口;


这个应该是很好理解的,不可能让每个上层软件的作者自己写驱动来访问硬件;所以必须要抽象出一套接口,但是需要哪些接口呢?


Linux 的 driver 实现很有意思,把任何东西都抽象成文件,包括硬件,这样对硬件的操作也就只需提供 vfs 所需的一套接口,当然其实这些接口的类型在很大程度上划分了 driver 的类型,而这些接口也可以提供其它的形式,比如网卡就不提供这些接口;


Open :打开,也就是打开这个设备,这只是抽象的概念,很多硬件设备不存在这样的物理动作,比如门,我们才说打开,当然如果理解所谓“打开心扉”也就很好理解这里的 driver 所抽象出的打开了;先前所说硬件的初始化可以在 probe 函数里面做,当然我想有些动作放在 open 里面做也可以的,但是必须要考虑的是, open 是可能出现许多个进程来访问的,或者你自己要上锁或者你就要考虑可重入性,也就是说如果一些动作只能做一次,那么显然放在 open 里面来做就不合适了。


Close :这个和 open 相反的动作,但是这个 close 是上层才有的一个概念,在驱动里面用来一个叫 release 的接口实现,至于 close 到 release 之间的转换就需要去看看文件系统这层的实现了。


Read :这就是读取数据了,没什么好说,比如串口,比如 touch panel ;


Write :这就是写了,也就是往设备里面写数据;


其它的一些对上的接口 …. 就不讨论了;


而 driver 还必须要提供其它的接口:那就是做为 linux driver 本身说必须要提供的接口,这些接口通常是内核自身所调用的;


那就是


Probe :这个函数很关键,它作为一个桥梁连接设备和 driver ,并且必须要对硬件进行初始化,以便在以后的用户接口调用的时候设备能正常工作;从软件的角度说,许多空间的分配等都在这里做,通过 dev_set_drvdata 和 dev_get_drvdata 设置和获取相应的数据;


Remove :这个函数和上面这个函数是相反的,也就是负责清理扫尾的工作,负责释放数据结构,停止硬件等;


Suspend :这个是在睡眠的时候调用的,在这里就必须要进行功耗控制;


Resume :这个就是唤醒的时候调用的,它需要执行相应的恢复动作,以便硬件能正常工作;

2,  实现对硬件设备的访问控制;


只提供接口不负责实现,就是所谓的“银样蜡枪头 ” ,所以驱动最复杂的地方在于如何控制硬件实现我们想实现的功能;而这些具体的实现通常就包含在给上层提供的接口中;而另外一方面一个驱动要实现某项功能很多时候不是自己独立实现的,而是依赖于其它的模块,比如 WIFI 驱动,在我们的平台上,是通过 sdio 口连接的,而 sdio 口是 mmc controller 提供的,而 mmc 本身又是通过 gpio 口实现的,换句话说,如果要 wifi 能正常工作,就必须要 mmc 这一套能正常工作;那么就需要先配置 gpio 口,再移植 mmc 驱动,最后才是 wifi 驱动,而 wifi 芯片本身说不定还需要其它的动作才能正常,比如上电等等;


Driver 和设备之间的连接


如果说 driver 就是由 struct device_driver 来描述,而设备也是有 struct device 来描述的话,那么如何描述 driver 和 driver 所驱动的设备这种关系呢?也就是说如果是父子,或夫妻关系的话,他们是如何相认的,又是如何维持这种纽带的?一个设备通常由一个 driver 驱动,而一个 driver 有时候可以驱动多个设备(这也许是在美好的一夫多妻制时代)


1,  家族的概念


每个人都诞生于一个家庭,而由一系列有血缘关系的家庭又组成家族,我想 bus 的概念有点类似家族;


2,  设备的诞生


当一个设备通过 device_add 的方式调用的话,对它所属的家族来说意味着一个新的小孩诞生了;如果这时候它的配偶已经由上帝指定的话,意味着它的配偶已经住进了它们的家族(挂在了 bus 上),那么上帝就会把它的配偶 driver 指向这个 device 完成婚姻,通常这时候就会调用 driver 提供的 probe 函数进行一系列的初始化操作,这就像丈夫对妻子进行思想教育一样;


3,  driver 的诞生


当一个 driver 调用 driver_register 的时候,也就宣布着新的孩子诞生了,如果它的配偶已经住进他们的家族,也就是说如果驱动的设备已经挂在了总线上,那么就可以结婚了(估计他们是属于一出生就比较成熟的类型),同样这时候就要调用 driver 的 probe 函数进行初始化工作了;


4,  人口登记


不管是男孩还是女孩,对政府来说都是 kobject ,所以一旦有孩子诞生不管是男是女都会调用 kobject_add 登记在案,这样政府就可以完全掌控所有的信息;


5,  配偶的寻找


如果是先有 device ,那么匹配就是通过遍历 bus 上的 driver 来进行的,反之就是通过遍历 bus 上的设备进行的,而匹配的前提就是设备已经注册,并且符合这个家族的族规,而族规通常是由 bus 的 match 函数来控制,而很多时候都是比较设备的名字和 driver 的名字是否相同(这个比结婚要比的东西简朴许多啊,现在结婚比的是 money )当然还有其它许多的匹配方式,如果通过这个测试就可以调用 driver 提供的 probe 函数进行初始化了;


这样的话,设备和 driver 就可以连接起来了,有了这样的人口登记和家族联系,就可以实现诸如睡眠唤醒这样的功能了。

赞(0) 打赏
转载请注明出处:服务器评测 » Linux Driver 编写思考
分享到: 更多 (0)

听说打赏我的人,都进福布斯排行榜啦!

支付宝扫一扫打赏

微信扫一扫打赏