Linux设备驱动开发——platform设备驱动应⽤实例解析
前⾯我们已经学习了platform设备的理论知识 ,下⾯将通过⼀个实例来深⼊我们的学习。
⼀、platform 驱动的⼯作过程
platform模型驱动编程,需要实现platform_device(设备)与platform_driver(驱动)在platform(虚拟总线)上的注册、匹配,相互绑定,然后再做为⼀个普通的字符设备进⾏相应的应⽤,总之如果编写的是基于字符设备的platform驱动,在遵循并实现platform总线上驱动与设备的特定接⼝的情况下,最核⼼的还是字符设备的核⼼结构:cdev、 file_operations(他包含的操作函数接⼝)、dev_t(设备号)、设备⽂件(/dev)等,因为⽤platform机制编写的字符驱动,它的本质是字符驱动。
我们要记住,platform 驱动只是在字符设备驱动外套⼀层platform_driver 的外壳。
暗黑2死灵法师加点在⼀般情况下,2.6内核中已经初始化并挂载了⼀条platform总线在sysfs⽂件系统中。那么我们编写platform模型驱动时,需要完成两个⼯作:
a -- 实现platform驱动
b -- 实现platform设备
然⽽在实现这两个⼯作的过程中还需要实现其他的很多⼩⼯作,在后⾯介绍。platform模型驱动的实现过程核⼼架构就很简单,如下所⽰:
于小卉platform驱动模型三个对象:platform总线、platform设备、platform驱动。
platform总线对应的内核结构:struct bus_type-->它包含的最关键的函数:match() (要注意的是,这块由内核完成,我们不参与)
platform设备对应的内核结构:struct platform_device-->注册:platform_device_register(unregister)
platform驱动对应的内核结构:struct platform_driver-->注册:platform_driver_register(unregister)
那具体platform驱动的⼯作过程是什么呢:
设备(或驱动)注册的时候,都会引发总线调⽤⾃⼰的match函数来寻⽬前platform总线是否挂载有与该设备(或驱动)名字匹配的驱动(或设备),如果存在则将双⽅绑定;
如果先注册设备,驱动还没有注册,那么设备在被注册到总线上时,将不会匹配到与⾃⼰同名的驱动,然后在驱动注册到总线上时,因为设备已注册,那么总线会⽴即匹配与绑定这时的同名的设备与驱动,再调⽤驱动中的probe函数等;
如果是驱动先注册,同设备驱动⼀样先会匹配失败,匹配失败将导致它的probe函数暂不调⽤,⽽是要等到设备注册成功并与⾃⼰匹配绑定后才会调⽤。
⼆、实现platform 驱动与设备的详细过程
1、思考问题?
在分析platform 之前,可以先思考⼀下下⾯的问题:
a -- 为什么要⽤ platform 驱动?不⽤platform驱动可以吗?
b -- 设备驱动中引⼊platform 概念有什么好处?
现在先不回答,看完下⾯的分析就明⽩了,后⾯会附上总结。
2、platform_device 结构体 VS platform_driver 结构体
这两个结构体分别描述了设备和驱动,⼆者有什么关系呢?先看⼀下具体结构体对⽐
财务管理和会计的区别设备(硬件部分):中断号,寄存器,DMA等 platform_device 结构体
驱动(软件部分)
platform_driver 结构体
struct platform_device {
const char *name; 名字
int id;
bool id_auto;
struct device dev; 硬件模块必须包含该结构体 u32 num_resources; 资源个数
struct resource *resource; 资源⼈脉
const struct platform_device_id * id_entry;
/* arch specific additions */
struct pdev_archdata archdata;
};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_t state);
int (*resume)(struct platform_device *);
struct device_driver driver;内核⾥所有的驱动程序必须包含该结构体 const struct platform_device_id * id_table; ⼋字
};
设备实例:
static struct platform_device hello_device= {
.name = "bigbang",
.id = -1,
.lease = hello_release,
};驱动实例:
static struct platform_driver hello_driver= {
.driver.name = "bigbang",
.probe = hello_probe,
.remove = hello_remove,
};
前⾯提到,实现platform模型的过程就是总线对设备和驱动的匹配过程 。打个⽐⽅,就好⽐相亲,总线是红娘,设备是男⽅,驱动是⼥⽅:
a -- 红娘(总线)负责男⽅(设备)和⼥⽅(驱动)的撮合;
b -- 男⽅(⼥⽅)到红娘,说我来登记⼀下,看有没有合适的姑娘(汉⼦)—— 设备或驱动的注册;
c -- 红娘这时候就需要看看有没有⼋字(⼆者的name 字段)匹配的姑娘(汉⼦)——match 函数进⾏匹配,看name是否相同;
d -- 如果⼋字不合,就告诉男⽅(⼥⽅)没有合适的对象,先等着,别急着乱做事 —— 设备和驱动会等待,直到匹配成功;
e -- 终于遇到⼋字匹配的了,那就结婚呗!接完婚,男⽅就向⼥⽅交代,我有多少存款,我的房⼦在哪,钱放在哪等等( struct resource *resource),⼥⽅说好啊,于是去房⼦⾥拿钱,去给男⽅买菜啦,给⾃⼰买⾐服、化妆品、⾸饰啊等等(int (*probe)(struct
platform_device *) 匹配成功后驱动执⾏的第⼀个函数),当然如果男的跟⼩三跑了(设备卸载),⼥⽅也不会继续待下去的( int
(*remove)(struct platform_device *))。
3、设备资源结构体
在struct platform_device 结构体中有⼀重要成员 struct resource *resource
struct resource {
resource_size_t start; 资源起始地址
resource_size_t end; 资源结束地址
const char *name;
unsigned long flags; 区分是资源什么类型的
struct resource *parent, *sibling, *child;
};
#define IORESOURCE_MEM 0x00000200
#define IORESOURCE_IRQ 0x00000400
flags 指资源类型,我们常⽤的是 IORESOURCE_MEM、IORESOURCE_IRQ 这两种。start 和 end 的含义会随着 flags⽽变更,如
a -- flags为IORESOURCE_MEM 时,start 、end 分别表⽰该platform_device占据的内存的开始地址和结束值;
b -- flags为 IORESOURCE_IRQ 时,start 、end 分别表⽰该platform_device使⽤的中断号的开始地址和结束值;
下⾯看⼀个实例:
static struct resource beep_resource[] =
{
[0] = {
.start = 0x114000a0,
.end = 0x114000a0+0x4,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = 0x139D0000,
.end = 0x139D0000+0x14,
.flags = IORESOURCE_MEM,
},
};
4、将字符设备添加到 platform的driver中
前⾯我们提到platform 驱动只是在字符设备驱动外套⼀层platform_driver 的外壳,下⾯我们看⼀下添加的过程:
static struct file_operations hello_ops=
{
.open = hello_open,
.release = hello_release,
.unlocked_ioctl = hello_ioctl,
};
static int hello_remove(struct platform_device *pdev)
{
注销分配的各种资源
}
static int hello_probe(struct platform_device *pdev)
{
1.申请设备号
2.cdev初始化注册,&hello_ops
3.从pdev读出硬件资源
4.对硬件资源初始化,ioremap,request_irq( )
}
static int hello_init(void)
万爱千恩原唱{
只注册 platform_driver
}
static void hello_exit(void)
{
只注销 platform_driver
}
可以看到,模块加载和卸载函数仅仅通过paltform_driver_register()、paltform_driver_unregister() 函数进⾏ platform_driver 的注册和注销,⽽原先注册和注销字符设备的⼯作已经被移交到 platform_driver 的 probe() 和 remove() 成员函数中。
5、platform是如何匹配device和driver
这时就该总线出场了,系统为platform总线定义了⼀个bus_type 的实例platform_bus_type,其定义如
下:哥斯达黎加足球队实力
struct bus_type platform_bus_type = {
.name = "platform",
.dev_groups = platform_dev_groups,
.match = platform_match,
.uevent = platform_uevent,
.pm = &platform_dev_pm_ops,
};
其⼜是怎样⼯作的呢?在platform.c (e:\linux-3.14-fs4412\drivers\base) 31577 2014/3/31 中可以看到
__platform_driver_register()
{
drv->driver.bus = &platform_bus_type; 536⾏
}
在 platform_bus_type 中调⽤了platform_match:
static int platform_match(struct device *dev, struct device_driver *drv)
{
struct platform_device *pdev = to_platform_device(dev);
struct platform_driver *pdrv = to_platform_driver(drv);
匹配设备树信息,如果有设备树,就调⽤ of_driver_match_device() 函数进⾏匹配
if (of_driver_match_device(dev, drv))
return 1;
匹配id_table
if (pdrv->id_table)
return platform_match_id(pdrv->id_table, pdev) != NULL;
最基本匹配规则
return (strcmp(pdev->name, drv->name) == 0);
}
6、解决问题
现在可以回答这两个问题了
a -- 为什么要⽤ platform 驱动?不⽤platform驱动可以吗?
b -- 设备驱动中引⼊platform 概念有什么好处?
引⼊platform模型符合Linux 设备模型 —— 总线、设备、驱动,设备模型中配套的sysfs节点都可以⽤,⽅便我们的开发;当然你也可以选择不⽤,不过就失去了⼀些platform带来的便利;
设备驱动中引⼊platform 概念,隔离BSP和驱动。在BSP中定义platform设备和设备使⽤的资源、设备的具体匹配信息,⽽在驱动中,只需要通过API去获取资源和数据,做到了板相关代码和驱动代码的分离,使得驱动具有更好的可扩展性和跨平台性。
三、实例
这是⼀个蜂鸣器的驱动,其实前⾯已经有解析 , 下⾯来看⼀下,套上platform 外壳后的程序:
1、device.c
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论