插件窝 干货文章 Linux驱动 | 在驱动中创建sysfs接口

Linux驱动 | 在驱动中创建sysfs接口

attr struct kobj my 251    来源:    2025-04-14

在Linux驱动中创建sysfs接口

sysfs是Linux内核提供的一个虚拟文件系统,用于将内核对象、属性和它们之间的关系导出到用户空间。在驱动程序中创建sysfs接口可以让用户空间程序方便地获取设备信息或配置设备参数。

基本步骤

1. 包含必要的头文件

#include <linux/sysfs.h>
#include <linux/kobject.h>

2. 定义属性和属性组

单个属性定义

static ssize_t my_attr_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf);
static ssize_t my_attr_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count);

static struct kobj_attribute my_attr = __ATTR(my_attribute, 0644, my_attr_show, my_attr_store);

属性组定义

static struct attribute *my_attrs[] = {
    &my_attr.attr,
    NULL,
};

static struct attribute_group my_attr_group = {
    .attrs = my_attrs,
};

3. 实现show和store函数

static ssize_t my_attr_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
{
    // 从驱动中读取数据并写入buf
    return sprintf(buf, "%d\n", some_value);
}

static ssize_t my_attr_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count)
{
    unsigned long val;
    int ret;

    ret = kstrtoul(buf, 10, &val);
    if (ret < 0)
        return ret;

    // 使用val更新驱动状态
    some_value = val;

    return count;
}

4. 在驱动中创建/删除sysfs接口

方法1:使用设备类

static struct class *my_class;

// 在init函数中
my_class = class_create(THIS_MODULE, "my_class");
if (IS_ERR(my_class)) {
    return PTR_ERR(my_class);
}

// 为每个设备创建
sysfs_create_group(&dev->kobj, &my_attr_group);

// 在exit函数中
class_destroy(my_class);

方法2:直接使用kobject

static struct kobject *my_kobj;

// 在init函数中
my_kobj = kobject_create_and_add("my_kobject", kernel_kobj);
if (!my_kobj) {
    return -ENOMEM;
}

sysfs_create_group(my_kobj, &my_attr_group);

// 在exit函数中
kobject_put(my_kobj);

完整示例

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/sysfs.h>
#include <linux/kobject.h>

static int my_value = 0;

static ssize_t my_attr_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
{
    return sprintf(buf, "%d\n", my_value);
}

static ssize_t my_attr_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count)
{
    int ret;
    unsigned long val;

    ret = kstrtoul(buf, 10, &val);
    if (ret < 0)
        return ret;

    my_value = val;
    return count;
}

static struct kobj_attribute my_attr = __ATTR(my_value, 0644, my_attr_show, my_attr_store);

static struct attribute *my_attrs[] = {
    &my_attr.attr,
    NULL,
};

static struct attribute_group my_attr_group = {
    .attrs = my_attrs,
};

static struct kobject *my_kobj;

static int __init my_init(void)
{
    int ret;

    my_kobj = kobject_create_and_add("my_module", kernel_kobj);
    if (!my_kobj) {
        return -ENOMEM;
    }

    ret = sysfs_create_group(my_kobj, &my_attr_group);
    if (ret) {
        kobject_put(my_kobj);
        return ret;
    }

    printk(KERN_INFO "sysfs interface created\n");
    return 0;
}

static void __exit my_exit(void)
{
    kobject_put(my_kobj);
    printk(KERN_INFO "sysfs interface removed\n");
}

module_init(my_init);
module_exit(my_exit);

MODULE_LICENSE("GPL");

高级用法

1. 二进制属性

static struct bin_attribute my_bin_attr = {
    .attr = {
        .name = "binary_data",
        .mode = 0644,
    },
    .read = my_bin_read,
    .write = my_bin_write,
    .size = MAX_SIZE,
};

// 然后使用 sysfs_create_bin_file() 创建

2. 设备特定属性

static DEVICE_ATTR(my_device_attr, 0644, dev_attr_show, dev_attr_store);

// 然后使用 device_create_file() 创建

3. 动态属性

可以使用 sysfs_add_file_to_group() 在运行时动态添加属性。

注意事项

  1. 确保适当的权限设置(0644等)
  2. 注意并发访问,必要时使用锁
  3. 确保show/store函数不会阻塞
  4. 错误处理要完善
  5. 在模块卸载时清理所有创建的sysfs条目

通过sysfs接口,用户空间可以通过简单的文件操作(cat/echo等)与驱动程序交互,这是一种非常方便的用户空间-内核空间通信机制。