一、存共享硬件对象
static int my_open(struct inode *inode, struct file *file) { struct my_dev *dev = container_of(inode->i_cdev, struct my_dev, cdev); file->private_data = dev; // 存共享硬件对象,省去后续 container_of return 0; } static long my_ioctl(struct file *file, ...) { struct my_dev *dev = file->private_data; // 直接使用,O(1) 访问 }典型例子:RTC 驱动
static int rtc_dev_open(struct inode *inode, struct file *file) { struct rtc_device *rtc = container_of(inode->i_cdev, struct rtc_device, cdev); file->private_data = rtc; // 所有 fd 共享同一个 rtc_device return 0; }读取 RTC 时间,硬件只有一个“当前时间”,读 10 次都一样
不需要记录“我这个 fd 读到了第几个字节”
rtc->ops->read_time()是纯函数,无副作用
二、存本文件私有结构
struct my_file_data { int pos; // 本文件的读写位置 int flags; // 本文件的 O_NONBLOCK 等标志 wait_queue_head_t wait; // 本文件的等待队列 struct my_dev *dev; // 再存一份硬件指针(可选) }; static int my_open(struct inode *inode, struct file *file) { struct my_dev *dev = container_of(inode->i_cdev, struct my_dev, cdev); struct my_file_data *data = kmalloc(sizeof(*data), GFP_KERNEL); >代码位置:drivers/rtc/class.c rtc_class->dev_groups = rtc_groups; // 所有 rtc 设备自动继承 效果: 所有 struct rtc_device 都会长出 time、date、wakealarm 等文件 驱动 不写一行代码,属性就自动有了 属性是 “通用属性” ,与具体芯片无关 适用场景: 子系统框架(RTC、input、video、sound) 属性是所有同类设备共有的(如 RTC 都必须能读时间)2. 设备层级绑定(驱动私有属性)
代码位置:某个具体驱动的probe()里
static int my_sensor_probe(struct i2c_client *client, ...) { struct device *dev = &client->dev; // 创建驱动私有的属性 device_create_file(dev, &dev_attr_custom_reg); device_create_file(dev, &dev_attr_sampling_rate); return 0; } 效果: 只有 这个设备 有 custom_reg 文件 其他同芯片的驱动不会自动继承 属性是 “私有属性” ,只有这个驱动需要 适用场景: 某个芯片有特殊寄存器(如校准值、工作模式) 属性只对这个设备有意义3. 驱动层级绑定(driver 属性)
代码位置:struct device_driver定义时
static struct driver_attribute drv_attr_version = __ATTR_RW(version); static struct attribute *sensor_driver_attrs[] = { &drv_attr_version.attr, NULL }; ATTRIBUTE_GROUPS(sensor_driver); static struct platform_driver sensor_driver = { .driver = { .name = "sensor", .groups = sensor_driver_groups, // 绑在 driver 上 }, .probe = sensor_probe, }; 效果: 属性出现在 /sys/bus/platform/drivers/sensor/ 目录下 与 具体设备实例无关(没 probe 也能看到) 存的是驱动版本号、固件信息等全局信息 适用场景: 驱动自身的元信息(版本、编译时间、许可证) 调试开关(影响所有设备实例)4、一张图对比三者的作用域
/sys/ ├── class/rtc/ ← 类绑定(通用属性) │ └── rtc0/ │ ├── time ← 所有 RTC 都有 │ ├── date │ └── wakealarm │ ├── bus/i2c/devices/ ← 设备绑定(私有属性) │ └── 1-0050/ │ ├── name │ └── custom_reg ← 只有这个传感器有 │ └── bus/platform/drivers/my-sensor/ ← 驱动绑定(驱动级属性) └── version ← 驱动版本信息5. 一句话总结
类绑定是“批发”(所有设备自动有),设备绑定是“零售”(单个设备自己加),驱动绑定是“品牌标签”(驱动自己的信息)。RTC 用类绑定因为它是个框架,具体芯片驱动用设备绑定加私有寄存器。
1. 基本用法
#include <linux/io.h> // 错误做法:可能被优化掉 u32 value = *reg; // 编译器可能认为没变化,直接复用旧值 // 正确做法:强制读 u32 value = readl(reg); readb(addr) 8 位 (byte) 读 1 字节寄存器 readw(addr) 16 位 (word) 读 2 字节寄存器 readl(addr) 32 位 (long) 读 4 字节寄存器 readq(addr) 64 位 (quad) 读 8 字节寄存器(64 位系统) u32 value = readl(register_address); 等价于: u32 value = *(volatile u32 __iomem *)register_address;#include <linux/io.h> // readl/writel #include <linux/ioport.h> // request_mem_region #include <linux/of_address.h> // of_iomap (设备树) struct my_dev { void __iomem *regs; // 寄存器基地址 }; static int my_probe(...) { // 方式 A:设备树获取并映射 dev->regs = of_iomap(pdev->dev.of_node, 0); // 方式 B:物理地址硬编码 dev->regs = ioremap(0x10003000, SZ_4K); // 读寄存器 u32 val = readl(dev->regs + 0x10); } static int my_remove(...) { iounmap(dev->regs); // 解除映射 }✅您的理解完全正确:
platform_device最终调用device的注册逻辑,platform_driver最终调用driver的注册逻辑✅实现方式:通过结构体嵌套(不是继承),将通用设备模型能力"组合"到具体设备类型中
✅核心价值:Linux设备模型提供统一框架,各总线子系统只需定义自己的封装结构,实现代码复用和统一管理
✅生命周期:
platform_device和platform_driver的生命周期由内部的device和driver管理,通过container_of实现双向访问
四、实际是"资源"与"实体"的区别
evm_ 只适用于 probe 期间获取的"资源": ✅ 内存(kmalloc) ✅ IO 映射(ioremap) ✅ 中断(request_irq) ✅ GPIO(gpio_request) ✅ 驱动主动创建的设备(如 rtc_device) 不适用于"实体"本身: ❌ 传入的 platform_device(外部定义) ❌ platform_driver(模块级) ❌ i2c_client(由 i2c-core 管理) ❌ spi_device(由 spi-core 管理)五、Linux 2.4 版本情况确认
结论:Linux 2.4完全没有
devm_函数。引入时间:
devm_机制在Linux 2.6.32(2009年)才正式引入