在嵌入式Linux系统开发中,设备驱动开发一直是一个关键且复杂的环节。早期的驱动开发采用硬编码方式,导致代码冗余、维护困难、功能扩展受限等问题。随着Linux内核架构的演进,平台总线(Platform Bus)模型的引入为驱动开发带来了革命性的改进。本文将深入探讨平台总线式驱动的核心机制,并通过实例演示如何构建符合现代Linux内核标准的设备驱动。
早期的驱动开发模式存在四大核心问题:
内核开发者提出革命性的解决方案:
类比现实中的婚介所:
https://via.placeholder.com/600x400?text=SOC+Architecture
c
Copy
struct platform_device {
const char *name; // 设备标识名
int id; // 设备实例ID
struct device dev; // 继承的通用设备结构
struct resource *resource; // 硬件资源数组
u32 num_resources; // 资源数量
// ...其他成员
};
关键成员解析:
c
Copy
struct platform_driver {
int (*probe)(struct platform_device *);
int (*remove)(struct platform_device *);
struct device_driver driver;
const struct platform_device_id *id_table;
// ...电源管理相关
};
核心功能函数:
c
Copy
struct resource {
resource_size_t start; // 起始地址/中断号
resource_size_t end; // 结束地址
const char *name; // 资源名称
unsigned long flags; // 类型标识
};
常用类型标志:
c
Copy
#define IORESOURCE_MEM 0x00000200 // 内存区域
#define IORESOURCE_IRQ 0x00000400 // 中断资源
匹配方式 | 优先级 | 适用场景 |
---|---|---|
设备树匹配 | 最高 | 现代设备,支持自动初始化 |
ID表匹配 | 中等 | 支持多设备的通用驱动 |
名称匹配 | 最低 | 简单设备,快速开发 |
设备端配置:
c
Copy
struct platform_device my_dev = {
.name = "led_ctrl",
.id = -1,
.resource = led_resources,
.num_resources = ARRAY_SIZE(led_resources),
};
驱动端配置:
c
Copy
struct platform_driver my_drv = {
.driver = {
.name = "led_ctrl",
},
.probe = led_probe,
.remove = led_remove,
};
匹配流程图:
Image
Code
名称相同platform_device_register总线设备列表platform_driver_register总线驱动列表匹配检查调用probe函数
c
Copy
static struct resource led_res[] = {
[0] = {
.start = 0x11000C40, // GPIO控制寄存器
.end = 0x11000C40 + 0x4,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = 5, // 使用GPIO5
.flags = IORESOURCE_IRQ,
}
};
c
Copy
static void led_release(struct device *dev) {
printk("LED device released\n");
}
struct platform_device led_device = {
.name = "sunxi_led",
.id = -1,
.dev = {
.release = led_release,
},
.resource = led_res,
.num_resources = ARRAY_SIZE(led_res),
};
static int __init led_dev_init(void) {
return platform_device_register(&led_device);
}
c
Copy
static int led_probe(struct platform_device *pdev) {
struct resource *mem = platform_get_resource(pdev,
IORESOURCE_MEM, 0);
struct resource *irq = platform_get_resource(pdev,
IORESOURCE_IRQ, 0);
// 映射IO内存
void __iomem *base = ioremap(mem->start,
resource_size(mem));
// 配置GPIO方向
writel(0x01, base + 0x04);
return 0;
}
struct platform_driver led_driver = {
.driver = {
.name = "sunxi_led",
},
.probe = led_probe,
.remove = led_remove,
};
static int __init led_drv_init(void) {
return platform_driver_register(&led_driver);
}
c
Copy
struct resource *platform_get_resource(
struct platform_device *dev,
unsigned int type,
unsigned int num);
c
Copy
void __iomem *ioremap(phys_addr_t offset, size_t size);
c
Copy
unsigned int readl(void __iomem *addr);
void writel(u32 value, void __iomem *addr);
dts
Copy
leds {
compatible = "sunxi,led-ctrl";
reg = <0x11000C40 0x04>;
gpio = <&pio 5 0>;
interrupt-parent = <&gic>;
interrupts = <0 23 4>;
};
c
Copy
static const struct of_device_id led_dt_ids[] = {
{ .compatible = "sunxi,led-ctrl" },
{ /* Sentinel */ }
};
struct platform_driver led_driver = {
.driver = {
.of_match_table = led_dt_ids,
},
// ...其他成员不变
};
c
Copy
if (!res) {
dev_err(&pdev->dev, "Resource missing\n");
return -ENODEV;
}
bash
Copy
ls /sys/bus/platform/devices
bash
Copy
dmesg | grep "probe"
bash
Copy
dtc -I fs /proc/device-tree
平台总线模型通过标准化的接口设计,极大提升了Linux驱动开发的模块化程度。随着设备树的普及,驱动开发正朝着"一次编写,多处适配"的理想形态演进。掌握这些核心机制,开发者可以更高效地构建可维护、可扩展的嵌入式系统。