Linux在2.6版本引入了设备驱动模型,设备驱动模型负责统一实现和维护一些特性,诸如:热插拔、对象生命周期、用户空间和驱动空间的交互等基础设施
1.设备驱动模型基本概念
设备驱动模型主要包含:类(class)、总线(bus)、设备(device)、驱动(driver),它们的本质都是内核中的几种数据结构的“实例”
∙类的本质是class结构体类型,各种不同的类其实就是class的各种实例
∙总线的本质是bus_type结构体类型,各种不同的总线其实就是bus_type的各种实例∙设备的本质是device结构体类型,各种不同的设备其实就是device的各种实例∙驱动的本质是device_driver结构体类型,各种不同的驱动其实就是device_driver的各种实例
2.sysfs基本概念
sysfs其实就是/sys目录,其主要作用就是:展示设备驱动模型中各组件的层次关系,并将各组件的本体——内核中的数据结构以文件形式呈现,方便用户层查看及操作
3./sys目录结构与设备驱动模型
∙/sys目录结构很好的展示了驱动设备模型,如图:
∙注意:同一个设备可能在/sys中存在多个设备文件,比如一颗led的设备文件可能在/sys/bus/platform/devices/led1,同时还有一个在/sys/class/leds/led1。虽然他们都是同
一颗led的设备文件,但是他们的来源、机制、原理都是不同的,不能混为一谈4.各组件的特性与联系
∙kobject:设备驱动模型各实例的最基本单元,提供一些公用型服务如:提供该实例在sysfs中的操作方法(show和store);提供在sysfs中以文件形式存在的属性,其实就是应用接口;提供各个实例的层次架构,让sysfs中弄出目录结构。设备驱动模型中每个实例内部都会包含一个kobject
∙总线、设备、驱动,这三者有着密切的联系。在内核中,设备和驱动是分开注册的,注册设备的时候,并不需要驱动已经存在,而驱动被注册的时候,也不需要对应的设备已经被注册。而总线就是连接设备和驱动之间的纽带,如图:
linux内核设计与实现 pdf5.为什么要让总线匹配驱动和设备?
这么做是为了让算法和数据分离,驱动源码中不携带数据,只负责算法(对硬件的操作方法);而设备则负责携带硬件信息。这样最大程度保持驱动的独立性和适应性,并且可以实现一个驱动对应多个设备
有了上面的匹配机制,接下来就有了platform总线驱动。
基于platform总线的驱动分析
在设备驱动模型中,总线负责将设备和驱动绑定。在系统每注册一个设备的时候,会寻与之匹配的驱动;相反的,在系统每注册一个驱动的时候,会寻与之匹配的设备,而匹配由总线完成。
1.platform总线基本概念
∙设备和驱动若基于设备驱动模型,则它们通常都需要挂接在一种总线上。总线相对于设备和驱动,可谓是“媒人”担当
∙对于本身依附于USB、I2C、SPI 等的设备而言,这自然不是问题。但是很多的设备(比如led)实际并不依附于总线,没了“媒人”,设备和驱动怎么产生联系呢?于是内核为这些可怜的设备发明了一种虚拟的总线——platform(平台总线)∙挂接在platform上的设备和驱动,就称之为platform_device,和platform_driver
2.platform总线驱动工作流程
∙提供并注册platform_device/设备节点
∙提供并注册platform_driver
∙当platform总线内的mach函数会不停的匹配driver和device(老内核是根据driver 内的id、name元素;新内核是根据of_match_table中的compatible)∙一旦匹配成功,则调用driver的probe(探测)函数开始正式执行驱动代码
3.platform总线驱动的独立性和适应性
一个platform总线驱动程序可以对应多个设备,并且设备的变化也不会影响驱动。这是如何实现的呢?
∙简单的说,这是一种类似传参的机制。设备将底层信息(比如寄存器信息、使用到的中断号、设备名称等)传递给驱动,驱动本身代码不用变,只需要根据参数操作
底层,便可适应设备的变化
∙现代驱动设计理念就是算法和数据分离,驱动源码中不携带数据,只负责算法(对硬件的操作方法),这样最大程度保持驱动的独立性和适应性
∙具体的实现方法是:老内核中,platform_device包含了一个device结构体,其内部有一个void *platform_data; 这个有点类似于给用户提供的自留地,用户可以在里面存放各种底层信息。当driver的probe(探测)函数执行时,platform_device会作为参数传进去,这样驱动就能够间接的得到这个void *platform_data,从而据此操作
硬件;新内核则直接在设备节点属性中存放数据,驱动通过API读取节点里的数据4.老内核下platform总线驱动的编写方法
下面,以led驱动为实例,分析怎么使用platform来写驱动
∙根据上图的流程,首先应该进入mach-xxx.c完成platform设备的注册。
o第一步:创建适用于我们设备的platform_data类型(为自留地设计一种格式)
o第二步:为一个具体设备实例化一个platform_data,用来存放该类设备的底层信息
o第三步:创建一个具体platform设备(实例化一个platform_device),并把各种信息和platform_data填充入该设备
o第四步:把platform设备丢到专门存放platform_device的数组中,开机时系
统会注册数组中所有设备
∙先来看看mach-xxx.c中的情况,如果我们要写新的platform_device,要注意mach-xxx.c内有没有重复功能的。在该mach-xxx.c搜寻“platform”,寻专门存放
platform_device的数组,发现里面并没有led,看来我们要自己从头开始写了
∙/*sjh_add*/
∙
∙/*第一步:创建一个适用于我们设备的platform_data类型*/
∙struct s5pv210_led_platdata {
∙unsigned int gpio;
∙unsigned int flags;
∙char *name;
∙char *def_trigger;
∙};
∙
∙/*第二步:为一个具体设备实例化一个platform_data*/
∙static struct s5pv210_led_platdata x210_led1_pdata = {
∙ .gpio = S5PV210_GPJ0(3),
∙ .flags = NULL,
∙ .name = "led1",
∙ .def_trigger = NULL,
∙};
∙
∙/*第三步:实例化一个platform_device,正式创建设备*/
∙static struct platform_device x210_led1 = {
∙ .name = "s5pv210_led",//要和platform驱动中的名字对应
∙ .id = 1,
∙ .dev = {
∙ .platform_data = &x210_led1_pdata,//底层信息
∙ },
∙};
∙
∙/* 第四步:把我们的platform_device添加进数组,开机时系统会注册数组中所有设备*/
∙static struct platform_device *smdkc110_devices[] __initdata = {
∙/*sjh_add*/
∙ &x210_led1,
∙#ifdef CONFIG_FIQ_DEBUGGER
∙ &s5pv210_device_fiqdbg_uart2,
∙#endif
∙
∙...
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论