《驱动学习 —— sysfs文件系统的编写》
1.sysfs文件系统
sysfs文件系统可以把内核空间的数据、属性、链接等输出到用户空间。反过来,用户也可以通过sysfs文件系统,往对应的内核空间传递数据。
例如:echo 1 > /sys/class/gpio/gpio64/value,就是改变gpio64的值。
cat > /sys/class/gpio/gpio64/value,就是读取gpio64的值。
那么怎么在驱动中增加这个接口呢?
2.新增/sys接口
2.1 attribute属性
struct attribute { char *name; //sys下接口的名字 umode_t mode; //使用权限 };
mode为文件的权限,定义在kernel/include/uapi/linux/stat.h
#define S_IRWXU 00700 //用户可读写和执行 #define S_IRUSR 00400//用户可读 #define S_IWUSR 00200//用户可写 #define S_IXUSR 00100//用户可执行 #define S_IRWXG 00070//用户组可读写和执行 #define S_IRGRP 00040//用户组可读 #define S_IWGRP 00020//用户组可写 #define S_IXGRP 00010//用户组可执行 #define S_IRWXO 00007//其他可读写和执行 #define S_IROTH 00004//其他可读 #define S_IWOTH 00002//其他可写 #define S_IXOTH 00001//其他可执行
2.2 device_attribute
这个就是面对对象封装了一层
/* interface for exporting device attributes */ struct device_attribute { struct attribute attr; ssize_t (*show)(struct device *dev, struct device_attribute *attr, char *buf); ssize_t (*store)(struct device *dev, struct device_attribute *attr, const char *buf, size_t count); };
再用宏简化了device_attribute结构对象的定义和初始化
#define __ATTR(_name, _mode, _show, _store) { \ .attr = {.name = __stringify(_name), .mode = _mode}, \ .show = _show, .store = _store, } #define DEVICE_ATTR(_name, _mode, _show, _store) \ struct device_attribute dev_attr_##_name = __ATTR(_name, _mode, _show, _store)
其中_show和_store,分别代表在用户空间执行cat和echo所调用的函数,可以理解为read和write。
2.3 创建文件
int sysfs_create_file(struct kobject *kobj, struct attribute *attr); void sysfs_remove_file(struct kobject *kobj, struct attribute *attr);
2.4 kobject_create_and_add()函数
struct kobject *kobject_create_and_add(const char *name, struct kobject *parent)
功能:动态注册kobject并生成sysfs
3. 实例编程
3.1 使用sysfs_create_files函数
#include <linux/sysfs.h>
#include <linux/kobject.h>
static ssize_t led_store(struct device *dev,struct device_attribute *attr, const char *buf, size_t len) { printk("led_store()\n"); return len;//必须返回传入的长度 } //下面的show和store只是简单举例 static ssize_t led_show(struct device *dev, struct device_attribute*attr, char *buf) { printk("led_show()\n"); returnpr_info("store\n"); } //用DEVICE_ATTR宏创建属性led_sysfs文件,如果show()或是store()没有功能,就以NULL代替 static DEVICE_ATTR(led_sysfs, S_IWUSR, led_show,led_store); //最后一项必须以NUll结尾 static const struct attribute *atk_imx6ul_led_sysfs_attrs[] = { &dev_attr_led_sysfs.attr, NULL, }; //驱动中的初始化函数 static int __init xxxxx_mod_init(void) { led_kobj = kobject_create_and_add("led_test", NULL); sysfs_create_files(led_kobj,atk_imx6ul_led_sysfs_attrs); } //驱动中的退出函数 static void __exit xxxxx_mod_exit(void) { sysfs_remove_file(led_kobj, atk_imx6ul_led_sysfs_attrs);//驱动退出时释放结点 kobject_put(led_kobj);
}
编译insmod后就可以看到/sys/led_test/led_sysfs。
cat sys/led_test/led_sysfs就相当于调用led_show()函数。
echo 1 > /sys/led_test/led_sysfs就相当于调用led_store()函数。
注意:
其中结构体变量dev_attr_led_sysfs是不是没看到在哪里有定义。
static DEVICE_ATTR(led_sysfs, S_IWUSR, led_show,led_store);
当调用该宏定义的时候已经定义了
#define DEVICE_ATTR(_name, _mode, _show, _store) \ struct device_attribute dev_attr_##_name = __ATTR(_name, _mode, _show, _store)
3.2 使用sysfs_create_group函数
通常情况下,我们更多的将上述的struct attribute进行进一步的封装,并使用sysfs_create_group()来创建一个名为attribute_group.name的、包含struct attribute中的属性目录,这种方式更加的灵活,因为如果我们不指定目录的名字,那么效果个sysfs_create_file()是一样的。
#include <linux/sysfs.h>
#include <linux/kobject.h>
static struct kobject *led_kobj = NULL; static ssize_t led_store(struct device *dev,struct device_attribute *attr, const char *buf, size_t len) { printk("led_store()\n"); return len;//必须返回传入的长度 } //下面的show和store只是简单举例 static ssize_t led_show(struct device *dev, struct device_attribute*attr, char *buf) { printk("led_show()\n"); returnpr_info("store\n"); } //用DEVICE_ATTR宏创建属性led_sysfs文件,如果show()或是store()没有功能,就以NULL代替 static DEVICE_ATTR(led_sysfs, S_IWUSR, led_show,led_store); //最后一项必须以NUll结尾 static const struct attribute *atk_imx6ul_led_sysfs_attrs[] = { &dev_attr_led_sysfs.attr, NULL, }; static struct attribute_group led_sysfs_attr_group = { .name = "my_sysfs", .attrs = atk_imx6ul_led_sysfs_attrs }; //驱动中的初始化函数 static int __init xxxxx_mod_init(void) { led_kobj = kobject_create_and_add("led_test", NULL); sysfs_create_group(led_kobj,atk_imx6ul_led_sysfs_attrs); } //驱动中的退出函数 static void __exit xxxxx_mod_exit(void) { sysfs_remove_group(led_kobj, led_sysfs_attr_group);//驱动退出时释放结点 kobject_put(led_kobj); }
这样会生成/sys/led_test/my_sysfs/led_sysfs