sysfs 作為核心中的一種記憶體檔案系統,現階段很多子系統、裝置驅動程式都使用sysfs實作核心空間和使用者空間的互動。其挂載在/sys下。使用者通過對裝置節點的操作,進入到kernel空間,通過屬性檔案的讀寫操作(show store),操作硬體。
在核心中,sysfs的屬性一般都是由_ATTR系列的宏來實作。對裝置的使用 DEVICE_ATTR ,對總線使用 BUS_ATTR ,對驅動使用 DRIVER_ATTR ,對類别(class)使用 CLASS_ATTR。
以下源碼分析是在kernel4.14上 。
一、 幾個結構體
sysfs_create_file 裡的傳參是 struct attribute 結構體
sysfs_create_group 裡的傳參是 struct attribute_group 結構體
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
};
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;
};
struct attribute 和 struct attribute_group 差別:attribute_group 裡可以有好幾組 attribute 。
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);
};
struct device_attribute 是自定義decice屬性結構,讀寫該屬性的方法 show 和 store 會和自定義的屬性方法關聯起來。
二、 sysfs_create_file
static inline int __must_check sysfs_create_file(struct kobject *kobj,
const struct attribute *attr)
{
return sysfs_create_file_ns(kobj, attr, NULL);
}
sysfs_create_file :通過kobject建立sysfs節點。下面的3個函數内部都會調用到 sysfs_create_file 。
device_create_file:為裝置建立sys的節點。
bus_create_file : 為總線建立sys的節點。
driver_create_file : 為驅動建立sys的節點。
以 device_create_file 為例
int device_create_file(struct device *dev,
const struct device_attribute *attr)
{
int error = 0;
if (dev) {
WARN(((attr->attr.mode & S_IWUGO) && !attr->store),
"Attribute %s: write permission without 'store'\n",
attr->attr.name);
WARN(((attr->attr.mode & S_IRUGO) && !attr->show),
"Attribute %s: read permission without 'show'\n",
attr->attr.name);
error = sysfs_create_file(&dev->kobj, &attr->attr);
}
return error;
}
其中 struct device_attribute 可以用 DEVICE_ATTR() 實作,下面有詳細解釋;
如果是字元裝置,參數dev可以使用以下接口建立:
(1) class_create:建立一個類,類存放在sysfs下
(2) alloc_chrdev_region:向核心申請主裝置号
(3) device_create:建立好類之後,在/dev目錄下建立相應的裝置節點
(4) 這兩步(1)(3)做好之後,加載子產品的時候,使用者空間的udev會自動響應device_create函數,去/sysfs下尋找對應的類進而建立裝置節點
(5) cdev_init:将cdev和file_operations關聯起來
(6) cdev_add:将cdev和裝置号關聯起來
三、 sysfs_create_groups()
具體實作功能的函數 : 使用 show 和 store 進行互動
1. 定義 : struct device_attribute結構體,用 DEVICE_ATTR 宏實作
static DEVICE_ATTR(sgo_up);
static DEVICE_ATTR(sgo_middle);
static DEVICE_ATTR(sgo_down);
DEVICE_ATTR_RW 是 DEVICE_ATTR 的變體,代表可讀可寫。這些宏都是在 include/linux/device.h 以及衍生 include/linux/sysfs.h 中定義 。
是以這裡直接使用 static DEVICE_ATTR_RW(sgo_up);
宏分析 : static DEVICE_ATTR_RW(sgo_up);
#define DEVICE_ATTR_RW(_name) \
struct device_attribute dev_attr_##_name = __ATTR_RW(_name)
==>
struct device_attribute dev_attr_sgo_up = __ATTR_RW(sgo_up);
#define __ATTR_RW(_name) __ATTR(_name, (S_IWUSR | S_IRUGO), \
_name##_show, _name##_store)
==>
struct device_attribute dev_attr_sgo_up
= __ATTR(sgo_up, (S_IWUSR | S_IRUGO), sgo_up_show, sgo_up_store);
#define __ATTR(_name, _mode, _show, _store) { \
.attr = {.name = __stringify(_name), \
.mode = VERIFY_OCTAL_PERMISSIONS(_mode) }, \
.show = _show, \
.store = _store, \
}
==>
struct device_attribute dev_attr_sgo_up = {
.attr = {.name = __stringify(sgo_up),
.mode = VERIFY_OCTAL_PERMISSIONS(S_IWUSR | S_IRUGO)},
.show = sgo_up_show,
.store = sgo_up_store,
}
總結:
static DEVICE_ATTR(sgo_up)
;
等效為
static struct device_attribute dev_attr_sgo_up = {
.attr = {.name = __stringify(sgo_up),
.mode = VERIFY_OCTAL_PERMISSIONS(S_IWUSR | S_IRUGO)},
.show = sgo_up_show,
.store = sgo_up_store,
}
2. 建立接口 : 屬性結構體數組 struct attribute
static struct attribute *sgo_attrs[] = {
&dev_attr_sgo_up.attr,
&dev_attr_sgo_middle.attr,
&dev_attr_sgo_down.attr,
NULL,
};
(1) 當用DEVICE_ATTR實作的接口名字是 sgo_up 時, 則 struct attribute *sgo_attrs[] 中成員變量的名字必須是 &dev_attr_sgo_up.attr。
(2) 用DEVICE_ATTR實作了3個,struct attribute 結構體下就可以有3個。
(3) 屬性結構體數組的最後一項必須以 NULL 結尾 。
3. 封裝 :将屬性結構體數組加到屬性組中 struct attribute_group
ATTRIBUTE_GROUPS(sgo);
即 結構體 : static const struct attribute_group
#define ATTRIBUTE_GROUPS(_name) \
static const struct attribute_group _name##_group = { \
.attrs = _name##_attrs, \
}; \
__ATTRIBUTE_GROUPS(_name)
#define __ATTRIBUTE_GROUPS(_name) \
static const struct attribute_group *_name##_groups[] = { \
&_name##_group, \
NULL, \
}
ATTRIBUTE_GROUPS(sgo) 宏展開
static const struct attribute_group sgo_group = {
.attrs = sgo_attrs,
};
static const struct attribute_group *sgo_groups[] = { \
&sgo_group, \
NULL, \
}
至此總結一下這3個結構體,4個參數之間的關系:
static const struct attribute_group *sgo_groups[] = { \
&sgo_group, \
NULL, \
}
static const struct attribute_group sgo_group = {
.attrs = sgo_attrs,
};
static struct attribute *sgo_attrs[] = {
&dev_attr_sgo_up.attr,
&dev_attr_sgo_middle.attr,
&dev_attr_sgo_down.attr,
NULL,
};
static struct device_attribute dev_attr_sgo_up = {
.attr = {.name = __stringify(sgo_up),
.mode = VERIFY_OCTAL_PERMISSIONS(S_IWUSR | S_IRUGO)},
.show = sgo_up_show,
.store = sgo_up_store,
}
4. 調用 : 建立屬性檔案sysfs的接口 sysfs_create_groups
int sysfs_create_groups(struct kobject *kobj,
const struct attribute_group **groups)
{
int error = 0;
int i;
if (!groups)
return 0;
for (i = 0; groups[i]; i++) {
error = sysfs_create_group(kobj, groups[i]);
if (error) {
while (--i >= 0)
sysfs_remove_group(kobj, groups[i]);
break;
}
}
return error;
}
This function creates a group for the first time. It will explicitly warn and error if any of the attribute files being created already exist.
examlpe:
ret = sysfs_create_groups(&pdev->dev.kobj, sgo_groups);
if (ret) {
dev_err(pdev, "sipa fail to create sysfs device attributes\n");
goto groups_err;
}
5. 删除 : groups_err
6. struct device_attribute 中 show函數: sgo_up_show
從屬性檔案中讀取出資料 。當使用者cat時, 就是完成了一次read操作, 相當于kernel的show。
example:
static ssize_t sgo_up_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
char *a = "Usage:\n";
char *b = "\t1: bbbb\n";
char *c = "\t0: cccc\n";
return sprintf(buf, "\n%s%s%s\n", a, b, c);
}
7. struct device_attribute 中 store函數: sgo_up_store
向屬性檔案中寫入資料 。當使用者echo時, 就是完成了一次write操作, 相當于kernel的store。
example:
static ssize_t sgo_up_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
u8 cmd;
if (sscanf(buf, "%4hhx\n", &cmd) != 1)
return -EINVAL;
if (cmd) {
sprintf(cmd, "%d\n", cmd);
}
return count;
}
上面7步完成後,就可以用adb 操作 /sys 下上面注冊的三個屬性檔案 sgo_up, sgo_middle, sgo_down 。