Linux块设备驱动开发:无probe获取struct device及Regulator内核Oops问题
首先得明确你遇到的核心问题:手动vmalloc的struct device没有正确接入内核设备模型,而devm_regulator_get这类devm系列函数严重依赖设备模型的资源管理机制,这才导致了Oops。
为什么会触发Oops?
devm(device resource management)系列函数是基于设备的生命周期来管理资源的,它需要设备结构体(struct device)已经被正确初始化并加入到内核的设备树/总线体系中。你直接用vmalloc分配的device,既没有设置正确的父设备、总线类型,也没有调用device_initialize()和device_add()把它注册到设备模型里,devm的资源链表根本没建立,regulator子系统查找电源时自然会访问非法指针,触发Oops。
正确的解决思路(推荐复用Platform框架)
既然之前的字符设备是基于platform_device做的,完全没必要抛弃probe函数——反而应该复用platform框架来完成块设备的初始化,这才是符合Linux驱动规范的做法:
回归platform_driver的probe函数
把原来手动初始化GPIO、Regulator的逻辑移回probe里,这里的struct device *dev是platform_device自带的、已经正确注册到设备模型的指针,调用devm_regulator_get(dev, "power")(假设你的regulator在设备树里的名字是power)就不会有问题了。在probe中注册块设备
块设备的注册流程可以嵌入到probe函数里,大致步骤:- 调用
register_blkdev()注册块设备号 - 分配
gendisk结构体(用alloc_disk()) - 设置gendisk的major、first_minor、fops、queue等字段
- 绑定你的硬件操作逻辑(比如原来字符设备里的Clock/Data/Power引脚控制、私有协议读写)到块设备的request函数或者直接的读写回调
- 调用
add_disk()把gendisk加入系统
- 调用
迁移原有逻辑到块设备接口
你原来的字符设备是处理字节流,而块设备是按扇区(通常512字节)来操作的,需要把私有协议里的文件拆分存储逻辑,映射到扇区的读写请求上。比如:- 当块设备收到读扇区请求时,通过私有协议读取对应位置的数据,填充到请求的buffer里
- 写请求则把buffer里的扇区数据,通过私有协议写入硬件存储
如果非要手动初始化设备(不推荐)
如果你坚持不用probe,那必须把手动分配的device正确接入设备模型:
- 调用
device_initialize(&your_dev)初始化设备 - 设置
your_dev.parent为一个合法的父设备(比如sysfs里的某个总线节点,或者设备树里的父节点设备) - 设置
your_dev.bus为对应的总线类型(比如&platform_bus_type) - 调用
device_add(&your_dev)把设备注册到系统 - 之后再调用
devm_regulator_get(&your_dev, ...)才可能正常工作
但这种方式非常繁琐,容易引入其他设备模型相关的问题,完全没必要——复用platform框架是更稳妥、更符合Linux驱动设计理念的选择。
内容的提问来源于stack exchange,提问作者Arkaik

