如何在内核模块中与sysfs或导出到sysfs的驱动进行交互
结论
- 不需要重写DS18B20驱动,强烈不建议在内核空间直接读取sysfs文件:sysfs是面向用户空间设计的交互接口,内核态直接调用VFS接口读取sysfs既不符合内核开发规范,还容易引发锁竞争、权限校验绕过、跨版本兼容性差等问题,稳定性没有保障。
- 最优方案是直接调用Linux w1子系统对外暴露的标准内核API,和已加载的w1总线驱动交互,直接在你的自研模块里完成温度读取,不需要改动现有驱动,也不会产生资源冲突。
具体实现步骤
你的模块只要依赖内核w1核心层(对应内核配置项CONFIG_W1)即可,不需要依赖w1_therm模块的内部符号,实现流程如下:
- 引入必要的头文件
#include <linux/w1.h> #include <linux/delay.h> #include <linux/mutex.h>
- 匹配目标DS18B20设备
DS18B20的w1家族固定ID为0x28,你可以遍历w1总线上注册的所有从设备,匹配你已知的设备ROM ID(即sysfs路径里28-后面的062194159c7d),拿到对应的设备句柄,参考代码:
struct w1_slave *find_target_ds18b20(void) { struct w1_bus_master *master; struct w1_slave *slave = NULL; // 替换为你自己的设备ROM ID,注意字节序匹配 const u8 target_id[8] = {0x28, 0x06, 0x21, 0x94, 0x15, 0x9c, 0x7d, 0x00}; mutex_lock(&w1_mlock); list_for_each_entry(master, &w1_masters, w1_master_entry) { mutex_lock(&master->mutex); list_for_each_entry(slave, &master->slist, w1_slave_entry) { if (slave->reg_num.family == 0x28 && !memcmp(slave->reg_num.id, target_id + 1, 7)) { get_device(&slave->dev); // 增加引用计数,防止设备被意外释放 mutex_unlock(&master->mutex); goto found; } } mutex_unlock(&master->mutex); } slave = NULL; found: mutex_unlock(&w1_mlock); return slave; }
注意:如果遇到匹配不到设备的问题,可以先遍历所有0x28家族的设备,打印
slave->reg_num.id的十六进制值,和你的设备ROM ID比对调整字节序即可,不要硬猜字节顺序。
- 读取温度值
拿到w1_slave指针后,直接调用w1核心封装的总线读写接口,按照DS18B20的标准指令流程读取温度即可,不需要自己实现单总线时序,参考代码:
int read_ds18b20_temp(struct w1_slave *slave, int *temp_millic) { u8 txbuf[1], rxbuf[9]; s16 raw_temp; u8 crc; if (!slave || !temp_millic) return -EINVAL; mutex_lock(&slave->master->mutex); // 1. 复位总线,选通目标从设备 w1_reset_select_slave(slave); // 2. 发送0x44指令启动温度转换,12位精度最长转换时间750ms txbuf[0] = 0x44; w1_write_block(slave->master, txbuf, 1); mutex_unlock(&slave->master->mutex); msleep(800); // 等待转换完成 mutex_lock(&slave->master->mutex); // 3. 复位选通设备,发送0xBE指令读取暂存器 w1_reset_select_slave(slave); txbuf[0] = 0xBE; w1_write_block(slave->master, txbuf, 1); w1_read_block(slave->master, rxbuf, 9); mutex_unlock(&slave->master->mutex); // 4. CRC校验,w1核心自带标准crc8校验函数 crc = w1_calc_crc8(rxbuf, 8); if (crc != rxbuf[8]) return -EIO; // 5. 转换为毫摄氏度单位,比如返回25000对应25.000℃ raw_temp = le16_to_cpup((__le16 *)rxbuf); *temp_millic = raw_temp * 625; return 0; }
- 资源回收
设备使用完之后,记得调用put_device(&slave->dev);释放之前持有的设备引用计数,避免内存泄漏。
其他方案的问题说明
- 重写DS18B20驱动属于重复造轮子,而且会和已加载的w1-gpio驱动产生GPIO资源、总线控制权冲突,最终导致两个驱动都无法正常读取传感器数据,没有必要。
- 内核态读取sysfs的方案本质是绕开内核驱动框架走用户空间接口,不仅性能差,还可能因为内核版本迭代、sysfs节点权限变化、锁顺序错误触发死锁或者内核panic,生产环境绝对不要用。
内容的提问来源于stack exchange,提问作者SSBjY_Dave
相关产品推荐
相关产品推荐

