Linux Platform驱动框架:设备与驱动分离的优雅设计
·
一、顶级架构一句话总结
platform_device(设备描述) ← Platform总线 → platform_driver(驱动实现)
Platform框架实现了设备与驱动的解耦,让同一份驱动代码可以适配不同的硬件平台。
二、为什么需要Platform框架?
传统驱动的问题
在Linux 2.6之前,设备信息硬编码在驱动中:
// 传统方式:硬件信息硬编码
static struct my_device {
void __iomem *base = ioremap(0x10000000, 0x1000); // 硬编码地址
int irq = 32; // 硬编码中断号
};
问题:
- 更换硬件需要修改驱动代码
- 无法支持设备热插拔
- 代码可移植性差
Platform框架的解决方案
┌─────────────────────────────────────────────────────────┐
│ Platform框架架构 │
├─────────────────────────────────────────────────────────┤
│ │
│ platform_device platform_driver │
│ (描述硬件资源) ←──匹配──→ (实现设备操作) │
│ ↓ ↓ │
│ 设备树/代码定义 probe/remove回调 │
│ │
│ ↘ ↙ │
│ Platform总线 │
│ (负责匹配与绑定) │
└─────────────────────────────────────────────────────────┘
三、核心数据结构
1. platform_device
struct platform_device {
const char *name; // 设备名称(用于匹配)
int id; // 设备ID
struct device dev; // 内嵌device结构
u32 num_resources; // 资源数量
struct resource *resource; // 资源数组
};
struct resource {
resource_size_t start; // 资源起始
resource_size_t end; // 资源结束
const char *name; // 资源名称
unsigned long flags; // 资源类型
};
2. platform_driver
struct platform_driver {
int (*probe)(struct platform_device *); // 匹配成功时调用
int (*remove)(struct platform_device *); // 设备移除时调用
void (*shutdown)(struct platform_device *); // 系统关机时调用
int (*suspend)(struct platform_device *, pm_message_t); // 休眠
int (*resume)(struct platform_device *); // 唤醒
struct device_driver driver; // 内嵌driver结构
const struct platform_device_id *id_table; // 匹配表
};
四、匹配机制详解
匹配优先级
匹配顺序:
1. of_match_table(设备树compatible) ← 推荐
2. id_table(平台设备ID表)
3. name(设备名称直接匹配)
匹配流程图
platform_device注册
↓
platform_driver注册
↓
遍历所有已注册的device
↓
检查匹配规则(按优先级)
↓
匹配成功 → 调用driver的probe函数
↓
绑定device和driver
五、设备定义方式
方式1:设备树定义(推荐)
/ {
mydevice: mydevice@10000000 {
compatible = "mycompany,mydevice";
reg = <0x10000000 0x1000>;
interrupts = <0 32 4>;
clocks = <&clk_periph>;
status = "okay";
};
};
方式2:代码静态定义
static struct resource my_resources[] = {
[0] = {
.start = 0x10000000,
.end = 0x10000000 + 0x1000 - 1,
.flags = IORESOURCE_MEM,
.name = "regs",
},
[1] = {
.start = 32,
.end = 32,
.flags = IORESOURCE_IRQ,
.name = "irq",
},
};
static struct platform_device my_device = {
.name = "mydevice",
.id = 0,
.num_resources = ARRAY_SIZE(my_resources),
.resource = my_resources,
.dev = {
.platform_data = &my_pdata,
},
};
// 注册设备
platform_device_register(&my_device);
六、驱动实现完整示例
1. 头文件和结构定义
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/io.h>
#include <linux/interrupt.h>
#include <linux/of.h>
#include <linux/of_device.h>
struct my_device_data {
void __iomem *base;
int irq;
struct clk *clk;
struct mutex lock;
};
2. probe函数实现
static int my_probe(struct platform_device *pdev)
{
struct my_device_data *data;
struct resource *res;
int ret;
pr_info("my_driver: probe called\n");
// 分配设备数据
data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
// 获取内存资源
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(&pdev->dev, "No memory resource\n");
return -ENODEV;
}
// 映射寄存器
data->base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(data->base))
return PTR_ERR(data->base);
// 获取中断号
data->irq = platform_get_irq(pdev, 0);
if (data->irq < 0)
return data->irq;
// 获取时钟
data->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(data->clk))
return PTR_ERR(data->clk);
// 使能时钟
ret = clk_prepare_enable(data->clk);
if (ret)
return ret;
// 初始化互斥锁
mutex_init(&data->lock);
// 保存设备数据
platform_set_drvdata(pdev, data);
dev_info(&pdev->dev, "Device initialized successfully\n");
return 0;
}
3. remove函数实现
static int my_remove(struct platform_device *pdev)
{
struct my_device_data *data = platform_get_drvdata(pdev);
clk_disable_unprepare(data->clk);
dev_info(&pdev->dev, "Device removed\n");
return 0;
}
4. 设备树匹配表
static const struct of_device_id my_of_match[] = {
{ .compatible = "mycompany,mydevice", },
{ .compatible = "mycompany,mydevice-v2", },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, my_of_match);
5. 平台驱动注册
static struct platform_driver my_driver = {
.probe = my_probe,
.remove = my_remove,
.driver = {
.name = "my_driver",
.of_match_table = my_of_match,
.pm = &my_pm_ops, // 电源管理操作
},
};
module_platform_driver(my_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Driver Developer");
MODULE_DESCRIPTION("Platform Driver Example");
七、资源获取API
常用资源获取函数
// 获取内存资源
struct resource *platform_get_resource(struct platform_device *pdev,
unsigned int type, unsigned int num);
// 获取中断号
int platform_get_irq(struct platform_device *pdev, unsigned int num);
// 获取资源并映射(推荐)
void __iomem *devm_ioremap_resource(struct device *dev, struct resource *res);
// 获取设备树节点
struct device_node *pdev->dev.of_node;
// 获取时钟
struct clk *devm_clk_get(struct device *dev, const char *id);
// 获取调节器
struct regulator *devm_regulator_get(struct device *dev, const char *id);
// 获取GPIO
int of_get_named_gpio(struct device_node *np, const char *propname, int index);
八、Platform vs 传统字符设备
| 特性 | 传统字符设备 | Platform设备 |
|---|---|---|
| 设备信息 | 硬编码在驱动中 | 设备树/代码分离 |
| 热插拔 | 不支持 | 支持 |
| 电源管理 | 需手动实现 | 框架支持 |
| 设备匹配 | 手动 | 自动匹配 |
| 代码复用 | 差 | 好 |
九、调试技巧
查看已注册的Platform设备
# 查看所有platform设备
ls /sys/bus/platform/devices/
# 查看设备详细信息
cat /sys/bus/platform/devices/*.name/uevent
# 查看驱动绑定状态
ls -la /sys/bus/platform/drivers/
查看匹配过程
# 查看内核日志
dmesg | grep -i "platform"
dmesg | grep -i "probe"
十、终极总结
Platform框架 = 设备与驱动分离的优雅设计
- 核心价值:设备信息与驱动代码解耦,提高可移植性
- 关键机制:通过compatible/name匹配,自动调用probe
- 资源管理:统一API获取内存、中断、时钟等资源
- 最佳实践:优先使用设备树定义设备信息
Platform框架是Linux驱动开发的基石!
更多推荐




所有评论(0)