1.简介
使用DEVICE_ATTR宏,可以实现驱动程序中在sys目录自动创建文件,我们只需要实现show和store函数即可.
使用该宏创建出来的文件,在应用层就能通过cat和echo命令来对文件进行读写驱动设备,实现交互.
这个宏真真是极好的,为我们大大地提供了便利,并且非常简单方便好用
2.DEVICE_ATTR宏定义
DEVICE_ATTR定义位于kernel/include/linux/device.h中,定义如下所示:
#define DEVICE_ATTR(_name, _mode, _show, _store) \
struct device_attribute dev_attr_##_name = __ATTR(_name, _mode, _show, _store)
其中又使用了device_attribute结构体与__ATTR宏
device_attribute结构体也定义在kernel/include/linux/device.h中,__ATTR定义在kernel/include/linux/sysfs.h中。
定义如下所示:
#define __ATTR(_name, _mode, _show, _store) { \
.attr = {.name = __stringify(_name), \
.mode = VERIFY_OCTAL_PERMISSIONS(_mode) }, \
.show = _show, \
.store = _store, \
}
/* 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_ATTR中的四个参数含义为:
_name:在sysfs中生产的文件名称
_mode: 该文件具有的权限,与普通文件相同,UGO(usr/group/others)格式
_show: 当cat命令作用于该文件时调用该函数
_store: 当echo数据到该文件时调用该函数
__ATTR宏中VERIFY_OCTAL_PERMISSIONS的作用是对权限进行限制检查,该宏也定义在kernel/include/linux/sysfs.h中
/* Permissions on a sysfs file: you didn't miss the 0 prefix did you? */
#define VERIFY_OCTAL_PERMISSIONS(perms) \
(BUILD_BUG_ON_ZERO((perms) < 0) + \
BUILD_BUG_ON_ZERO((perms) > 0777) + \
/* User perms >= group perms >= other perms */ \
BUILD_BUG_ON_ZERO(((perms) >> 6) < (((perms) >> 3) & 7)) + \
BUILD_BUG_ON_ZERO((((perms) >> 3) & 7) < ((perms) & 7)) + \
/* Other writable? Generally considered a bad idea. */ \
BUILD_BUG_ON_ZERO((perms) & 2) + \
(perms))
这里从注释上也看的比较清晰权限的要求:
1.User perms >= group perms >= other perms
2. Other writable? Generally considered a bad idea.
关于改宏的定义就介绍到这里
3.展开DEVICE_ATTR宏定义
//读函数,也就是cat命令时将会调用该函数
static ssize_t reg_show(struct device *dev, struct device_attribute *attr, char *buf)
{
return buf;
}
//写函数,也就是echo命令时,将会调用该函数.
static ssize_t reg_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t len)
{
return len;
}
//定义一个名字为reg的设备属性文件
static DEVICE_ATTR(reg, S_IWUSR|S_IRUSR, reg_show, reg_store);
将其展开,实际就是定义了一个为device_attribute类型的结构体,还将其成员变量进行了初始化,如下:
struct device_attribute dev_attr_reg ={
.attr = {.name = "reg",
.mode = S_IWUSR|S_IRUSR },
.show = reg_show,
.store = reg_store,
}
4.使用示例
#include <board.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/gpio.h>
#include <linux/delay.h>
#include <linux/regulator/consumer.h>
#include <sound/soc.h>
#include <sound/jack.h>
static char dataBuf[100] = "123";
//读函数,也就是cat命令时将会调用该函数
static ssize_t reg_show(struct device *dev, struct device_attribute *attr, char *buf)
{
return sprintf(buf, "%s\n", dataBuf);
}
//写函数,也就是echo命令时,将会调用该函数.
static ssize_t reg_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t len)
{
sprintf(dataBuf, "%s", buf);
return len;
}
//定义一个名字为reg的设备属性文件
static DEVICE_ATTR(reg, S_IWUSR|S_IRUSR, reg_show, reg_store);
struct file_operations my_test_ops = {
.owner = THIS_MODULE,
};
static int major;
static struct class *cls;
static int my_test_init(void)
{
struct device *mydev;
major = register_chrdev(0,"my_test", &my_test_ops);
cls = class_create(THIS_MODULE, "my_test_class");
mydev = device_create(cls, 0, MKDEV(major,0), NULL, "dev_attr_test");
//在dev_attr_test设备目录下创建一个reg属性文件
if(sysfs_create_file(&(mydev->kobj), &dev_attr_reg.attr)){
return -1;
}
return 0;
}
static void my_test_exit(void)
{
device_destroy(cls, MKDEV(major,0));
class_destroy(cls);
unregister_chrdev(major, "my_test");
}
module_init(my_test_init);
module_exit(my_test_exit);
MODULE_LICENSE("GPL");
//因为本人的是个比较大的驱动文件,为了展示方便,以上代码参考了这篇文章:[感谢作者](https://www.cnblogs.com/lifexy/p/9799778.html)
实际的驱动片段是这样的:
static DEVICE_ATTR(reg, S_IWUSR | S_IRUGO, aw21024_reg_show, aw21024_reg_store);
static DEVICE_ATTR(hwen, S_IWUSR | S_IRUGO, aw21024_hwen_show, aw21024_hwen_store);
static DEVICE_ATTR(rgbcolor, S_IWUSR | S_IRUGO, NULL, aw21024_rgbcolor_store);
static DEVICE_ATTR(effect, S_IWUSR | S_IRUGO, aw21024_effect_show, aw21024_effect_store);
static struct attribute *aw21024_attributes[] = {
&dev_attr_reg.attr,
&dev_attr_hwen.attr,
&dev_attr_rgbcolor.attr,
&dev_attr_effect.attr,
NULL
};
static struct attribute_group aw21024_attribute_group = {
.attrs = aw21024_attributes
};
ret = sysfs_create_group(&aw21024->cdev.dev->kobj, &aw21024_attribute_group);
于是我们在设备节点下就能看到相关的文件,比如reg、effect、hwen等,这些都是我们通过DEVICE_ATTR宏实现的,
然后通过cat与echo命令就可以对该文件进行读写,而读写时实际调用的就是后面的show与rstore函数