平台驱动模型
0赞平台驱动模型
概述:平台驱动模型是由两部分组成,一部分叫做device设备层,另一部分叫做driver驱动层。设备层向驱动层传递设备的信息,比如GPIO端口号,irq中断号,DMA相关的配置信息,驱动收到这些信息之后,再根据具体的细节来对设备进行初始化,设备层相对简单,只写传递编写驱动需要用到的信息,而驱动层除了对设备进行初始化之外,还要编写如何使用设备的函数,如设备操作接口的函数就应该卸载驱动层当中,真正使用设备的时候所调用的函数应该全部是由驱动层提供的。
虚拟总线:将操作相似的一类设备虚拟成为一组总线,比如寄存器配置方法和操作方法完全一样的GPIO端口或者是I2C,SPI等等,就可以形成一组虚拟总线,因为操作相似,在选择或驱动不同设备的时候,只需要修改GPIO端口号,就可以实现对多个设备的驱动。
好处:让驱动的编写和同设备的替换变得十分的方便。在LCD驱动移植的时候将体现的很清楚。
注意:平台驱动模型和之前学过的早期,杂项,经典字符设备注册模型不一样,平台驱动仅仅是一个平台,不会生成节点,如果需要通过节点来操作设备,应该在平台驱动内嵌套之前学过的三种设备注册方式,或者是后来我们要学习的input子系统等等方式
33-1:头文件
#include <linux/platform_device.h>
33-2:结构体原型
1) 设备层:此结构体主要用来收集具体的设备信息
a. struct platform_device {
const char * name; //用来和驱动层进行配对的名字,如果陪对不上,那么驱动
工作
int id;//用于批量注册设备时的编号,一般我们填-1不启动这个功能
struct device dev;// 见2)
u32 num_resources;//要传递的数据就叫做资源,这里是资源的数量
struct resource *resource; //资源数组的地址
。。。。。。
};
b. struct device{
。。。。。。
void *platform_data;//平台数据,传递一些用户自定义的数据,自由成员
void (*release)(structdevice *dev); //设备层卸载的时候会自动调用这个函数,释
放kzalloc申请的空间,释放互斥锁,还有一些
用户自定义的注销或者卸载操作
。。。。。。
}
c. struct resource {
resource_size_t start; //开始地址,可以传寄存器的开始地址
resource_size_t end; //结束地址,可以传寄存器的结束地址
const char *name; //给你的资源命个名,可选组件
unsigned long flags; //传递的资源的类型如寄存器资源IORESOURCE_MEM
struct resource *parent, *sibling,*child;
};
注意:资源默认标识可以在ioprot.h文件当中查询:如下:
2) 驱动层:此结构体接收设备层传递过来的数据并进行驱动的编写
a. struct platform_driver {
int (*probe)(struct platform_device *);
int (*remove)(struct platform_device *);
void (*shutdown)(struct platform_device *);
int (*suspend)(struct platform_device *, pm_message_tstate);
int (*resume)(struct platform_device *);
struct device_driver driver;
const struct platform_device_id *id_table;
};
probe:侦测函数,一旦设备层和驱动层配对成功,此函数就会调用,相当于模块模型的init函数,其中应该执行获取设备信息和初始化硬件等等的功能
remove:卸载函数,类似于模块模型的exit函数,在驱动层结构体卸载的时候会调用这个函数,此函数中应该对硬件资源进行释放
shutdown:电源管理相关,关机的时候会调用这个函数
suspend:电源管理相关,系统挂起(睡眠)的时候调用这个函数
resume:颠换管理相关,系统从睡眠模式恢复的时候调用这个函数
driver:驱动结构体成员,我们要使用其结构体内部的两个成员
driver.name:用来和设备层进行配对的驱动名,特别重要,不能错
driver.owner:填入THIS_MODULE,内核对驱动计数用
id_table:这是用来进行多个设备层匹配的匹配名列表,此列表当中出现的设备名所对应的设备层都可以与当前驱动层进行配对
33-3:函数相关
1) 设备层:
a. intplatform_device_register(struct platform_device *pdev)
作用:平台设备层注册函数,此函数写在模块的注册函数当中
参数:平台设备层结构体指针
返回成功:0
返回失败:小于0
b. voidplatform_device_unregister(struct platform_device *pdev)
作用:平台设备层卸载函数,此函数写在模块的卸载函数中
参数:平台设备层结构体指针
2) 驱动层:
a. intplatform_driver_register(struct platform_driver *drv)
作用:平台驱动层注册函数,此函写在载模块注册函数当中
参数:平台驱动层结构体指针
返回成功:0
返回失败:-1
b. voidplatform_driver_unregister(struct platform_driver *drv)
作用:平台驱动层卸载函数,此函数写在模块卸载函数当中
参数:平台驱动层结构体指针
33-4:平台设备层的代码编写
33-5:平台驱动层的代码编写
33-6:模块加载和卸载几个函数调用的先后关系
1) 设备层和驱动层都加载之后,如果配对成功,侦测函数probe将执行
2) 设备层加载,然后卸载,会执行release函数,不管有没有驱动层,都会执行
3) 当设备层和驱动层配对成功,单独卸载设备层,会执行release函数和remove函数
4) 当设备层和驱动层配对成功,单独卸载驱动层,只会执行remove函数
综合结论:在安装和卸载两个模块的过程当中,release probe remove函数分别只执行一次,release函数是和设备层模块绑定在一起的,只要设备层模块卸载就会执行,remove同时函数绑定设备层模块和驱动层模块,只要卸载其中任意一个,都会执行remove函数,只有配对成功,才会执行probe函数,先安装设备层或驱动层对整个过程没有影响
33-7:平台驱动层实现设备驱动所需要用到的函数
内核有专门提供给代码开发人员用来获取资源的函数
1) struct resource*platform_get_resource(struct platform_device *dev,
unsigned int type, unsigned int num)
作用:获取设备层传递过来的数据
参数1:设备层结构体,probe函数的参数提供
参数2:填入代表资源类型的宏如IORESOURCE_MEM
参数3:想要获得的资源在资源数组当中同类型的第几个,从0开始,注意:不是在资源数组中下标的编号,而是同类型资源的第几个,从0开始
返回成功:资源结构体指针
返回失败:NULL
2) int platform_get_irq(structplatform_device *dev, unsigned int num)
作用:专门用来获得irq中断资源的函数,也可以不用,直接用1)也是可以的
参数1:设备层结构体指针
参数2:资源在同类型资源当中的顺序编号,从0开始
返回成功:返回的是传递过来的中断编号
返回失败:小于0
3) request_mem_region(start,n,name)
作用:申请寄存器的内存资源,判断有没有虚拟寄存器可以分配的,本质上是防止同一组寄存器被多次映射到不同的内存
参数1:寄存器地址的起始地址
参数2:要申请的寄存器长度
参数3:申请者登记名,可以填入NULL
返回成功:struct resource *型的资源指针
返回失败:NULL
4) release_mem_region(start,n)
作用:释放申请到的寄存器内存资源,与2)是一对,一个申请,一个释放
参数1:寄存器地址开始的地址
参数2:要释放的寄存器地址长度
33-8:代码:按键中断控制LED
请参考视频和代码
作业:自己实现平台驱动的模型,并且在设备层和驱动层实现同一套驱动既可以驱动LED,有可以控制BEEP,通过不同的按键中断来控制
33-9:驱动层和设备层进行匹配的另外一种方式
可以使用struct platform_driver结构体的成员const struct platform_device_id *id_table来作为驱动名与设备层的设备名进行配对,先看结构体原型:
struct platform_device_id {
charname[PLATFORM_NAME_SIZE]; //这就是用来匹配的驱动名
kernel_ulong_tdriver_data //可以传递数据,一般未使用
__attribute__((aligned(sizeof(kernel_ulong_t))));
};
注意:当我们实现了此结构体的成员name[PLATFORM_NAME_SIZE]之后,原本用来进行匹配的结构体成员driver.name就失效了,但是在代码编写的时候还是要去实现这个driver.name,可以无效,但是不能没有。另外,此结构体在平台驱动层结构体内部是以结构体指针的形式存在的,所以支持多个驱动名进行匹配,以数组的方式带入即可
33-10:平台数据---一个很重要的设备层成员
Struct platform_device mydevice;
此成员与设备层结构体原型的关系:mydevice.dev.platform_data
其原型为:void *platform_data 因此可以传递任意数据,并且这个成员不会被其他的系统功能调用,完全是提供给代码开发人员使用的。在实际的驱动开发过程中,这个成员非常有用
详情请咨询:http://www.edu118.com