文章目录代码引入1. 定义属性结构体2. 在 probe 中创建属性文件3. 在 remove 中清理属性添加sysfs套路解读1、属性定义2、注册属性3、文件操作绑定4、回调到具体函数_show / _store函数宏展开原理为什么不直接使用函数名在 Linux 内核中通过 device_create_file 创建的 sysfs 属性文件其读写操作最终会回调到我们提供的 show 和 store 函数这是由 sysfs 框架和 VFS虚拟文件系统 共同实现的。以下是简要的原理说明代码引入这段代码截取自是充电ic芯片圣邦微sgm41513驱动。我将HIZ 模式寄存器的读写暴露在了sys/class/power_supply节点下。以下是代码片段1. 定义属性结构体在 sgm415xx.c 中添加一个 struct device_attribute 和一个对应的 show / store 函数。staticssize_thiz_enable_show(structdevice*dev,structdevice_attribute*attr,char*buf){structsgm4154x_device*sgmdev_get_drvdata(dev-parent);// 注意dev 是 power_supply 设备它的 parent 是 i2c 设备其 drvdata 指向 sgmbool en;intret;if(!sgm||!sgm-chg_dev)return-EINVAL;retsgm4154x_is_enable_hiz(sgm-chg_dev,en);if(ret)returnret;returnscnprintf(buf,PAGE_SIZE,%d\n,en?1:0);}staticssize_thiz_enable_store(structdevice*dev,structdevice_attribute*attr,constchar*buf,size_tcount){structsgm4154x_device*sgmdev_get_drvdata(dev-parent);bool en;intret;if(!sgm||!sgm-chg_dev)return-EINVAL;retkstrtobool(buf,en);if(ret)returnret;retsgm4154x_set_hiz_en(sgm-chg_dev,en);if(ret)returnret;returncount;}staticDEVICE_ATTR_RW(hiz_enable);注意dev_get_drvdata(dev-parent) 是因为 dev 是 power_supply 设备它的 parent 指向 i2c 设备而 i2c 设备的 drvdata 正是 sgm 结构体。2. 在 probe 中创建属性文件在 sgm4154x_driver_probe 函数中当 power_supply 注册成功后调用 device_create_file 创建属性。staticintsgm4154x_driver_probe(structi2c_client*client){// ... 现有代码 ...retsgm4154x_power_supply_init(sgm,dev);if(ret){pr_err(Failed to register power supply\n);returnret;}// 创建 hiz_enable 属性retdevice_create_file(sgm-charger-dev,dev_attr_hiz_enable);if(ret)dev_warn(dev,Failed to create hiz_enable sysfs file\n);// ... 后续代码 ...}3. 在 remove 中清理属性在 sgm4154x_charger_remove 中删除该属性文件。staticvoidsgm4154x_charger_remove(structi2c_client*client){structsgm4154x_device*sgmi2c_get_clientdata(client);device_remove_file(sgm-charger-dev,dev_attr_hiz_enable);// ... 其他清理 ...}添加sysfs套路解读1、属性定义#defineDEVICE_ATTR_RW(_name)\structdevice_attributedev_attr_##_name__ATTR_RW(_name)使用 DEVICE_ATTR_RW(hiz_enable) 宏会生成一个 struct device_attribute 类型的变量 dev_attr_hiz_enable其中包含两个函数指针structdevice_attribute{structattributeattr;ssize_t(*show)(structdevice*dev,structdevice_attribute*attr,char*buf);ssize_t(*store)(structdevice*dev,structdevice_attribute*attr,constchar*buf,size_tcount);};hiz_enable_show 和 hiz_enable_store 函数地址被赋值给这两个指针。2、注册属性device_create_file(sgm-charger-dev, dev_attr_hiz_enable) 将该属性关联到具体设备sgm-charger-dev。内核会在 sysfs 中创建对应的文件节点并记录该文件对应的 device_attribute 结构。3、文件操作绑定sysfs 文件系统为所有属性文件提供了统一的 file_operations如 sysfs_file_operations其中包含 .read sysfs_read_file 和 .write sysfs_write_file 等回调。当用户进程读写该文件时VFS 会调用这些统一的函数。4、回调到具体函数统一的读写函数内部会根据文件的 inode 找到关联的 device_attribute 结构然后调用其 show 或 store 方法。例如读操作最终会调用 hiz_enable_show传入对应的 dev 和 attr 参数。写操作最终会调用 hiz_enable_store。因此当您在用户空间执行 echo 1 /sys/class/power_supply/sgm4154x-charger/hiz_enable 时内核会自动将写入操作路由到您实现的 hiz_enable_store 函数中完成对 HIZ 模式的设置。_show / _store函数宏展开原理hiz_enable_show 和 hiz_enable_store 之所以与属性文件自动关联并不是因为文件名本身而是因为 DEVICE_ATTR_RW 宏的展开规则强制要求函数名与宏参数一致。#defineDEVICE_ATTR_RW(_name)\structdevice_attributedev_attr_##_name__ATTR_RW(_name)#define__ATTR_RW(_name)__ATTR(_name,0644,_name##_show,_name##_store)#define__ATTR(_name,_mode,_show,_store){\.attr{.name__stringify(_name),.mode_mode},\.show_show,\.store_store,\}DEVICE_ATTR_RW(hiz_enable) 展开后生成一个名为 dev_attr_hiz_enable 的 struct device_attribute 变量。该变量的 .show 成员被赋值为 hiz_enable_show.store 成员被赋值为 hiz_enable_store。如果这两个函数不存在编译会报错为什么不直接使用函数名宏通过字符串拼接##将传入的 _name 与 _show / _store 拼接成函数名这是一种编译时的命名约定目的是让开发者只需关心属性名称而无需显式填写函数指针。这种方式在内核的 DEVICE_ATTR 系列宏中广泛使用。