天天看點

linux kernel --- sysfs檔案系統

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 。

繼續閱讀