sysfs是一个基于RAM的文件系统,它和kobject一块儿,能够将Kernel的数据结构导出到用户空间,以文件目录结构的形式,提供对这些数据结构(以及数据结构的属性)的访问支持。
在sysfs下每个目录 由Kobject表示,每一个文件由attribute 表示;或者说,看到的文件对应程序当中的attribute 结构体,看到的目录对应于程序当中的 Kobject结构体。
attribute
首先是struct attribute结构体,其定义在include/linux/sysfs.h中,结构体定义如下所示:
struct attribute {
const char *name;
umode_t mode;
#ifdef CONFIG_DEBUG_LOCK_ALLOC
bool ignore_lockdep:1;
struct lock_class_key *key;
struct lock_class_key skey;
#endif
};
该结构体有两个重要的成员,分别是name和mode,其中name代表属性的名称,一般表示为文件名,mode代表该属性的读写权限,也就是属性文件的读写权限。
此外还有类似的attribute_group
/**
* struct attribute_group - data structure used to declare an attribute group.
* @name: Optional: Attribute group name
* If specified, the attribute group will be created in
* a new subdirectory with this name.
* @is_visible: Optional: Function to return permissions associated with an
* attribute of the group. Will be called repeatedly for each
* non-binary attribute in the group. Only read/write
* permissions as well as SYSFS_PREALLOC are accepted. Must
* return 0 if an attribute is not visible. The returned value
* will replace static permissions defined in struct attribute.
* @is_bin_visible:
* Optional: Function to return permissions associated with a
* binary attribute of the group. Will be called repeatedly
* for each binary attribute in the group. Only read/write
* permissions as well as SYSFS_PREALLOC are accepted. Must
* return 0 if a binary attribute is not visible. The returned
* value will replace static permissions defined in
* struct bin_attribute.
* @attrs: Pointer to NULL terminated list of attributes.
* @bin_attrs: Pointer to NULL terminated list of binary attributes.
* Either attrs or bin_attrs or both must be provided.
*/
struct attribute_group {
const char *name;
umode_t (*is_visible)(struct kobject *,
struct attribute *, int);
umode_t (*is_bin_visible)(struct kobject *,
struct bin_attribute *, int);
struct attribute **attrs;
struct bin_attribute **bin_attrs;
};
DEVICE_ATTR
进一步分析宏DEVICE_ATTR的实现,在Linux内核源码中,宏DEVICE_ATTR的定义在include/linux/device.h文件中,如下:
#define DEVICE_ATTR(_name, _mode, _show, _store) \
struct device_attribute dev_attr_##_name = __ATTR(_name, _mode, _show, _store)
而__ATTR宏的定义在include/linux/sysfs.h文件中,如下:
#define __ATTR(_name, _mode, _show, _store) { \
.attr = {.name = __stringify(_name), \
.mode = VERIFY_OCTAL_PERMISSIONS(_mode) }, \
.show = _show, \
.store = _store, \
}
通过上面的宏展开可以发现,其实宏DEVICE_ATTR实现的功能就是定义了一个struct device_attribute结构体变量dev_attr_name,并对里面的成员进行初始化,包括struct attribute结构体里面的name和mode成员变量,然后还有实现属性文件读写的show和store函数赋值,非常简单。
类似的衍生宏还有DEVICE_ATTR_RW,DEVICE_ATTR_RO等
#define __ATTR(_name, _mode, _show, _store) { \
.attr = {.name = __stringify(_name), \
.mode = VERIFY_OCTAL_PERMISSIONS(_mode) }, \
.show = _show, \
.store = _store, \
}
#define __ATTR_RW(_name) __ATTR(_name, 0644, _name##_show, _name##_store)
#define DEVICE_ATTR_RW(_name) \
struct device_attribute dev_attr_##_name = __ATTR_RW(_name)
此外还有平级的几个宏
如对设备的使用 DEVICE_ATTR
对驱动使用 DRIVER_ATTR
对总线使用 BUS_ATTR
对类别 (class) 使用 CLASS_ATTR
使用
如果你完成了DEVICE_ATTR函数宏的填充,下面就需要创建接口了
例如:
static DEVICE_ATTR(polling, S_IRUGO | S_IWUSR, show_polling, set_polling);
static struct attribute *dev_attrs[] = {
&dev_attr_polling.attr,
NULL,
};
当你想要实现的接口名字是polling的时候,需要实现结构体struct attribute *dev_attrs[]
其中成员变量的名字必须是&dev_attr_polling.attr
然后再封装
static struct attribute_group dev_attr_grp = {
.attrs = dev_attrs,
};
在利用sysfs_create_group(&pdev->dev.kobj, &dev_attr_grp);创建接口
实例
drivers/leds/led-class.c
https://elixir.bootlin.com/linux/latest/source/drivers/leds/led-class.c#L98
static ssize_t brightness_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct led_classdev *led_cdev = dev_get_drvdata(dev);
/* no lock needed for this */
led_update_brightness(led_cdev);
return sprintf(buf, "%u\n", led_cdev->brightness);
}
static ssize_t brightness_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
struct led_classdev *led_cdev = dev_get_drvdata(dev);
unsigned long state;
ssize_t ret;
mutex_lock(&led_cdev->led_access);
if (led_sysfs_is_disabled(led_cdev)) {
ret = -EBUSY;
goto unlock;
}
ret = kstrtoul(buf, 10, &state);
if (ret)
goto unlock;
if (state == LED_OFF)
led_trigger_remove(led_cdev);
led_set_brightness(led_cdev, state);
flush_work(&led_cdev->set_brightness_work);
ret = size;
unlock:
mutex_unlock(&led_cdev->led_access);
return ret;
}
static DEVICE_ATTR_RW(brightness);
static ssize_t max_brightness_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct led_classdev *led_cdev = dev_get_drvdata(dev);
return sprintf(buf, "%u\n", led_cdev->max_brightness);
}
static DEVICE_ATTR_RO(max_brightness);
static struct attribute *led_class_attrs[] = {
&dev_attr_brightness.attr, //前面DEVICE_ATTR宏已经定义了结构体dev_attr_brightness
//&dev_attr_max_brightness.attr,
NULL,
};
static const struct attribute_group led_group = {
.attrs = led_class_attrs,
};
static const struct attribute_group *led_groups[] = {
&led_group,
//#ifdef CONFIG_LEDS_TRIGGERS
//&led_trigger_group,
//#endif
NULL,
};
static int __init leds_init(void)
{
leds_class = class_create(THIS_MODULE, "leds");
if (IS_ERR(leds_class))
return PTR_ERR(leds_class);
leds_class->pm = &leds_class_dev_pm_ops;
leds_class->dev_groups = led_groups;
return 0;
}