DEVICE_ATTR使用

在 Linux 内核开发中,DEVICE_ATTR 是一个非常有用的宏,用于在设备驱动中创建设备属性(device attributes)。这些属性可以通过 sysfs 文件系统暴露给用户空间,允许用户空间程序读取或修改设备的某些特性。DEVICE_ATTR 是 Linux 设备模型的一部分,广泛用于驱动开发。

本文将详细讲解 DEVICE_ATTR 的用法,并提供一个简单的例程。


一、DEVICE_ATTR 详解

1. 什么是 DEVICE_ATTR

DEVICE_ATTR 是一个宏,用于定义设备属性。它会生成一个 struct device_attribute 类型的变量,并关联到 sysfs 文件系统中。用户空间可以通过 /sys/devices/ 目录下的文件与内核交互。

定义在头文件 <linux/device.h> 中,DEVICE_ATTR 的作用是:

  • 创建一个 sysfs 文件,允许用户空间读取或写入设备的属性。
  • 提供回调函数(show 和 store),用于处理用户空间对属性的读写操作。
2. DEVICE_ATTR 的定义

DEVICE_ATTR 的宏定义如下:

#define DEVICE_ATTR(_name, _mode, _show, _store) \
    struct device_attribute dev_attr_##_name = __ATTR(_name, _mode, _show, _store)
  • 参数说明

    • _name:属性的名称。最终会在 sysfs 中生成一个名为 _name 的文件。
    • _mode:文件的权限模式,例如 S_IRUGO(只读)、S_IWUSR(只写)、S_IRUGO | S_IWUSR(读写)。这些权限定义在 <linux/stat.h> 中。
    • _show:读取属性时的回调函数,类型为 ssize_t (*show)(struct device *dev, struct device_attribute *attr, char *buf)
    • _store:写入属性时的回调函数,类型为 ssize_t (*store)(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
  • 生成的变量
    DEVICE_ATTR 会生成一个名为 dev_attr_<name> 的 struct device_attribute 变量。这个变量可以用于向 sysfs 注册属性。

3. 回调函数的实现
  • show 函数:当用户空间读取 sysfs 文件时调用。函数需要将数据写入 buf 并返回写入的字节数。

    ssize_t show(struct device *dev, struct device_attribute *attr, char *buf);
    
    • dev:设备结构体指针。
    • attr:设备属性结构体指针。
    • buf:用户空间读取数据时用来存放数据的缓冲区。
    • 返回值:写入 buf 的字节数,或者错误码(负值)。
  • store 函数:当用户空间写入 sysfs 文件时调用。函数需要解析 buf 中的数据并返回写入的字节数。

    ssize_t store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
    
    • buf:用户空间写入的数据。
    • count:写入数据的字节数。
    • 返回值:成功时返回 count,或者错误码(负值)。
4. 如何使用 DEVICE_ATTR
  • 使用 DEVICE_ATTR 定义属性。
  • 使用 device_create_file 将属性注册到 sysfs。
  • 在驱动卸载时,使用 device_remove_file 移除属性。

二、简单例程

下面是一个简单的 Linux 内核模块例程,展示如何使用 DEVICE_ATTR 创建一个设备属性。例程的功能是:

  • 创建一个虚拟设备。
  • 提供一个 sysfs 属性 value,用户空间可以读取或写入该属性。
  • 属性 value 存储一个整数值。
1. 代码实现
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/uaccess.h>

static struct class *my_class;
static struct device *my_device;
static int my_value = 0; // 用于存储属性值的变量

/* show 函数:用户读取属性时调用 */
static ssize_t value_show(struct device *dev, struct device_attribute *attr, char *buf)
{
    return sprintf(buf, "%d\n", my_value); // 将 my_value 写入 buf
}

/* store 函数:用户写入属性时调用 */
static ssize_t value_store(struct device *dev, struct device_attribute *attr, 
                          const char *buf, size_t count)
{
    int ret;

    // 将用户空间的输入解析为整数
    ret = kstrtoint(buf, 10, &my_value);
    if (ret < 0) {
        return ret; // 解析失败,返回错误
    }

    return count; // 返回写入的字节数,表示成功
}

/* 定义设备属性:名称为 value,权限为读写,关联 show 和 store 函数 */
static DEVICE_ATTR(value, S_IRUGO | S_IWUSR, value_show, value_store);

static int __init my_driver_init(void)
{
    int ret;

    /* 创建一个类 */
    my_class = class_create(THIS_MODULE, "my_class");
    if (IS_ERR(my_class)) {
        printk(KERN_ERR "Failed to create class\n");
        return PTR_ERR(my_class);
    }

    /* 创建一个设备 */
    my_device = device_create(my_class, NULL, MKDEV(0, 0), NULL, "my_device");
    if (IS_ERR(my_device)) {
        printk(KERN_ERR "Failed to create device\n");
        class_destroy(my_class);
        return PTR_ERR(my_device);
    }

    /* 注册设备属性到 sysfs */
    ret = device_create_file(my_device, &dev_attr_value);
    if (ret < 0) {
        printk(KERN_ERR "Failed to create device attribute\n");
        device_destroy(my_class, MKDEV(0, 0));
        class_destroy(my_class);
        return ret;
    }

    printk(KERN_INFO "My driver initialized\n");
    return 0;
}

static void __exit my_driver_exit(void)
{
    /* 移除设备属性 */
    device_remove_file(my_device, &dev_attr_value);

    /* 销毁设备和类 */
    device_destroy(my_class, MKDEV(0, 0));
    class_destroy(my_class);

    printk(KERN_INFO "My driver exited\n");
}

module_init(my_driver_init);
module_exit(my_driver_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("A simple device attribute example");
2. 代码说明
  • 属性定义DEVICE_ATTR(value, S_IRUGO | S_IWUSR, value_show, value_store) 定义了一个名为 value 的属性,权限为读写,关联的回调函数分别是 value_show 和 value_store
  • 初始化:在模块初始化函数中,使用 class_create 和 device_create 创建一个虚拟设备,并通过 device_create_file 注册属性。
  • 卸载:在模块卸载函数中,使用 device_remove_file 移除属性,并销毁设备和类。
  • 读写操作
    • 读取属性时,value_show 将 my_value 的值写入缓冲区。
    • 写入属性时,value_store 解析用户输入的字符串并更新 my_value
3. 编译和测试
  • 编写 Makefile

    obj-m += my_driver.o
    
    all:
        make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
    
    clean:
        make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
    
  • 编译模块

    make
    
  • 加载模块

    sudo insmod my_driver.ko
    
  • 查看 sysfs 文件
    加载模块后,属性文件会出现在 /sys/class/my_class/my_device/value。可以用以下命令测试:

    # 读取属性
    cat /sys/class/my_class/my_device/value
    
    # 写入属性
    echo 42 > /sys/class/my_class/my_device/value
    
    # 再次读取属性,验证写入是否成功
    cat /sys/class/my_class/my_device/value
    
  • 卸载模块

    sudo rmmod my_driver
    
4. 测试输出
  • 初始读取 value 时,会输出 0(初始值)。
  • 写入 42 后,再次读取会输出 42

三、注意事项

  1. 权限设置

    • 确保 DEVICE_ATTR 的权限设置合理。如果属性只读,使用 S_IRUGO;如果只写,使用 S_IWUSR;如果读写,使用 S_IRUGO | S_IWUSR
    • 权限设置会影响用户空间对 sysfs 文件的操作权限。
  2. 错误处理

    • 在 store 函数中,解析用户输入时要做好错误处理。例如,使用 kstrtoint 或类似函数时,检查返回值。
    • 在 show 和 store 函数中,返回负值表示错误,会被用户空间程序感知。
  3. 缓冲区大小

    • show 函数写入 buf 的数据不要超过 PAGE_SIZE(通常是 4096 字节)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值