我們在Linux設備管理(一)_kobject, kset,ktype分析一文中介紹了kobject的相關知識,在Linux設備管理(二)_從cdev_add說起和Linux設備管理(三)_總線設備的掛接舉例介紹了內核中是如何進行設備管理的,並在Linux設備管理(四)_從sysfs回到ktype一文中結合sysfs機制和kobject對內核的設備管理機制進行一定深度的討論,從中可以看出,字符設備的cdev本身的kobject是沒有初始化的,也沒有在sysfs中創建任何目錄,平台設備是將設備掛接到總線上,在掛接的過程中就會在相應的sysfs目錄創建相應的文件。在這裡,本文將搭建一個sysfs模塊接口的框架,此後,就可以為我們自己的驅動在sysfs中添加屬性讀寫接口。
我們知道,呈現在sysfs中的文件名其實都是內核中ktype的屬性值,而從用戶空間對這些屬性值進行讀寫其實就是回調了我們在ktype結構中注冊讀寫函數,所以,這裡我們准備了兩個函數,值得注意的是,內核會將用戶空間的buf轉換到內核空間並當作參數傳入回調函數,所以我們就不用再進行這個轉換。這裡由於沒有實際的屬性,我就只是打印一下信息,實際使用的時候這兩個函數要對內核中的真實屬性進行讀寫。
static char kbuf[1024] = {0};
static ssize_t my_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
{
char info[]="my_show is called\n";
return scnprintf(buf,sizeof(info),info);
}
static ssize_t my_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count)
{
printk("%s is called\n",__func__);
strncpy(kbuf,buf,count);
printk("user_buf:%s,count:%ld|after copy,kbuf:%s\n",buf,count,kbuf);
return count;
}
准備好了原材料,第一道工序就是將屬性和回調接口封裝到一個kobj_attribute結構對象中,當然對這個屬性的讀寫權限等信息也應該進行封裝,我們來回顧一下這個結構
//linux/kobject.h
139 struct kobj_attribute {
140 struct attribute attr;
141 ssize_t (*show)(struct kobject *kobj, struct kobj_attribute *attr,
142 char *buf);
143 ssize_t (*store)(struct kobject *kobj, struct kobj_attribute *attr,
144 const char *buf, size_t count);
145 };
//linux/sysfs.h
29 struct attribute {
30 const char *name;
31 umode_t mode; //權限
32 ...
37 };
當然,內核也給我們提供了相應的宏來快速的構造這個結構
//linux/sysfs.h
100 #define __ATTR(_name, _mode, _show, _store) { \
101 .attr = {.name = __stringify(_name), \
102 .mode = VERIFY_OCTAL_PERMISSIONS(_mode) }, \
103 .show = _show, \
104 .store = _store, \
105 }
使用了這個宏,我們就可以快速的構造我們的kobj_attribute結構
//show是name,就是sys中的文件名
static struct kobj_attribute my_sysfs_read =__ATTR(show, S_IRUSR, my_show, NULL);
static struct kobj_attribute my_sysfs_write =__ATTR(write, S_IWUSR, NULL,my_store);
一個kobject網完對應多個attribute,此時就需要將這些attribute封裝成一個結構體數組,注意這個數組最後一個元素一定要是NULL。
static struct attribute *my_sysfs_attr[] = {
&my_sysfs_read.attr,
&my_sysfs_write.attr,
NULL,
};
如果這些屬性直接放到kobject的目錄中,我們可以直接使用sysfs_create_file(),但通常情況下,我們更多的將上述的struct attribute進行進一步的封裝,並使用sysfs_create_group()來創建一個名為attribute_group.name的、包含struct attribute中的屬性目錄,這種方式更加的靈活,因為如果我們不指定目錄的名字,那麼效果個sysfs_create_file()是一樣的。
static struct attribute_group my_sysfs_attr_group = {
.name = "sub_my_attr", //不寫這個成員就不會創建子文件夾
.attrs = my_sysfs_attr,
};
struct kobject *my_kobj = NULL;
int mysys_init(void)
{
...
my_kobj = kobject_create_and_add("my_sysfs", NULL);
sysfs_create_group(my_kobj, &my_sysfs_attr_group);
...
}
void mysys_exit(void)
{
...
sysfs_remove_group(my_kobj, &my_sysfs_attr_group);
kobject_put(my_kobj);
}
將上述的程序編譯成模塊,我們就可以觀察到下面的輸出結果。