原标题:详细教Linux驱动10平台总线
平台总线是学习linux驱动程序必须掌握的知识点。
一.概念
嵌入式系统中有许多物理总线:I2c、SPI、USB、uart、PCIE、APB、AHB
Linux从2.6开始加入了驱动管理和注册的新机制。平台总线是虚拟总线,不是物理总线。
与PCI和USB相比,主要用于描述SOC上的片上资源。平台描述的资源有一个共同点:直接在CPU的总线上访问。
平台设备被分配一个名称(用于驱动程序绑定)和一系列资源,如地址和中断请求号(IRQ)。
设备由platform_device表示,驱动程序由platform_driver注册。
与传统的总线/设备/驱动机制相比,平台由内核管理,使用驱动中的资源,提高了代码的安全性和可移植性。
第二,平台1。平台总线最重要的两种结构
平台维护的所有驱动程序必须由以下结构定义:
platform _ driver struct platform _ driver {
int(* probe)(struct platform _ device *);//
int(* remove)(struct platform _ device *);
void(* shut down)(struct platform _ device *);
int(* suspend)(struct platform _ device *,pm _ message _ ts state);
int(* resume)(struct platform _ device *);
structdevice _ driverdriver
constructplatform _ device _ id * id _ table;
boolprevent _ delivered _ probe;
};
该结构用于将驱动程序注册到平台总线。
成员 意义 探针 当驱动和硬件信息匹配成功时,会调用探测函数,所有驱动资源的注册和初始化都放在探测函数中 除去 硬件信息已被删除,或者驱动程序已被卸载,必须全部释放。释放资源的操作放在这个函数中 struct设备驱动程序 内核维护的所有驱动都必须包含这个成员,通常是驱动->名称用于与设备匹配 const struct platform _ device _ id * id _ table 通常,一个驱动程序可以同时支持多个硬件,这些硬件的名称都放在结构数组中 我们写驱动的时候,经常需要填写上面的成员
平台设备
平台总线用于描述设备硬件信息的结构,包括所有资源(io、内存、中断、DMA等。)的硬件。
structplatform_device{
constchar * name
intid
boolid _ auto
structdevicedev
u32 num _ resources
structresource*资源;
constructplatform _ device _ id * id _ entry;
/* MFD单元格指针*/
structmfd _ cell * mfd _ cell
/* arch特定附加功能*/
structpdev _ archdataarchdata
};
成员 意义 const char *名称 用于匹配驱动程序的设备名称 struct devicedev 内核中维护的所有设备都必须包含该成员。 u32num_resources 资源数量 结构资源*资源 描述资源 struct devicedev->必须实施该版本。
描述了硬件信息的成员结构资源
0x139d0000
structresource{
resource _ size _ tstart//指示资源的起始值。
resource _ size _ tend//表示资源最后一个字节的地址。如果是中断,end和satrt是相同的
constchar * name//不写
unsignedlongflags//资源类型
structresource*父级,*同级,*子级;
};
标志的类型描述
# defineoresource _ mem 0x 00000200//内存
# defineoresource _ IRQ 0x 00000400//中断
所有由内核管理的驱动程序必须包含一个名为struct device_driver的成员。//男人描述的硬件必须包含结构设备结构成员。//女
structdevice_driver{
constchar * name
structbus _ type * bus
structmodule*所有者;
constchar * mod _ name/*用于内置模块*/
boolsuppress _ bind _ attrs/*通过sysfs禁用绑定/解除绑定*/
construct of _ device _ id * of _ match _ table;
constructacpi _ device _ id * acpi _ match _ table;
int(* probe)(struct device * dev);
int(* remove)(struct device * dev);
void(* shut down)(struct device * dev);
int(*suspend) (struct device *dev,pm _ message _ ts state);
int(* resume)(struct device * dev);
construct attribute _ group * * group;
conststructdev _ pm _ ops * pm
struct driver _ private * p;
};
其中:
constchar * name
用于与硬件匹配。
内核描述硬件,必须包含结构设备结构成员:
structdevice{
structdevice*父级;
struct device _ private * p;
structkobjectkobj
constchar * init _ name/*设备的初始名称*/
conststructdevice _ type * type
structmutexmutex/*要同步调用的互斥体
*它的驱动程序。
*/
structbus _ type * bus/*总线设备类型开启*/
structdevice_driver*驱动程序;/*哪个驱动程序分配了这个
设备*/
void * platform _ data/*平台特定数据、设备
core不碰它*/
structdev _ pm _ infopower
structdev _ pm _ domain * pm _ domain
# ifdefCONFIG_PINCTRL
structdev _ pin _ info * pins
# endif
# ifdefCONFIG_NUMA
intnuma _ node/*此设备靠近的NUMA节点*/
# endif
u64 * dma _ mask/* dma掩码(如果是可dma设备)*/
u64 coherent _ dma _ mask/*像dma_mask,但是对于
alloc_coherent映射为
并非所有硬件都支持
一致的64位地址
像deors这样的分配。*/
struct device _ DMA _ parameters * DMA _ parms;
structlist _ headdma _ pools/* dma池(如果dma可用)*/
struct DMA _ coherent _ mem * DMA _ mem;/*内部用于连贯记忆
覆盖*/
# ifdefCONFIG_DMA_CMA
structcma * cma _ area/* DMA的连续存储区
分配*/
# endif
/* arch特定附加功能*/
structdev _ archdataarchdata
structdevice _ node * of _ node/*关联的设备树节点*/
structacpi _ dev _ nodeacpi _ node/*关联的ACPI设备节点*/
dev _ tdevt/* dev_t,创建sysfs“dev”*/
u32 id/*设备实例*/
spinlock _ tdevres _ lock
structlist _ headdevres _ head
structklist _ nodeknode _ class
structclass * class
construct attribute _ group * * group;/*可选组*/
void(* release)(struct device * dev);
structiommu _ group * iommu _ group
booloffline _ disabled:1;
booloffline:1;
};
其中:
void(* release)(struct device * dev);
它不能为空。
2.如何注册
向注册平台驱动程序
1)注册驱动平台_设备_寄存器
/**
* platform_device_register -添加平台级设备
* @pdev:我们正在添加的平台设备
*/
int platform _ device _ register(struct platform _ device * pdev)
{
device _ initialize(& amp;pdev->dev);
arch _ setup _ pdev _ archdata(pdev);
return platform _ device _ add(pdev);
}
2)注册设备platform_driver_register
# defineplatform _ driver _ register(drv)
_ _平台_驱动程序_寄存器(drv,THIS _ MODULE)
三.例1。开发步骤
平台总线下驱动的开发步骤如下:
设备
要实现的结构是platform_device。
1)初始化资源结构变量
2)初始化平台_设备结构变量
3)向系统注册设备:platform_device_register。
以上三个步骤必须在加载设备驱动之前,也就是在执行platform_driver_register之前完成,因为驱动注册需要匹配内核中所有注册的设备名称。
在platform_driver_register中向内核添加设备是最终要调用的device_add函数。
Platform_device_add和device_add的主要区别是insert_resource(p,r),即把平台资源添加到内核中,由内核统一管理。
开车
在驱动注册中,要实现的结构是platform_driver。
在驱动的初始化函数中,调用platform_driver_register来注册platform_driver。
需要注意的是,platform_driver和platform_device中的name变量值必须相同[不考虑设备树,后面会写一篇关于设备树的新文章详细介绍]。
这样,在注册platform_driver_register时,会将当前注册的platform_driver中的name变量值与所有注册的platform_devices中的name变量值进行比较,只有找到同名的platform _ device,注册才能成功。
注册成功后,将调用platform_driver结构元素的探测函数指针。
例1
这个例子很简单,只用来测试platform_driver和platform_device能否匹配成功。
左边是platform_device结构注册的代码,右边是platform_driver结构注册的代码。
Platform_driver定义和注册:
1#包括<。Linux/init . h & gt;
2#包括<。linux/module.h>。
3#包括<。Linux/platform _ device . h & gt;
4#包括<。Linux/I port . h & gt。
五
6 staticintello _ probe(struct platform _ device * pdev)
7 {
8 printk(" match ok n ");
9return0
10}
11 staticinthello _ remove(struct platform _ device * pdev)
12 {
13 printk(" hello _ remove n ");
14return0
15}
16 staticstructplatform _ driverhello _ driver =
17 {
18.probe = hello_probe,
19.driver.name = "duang ",
20.remove = hello_remove,
21};
22staticinthello_init( void)
23 {
24 printk(" hello _ init n ");
25 return platform _ driver _ register(& amp;hello _ driver);
26}
27staticvoidhello_exit( void)
28 {
29 printk(" hello _ exit n ");
30 platform _ driver _ unregister(& amp;hello _ driver);
31return
32}
33 module _ LICENSE(" GPL ");
34 module _ init(hello _ init);
35 module _ exit(hello _ exit);
平台_设备定义和注册:
1#包括<。Linux/init . h & gt;
2#包括<。linux/module.h>。
3#包括<。Linux/platform _ device . h & gt;
4#包括<。Linux/I port . h & gt。
五
6staticvoidhello_release(结构设备*dev)
7 {
8返回;
9}
10 statisticstructplatform _ device hello _ device =
11 {
12.name = "duang ",
13.id = -1,
14.dev.release = hello_release,
15};
16
17
18staticinthello_init( void)
19 {
20 printk(" hello _ init n ");
21 return platform _ device _ register(& amp;hello _ device);
22
23}
24 staticvodihello _ exit(void)
25 {
26 printk(" hello _ exit n ");
27 platform _ device _ unregister(& amp;hello _ device);
28return
29}
30MODULE _ LICENSE(" GPL ");
31 module _ init(hello _ init);
32 module _ exit(hello _ exit);
本程序仅用于测试平台框架是否能成功匹配,struct platform _ device hello _ device不设置任何硬件信息。
Makfile
1 fneq($(KERNELREESON),)
2 bj-m:= device . o driver . o
3else
4KDIR:=/lib/modules/$(shell uname-r)/build
5PWD :=$(shell pwd)
6所有:
7make -C $(KDIR) M=$(PWD)模块
8清洁:
9rm -f *。ko *。o *.mod.o *。symvers *。cmd *.mod.c *。顺序
10endif
makefile可以同时将两个c文件编译成ko文件。
编译:
编辑和翻译
编译生成的文件:
在此插入图片描述
输入程序片
清除日志信息
sudo dmesg -c
匹配成功示例2
硬件信息添加到结构platform_device中,可以在内核中读出。在本例中,以下信息被添加到hello_device结构中:
基址寄存器地址0x139d0000,该地址的空间为0x4
中断号199【注意】在实际内核中,会根据HW id从外设的中断号中计算出一个新的中断号(通常在soc厂商设备soc时会为每个中断源定义一个唯一的id),这个中断号会被cpu识别。
device.c
structure resources
结果显示探针功能正确读取硬件信息。
4.platform_device是如何管理的?1.没有设备树
在没有设备树的情况下,以三星Cortex-A8 s5pc100为例,硬件信息放置在以下位置
archarmmach-s5pc 100 mach-smdkc 100 . c
archarmplat-samsung
注册平台_设备
平台_设备定义
这个数组存储内核启动时需要初始化的硬件信息。
2.如果有设备树,
内核会有完整的设备初始化代码,在内核启动的时候会解析初始化设备树信息,将硬件信息初始化到对应的链表中。总线匹配成功后,硬件信息将传递给探针功能。
四、与总线1相关的其他知识点。内核总线相关结构变量
内核维护的所有总线都需要注册一个结构如下的变量。
structbus_type{
constchar * name
constchar * dev _ name
structdevice * dev _ root
struct device _ attribute * dev _ attrs;/*改用dev _ groups
construct attribute _ group * * bus _ groups;
construct attribute _ group * * dev _ groups;
construct attribute _ group * * drv _ groups;
int(*match)(struct device *dev,struct device _ driver * drv);
int(*uevent)(struct device *dev,struct kobj _ uevent _ env * env);
int(* probe)(struct device * dev);
int(* remove)(struct device * dev);
void(* shut down)(struct device * dev);
int(* online)(struct device * dev);
int(* offline)(struct device * dev);
int(*suspend)(struct device *dev,pm _ message _ ts state);
int(* resume)(struct device * dev);
conststructdev _ pm _ ops * pm
structiommu _ ops * iommu _ ops
struct subsys _ private * p;
structlock _ class _ keylock _ key
};
平台总线变量struct bus _ type的定义平台总线_ type定义如下:
struct bus _ type platform _ bus _ type = {
。name = "平台"、
。dev_groups = platform_dev_groups,
。match = platform_match,
。uevent = platform_uevent,
。pm = & ampplatform_dev_pm_ops,
};
最重要的成员是**。匹配**。
当一个设备的硬件信息注册在platform_bus_type总线上时,它会遍历平台总线维护的所有驱动程序,并按名称进行匹配。如果二者相同,说明硬件信息与驱动匹配,驱动的platform _ driver会被调用->: Probe函数,初始化驱动的所有资源,让驱动生效。
当一个设备驱动在platform_bus_type总线上注册时,它会遍历平台总线维护的所有硬件信息,并按名称进行匹配。如果相同,说明硬件信息与驱动匹配,驱动的platform _ driver会被调用->: Probe函数,初始化驱动的所有资源,让驱动生效。
注册位置
driversbasePlatform.c
平台_总线_类型五注册.注册码流程详解
该架构的优势在于它可以帮助我们定位问题
1.什么时候调用match函数?2.何时调用探测函数
以下是上述两个问题代码的调用过程:
代码调用过程返回搜狐查看更多
负责编辑: