Linux platform总线驱动框架
![[总线驱动模型简图.png]]
总线(bus) :负责管理挂载对应总线的设备以及驱动;
设备(device) :挂载在某个总线的物理设备;
驱动(driver) :与特定设备相关的软件,负责初始化该设备以及提供一些操作该设备的操作方式;

个人理解
- platform_device表示板子上的硬件资源,其中的资源用struct resource表示,见下文[[#platform_device]]。
- 见下文[[#platform_driver]]
流程:platform_device从设备树那获得具体的硬件资源,platform_bus匹配platform_device和platform_driver。具体匹配过程由platform_match实现。匹配成功后platform_driver负责具体驱动的设备(如cdev)的管理,初始化,卸载要干什么。
Linux驱动与单片机驱动联系与区别
| 特性 | 单片机 (裸机/RTOS) | Linux 驱动 |
|---|---|---|
| 核心逻辑 | 你写代码的就是上帝,可以直接访问物理地址 | 驱动是内核的一个模块 |
| 内存访问 | 直接访问物理地址 | 有MMU (内存管理单元),Linux驱动复杂的一个重要原因 。想访问物理地址,必须映射为虚拟地址 (ioremap)。 |
| 并发与竞争 | 一般单核CPU | 多核CPU |
| 中断处理 | ISR 处理所有逻辑,尽量快 | 顶半部 (Top half) + 底半部 (Bottom half: Tasklet/Workqueue)...... |
| 数据交互 | 直接函数调用或全局变量 | 用户空间与内核空间拷贝 (copy_from_user) |
| 驱动标准化 | 厂商库 (HAL) 或 自定义接口 | 严格的子系统框架 (Input, Net, Block, Char...) |
| 复用性 | 差,换个芯片可能要重写 | 强,通过设备树 (Device Tree) 修改硬件描述即可复用驱动代码 |
| 调试手段 | JTAG/SWD 硬件断点,单步调试 | printk, dmesg, proc/sys 文件系统, Oops 分析 |
| 单片机没有像Linux有复制的驱动的系统,因为单片机资源太少,没必要这么干。但其存在类似的抽象模型: |
- HAL(STM32Cube HAL)
- BSP(Board Support Package)
- Middleware(USB, LWIP)
- User app
不过没有 Linux 那样 自动设备创建 + 总线模型 + 热插拔 + power management + driver binding。
参考链接
一张图掌握 Linux platform 平台设备驱动框架!【建议收藏】-CSDN博客
【linux】驱动-6-总线-设备-驱动 - 李柱明 - 博客园 (cnblogs.com)
总线驱动模型简介
总线
总线设备(硬件)
在 Linux 系统中,每个设备都用一个 struct device 实例来表示。但需要注意的是,这些设备通常不仅仅是用一个简单的 struct device 实例来表示。相反,struct device 通常是嵌入在更高级别的设备表示结构中。每个设备的子系统(如 PCI、USB、平台设备等)都会在 struct device 基础上扩展,以包含子系统特定的信息。
c
struct device //device.h
{
struct bus_type *bus;//代表该设备挂在哪条总线上
void (*release)(struct device *dev); //release 是必须实现的
//用于设备匹配的结构体
const struct of_device_id *of_match_table;
/* associated device tree node */
//用于表示设备树节点的结构体
struct device_node *of_node;
...
}
//通常会与 of_device_id 结构体来匹配设备
struct device_node {
};
设备驱动(软件)
设备模型跟踪所有系统所知道的设备,进行跟踪的主要原因是让驱动程序核心协调驱动程序与新设备之间的关系。
设备驱动程序可以导出信息和配置变量,这些是独立于任何特定设备的。
c
//基础设备驱动程序结构体,这个结构体用于描述设备驱动程序的各种属性和操作函数,以便驱动核心能够管理和匹配驱动程序与设备。
struct device_driver //device.h
{
//用于和硬件进行匹配
const char *name;
struct bus_type *bus;
int (*probe) (struct device *dev);
int (*remove) (struct device *dev);
...
}
platform平台总线
platform_device
"继承" 于 device 的 platform 设备,用来描述硬件,存储硬件使用的资源信息和容易变化的信息
c
struct platform_device {
const char *name; // 设备名称
int id; // 设备 ID
bool id_auto; // 是否自动生成 ID
struct device dev; // 嵌入的基本设备结构
u32 num_resources; // 资源数量
struct resource *resource; // 资源数组
// 设备 ID 条目
const struct platform_device_id *id_entry;
// 强制匹配的驱动程序名称
char *driver_override;
/* MFD cell pointer */
// 多功能设备 (MFD) 单元指针
struct mfd_cell *mfd_cell;
/* arch specific additions */
// 架构特定的数据
struct pdev_archdata archdata;
};
其中 struct resource
c
struct resource {
resource_size_t start; //表示资源的起始值,
resource_size_t end; //表示资源的最后一个字节的地址, 如果是中断,end和satrt相同
const char *name; // 可不写
unsigned long flags; //资源的类型
struct resource *parent, *sibling, *child;
};
// [flags] 类型,通常将该硬件使用的物理地址、中断号视为资源:
#define IORESOURCE_MEM 0x00000200 //内存: 物理地址资源
#define IORESOURCE_IRQ 0x00000400 //中断: 中断号资源
platform_driver
继承于 device_driver 的 platform_driver 设备驱动
platform_driver表示一个抽象驱动,其主要作用为管理具体驱动(如cdev字符设备)。
- platform_driver不包括file_operations(自己写的具体驱动操作部分),file_operations包含在cdev里。cdev和platform_driver是两个相互独立,又协同作用的机制。
- platform的主要作用有:
- 内核将platform_device和platform_driver匹配后,platform_driver下的probe函数会获得设备资料,并进行初始化硬件 。
- 驱动和设备的匹配过程不是platform_driver主动去找,而是由platform_bus去匹配,具体匹配过程为platform_match()完成。每当有 device 或 driver 注册,Linux 内核都会主动寻找并尝试匹配另一方。
- remove:设备移除时会进行什么操作
- shutdow:系统关闭时调用,进行必要的硬件状态保存或清理工作
- pm:电源管理接口
- 内核将platform_device和platform_driver匹配后,platform_driver下的probe函数会获得设备资料,并进行初始化硬件 。
c
struct platform_driver
{
//当驱动和硬件信息匹配成功之后,就会调用probe函数,驱动所有的资源的注册和初始化全部放在probe函数中
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 *);
//内核维护的所有的驱动必须包含该成员,通常driver->name用于和设备进行匹配
struct device_driver driver;
//往往一个驱动可能能同时支持多个硬件,这些硬件的名字都放在该结构体数组中
const struct platform_device_id *id_table;
bool prevent_deferred_probe;
}
c
struct of_device_id {
char name[32];
char type[32];
char compatible[128];
const void *data;
};
匹配规则
c
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);
/* 1.当 driver_override 被设置时,只绑定到匹配的驱动程序 */
if (pdev->driver_override)
return !strcmp(pdev->driver_override, drv->name);
/* 2.首先尝试 OF 风格的匹配 */
if (of_driver_match_device(dev, drv))
return 1;
/* 然后尝试 ACPI 风格的匹配 */
if (acpi_driver_match_device(dev, drv))
return 1;
/* 3.然后尝试匹配 id 表 */
if (pdrv->id_table)
return platform_match_id(pdrv->id_table, pdev) != NULL;
/* 4.最后回退到驱动程序名称匹配 */
return (strcmp(pdev->name, drv->name) == 0);
}
以下1,2,3,4与上述代码对应
- 最先比较:是否强制选择某个 driver
platform_device.driver_override 和platform_driver.driver.name可以设置 platform_device 的 driver_override,强制选择某个 platform_driver。 - 然后比较:设备树信息来判断 dev 和 drv 是否配对
c
struct platform_driver下struct device_driver driver下struct of_device_id *of_match_table
struct of_device_id {
char name[32];
char type[32];
char compatible[128];
const void *data;
};
-
首先,如果 of_match_table 中含有 compatible 值,就跟dev的compatile属性(即设备树compatible节点属性)比较,若一致则成功,否则返回失败。
-
其次,如果 of_match_table 中含有 type 值,就跟 dev 的 device_type 属性比较,若一致则成功,否则返回失败;
-
最后,如果 of_match_table 中含有 name 值,就跟 dev 的 name 属性比较,若一致则成功,否则返回失败。
设备树中建议不再使用 devcie_type 和 name 属性,所以基本上只使用设备节点的 compatible 属性来寻找匹配的platform_driver。
-
接下来比较:platform_device_id
比较 platform_device.name(设备树上的节点名字) 和 platform_driver.id_table[i].name,id_table 中可能有多项。
platform_driver.id_table 是"platform_device_id"指针,表示该 drv支持若干个 device,它里面列出了各个 device 的{.name, .driver_data},其中的"name"表示该 drv 支持的设备的名字,driver_data 是些提供给该device 的私有数据
platform_device 可以自己注册,不一定要从设备树中转换。
- 最后比较
platform_device.name 和 platform_driver.driver.name
platform_driver.id_table 可能为空,
这时可以根据platform_driver.driver.name来 寻 找 同 名 的platform_device。
(不就是2.3)
设备树下的 platform 驱动
- 设备树中创建设备节点
- 编写platform驱动的时候要注意兼容属性
- 编写platform驱动
OF函数
Linux 内核给我们提供了一系列的函数来获取设备树中的节点或者属性信息,这一系列的函数都有一个统一的前缀"of_",所以在很多资料里面也被叫做 OF 。
这些 OF原型都定义在include/linux/of.h 文件中。