嵌入式Linux之platform设备驱动框架详解

前言

在platform设备驱动模型中,需关心总线、设备和驱动这3个实体,总线将设备和驱动绑定。在系统中每注册一个设备的时候,会寻找与之匹配的驱动;相同的,在系统中每注册一个驱动的时候,会寻找与之匹配的设备,而匹配由总线完成。

设备

设备注册部分的流程图如下图所示:

定义一个platform_device结构体

static struct platform_device led_dev = {
    .name         = "myled",						//#1
    .id       = -1,									//#2
    .num_resources    = ARRAY_SIZE(led_resource),	//#3
    .resource     = led_resource,					//#4
    .dev = { 										//#5
    	.release = led_release, 
	},
}; 

复制代码
  • #1: 设备的名字,一般通过名字与相应的驱动进行匹配
  • #2: 表示设备编号,-1表示只有一个这样的设备。
  • #3和#4: 设备能提供的资源信息和资源的个数
  • #5: 这个就是要注册进内核的设备

定义resource结构体数组

static struct resource led_resource[] = {
    [0] = {  									//#1
        .start = 0x56000050,					//#2
        .end   = 0x56000050 + 8 - 1,			//#3
        .flags = IORESOURCE_MEM,				//#4
    },
    [1] = {										//#5
        .start = 5,
        .end   = 5,
        .flags = IORESOURCE_IRQ,
    }

};
复制代码
  • #1: 该资源表示硬件资源的寄存器地址,在本例中表示io配置寄存器和数据寄存器地址
  • #2和#3: 资源的起始地址和终止地址
  • #4: 表示资源的类型,这里表示该资源是内存地址,
  • #5: 该资源是可以操作的数据寄存器的哪一位,可以喝#1配合定位到具体的io口。

platform_device_register函数将设备挂到总线

static int led_dev_init(void) 
{
	platform_device_register(&led_dev);			//#1
	return 0;
}
int platform_device_register(struct platform_device * pdev)
{
	device_initialize(&pdev->dev);              //#2
	return platform_device_add(pdev);			//#3
}
复制代码
  • #1: 该函数在init中调用
  • #2: 首先进行dev的初始化

#3函数主要内容如下:

int platform_device_add(struct platform_device *pdev)
{
	pdev->dev.bus = &platform_bus_type;   		//#1
    ...insert_resource(...)...					//#2
	ret = device_add(&pdev->dev);    			//#3
}
int device_add(struct device *dev)
{
	bus_attach_device(dev);						//#4
}
void bus_attach_device(struct device * dev)
{
	ret = device_attach(dev);					//#5
}
int device_attach(struct device * dev)
{
	//#6
	ret = bus_for_each_drv(dev->bus, NULL, dev, __device_attach);	
}
复制代码
  • #1: 将device挂到bus上
  • #2:将资源提交给内核
  • #3 ~ #6: 完成device与bus上的驱动一一匹配
  • #6: 其中的__device_attach函数,就是匹配成功后执行的操作,该函数内容在driver部分介绍。

驱动

drvier部分的流程如下:

定义platform_driver结构体

struct platform_driver led_drv = {
	.probe		= led_probe,		
	.remove		= led_remove,
	.driver		= {
		.name	= "myled",
	}
};	
复制代码

定义probe函数和remove函数

这两个函数的内容类似于字符设备驱动中的init函数和exit函数,前者就是把通过设备层上传的资源进行端口初始化和生成节点,后者则是卸载掉这些初始化的结点和资源。

掉用platform_driver_register将结构体挂到总线

static int led_drv_init(void)
{
	platform_driver_register(&led_drv);              //#1
	return 0;
}
int platform_driver_register(struct platform_driver *drv)
{
	drv->driver.bus = &platform_bus_type;			//#2
    driver_register(&drv->driver);					//#3
}
复制代码
  • #1:驱动装载时执行该函数
  • #2:指定要挂载的总线

#3函数内容如下:

int driver_register(struct device_driver * drv)
{
	bus_add_driver(drv);						//#1
}
int bus_add_driver(struct device_driver *drv)
{	
	error = driver_attach(drv);					//#2	
}
int driver_attach(struct device_driver * drv)
{
	//#3
	bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);	
}
复制代码
  • #1和#2: 函数依次执行最后到执行#3
  • #3:当检测到设备和驱动匹配后,执行__driver_attach函数,这个和设备层的过程一样。

__device_attach函数的大致内容如下。

 static int __driver_attach(struct device * dev, void * data)
 {
 		driver_probe_device(drv, dev);					//#1
 }
 
 int driver_probe_device(struct device_driver * drv, struct device * dev)
 {
 		ret = really_probe(dev, drv);					//#2
 }
 static int really_probe(struct device *dev, struct device_driver *drv)
 {
 	if (dev->bus->probe) {								//#3
		ret = dev->bus->probe(dev);
	} else if (drv->probe) {							//#4
		ret = drv->probe(dev);				
	}	
 }
复制代码
  • #1和#2:依次调用这些函数进入到driver_probe_device函数
  • #4: 当从总线上能找到probe函数时,调用probe()函数。
  • #5: 若从总线上没找到,就执行驱动里的probe()函数。一般情况下都是都是调用自己驱动里的probe函数,只有在极少数的总线中,才会调用总线的probe函数。
  • 这段代码在不管从设备的初始化还是驱动的初始化都可以进行probe函数的调用。谁后初始化,谁调用probe函数。

总线

总线是内核已经初始化好的,我们只需要用就行,它只起到连接设备和驱动的作用。分离分层模型只是一种概念,一种方法,可以用这用方法把任何驱动分成设备层、驱动层和总线,输入子系统也是用了这种概念编写的框架。


作者:小鹏不会飞
链接:https://juejin.cn/post/6864914630788153352
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值