Linux上设备类属性

1.设备类属性

在Linux上,设备类属性(sysfs)是一种用于访问和修改设备属性的机制,sysfs提供了 一个虚拟文件系统,它代表了内核中的设备树,通过这个文件系统,可以查看和修改设备 及其属性状态,在硬件调试阶段非常实用。 再简化理解: class_attribute是一种用于向用户空间暴露设备类(class)属性的机制, 它允许内核驱动将设备类的特定信息,导出到/sys/class/目录下,供用户空间程序读取或修改。

2.class_attribute使用API接口

class_attribute结构体构成:

struct class_attribute {
    struct attribute attr;
    ssize_t (*show)(struct class *class, struct class_attribute *attr, char *buf);//cat 读取值
    ssize_t (*store)(struct class *class, struct class_attribute *attr, const char *buf, size_t count);//echo 修改值
};

show---读取时回调 store--修改存储时回调 常见使用方法:

// 注册类属性
int class_create_file(struct class *class, const struct class_attribute *attr);

// 注销类属性
void class_remove_file(struct class *class, const struct class_attribute *attr);

3.实际平台使用案例

我们在平台上实现一个设备类属性操作,

3.1 class_attribute属性定义
//定义设备类属性
static struct class_attribute my_char_device_class_attrs[] = {
    __ATTR(device_attr_name, 0644, my_char_device_attr_show, my_char_device_attr_store),
    __ATTR_NULL,    //必须以NULL 结尾
    //上面权限控制
    //0444 只读属性
    //0644 可读可写属性
};

        //3. 创建设备节点 (可以不用,一般加上)
        //3.1 创建节点对象
        class_p = class_create(THIS_MODULE, "mychar");//生成sys/class/mychar
        if(IS_ERR(class_p))
        {
            ret = -ENOMEM;
            goto err3;
        }

        //注册类属性,这个设备类属性,用于供用户层调用
        //这个类属性,常用于设备控制(如调整采样频\暴露设备状态[温度\功耗等])
        //高级用法:批量注册类属性,class_add_attrs(class, class_attrs)
        ret = class_create_file(class_p, &my_char_device_class_attrs[0] );
        if(ret)
        {
            dev_err(device_p, "Failed to create attribute\r\n");
            return ret;
        }
        //设置类数据,以便在回调函数中访问
        // class_set_devdata(device_p,data);//通过class_get_devdata 获取!!

        for(i = 0; i < count; ++i)
        {
            char drv_name[20] = {0};
            sprintf(drv_name, "mychar%d", i);
            //3.2 创建设备节点 (导出设备信息到用户空间)
            device_p = device_create(class_p, NULL, MKDEV(MAJOR(dev_num),i), NULL, drv_name);
            if (IS_ERR(device_p))
            {
                ret = -ENOMEM;
                goto err4;
            }
        }
3.2 设备类属性读写

实际就是定义读写函数

static unsigned long my_char_device_attr_value = 0;
//定义类属性的show函数(读取属性值)
static ssize_t my_char_device_attr_show(struct class *class,
                                        struct class_attribute *attr,
                                        char *buf)
{
    //通过 cat /sys/class/class_attr_name
    //可以通过class,获取到deive data
    // class_get_devdata(class);
    return sprintf(buf,"device_attr: %u\n", my_char_device_attr_value);
}

//定义类属性的store函数(修改属性值)
static ssize_t my_char_device_attr_store(struct class *class,
                                        struct class_attribute *attr,
                                        const char *buf, size_t count)
{
    //对于store函数中,验证用户输入,防止非法值导致系统异常
    //对于并发访问,若属性涉及共享资源,需要添加适当的锁机制(如:mutex)
    //通过 echo xxx > /sys/class/class_attr_name
    int ret ;

    ret = kstrtoul(buf,10, &my_char_device_attr_value); //10表示 十进制

    if(ret)
        return ret;//失败
    return ret;
}

4. 结果分析

[root@dong:~]cd /sys/class
[root@dong:/sys/class]ls
backlight     i2c-adapter   mtd           scsi_changer  tty
bdi           i2c-dev       mychar        scsi_device   udc
block         ieee80211     net           scsi_disk     vc
bsg           input         phy           scsi_generic  vtconsole
dma           leds          power_supply  scsi_host     wakeup
drm           mdio_bus      pps           sound
extcon        mem           ptp           spi_master
gpio          misc          pwm           spidev
graphics      mmc_host      regulator     thermal
[root@dong:/sys/class]cd mychar
[root@dong:/sys/class/mychar]ls
device_attr_name  mychar1           mychar3
mychar0           mychar2
[root@dong:/sys/class/mychar]cat device_attr_name
device_attr: 0
[root@dong:/sys/class/mychar]echo 1 > device_attr_name
^C
[root@dong:/sys/class/mychar]cat device_attr_name
device_attr: 1
[root@dong:/sys/class/mychar]

5.总结

上述操作过程中,如果考虑多线程安全,需要对操作资源进行保护!!