1. Linux平台总线驱动概述在Linux设备驱动开发中平台总线(platform bus)是一种虚拟总线机制用于管理那些没有物理总线连接的设备。这种机制将硬件资源信息与驱动代码分离使得驱动开发更加模块化和可维护。1.1 为什么需要平台总线传统的字符设备驱动开发方式存在一个明显问题设备信息和驱动代码高度耦合。在这种模式下硬件寄存器地址、中断号等资源信息直接硬编码在驱动代码中。当硬件发生变化时必须修改驱动源码并重新编译。平台总线机制通过以下方式解决了这个问题将硬件资源信息抽象为platform_device结构将驱动逻辑封装在platform_driver结构通过总线机制自动匹配设备和驱动1.2 平台总线的核心组件平台总线架构包含三个核心组件平台设备(platform_device)描述硬件资源信息平台驱动(platform_driver)实现设备操作逻辑平台总线(platform_bus_type)负责设备和驱动的匹配这种分离的设计使得同一驱动可以支持不同硬件配置硬件变更无需修改驱动代码驱动开发更加标准化2. 平台设备(platform_device)详解2.1 platform_device结构体平台设备使用platform_device结构体表示其定义如下struct platform_device { const char *name; // 设备名称 int id; // 设备ID struct device dev; // 基础设备结构 u32 num_resources; // 资源数量 struct resource *resource; // 资源数组 const struct platform_device_id *id_entry; /* 其他成员省略 */ };关键成员说明name设备名称用于总线匹配id设备编号区分同名设备dev继承自基础设备模型resource描述硬件资源(I/O内存、中断等)2.2 硬件资源描述硬件资源通过struct resource描述struct resource { resource_size_t start; // 起始地址 resource_size_t end; // 结束地址 const char *name; unsigned long flags; // 资源类型标志 };常用资源类型标志IORESOURCE_MEM内存映射资源IORESOURCE_IOI/O端口资源IORESOURCE_IRQ中断资源2.3 设备私有数据对于无法用标准资源描述的硬件信息可以通过platform_data传递struct platform_device pdev { .dev { .platform_data custom_data, } };3. 平台驱动(platform_driver)实现3.1 platform_driver结构体平台驱动使用platform_driver结构体表示struct platform_driver { int (*probe)(struct platform_device *); int (*remove)(struct platform_device *); struct device_driver driver; const struct platform_device_id *id_table; };关键成员说明probe设备匹配成功后调用的初始化函数remove设备移除时调用的清理函数driver基础驱动结构id_table支持的设备ID表3.2 驱动匹配机制平台总线提供四种匹配方式按优先级排序设备树匹配通过compatible属性匹配ACPI匹配用于电源管理相关设备ID表匹配比较id_table中的name字段名称匹配直接比较设备和驱动的name字段3.3 资源获取接口驱动中获取设备资源的常用接口// 获取内存/I/O资源 struct resource *platform_get_resource( struct platform_device *dev, unsigned int type, unsigned int num); // 获取中断号 int platform_get_irq( struct platform_device *pdev, unsigned int num); // 获取私有数据 void *dev_get_platdata(const struct device *dev);4. 平台总线工作原理4.1 总线注册流程Linux内核启动时会自动注册平台总线struct bus_type platform_bus_type { .name platform, .match platform_match, /* 其他成员 */ }; int __init platform_bus_init(void) { return bus_register(platform_bus_type); }4.2 设备驱动匹配过程匹配函数platform_match的实现逻辑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. 尝试设备树匹配 if (of_driver_match_device(dev, drv)) return 1; // 3. 尝试ACPI匹配 if (acpi_driver_match_device(dev, drv)) return 1; // 4. 尝试ID表匹配 if (pdrv-id_table) return platform_match_id(pdrv-id_table, pdev) ! NULL; // 5. 回退到名称匹配 return (strcmp(pdev-name, drv-name) 0); }5. 实战LED平台驱动开发5.1 平台设备定义定义LED设备的硬件资源// GPIO寄存器定义 #define GPIO_BASE 0xfdd60000 #define GPIO_DR_H (GPIO_BASE 0x0004) #define GPIO_DDR_H (GPIO_BASE 0x000C) // 资源定义 static struct resource pdev_led_resource[] { [0] DEFINE_RES_MEM(GPIO_DR_H, 4), [1] DEFINE_RES_MEM(GPIO_DDR_H, 4), }; // 私有数据(引脚偏移) unsigned int pdev_led_hwinfo[1] { 7 }; // 平台设备定义 static struct platform_device pdev_led { .name pdev_led, .id 0, .num_resources ARRAY_SIZE(pdev_led_resource), .resource pdev_led_resource, .dev { .platform_data pdev_led_hwinfo, }, };5.2 平台驱动实现实现LED平台驱动// 设备ID表 static struct platform_device_id pdev_led_ids[] { {.name pdev_led}, {} }; // probe函数 static int pdrv_led_probe(struct platform_device *pdev) { // 1. 获取硬件资源 struct resource *mem_dr platform_get_resource(pdev, IORESOURCE_MEM, 0); struct resource *mem_ddr platform_get_resource(pdev, IORESOURCE_MEM, 1); // 2. 映射寄存器 void __iomem *va_dr devm_ioremap(pdev-dev, mem_dr-start, resource_size(mem_dr)); void __iomem *va_ddr devm_ioremap(pdev-dev, mem_ddr-start, resource_size(mem_ddr)); // 3. 初始化GPIO unsigned int val ioread32(va_ddr); val | (1 (pin 16)) | (1 pin); iowrite32(val, va_ddr); // 4. 注册字符设备 // ...字符设备注册代码... return 0; } // 平台驱动定义 static struct platform_driver pdrv_led { .probe pdrv_led_probe, .remove pdrv_led_remove, .driver { .name pdev_led, }, .id_table pdev_led_ids, };5.3 模块加载与测试加载驱动模块并测试# 加载平台设备模块 insmod pdev_led.ko # 加载平台驱动模块 insmod pdrv_led.ko # 测试LED控制 echo 1 /dev/pdrv_led # 点亮LED echo 0 /dev/pdrv_led # 熄灭LED6. 常见问题与调试技巧6.1 匹配失败排查当设备和驱动没有成功匹配时可以检查确认设备和驱动的name字段完全一致检查/sys/bus/platform/devices和/sys/bus/platform/drivers目录使用dmesg查看内核日志中的匹配错误信息6.2 资源获取失败处理资源获取失败的可能原因资源索引号不正确资源类型不匹配设备未正确注册建议添加错误检查代码struct resource *res platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { dev_err(pdev-dev, Failed to get MEM resource\n); return -ENODEV; }6.3 设备树兼容性处理对于支持设备树的平台建议实现of_match_tablestatic const struct of_device_id led_of_match[] { { .compatible mycompany,led }, {}, }; static struct platform_driver pdrv_led { .driver { .of_match_table led_of_match, }, };7. 性能优化建议7.1 资源管理优化使用devm_系列函数自动管理资源// 自动释放的ioremap void __iomem *va devm_ioremap(pdev-dev, res-start, resource_size(res)); // 自动释放的内存分配 struct my_data *data devm_kzalloc(pdev-dev, sizeof(*data), GFP_KERNEL);7.2 延迟初始化对于耗时的初始化操作可以使用延迟工作队列static int pdrv_led_probe(struct platform_device *pdev) { INIT_DELAYED_WORK(data-work, led_init_work); schedule_delayed_work(data-work, msecs_to_jiffies(100)); return 0; }7.3 电源管理实现电源管理回调以节省功耗static int __maybe_unused pdrv_led_suspend(struct device *dev) { // 保存状态并进入低功耗模式 return 0; } static int __maybe_unused pdrv_led_resume(struct device *dev) { // 恢复状态 return 0; } static const struct dev_pm_ops pdrv_led_pm_ops { SET_SYSTEM_SLEEP_PM_OPS(pdrv_led_suspend, pdrv_led_resume) };8. 进阶话题8.1 平台总线与设备树现代Linux驱动开发中平台设备信息通常通过设备树描述led-controllerfdd60000 { compatible mycompany,led; reg 0xfdd60000 0x1000; led-pin 7; };驱动中通过of_函数读取设备树信息int pin; struct device_node *np pdev-dev.of_node; of_property_read_u32(np, led-pin, pin);8.2 多设备支持通过id_table支持多个设备变体static struct platform_device_id pdev_led_ids[] { {.name led-v1, .driver_data (kernel_ulong_t)v1_data}, {.name led-v2, .driver_data (kernel_ulong_t)v2_data}, {} };8.3 平台总线与字符设备平台总线常与字符设备结合使用平台总线处理硬件资源管理字符设备提供用户空间接口probe函数中完成字符设备注册这种组合既保持了硬件抽象又提供了标准的用户接口。