当前位置: 首页 > news >正文

【Linux 平台总线驱动开发实战】

Linux 平台总线驱动开发实战

  • 一、平台总线驱动基础概念
  • 二、核心数据结构解析
  • 2.1 设备结构体 struct platform_device
    • 2.2 驱动结构体 struct platform_driver
    • 2.3 资源结构体 struct resource
  • 三、驱动开发完整流程
    • 3.1 设备注册
    • 3.2 驱动注册
    • 3.3 设备与驱动匹配
  • 四、编译与测试
    • 4.1 编译模块
    • 4.2 加载与验证
  • 五、总结

在 Linux 驱动开发领域,平台总线驱动是连接硬件设备与内核的重要桥梁,它通过将设备和驱动分离管理,极大提升了驱动的复用性和系统的可维护性。本文将深入剖析平台总线驱动的工作原理,并结合完整代码示例,帮助开发者掌握其核心开发流程。

一、平台总线驱动基础概念

Linux 平台总线驱动基于设备、驱动和总线三者的协同工作,形成了一套高效的设备管理机制:

设备(Device): 代表具体的硬件实体,描述设备名称、资源占用(如内存地址、中断号)等信息。
驱动(Driver): 包含操作硬件的核心代码,通过probe、remove等回调函数完成设备初始化与资源释放。
总线(Bus): 负责两者的注册、匹配与通信,确保驱动能正确识别并控制设备。

平台总线的核心优势在于分离设备与驱动,同一驱动可适配多种同类设备,设备升级时仅需修改设备描述,无需改动驱动代码,显著提升开发效率。

二、核心数据结构解析

2.1 设备结构体 struct platform_device

struct platform_device {const char  *name;      // 设备名称,用于驱动匹配int         id;         // 设备ID(-1表示自动分配)struct device dev;      // 通用设备结构u32         num_resources;  // 资源数量struct resource *resource;  // 硬件资源数组// 其他字段
};

通过填充resource数组,可指定设备的内存、中断等资源。例如:

static struct resource my_gpio_resources[] = {[0] = {.start = 0xFDD60000,       // GPIO控制器基地址.end   = 0xFDD60004,.flags = IORESOURCE_MEM,},[1] = {.start = 15,       .end   = 15,.flags = IORESOURCE_IRQ,}};

2.2 驱动结构体 struct 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; // 支持的设备ID表// 其他字段
};

probe函数是驱动核心,用于初始化设备;id_table定义驱动支持的设备列表,辅助总线完成匹配。

2.3 资源结构体 struct resource

struct resource {resource_size_t start;  // 资源起始地址resource_size_t end;    // 资源结束地址unsigned long flags;    // 资源类型(如`IORESOURCE_MEM`、`IORESOURCE_IRQ`)// 其他字段
};

三、驱动开发完整流程

3.1 设备注册

定义设备资源: 通过struct resource数组描述硬件资源。
初始化设备结构体: 填充struct platform_device,指定名称、ID 和资源。
注册设备到总线: 调用platform_device_register函数。

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/ioport.h>// 设备名称(用于匹配驱动)
#define DEVICE_NAME "my_gpio_controller"// 定义设备资源(内存区域和中断)
static struct resource my_gpio_resources[] = {[0] = {.start = 0xFDD60000,       // GPIO控制器基地址.end   = 0xFDD60004,.flags = IORESOURCE_MEM,},[1] = {.start = 15,       .end   = 15,.flags = IORESOURCE_IRQ,}};static void my_gpio_release(struct device *dev){printk(KERN_ERR "my_gpio_release\n");
}// 平台设备结构体
static struct platform_device my_gpio_device = {.name           = DEVICE_NAME,.id             = -1,          // 自动分配ID.num_resources  = ARRAY_SIZE(my_gpio_resources),.resource       = my_gpio_resources,.dev            = {.release    = my_gpio_release},
};// 模块初始化函数
static int __init platform_device_init(void) {int ret;// 注册平台设备ret = platform_device_register(&my_gpio_device);if (ret) {printk(KERN_ERR "Failed to register platform device: %d\n", ret);return ret;}printk(KERN_INFO "Platform device registered: %s\n", DEVICE_NAME);return 0;
}// 模块退出函数
static void __exit platform_device_exit(void) {// 注销平台设备platform_device_unregister(&my_gpio_device);printk(KERN_INFO "Platform device unregistered\n");
}module_init(platform_device_init);
module_exit(platform_device_exit);MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Example Platform Device");
MODULE_AUTHOR("cmy");

3.2 驱动注册

实现驱动回调函数: 编写probe、remove等核心函数。
初始化驱动结构体: 指定驱动名称、支持的设备列表等。
注册驱动到总线: 调用platform_driver_register函数。

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/cdev.h>
#include <linux/platform_device.h>
#include <linux/mod_devicetable.h>
#include <linux/device.h>// 驱动支持的设备名称
#define DRIVER_NAME "my_gpio_controller"// 寄存器偏移量
#define GPIO_SWPORT_DDR 0x0008 //输入输出偏移量
#define GPIO_SWPORT_DR 0x0000 //高低电平偏移量// 设备私有数据结构
struct gpio_dev {struct device *dev;dev_t dev_num;void __iomem *regs;         // 映射后的寄存器基址int irq;                    // 中断号struct class *class;        // 设备类struct cdev cdev;			// 字符设备结构
};// 文件操作函数
static ssize_t gpio_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos) {struct gpio_dev *gdev = filp->private_data;int value = 0;if (!gdev->regs) {printk(KERN_ERR "gpio_read gdev->regs can't be null\n");return -EFAULT;}u64 gp_cfg = ioread64(gdev->regs + GPIO_SWPORT_DR);value = (gp_cfg >> 15) & 1;//获取第15位printk(KERN_INFO "gpio_read value = %d\n", value);if (copy_to_user(buf, &value, sizeof(int))) {return -EFAULT;}return 0;
}static ssize_t gpio_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos) {struct gpio_dev *gdev = filp->private_data;int value = 0;if (copy_from_user(&value, buf, sizeof(int))) {return -EFAULT;}if (!gdev->regs) {printk(KERN_ERR "gpio_write gdev->regs can't be null\n");return -EFAULT;}u64 gp_cfg = ioread64(gdev->regs + GPIO_SWPORT_DR);printk(KERN_INFO "gpio_write GPIO_SWPORT_DR gp_cfg = %llx\n", gp_cfg);if(value){gp_cfg |= 1 << 31;//Write access enablegp_cfg |= 1 << 15;//high}else{gp_cfg |= 1 << 31;//Write access enableprintk(KERN_INFO " gpio_write Write access gp_cfg = %llx\n", gp_cfg);gp_cfg &= ~(1 << 15);//lowprintk(KERN_INFO " gpio_write low gp_cfg = %llx\n", gp_cfg);}iowrite64(gp_cfg, gdev->regs + GPIO_SWPORT_DR);printk(KERN_INFO " gpio_write gp_cfg = %llx\n", gp_cfg);return 0;
}static int gpio_open(struct inode *inode, struct file *filp) {struct gpio_dev *gdev = container_of(inode->i_cdev, struct gpio_dev, cdev);filp->private_data = gdev;return 0;
}static int gpio_release(struct inode *inode, struct file *filp) {return 0;
}// 文件操作表
static const struct file_operations gpio_fops = {.owner = THIS_MODULE,.open = gpio_open,.read = gpio_read,.write = gpio_write,.release = gpio_release,
};// 驱动probe函数(设备匹配成功时调用)
static int gpio_probe(struct platform_device *pdev) {struct gpio_dev *gdev;struct resource *res;int ret;// 分配并初始化设备结构体gdev = devm_kzalloc(&pdev->dev, sizeof(*gdev), GFP_KERNEL);if (!gdev) {dev_err(&pdev->dev, "Failed to allocate memory\n");return -ENOMEM;}gdev->dev = &pdev->dev;platform_set_drvdata(pdev, gdev);// 获取内存资源并映射res = platform_get_resource(pdev, IORESOURCE_MEM, 0);printk(KERN_INFO "gpio_probe res->start = %llx, res->end = %llx\n", res->start, res->end);/*if (!res) {dev_err(&pdev->dev, "Missing memory resource\n");return -ENODEV;}gdev->regs = devm_ioremap_resource(&pdev->dev, res);if (IS_ERR(gdev->regs)) {dev_err(&pdev->dev, "Failed to map memory resource\n");return PTR_ERR(gdev->regs);}*/// 注册字符设备ret = alloc_chrdev_region(&gdev->dev_num, 0, 1, pdev->name);if (ret) {dev_err(&pdev->dev, "Failed to allocate char device region\n");return ret;}cdev_init(&gdev->cdev, &gpio_fops);gdev->cdev.owner = THIS_MODULE;ret = cdev_add(&gdev->cdev, gdev->dev_num, 1);if (ret) {dev_err(&pdev->dev, "Failed to add char device\n");unregister_chrdev_region(gdev->dev_num, 1);return ret;}// 创建设备类gdev->class = class_create(THIS_MODULE, pdev->name);if (IS_ERR(gdev->class)) {dev_err(&pdev->dev, "Failed to create class\n");cdev_del(&gdev->cdev);unregister_chrdev_region(gdev->dev_num, 1);return PTR_ERR(gdev->class);}// 创建设备节点gdev->dev = device_create(gdev->class, NULL, gdev->dev_num, NULL, pdev->name);if (IS_ERR(gdev->dev)) {dev_err(&pdev->dev, "Failed to create device\n");class_destroy(gdev->class);cdev_del(&gdev->cdev);unregister_chrdev_region(gdev->dev_num, 1);return PTR_ERR(gdev->dev);}// 映射GPIO寄存器gdev->regs = ioremap(res->start, res->end - res->start);if (!gdev->regs) {printk(KERN_ERR "gpio_probe Failed to ioremap\n");cdev_del(&gdev->cdev);unregister_chrdev_region(gdev->dev_num, 1);return -ENOMEM;}printk(KERN_INFO "gpio_probe gdev->regs = %llx\n", gdev->regs);u64 gp_cfg = ioread64(gdev->regs + GPIO_SWPORT_DDR);printk(KERN_INFO "gpio_probe GPIO_SWPORT_DDR gp_cfg = %llx\n", gp_cfg);gp_cfg |= 1 << 31;// Write access enablegp_cfg |= 1 << 15;//Output//设置输出模式iowrite64(gp_cfg, gdev->regs + GPIO_SWPORT_DDR);gp_cfg = ioread64(gdev->regs + GPIO_SWPORT_DDR);printk(KERN_INFO "gpio_probe initialized successfully gp_cfg = %llx\n", gp_cfg);dev_info(&pdev->dev, "GPIO driver initialized\n");return 0;
}// 驱动remove函数(设备移除时调用)
static int gpio_remove(struct platform_device *pdev) {struct gpio_dev *gdev = platform_get_drvdata(pdev);if (gdev->regs) {iounmap(gdev->regs);  // 解除映射gdev->regs = NULL;}// 清理资源device_destroy(gdev->class, gdev->dev_num);class_destroy(gdev->class);cdev_del(&gdev->cdev);unregister_chrdev_region(gdev->dev_num, 1);dev_info(&pdev->dev, "GPIO driver removed\n");return 0;
}// 驱动支持的设备ID表const struct platform_device_id gpio_device_id = {.name = DRIVER_NAME,
};// 平台驱动结构体
static struct platform_driver gpio_driver = {.probe      = gpio_probe,.remove     = gpio_remove,.driver     = {.name   = DRIVER_NAME,.owner  = THIS_MODULE,},.id_table   = &gpio_device_id,
};// 模块初始化
static int __init gpio_driver_init(void) {return platform_driver_register(&gpio_driver);
}// 模块退出
static void __exit gpio_driver_exit(void) {platform_driver_unregister(&gpio_driver);
}module_init(gpio_driver_init);
module_exit(gpio_driver_exit);MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Example Platform Driver");
MODULE_AUTHOR("cmy");

3.3 设备与驱动匹配

总线通过设备和驱动的name字段进行匹配,匹配成功后自动调用驱动的probe函数初始化设备。若id_table存在,总线会优先检查设备是否在支持列表中。

四、编译与测试

4.1 编译模块

创建Makefile:

export ARCH=arm64export CROSS_COMPILE=/home/chenmy/rk356x/RK356X_Android11.0/prebuilts/gcc/linux-x86/aarch64/gcc-linaro-6.3.1-2017.05-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu-obj-m += platform_device_example.oobj-m += platform_driver_example.oKERNEL_DIR:=/home/chenmy/rk356x/RK356X_Android11.0/kernelall:make -C $(KERNEL_DIR) M=$(PWD) modules
clean:make -C $(KERNEL_DIR) M=$(PWD) clean

执行make生成.ko模块文件。

4.2 加载与验证

 # 加载设备模块
insmod platform_device_example.ko
# 加载驱动模块
insmod platform_driver_example.ko# 查看设备与驱动状态
ls /sys/bus/platform/devices | grep "my"
ls /sys/bus/platform/drivers | grep "my"

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

五、总结

Linux 平台总线驱动通过标准化的设备与驱动分离模型,显著提升了驱动开发的效率与代码复用性。本文通过原理解析与完整代码示例,展示了从设备注册、驱动实现到匹配测试的全流程。在实际开发中,开发者可根据硬件需求灵活调整资源配置与回调函数逻辑,构建稳定高效的驱动程序。

相关文章:

  • 三种语言写 MCP
  • 大数据量的分页,怎么办?
  • MagnTek MT6816-ACD 一款基于各向异性磁阻(AMR)技术的磁性角度传感器 IC
  • 现代浏览器剪贴板操作指南 + 示例页面 navigator.clipboard 详解与实战
  • 1.20.1 服务器系统(windows,Rocky 和 Ubuntu )体验
  • 浅议 3D 展示技术为线上车展新体验带来的助力​
  • 【Docker基础】Docker镜像管理:docker rmi、prune详解
  • 基于Spring Boot瀚森健身房会员管理系统设计与实现【源码+文档】
  • React JSX语法
  • 基于YOLO的智能车辆检测与记录系统
  • Vue.js 粒子连线动画组件 - FlyingLines
  • 【RAG优化】深度解析开源项目MinerU:从PDF解析到多模态理解的工业级解决方案
  • 《Whisper:开启语音识别新时代的钥匙》
  • 什么是跨域问题?后端如何解决跨域问题?
  • JVM(面试)
  • 【C/C++】Gtest + GMock 简单使用示例
  • 简说MQTT
  • 【分布式理论】读确认数与写确认数:分布式一致性的核心概念
  • C# 将 Enum枚举转成List,并显示在下拉列表中
  • 如何开发HarmonyOS 5的分布式通信功能?
  • 网站建设套餐电话/百度助手
  • 做视频赚钱的网站/2021搜索引擎排名
  • 广东深圳地图全图/关键词seo排名优化推荐
  • 百度云建设网站/培训平台有哪些
  • 移除wordpress4版本号/seo推广编辑
  • 上海地产网站建设/郑州网站排名优化外包