linux设备驱动模型一三基础结构之示例

来源:互联网 发布:日历记事本软件下载 编辑:程序博客网 时间:2024/05/02 01:50

前面我们了解了设备驱动模型的三个基本结构,以及它们的相关操作函数,这里我们以实例来看一下它们的用法。

首先定义一个内嵌的kobject结构:

struct test_container {int member;struct kobject kobj;};

定义了一个kobject和一个member成员,主要就是嵌套了kobject结构。

struct test_container *con1, *con2, *con3;struct kset *test_kset;struct kobj_type test_type;

定义了三个test_container变量,一个kset,一个kobj_type.

struct attribute name_attr = {.name = "name",.mode = 0666,};struct attribute val_attr = {.name = "member",.mode = 0666,};struct attribute *test_attrs[] = {&name_attr, &val_attr,NULL,};

定义了两个属性变量,name和member,以及它们的访问权限,最后把它们都放到一个属性数组中。这里要注意的是属性数组中的最后一项为NULL.

ssize_t test_show(struct kobject *kobj, struct attribute *attr, char *buffer){struct test_container *obj = container_of(kobj, struct test_container, kobj);ssize_t count = 0;if (strcmp(attr->name, "name") == 0) {count = sprintf(buffer, "%s\n", kobject_name(kobj));} else if (strcmp(attr->name, "member") == 0) {count = sprintf(buffer, "%d\n", obj->member);}return count;}ssize_t test_store(struct kobject *kobj, struct attribute *attr, const char *buffer, size_t size){struct test_container *obj = container_of(kobj, struct test_container, kobj);if (strcmp(attr->name, "member") == 0) {sscanf(buffer, "%d", &obj->member);}return size;}struct sysfs_ops test_sysfsops = {.show = test_show,.store = test_store,};

定义文件读写函数,并赋值到sysfs_ops结构的变量test_sysfsops中。

void obj_release(struct kobject *kobj){struct test_container *obj = container_of(kobj, struct test_container, kobj);printk(KERN_INFO "obj_release,name is: %s\n", kobject_name(&obj->kobj));kfree(obj);}
当konject释放的时候调用。

static int __init testkset_init(void){printk(KERN_WARNING "test_init\n");test_kset = kset_create_and_add("test_kset", NULL, NULL);if (!test_kset) {return -ENOMEM;}con1 = kzalloc(sizeof(struct test_container), GFP_KERNEL);if (!con1) {kset_unregister(test_kset);return -ENOMEM;}con1->member = 1;con2 = kzalloc(sizeof(struct test_container), GFP_KERNEL);if (!con2) {kset_unregister(test_kset);kfree(con1);return -ENOMEM;}con2->member = 2;con3 = kzalloc(sizeof(struct test_container), GFP_KERNEL);if (!con3) {kset_unregister(test_kset);kfree(con1);kfree(con2);return -ENOMEM;}con3->member = 3;con1->kobj.kset = test_kset;con2->kobj.kset = test_kset;con3->kobj.kset = test_kset;test_type.release = obj_release;test_type.default_attrs = test_attrs;test_type.sysfs_ops = &test_sysfsops;kobject_init_and_add(&con1->kobj, &test_type, NULL, "con1");kobject_init_and_add(&con2->kobj, &test_type, &con1->kobj, "con2");kobject_init_and_add(&con3->kobj, &test_type, NULL, "con3");return 0;}

这里主要进行kset,kobject的初始化操作,对于kset,直接调用kset_create_and_add初始化并添加到体系结构中,而对于 kobject,首先是赋值,然后设置kset,最后调用kobject_init_and_add添加到体系结构。这里还对这个ktype进行了赋值,进行了属性的相关设置 。

static void __exit testkset_exit(void){printk(KERN_INFO "test_kset_exit\n");kobject_del(&con1->kobj);kobject_put(&con1->kobj);kobject_del(&con2->kobj);kobject_put(&con2->kobj);kobject_del(&con3->kobj);kobject_put(&con3->kobj);kset_unregister(test_kset);return;}
kobject_del的作用是把kobject从设备模型的那棵树里摘掉,同时sysfs里相应的目录也会删除。这里需要指出的是,释放的顺序应该是先子对象,后父对象。因为kobject_init_and_add和kobject_add这两个函数会调用kobject_get来增加父对象的引用计数,所以kobject_del需要调用kobject_put来减少父对象的引用计数。在本例中,如果先通过kobject_put来释放obj1,那kobject_del(&obj2->kobj)就会出现内存错误。

这个示例构建了一个如下的架构:


相关的/sys目录结构及属性操作:

root@leaves-desktop:/home/leaves/Test/ldm/ldm2# insmod ldm2.ko root@leaves-desktop:/home/leaves/Test/ldm/ldm2# ll /systotal 4drwxr-xr-x  13 root root    0 May 15 14:39 ./drwxr-xr-x  26 root root 4096 Mar 14 14:28 ../drwxr-xr-x   2 root root    0 May 15 14:39 block/drwxr-xr-x  22 root root    0 May 15 14:39 bus/drwxr-xr-x  45 root root    0 May 15 14:39 class/drwxr-xr-x   4 root root    0 May 15 14:39 dev/drwxr-xr-x  14 root root    0 May 15 14:39 devices/drwxr-xr-x   4 root root    0 May 15 14:39 firmware/drwxr-xr-x   6 root root    0 May 15 14:39 fs/drwxr-xr-x   7 root root    0 May 15 14:39 kernel/drwxr-xr-x 105 root root    0 May 15 14:39 module/drwxr-xr-x   2 root root    0 May 15 14:39 power/drwxr-xr-x   4 root root    0 May 15 14:43 test_kset/root@leaves-desktop:/home/leaves/Test/ldm/ldm2# ll /sys/test_kset/total 0drwxr-xr-x  4 root root 0 May 15 14:43 ./drwxr-xr-x 13 root root 0 May 15 14:39 ../drwxr-xr-x  3 root root 0 May 15 14:43 con1/drwxr-xr-x  2 root root 0 May 15 14:43 con3/root@leaves-desktop:/home/leaves/Test/ldm/ldm2# ll /sys/test_kset/concon1/ con3/ root@leaves-desktop:/home/leaves/Test/ldm/ldm2# ll /sys/test_kset/concon1/ con3/ root@leaves-desktop:/home/leaves/Test/ldm/ldm2# ll /sys/test_kset/con1/total 0drwxr-xr-x 3 root root    0 May 15 14:43 ./drwxr-xr-x 4 root root    0 May 15 14:43 ../drwxr-xr-x 2 root root    0 May 15 14:43 con2/-rw-rw-rw- 1 root root 4096 May 15 14:43 member-rw-rw-rw- 1 root root 4096 May 15 14:43 nameroot@leaves-desktop:/home/leaves/Test/ldm/ldm2# cat /sys/test_kset/con1/member 1root@leaves-desktop:/home/leaves/Test/ldm/ldm2# cat /sys/test_kset/con1/name con1root@leaves-desktop:/home/leaves/Test/ldm/ldm2# ll /sys/test_kset/con1/con2/   total 0drwxr-xr-x 2 root root    0 May 15 14:43 ./drwxr-xr-x 3 root root    0 May 15 14:43 ../-rw-rw-rw- 1 root root 4096 May 15 14:44 member-rw-rw-rw- 1 root root 4096 May 15 14:44 nameroot@leaves-desktop:/home/leaves/Test/ldm/ldm2# cat /sys/test_kset/concon1/ con3/ root@leaves-desktop:/home/leaves/Test/ldm/ldm2# cat /sys/test_kset/con1/con2/member 2root@leaves-desktop:/home/leaves/Test/ldm/ldm2# echo 4 /sys/test_kset/con1/member 4 /sys/test_kset/con1/memberroot@leaves-desktop:/home/leaves/Test/ldm/ldm2# cat /sys/test_kset/con1/member        1root@leaves-desktop:/home/leaves/Test/ldm/ldm2# echo 4 > /sys/test_kset/con1/member root@leaves-desktop:/home/leaves/Test/ldm/ldm2# cat /sys/test_kset/con1/member      4root@leaves-desktop:/home/leaves/Test/ldm/ldm2# rmmod ldm2.ko root@leaves-desktop:/home/leaves/Test/ldm/ldm2# 


上面的case中,我们介绍到如何使用default attribute。Default attribute使用很方便,但不够灵活。比如上面的那个例子,name和val这两个attribute使用同一个show/store函数来访问,如果attribute非常多,show/store函数里的分支就会很凌乱。
为了解决这个问题,我们可以参考内核提供的kobj_attribute。在内核里,kobj_attibute是这样定义的:

static ssize_t kobj_attr_show(struct kobject *kobj, struct attribute *attr,      char *buf){struct kobj_attribute *kattr;ssize_t ret = -EIO;kattr = container_of(attr, struct kobj_attribute, attr);if (kattr->show)ret = kattr->show(kobj, kattr, buf);return ret;}static ssize_t kobj_attr_store(struct kobject *kobj, struct attribute *attr,       const char *buf, size_t count){struct kobj_attribute *kattr;ssize_t ret = -EIO;kattr = container_of(attr, struct kobj_attribute, attr);if (kattr->store)ret = kattr->store(kobj, kattr, buf, count);return ret;}const struct sysfs_ops kobj_sysfs_ops = {.show= kobj_attr_show,.store= kobj_attr_store,};static void dynamic_kobj_release(struct kobject *kobj){pr_debug("kobject: (%p): %s\n", kobj, __func__);kfree(kobj);}static struct kobj_type dynamic_kobj_ktype = {.release= dynamic_kobj_release,.sysfs_ops= &kobj_sysfs_ops,};

kobj_attribute是内核提供给我们的一种更加灵活的处理attribute的方式,但是它还不够。只有当我们使用kobject_create来创建kobject时,使用kobj_attribute才比较方便,但大部分情况下,我们是把kobject内嵌到自己的结构里,此时就无法直接使用内核提供的dynamic_kobj_ktype,因此,我们需要创建自己的kobj_attribute。

struct my_attribute {struct attribute attr;ssize_t (*show)(struct my_kobj *obj, struct my_attribute *attr, char *buf);ssize_t (*store)(struct my_kobj *obj, struct my_attribute *attr, const char *buf, size_t count);};

在my_attribute里,我们的show/store直接操作my_kobj,这样更加方便。
参考Linux内核,kobj_type里的sysfs_ops这样定义:

static ssize_t my_attr_show(struct kobject *kobj, struct attribute *attr,      char *buf){struct my_attribute *my_attr;ssize_t ret = -EIO;my_attr = container_of(attr, struct my_attribute, attr);if (my_attr->show)ret = my_attr->show(container_of(kobj, struct my_kobj, kobj), my_attr, buf);return ret;}static ssize_t my_attr_store(struct kobject *kobj, struct attribute *attr,       const char *buf, size_t count){struct my_attribute *my_attr;ssize_t ret = -EIO;my_attr = container_of(attr, struct my_attribute, attr);if (my_attr->store)ret = my_attr->store(container_of(kobj, struct my_kobj, kobj), my_attr, buf, count);return ret;}

下面就可以分别对name和val两个attribute定义自己的show/store。name这个attribute是只读的,只要为它定义show即可。

ssize_t name_show(struct my_kobj *obj, struct my_attribute *attr, char *buffer){return sprintf(buffer, "%s\n", kobject_name(&obj->kobj));}ssize_t val_show(struct my_kobj *obj, struct my_attribute *attr, char *buffer){return sprintf(buffer, "%d\n", obj->val);}ssize_t val_store(struct my_kobj *obj, struct my_attribute *attr, const char *buffer, size_t size){sscanf(buffer, "%d", &obj->val);return size;}

接下来,利用内核提供的宏__ATTR来初始化my_attribute,并建立attribute数组。

struct my_attribute name_attribute = __ATTR(name, 0444, name_show, NULL);struct my_attribute val_attribute = __ATTR(val, 0666, val_show, val_store);struct attribute *my_attrs[] = {&name_attribute.attr, &val_attribute.attr,NULL,};

其中,宏__ATTR的定义如下:

#define __ATTR(_name,_mode,_show,_store) { \.attr = {.name = __stringify(_name), .mode = _mode },\.show= _show,\.store= _store,\}
在module_init里,我们调用sysfs_create_files来把attribute增加到sysfs中。

retval = sysfs_create_files(&obj->kobj, (const struct attribute **)my_attrs);if (retval) {// ...}
在kobject对应的目录里,还可以创建子目录,Linux内核里是用attribute_group来实现。在本例中,我们可以这么做:

struct attribute_group my_group = {.name     = "mygroup",.attrs    = my_attrs,};
然后在module_init里调用sysfs_create_group来添加。
retval = sysfs_create_group(&obj->kobj, &my_group);if (retval) {// ...}
本例创建的attribute_group中包含的attribute也是my_attrs,所以在子目录mygroup下的文件和mykobj目录下的文件完全一致。最后我们得到的目录结构是这样的。

mykobj/|-- mygroup|   |-- name|   `-- val|-- name`-- val


完成这个实作之后,你可以用命令echo 2 > /sys/mykobj/val来修改mykobj下的val文件,可以观察到/sys/mykobj/mygroup/val的内容也会变成2,反之亦然。


总结:

kobj、kset对应着sys目录下面的一个个文件夹,而ktype反映的是sys目录下面的文件及操作其show 和store的方法


测试代码:

http://download.csdn.net/detail/new_abc/4312872