Linux平台设备驱动开发:platform_driver与platform_device详解

发布时间:2026/7/19 1:55:26
Linux平台设备驱动开发:platform_driver与platform_device详解 1. Linux平台设备驱动基础解析在Linux驱动开发中platform_driver_register()和platform_device_register()是两个核心函数它们构成了Linux平台设备驱动框架的基础。这个框架主要解决硬件资源与驱动代码耦合的问题使得驱动开发更加模块化和可维护。1.1 平台设备驱动模型概述传统字符设备驱动开发方式存在一个明显缺陷硬件信息如寄存器地址、中断号等直接硬编码在驱动代码中。这种方式的弊端在于硬件变更需要修改驱动源码同一驱动难以适配不同硬件配置代码复用性差平台设备驱动模型通过引入虚拟的platform总线将设备信息与驱动代码分离平台设备(platform_device)描述硬件资源平台驱动(platform_driver)实现设备操作平台总线(platform_bus)负责设备与驱动的匹配1.2 平台设备注册详解1.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总线匹配时使用的设备名称resource描述设备占用的硬件资源内存、中断等dev.platform_data设备私有数据指针1.2.2 资源描述方式硬件资源通过resource结构体描述struct resource { resource_size_t start; // 起始地址 resource_size_t end; // 结束地址 const char *name; unsigned long flags; // 资源类型标志 };常用资源类型标志IORESOURCE_MEM内存映射资源IORESOURCE_IOI/O端口资源IORESOURCE_IRQ中断资源1.2.3 设备注册流程注册平台设备的典型代码流程// 1. 定义资源 static struct resource demo_res[] { DEFINE_RES_MEM(0xFDD60000, 0x1000), // 内存区域 DEFINE_RES_IRQ(32), // 中断号 }; // 2. 定义平台设备 static struct platform_device demo_dev { .name demo_device, .id 0, .num_resources ARRAY_SIZE(demo_res), .resource demo_res, }; // 3. 注册设备 platform_device_register(demo_dev);1.3 平台驱动注册详解1.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.name驱动名称用于匹配id_table支持的设备ID表1.3.2 驱动注册流程注册平台驱动的典型代码流程// 1. 定义ID表 static const struct platform_device_id demo_ids[] { {demo_device, 0}, {} }; // 2. 实现probe/remove函数 static int demo_probe(struct platform_device *pdev) { // 初始化设备 return 0; } static int demo_remove(struct platform_device *pdev) { // 清理资源 return 0; } // 3. 定义平台驱动 static struct platform_driver demo_drv { .driver { .name demo_device, }, .probe demo_probe, .remove demo_remove, .id_table demo_ids, }; // 4. 注册驱动 platform_driver_register(demo_drv);1.4 总线匹配机制平台总线通过platform_match函数实现设备与驱动的匹配匹配优先级为设备树匹配of_driver_match_deviceACPI匹配acpi_driver_match_deviceID表匹配id_table名称匹配strcmp匹配成功后总线会调用驱动的probe函数并传入匹配的平台设备指针。2. 平台设备驱动实战开发2.1 开发环境准备开发平台驱动需要Linux内核源码版本与目标系统一致交叉编译工具链嵌入式开发需要目标硬件规格书寄存器定义等2.2 典型开发流程2.2.1 定义平台设备以GPIO设备为例// GPIO寄存器定义 #define GPIO_BASE 0xFDD60000 #define GPIO_DR (GPIO_BASE 0x00) #define GPIO_DDR (GPIO_BASE 0x08) // 定义资源 static struct resource gpio_res[] { DEFINE_RES_MEM(GPIO_DR, 8), // 寄存器区域 DEFINE_RES_IRQ(45), // GPIO中断 }; // 定义私有数据引脚号 static unsigned int gpio_pin 7; // 定义平台设备 static struct platform_device gpio_dev { .name gpio_device, .id 0, .num_resources ARRAY_SIZE(gpio_res), .resource gpio_res, .dev { .platform_data gpio_pin, }, };2.2.2 定义平台驱动// 设备数据结构 struct gpio_data { void __iomem *reg_base; unsigned int pin; int irq; }; // probe函数实现 static int gpio_probe(struct platform_device *pdev) { struct resource *mem; struct gpio_data *data; // 分配设备数据 data devm_kzalloc(pdev-dev, sizeof(*data), GFP_KERNEL); // 获取内存资源 mem platform_get_resource(pdev, IORESOURCE_MEM, 0); >static int gpio_probe(struct platform_device *pdev) { // ...获取资源 // 注册字符设备 alloc_chrdev_region(devno, 0, 1, gpio_dev); cdev_init(data-cdev, gpio_fops); cdev_add(data-cdev, devno, 1); // 创建设备节点 >gpio_device: gpiofdd60000 { compatible vendor,gpio-device; reg 0xfdd60000 0x1000; interrupts 45 IRQ_TYPE_LEVEL_HIGH; pin 7; };驱动匹配static const struct of_device_id gpio_dt_ids[] { { .compatible vendor,gpio-device }, {} };3.2 多设备支持通过id_table支持多个设备static const struct platform_device_id gpio_ids[] { { gpio-device-v1, (kernel_ulong_t)v1_data }, { gpio-device-v2, (kernel_ulong_t)v2_data }, {} }; static int gpio_probe(struct platform_device *pdev) { const struct platform_device_id *id platform_get_device_id(pdev); struct gpio_version_data *data (void *)id-driver_data; // 使用版本特定数据 }3.3 电源管理实现电源管理回调static int gpio_suspend(struct device *dev) { struct gpio_data *data dev_get_drvdata(dev); // 保存状态进入低功耗模式 return 0; } static int gpio_resume(struct device *dev) { struct gpio_data *data dev_get_drvdata(dev); // 恢复状态 return 0; } static const struct dev_pm_ops gpio_pm_ops { SET_SYSTEM_SLEEP_PM_OPS(gpio_suspend, gpio_resume) };4. 调试与问题排查4.1 常见问题匹配失败检查设备/驱动名称是否一致确认id_table或设备树compatible是否正确检查总线类型是否正确资源获取失败确认资源索引是否正确检查设备是否提供了所需资源验证资源地址和大小内存映射问题检查ioremap返回值确认物理地址是否正确验证访问权限4.2 调试技巧查看已注册设备ls /sys/bus/platform/devices/查看驱动匹配cat /sys/bus/platform/drivers/*/bind cat /sys/bus/platform/drivers/*/unbind动态日志dev_dbg(pdev-dev, Probe called for device %s\n, pdev-name);4.3 性能优化使用devm_接口管理资源data-regs devm_ioremap(pdev-dev, res-start, resource_size(res));延迟初始化static int gpio_probe(struct platform_device *pdev) { // 立即必要的初始化 // ... // 延迟非关键初始化 INIT_DELAYED_WORK(data-init_work, delayed_init); schedule_delayed_work(data-init_work, HZ); return 0; }中断优化使用线程化中断合理设置中断标志考虑使用中断共享5. 实际案例GPIO平台驱动5.1 设备定义static struct resource gpio_resources[] { [0] DEFINE_RES_MEM(0xFDD60000, 0x1000), [1] DEFINE_RES_IRQ(45), }; static unsigned int gpio_pin 7; static struct platform_device gpio_device { .name demo-gpio, .id 0, .num_resources ARRAY_SIZE(gpio_resources), .resource gpio_resources, .dev { .platform_data gpio_pin, }, };5.2 驱动实现struct gpio_drvdata { struct platform_device *pdev; void __iomem *regs; unsigned int pin; int irq; struct cdev cdev; }; static int gpio_open(struct inode *inode, struct file *file) { struct gpio_drvdata *data container_of(inode-i_cdev, struct gpio_drvdata, cdev); file-private_data data; return 0; } static const struct file_operations gpio_fops { .owner THIS_MODULE, .open gpio_open, // 其他操作... }; static int gpio_probe(struct platform_device *pdev) { struct gpio_drvdata *data; dev_t devno; int ret; // 分配驱动数据 data devm_kzalloc(pdev-dev, sizeof(*data), GFP_KERNEL); // 获取资源 >static struct platform_driver gpio_driver { .driver { .name demo-gpio, }, .probe gpio_probe, .remove gpio_remove, }; static int __init gpio_init(void) { platform_device_register(gpio_device); return platform_driver_register(gpio_driver); } static void __init gpio_exit(void) { platform_driver_unregister(gpio_driver); platform_device_unregister(gpio_device); } module_init(gpio_init); module_exit(gpio_exit);6. 现代开发实践6.1 设备树优先现代Linux驱动开发推荐硬件描述放在设备树中驱动通过of_接口获取资源减少platform_data的使用6.2 使用现代APIdevm_资源管理data-regs devm_platform_ioremap_resource(pdev, 0);简化错误处理data-irq platform_get_irq(pdev, 0); if (data-irq 0) return>模块宏MODULE_DEVICE_TABLE(of, gpio_dt_ids); MODULE_AUTHOR(Author); MODULE_LICENSE(GPL);6.3 测试与验证单元测试使用内核测试框架模拟平台设备集成测试真实硬件验证长时间稳定性测试性能分析perf工具ftrace跟踪在实际开发中平台设备驱动模型为Linux驱动开发提供了标准化的框架使得驱动代码更加清晰、可维护。通过合理使用platform_device_register和platform_driver_register开发者可以创建出高质量、可移植的设备驱动。