您当前的位置:首页 > 淘宝百科

总线控制器驱动(Linux驱动中的platform总线详解)

时间:2023-02-07 22:48:38

总线控制器驱动(Linux驱动中的platform总线详解)

平台总线是学习linux驱动必须掌握的知识点。

一、概念

嵌入式系统中有很多物理总线:I2c、SPI、USB、uart、PCIE、APB、AHB。

Linux从2.6开始加入了驱动管理和注册的新机制。平台总线是虚拟总线,不是物理总线。

与PCI和USB相比,它主要用于描述SOC上的片上资源。平台中描述的资源有一个共同点:直接访问CPU总线。

平台设备被分配一个名称(用于驱动程序绑定)和一系列资源,如地址和中断请求号(IRQ)。

设备由platform_device表示,驱动程序由platform_driver注册。

与传统的总线/设备/驱动机制相比,平台由内核管理,使用驱动中的资源,提高了代码的安全性和可移植性。

二、平台

1.站台总线的两种最重要的结构。

平台维护的所有驱动程序必须由以下结构定义:

平台_驱动程序

结构平台_驱动程序{

int (*probe)(结构平台_设备*);//

int (*remove)(结构平台_设备*);

void (*shutdown)(结构平台_设备*);

int (*suspend)(结构平台_设备*,pm _消息_t状态);

int (*resume)(结构平台_设备*);

结构设备驱动程序驱动程序;

const struct platform _ device _ id * id _ table;

bool prevent _ deferred _ probe

};

该结构用于对站台总线进行注册和驱动,

成员含义

Probe当驱动和硬件信息匹配成功时,会调用probe函数,驱动资源的所有注册和初始化都会放在probe函数中。

Remove硬件信息已经删除,或者驱动已经卸载,这些都要释放,释放资源的操作放在这个函数里。

struct device_driver驱动内核维护的所有驱动都必须包含这个成员,通常使用driver-"名称来匹配设备。

const struct platform _ device _ id * id _ table往往一个驱动可能同时支持多个硬件,这些硬件的名字都放在结构数组中。

我们在写驱动的时候经常需要填写上面的成员。

平台_设备

平台总线用于描述设备硬件信息的结构,包括硬件的所有资源(io、内存、中断、DMA等。).

结构平台_设备{

const char * name

int id

bool id _ auto

结构设备开发;

u32数量_资源;

结构资源*资源;

构造结构platform _ device _ id * id _ entry

struct mfd _ cell * mfd _ cell

struct pdev _ archdata archdata

};

成员含义

const char*name设备的名称,用于匹配驱动程序。

struct devicedev内核中维护的所有设备都必须包含此成员。

u32num_resources资源的数量

Struct resource*resource描述资源。

必须实现struct devicedev-》release()。

其中描述了硬件信息的成员结构资源。

0x139d0000

结构资源{

resource_size_t开始;//指示资源的起始值,

resource_size_t结束;//指示资源最后一个字节的地址。如果是中断,end和satrt是一样的。

const char * name//不要写

无符号长标志;//资源的类型

结构资源*父级,*同级,*子级;

};

标志的类型描述

#定义io资源_内存0x 00000200//内存

#定义io资源_ IRQ0x 00000400//中断

内核管理的所有驱动都必须包含一个名为struct device_driver,men描述的硬件的成员,并且必须包含struct device结构的成员。//女性

结构设备驱动程序{

const char * name

struct bus _ type * bus

结构模块*所有者;

const char * mod _ name

bool suppress _ bind _ attrs

_match_table的_ device _ id *的const struct

const结构acpi _ device _ id * acpi _ match _ table;

int (*probe)(结构设备*开发);

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

void (*shutdown)(结构设备*开发);

int(*暂停)(结构设备*dev,pm_message_t状态);

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

const struct attribute _ group * * groups;

构造结构dev _ pm _ ops * pm

struct driver _ private * p;

};

其中:

常量字符*名称

用于和硬件进行匹配。

内核描述硬件,必须包含结构设备结构体成员:

结构设备{

结构设备*父设备;

struct device _ private * p;

结构对象

const char * init _ name

构造结构设备类型*类型

结构互斥体互斥体;/*要同步调用的互斥体

*它的驱动程序。

*/

结构总线类型*总线

结构设备驱动程序*驱动程序

void *平台_数据

结构开发_项目管理_信息能力

结构开发_项目管理_域*项目管理_域

#ifdef CONFIG_PINCTRL

结构开发引脚信息*引脚;

#endif

#ifdef配置_NUMA

int numa _ node

#endif

u64 * dma _ mask

u64相干_ dma _ mask

结构设备_ dma _参数* dma _参数;

struct list _ head dma _ pools

struct DMA _ coherent _ mem * DMA _ mem;

#ifdef CONFIG_DMA_CMA

结构cma * cma _ area

#endif

结构开发_建筑数据建筑数据

结构设备节点*的节点

结构acpi _开发_节点acpi _节点

dev _ t devt

u32 id

自旋锁_ t devres _ lock

结构列表_头设备_头

结构列表_节点节点_类

结构类*类

const struct attribute _ group * * groups;

无效(*释放)(结构设备*开发);

结构iommu _ group * iommu _ group

bool offline _ disabled:1;

弯曲件离线:1;

};

其中:

无效(*释放)(结构设备*开发);

不能为空。

2.如何注册

要用注册一个平台驱动的步骤

1)注册驱动平台_设备_寄存器

/**

*平台设备注册-添加平台级设备

* @pdev:我们正在添加的平台设备

*/

(同Internationalorganizations)国际组织平台设备寄存器(结构平台设备*pdev)

{

device _ initialize(pdev-》dev);

arch _ setup _ pdev _ arch数据(pdev);

返回平台_设备_添加(pdev);

}

2) 注册设备平台_驱动程序_寄存器

#定义平台_驱动程序_寄存器(drv)

_ _平台驱动程序寄存器(drv,THIS _模块)

三、举例

1.开发步骤

平台总线下驱动的开发步骤是:

设备

需要实现的结构体是:平台_设备。

1)初始化资源结构变量

2)初始化平台_设备结构变量

3)向系统注册设备:平台_设备_寄存器。

以上三步,必须在设备驱动加载前完成,即执行平台_驱动_寄存器()之前,原因是驱动注册时需要匹配内核中所有已注册的设备名。

平台_驱动_寄存器()中添加设备到内核最终还是调用的设备_添加函数。

平台_设备_添加和设备_添加最主要的区别是多了一步插入资源(p,r),即将平台资源(资源)添加进内核,由内核统一管理。

驱动

驱动注册中,需要实现的结构体是:平台驱动程序。

在驱动程序的初始化函数中,调用了平台_驱动_寄存器()注册平台_驱动程序。

需要注意的是:平台驱动程序和平台_设备中的名字变量的值必须是相同的【在不考虑设备树情况下,关于设备树,后面会写新的文章详细讲述】 。

这样在平台_驱动_寄存器()注册时,会将当前注册的平台_驱动程序中的名字变量的值和已注册的所有平台_设备中的名字变量的值进行比较,只有找到具有相同名称的平台_设备才能注册成功。

当注册成功时,会调用平台_驱动程序结构元素调查函数指针。

实例一

本例比较简单,只用于测试平台_驱动程序和平台_设备是否可以匹配成功。

左边是平台_设备结构体注册的代码,右边是平台_驱动程序结构体注册的代码。

平台_驱动程序定义和注册:

1 #包括《linux/init.h》

2 #包括《linux/module.h》

3 #包括《linux/platform_device.h》

4 #包括《linux/ioport.h》

5

6静态int hello_probe(结构平台设备*pdev)

7 {

8 printk(“匹配正常

");

9返回0;

10 }

11静态int hello _ remove(结构平台设备* pdev)

12 {

13 printk(“你好_删除

");

14返回0;

15 }

16静态结构平台_驱动程序hello_driver=

17 {

18 .probe=hello_probe,

19 .driver.name="duang",

20 .remove=hello_remove,

21 };

22静态int hello_init(void)

23 {

24 printk("hello_init

");

25返回平台_驱动_注册(hello _ driver);

26 }

27静态void hello_exit(void)

28 {

29 printk("hello_exit

");

30平台_驱动_注销(你好_驱动);

31返回;

32 }

33模块_许可证(“GPL”);

34模块_初始化(hello _ init);

35模块_出口(hello _ exit);

平台_设备定义和注册:

1 #包括《linux/init.h》

2 #包括《linux/module.h》

3 #包括《linux/platform_device.h》

4 #包括《linux/ioport.h》

5

6静态void hello_release(结构设备*开发)

7 {

8返回;

9 }

10静态结构平台_设备你好设备=

11 {

12 .name="duang",

13 .id=-1,

14 .dev.release=hello_release,

15 };

16

17

18静态int hello_init(void)

19 {

20 printk("hello_init

");

21返回平台_设备_寄存器(hello _ device);

22

23 }

24静态void hello_exit(void)

25 {

26 printk("hello_exit

");

27平台_设备_注销(你好_设备);

28返回;

29 }

30 MODULE _ LICENSE(“GPL”);

31模块_初始化(hello _ init);

32模块_出口(hello _ exit);

该程序只用于测试平台框架是否可以成功匹配,结构平台_设备你好_设备并没有设置任何硬件信息。

Makfile

1 ifneq ($(KERNELRELEASE),)

2 obj-m:=device.o driver.o

3其他

4 KDIR:=/lib/modules/$(shell uname-r)/build

5 PWD :=$(壳牌PWD)

6所有:

七个make -C $(KDIR) M=$(PWD)模块

8清洁:

9 rm -f * .ko * .o * .西蒙兹*。cmd *.mod.c * .命令

10 endif

该生成文件可以同时将两个C文件编译成击倒文件。

编译:

编译

编译生成的文件:

在这里插入图片描述

加载模块

清空原木信息

sudo dmesg -c

匹配成功

实例2

给结构体平台_设备增加硬件信息,并在内核中能够读取出来。本例向结构体你好设备增加信息如下:

基址寄存器地址0x139d0000,该地址的空间是0x4

中断号199【注意】实际的内核中会把外设的中断号根据硬件id(通常社会学厂商设备社会学的时候会给每一个中断源定义好唯一的ID)计算出一个新的中断号,该中断号会被中央处理器所识别。

设备。c

结构资源res[]={

[0]={。start=0x139d0000。end=0x139d00000x3。flags=IORESOURCE_MEM,

},

[1]={。start=199。end=199。flags=IORESOURCE_IRQ,

},

};

静态结构平台_设备你好设备=

{。name="duang"。id=-1。dev.release=hello_release。数量资源=数组大小(res)。资源=资源,

};

司机。c

静态int hello_probe(结构平台设备*pdev)

{

printk(“匹配正常

");

printk("mem=%x

》,pdev-《资源[0]。开始);

printk("irq=%d

》,pdev-《资源[1]。开始);

//注册中断、申请内存

返回0;

}

重新编译,卸载第一个例子的模块,并清除日志:

制造

sudo rmmod设备

sudo rmmod驱动程序

sudo dmesg -c

执行

由结果可知,探针函数正确读取到了硬件信息。

四、平台_设备是如何管理的?

1.没有设备树

在没有设备树的时候,以三星Cortex-A8 s5pc100为例,硬件信息放在以下位置

archarmmach-s5pc 100 mach-SMD KC 100。c

archarmplat-三星

注册平台_设备

平台_设备定义

这个数组存储了内核启动时需要初始化的硬件的信息。

2.如果有设备树。

内核会有设备初始化的完整代码,在内核启动时会解析并初始化设备树信息,并将硬件信息初始化到相应的链表中。总线匹配成功后,硬件信息将被传递给probe()函数。

四、其他与公交相关的知识点

1.内核总线相关的结构变量

内核维护的所有总线都需要用下面的结构注册一个变量。

结构总线类型{

const char * name

const char * dev _ name

结构设备* dev _ root

struct device _ attribute * dev _ attrs;/*改用dev _ groups

const结构attribute _ group * * bus _ groups

const结构attribute _ group * * dev _ groups

const struct attribute _ group * * drv _ groups;

int (*match)(struct device *dev,struct device _ driver * drv);

int (*uevent)(struct device *dev,struct kobj _ u event _ env * env);

int (*probe)(结构设备*开发);

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

void (*shutdown)(结构设备*开发);

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

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

int (*suspend)(结构设备*dev,pm_message_t状态);

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

const struct dev _ pm _ ops * pm

struct iommu _ ops * iommu _ ops

struct subsys _ private * p;

struct lock _ class _ key lock _ key

};

平台总线变量struct Bus _ Type的定义平台总线类型定义如下:

结构总线类型平台总线类型={。name="平台"。开发组=平台开发组。匹配=平台_匹配。uevent=platform_uevent。pm=平台_开发_ pm _操作,

};

最重要的成员是**。匹配**。

当一个设备的硬件信息注册到platform_bus_type总线时,所有由平台总线维护的驱动程序都将被遍历并按名称匹配。如果硬件信息和驱动相同,则调用驱动的platform_driver -》probe函数,初始化驱动的所有资源,使驱动生效。

当在platform_bus_type总线上注册设备驱动程序时,它将遍历由平台总线维护的所有硬件信息,并按名称匹配它。如果相同,说明硬件信息与驱动匹配,会调用驱动的platform_driver -》probe函数,初始化驱动的所有资源,使驱动生效。

注册位置

driversasePlatform.c

平台_总线_类型的注册

五、注册码流程的详细说明

平滑架构的好处是可以帮助我们定位问题。

1.什么时候调用match函数?

2.什么时候调用探测函数?

以下是上述两个问题代码的调用过程:

代码调用过程

原标题:详细教Linux驱动10平台总线

文章来源:【微信微信官方账号:Linux爱好者】欢迎添加关注!请注明文章出处。

标签:设备平台_

设备

最新文章