总线的概念:总线是计算机各种功能部件之间传送信息的公共通信干线,它是由 导线 组成的传输线束, 按照计算机所传输的信息种类,计算机的总线可以划分为 数据总线 、 地址总线 和 控制总线 ,分别用来传输数据、数据地址和 控制信号 。
平台总线的概念:相对于USB、PCI、I2C、SPI等物理总线来说,platform总线是一种虚拟、抽象出来的总线,实际中并不存在这样的总线。那为什么需要platform总线呢?其实是Linux设备驱动模型为了保持设备驱动的统一性而虚拟出来的总线。因为对于usb设备、i2c设备、pci设备、spi设备等等,他们与cpu的通信都是直接挂在相应的总线下面与我们的cpu进行数据交互的,但是在我们的嵌入式系统当中,并不是所有的设备都能够归属于这些常见的总线,在嵌入式系统里面,SoC系统中集成的独立的外设控制器、挂接在SoC内存空间的外设却不依附与此类总线。所以Linux驱动模型为了保持完整性,将这些设备挂在一条虚拟的总线上(platform总线),而不至于使得有些设备挂在总线上,另一些设备没有挂在总线上。
设备:
(1)struct device是硬件设备在内核驱动框架中的抽象
(2)device_register用于向内核驱动框架注册一个设备
(3)通常device不会单独使用,而是被包含在一个具体设备结构体中,如struct usb_device
驱动:
(1)struct device_driver是驱动程序在内核驱动框架中的抽象
(2)关键元素1:name,驱动程序的名字,很重要,经常被用来作为驱动和设备的匹配依据
(3)关键元素2:probe,驱动程序的探测函数,用来检测一个设备是否可以被该驱动所管理
平台总线下管理的2员大将:
(1)platform工作体系都定义在drivers/base/platform.c中
(2)两个结构体:platform_device和platform_driver
(3)两个接口函数:platform_device_register和platform_driver_register
相关代码位置:
platform总线相关代码:driver\base\platform.c 文件中
相关结构体定义:include\linux\platform_device.h 文件中
结构体分析:
platform_device结构体分析:(include\linux\platform_device.h)
struct platform_device { // platform总线设备
const char * name; // 平台设备的名字
int id; // ID 是用来区分如果设备名字相同的时候(通过在后面添加一个数字来代表不同的设备,因为有时候有这种需求)
struct device dev; // 内置的device结构体,device通用的一些东西
u32 num_resources; // 资源结构体数量
struct resource * resource; // 指向一个资源结构体数组
const struct platform_device_id *id_entry; // 用来进行与设备驱动匹配用的id_table表
/* arch specific additions */
struct pdev_archdata archdata; // 自留地 添加自己的东西
};
platform_device结构体中的struct resource结构体分析:
struct resource { // 资源结构体
resource_size_t start; // 资源的起始值,如果是地址,那么是物理地址,不是虚拟地址
resource_size_t end; // 资源的结束值,如果是地址,那么是物理地址,不是虚拟地址
const char *name; // 资源名
unsigned long flags; // 资源的标示,用来识别不同的资源
struct resource *parent, *sibling, *child; // 资源指针,可以构成链表
};
platform_driver结构体分析:(include\linux\platform_device.h)
struct platform_driver {
int (*probe)(struct platform_device *); // 这个probe函数其实和 device_driver中的是一样的功能,但是一般是使用device_driver中的那个
int (*remove)(struct platform_device *); // 卸载平台设备驱动的时候会调用这个函数,但是device_driver下面也有,具体调用的是谁这个就得分析了
void (*shutdown)(struct platform_device *); // 关闭一个设备
int (*suspend)(struct platform_device *, pm_message_t state); // 挂起一个设备
int (*resume)(struct platform_device *); //唤醒一个设备
struct device_driver driver; // 内置的device_driver 结构体 ,driver通用的一些东西
const struct platform_device_id *id_table; // 该设备驱动支持的设备的列表 他是通过这个指针去指向 platform_device_id 类型的数组
};
平台总线体系的工作流程
(1)第一步:系统启动时在bus系统中注册platform
(2)第二步:内核移植的人负责提供platform_device
(3)第三步:写驱动的人负责提供platform_driver
(4)第四步:platform的match函数发现driver和device匹配后,调用driver的probe函数来完成驱动的初始化和安装,然后设备就工作起来了
分析源码:
测试实例:
实例1:本例比较简单,只用于测试platform_driver 和platform_device是否可以匹配成功。
实例2:给结构体platform_device 增加硬件信息,并在内核中能够读取出来。
device.c
struct resource res[]={
[0] ={
.start = 0x139d0000,
.end = 0x139d0000 + 0x3,
.flags = IORESOURCE_MEM,
},
[1] ={
.start = 199,
.end = 199,
.flags = IORESOURCE_IRQ,
},
};
static struct platform_device hello_device =
{
.name = "duang",
.id = -1,
.dev.release = hello_release,
.num_resources = ARRAY_SIZE(res),
.resource = res,
};
driver.c
static int hello_probe(struct platform_device *pdev)
{
printk("match ok \n");
printk("mem = %x \n",pdev->resource[0].start);
printk("irq = %d \n",pdev->resource[1].start);
//注册中断、申请内存
return 0;
}
执行结果;
由结果可知,probe函数正确读取到了硬件信息。