一、设备驱动模型基本元素
1.1 kobject结构体
sysfs的一个目录,常用来表示基本驱动对象,不允许发送消息到用户空间。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| struct kobject { const char *name; struct list_head entry; struct kobject *parent; struct kset *kset; struct kobj_type *ktype; struct kernfs_node *sd; struct kref kref; #ifdef CONFIG_DEBUG_KOBJECT_RELEASE struct delayed_work release; #endif unsigned int state_initialized:1; unsigned int state_in_sysfs:1; unsigned int state_add_uevent_sent:1; unsigned int state_remove_uevent_sent:1; unsigned int uevent_suppress:1; };
|
由于kobject添加到内核时,需要根据名字注册到sysfs虚拟文件系统中,之后就不能再直接修改该名字。如果想改,需要调用kobject_rename接口,该接口会主动处理sysfs的相关事宜。kset如果没有指定的parent,则会把kset作为parent(kset是一个特殊的kobject)。uevent提供了“用空空间通知”的功能实现,当内核中有kobject的增删改等操作时,会通知用户空间。
1.2 kset结构体
sysfs的一个目录,常用来管理kobject,允许发送消息到用户空间。
1 2 3 4 5 6
| struct kset { struct list_head list; spinlock_t list_lock; struct kobject kobj; const struct kset_uevent_ops *uevent_ops; } __randomize_layout;
|
uevent_ops为该kset的uevent操作函数集(函数指针)。当kset的某些kobject对象发生状态变化需要通知用户空间时,调用其中对应的函数来完成。当任一kobject需要上报uevent时,都要调用它所属的kset的uevent_ops,添加环境变量,或者过滤uevent(kset可以决定哪些uevent可以上报)。因此一个kobject不属于任一kset时,是不允许发生uevent的。
1.3 kobj_type结构体
目录下属性文件的操作接口。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| struct kobj_type { void (*release)(struct kobject *kobj);
const struct sysfs_ops *sysfs_ops; struct attribute **default_attrs; const struct kobj_ns_type_operations *(*child_ns_type)(struct kobject *kobj); const void *(*namespace)(struct kobject *kobj); void (*get_ownership)(struct kobject *kobj, kuid_t *uid, kgid_t *gid); };
|
1.4 kobject机制的理解
kobject的核心功能是:保持一个引用计数,当该引用计数减为0时,自动释放kobject所占的内存空间(这决定了kobject必须是动态分配)。kobject的常见使用场景:内嵌在大型的数据结构中(如kset、device_driver等),因此这些大型的数据结构也必须是动态分配、动态释放。ktype的release回调函数负责释放kobject的内存空间。
- 通过parent指针,可以将所有kobject以层次结构的形式组合起来。
- 使用一个引用计数,来记录kobject被引用的次数,并在引用计数为0时释放kobject对象(这是kobject诞生时的唯一功能)。
- 和sysfs虚拟文件系统配合,将每一个kobject及其特性以文件形式显示到用户空间。
- 在Linux中,kobject几乎不会单独存在。它的主要功能就是内嵌在一个大型的数据结构中,为这个数据结构提供一些底层的功能实现。
- Linux驱动开发者很少会直接使用kobject以及它提供的接口,而是使用构建在kobject之上的设备模型接口。
二、kobject流程分析
2.1 kobject使用流程
kobject大多数情况下(有一例外)会嵌在其它数据结构中使用,使用流程如下:
- 定义一个struct kset类型的指针,并在初始化时为它分配空间,添加到内核中。
- 根据实际情况,定义内嵌有kobject的自己所需的数据结构原型。
- 定义一个适合自己的ktype,并实现其中回调函数release。
- 在需要使用到包含kobject的数据结构时,动态分配该数据结构,并分配kobject空间,添加到内核中。
- 每一次引用数据结构时,调用kobject_get接口增加引用计数;引用结束时,调用kobject_put接口,减少引用次数。
- 当引用计数为0时,kobject模块会自动调用ktype所提供的release接口,释放上层数据结构以及kobject的内存空间。
例外:开发者只需在sysfs中创建一个目录,而不需要其它的kset、ktype的操作。这是可以直接调用kobject_create_and_add接口,分配一个kobject结构并把它添加到内核中。(以他为例分析)
2.2 kobject_create_and_add
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
|
struct kobject *kobject_create_and_add(const char *name, struct kobject *parent) { struct kobject *kobj; int retval; kobj = kobject_create(); if (!kobj) return NULL; retval = kobject_add(kobj, parent, "%s", name); if (retval) { pr_warn("%s: kobject_add error: %d\n", __func__, retval); kobject_put(kobj); kobj = NULL; } return kobj; }
|
kobject_create_and_add()函数是kobject_create函数和kobject_add函数的组合。整体功能是创建一个名字为“name”的kobject对象,并将其添加到指定的父kobject对象下。其流程如下:





2.3 sysfs_create_group
上一节分析到:
kobject_create() → kobject_init(kobj, &dymic_kobj_ktype) → dymic_kobj_ktype.sysfs_ops = &kobj_sysfs_ops。
kobj_sysfs_ops中存放着统一的操作接口show和store。调用统一的操作接口时,会在内部进一步调用具体的操作接口。
sysfs_create_group会在/sys下创建相关的attribute文件,并让其与具体的操作接口关联起来。



三、kobject点灯测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136
| #include <linux/init.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/kobject.h> #include <linux/string.h> #include <linux/sysfs.h> #include <linux/fs.h> #include <linux/uaccess.h> #include <asm/io.h>
static void __iomem *IMX6U_CCM_CCGR1; static void __iomem *SW_MUX_GPIO1_IO04; static void __iomem *SW_PAD_GPIO1_IO04; static void __iomem *GPIO1_GDIR; static void __iomem *GPIO1_DR; static int foo; static ssize_t foo_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { return sprintf(buf, "%d\n", foo); } static ssize_t foo_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count) { int ret = kstrtoint(buf, 10, &foo); if(ret < 0) return ret; return count; } static ssize_t led_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { int var; if(strcmp(attr->attr.name, "led") == 0) var = 123; return sprintf(buf, "%d\n", var); } static ssize_t led_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count) { if(strcmp(attr->attr.name, "led") == 0){ if(!memcmp(buf, "on", 2)){ iowrite32(0<<4, GPIO1_DR); }else if(!memcmp(buf, "off", 3)){ iowrite32(1<<4, GPIO1_DR); } } return count; }
static struct kobj_attribute foo_attribute = __ATTR(foo, 0664, foo_show, foo_store); static struct kobj_attribute led_attribute = __ATTR(led, 0664, led_show, led_store); static struct attribute *attrs[] = { &foo_attribute.attr, &led_attribute.attr, NULL, }; static struct attribute_group attr_group = { .attrs = attrs, }; static struct kobject *led_kobj; static int __init led_init(void){ int retval; IMX6U_CCM_CCGR1 = ioremap(0x20c406c, 4); SW_MUX_GPIO1_IO04 = ioremap(0x20e006c, 4); SW_PAD_GPIO1_IO04 = ioremap(0x20e02f8, 4); GPIO1_GDIR = ioremap(0x0209c004, 4); GPIO1_DR = ioremap(0x0209c000, 4); iowrite32(0xffffffff, IMX6U_CCM_CCGR1); iowrite32(5, SW_MUX_GPIO1_IO04); iowrite32(0x10b0, SW_PAD_GPIO1_IO04); iowrite32(1<<4, GPIO1_GDIR); iowrite32(1<<4, GPIO1_DR);
led_kobj = kobject_create_and_add("led_kobject", NULL); if(!led_kobj) return -ENOMEM; retval = sysfs_create_group(led_kobj, &attr_group); if(retval) kobject_put(led_kobj); return 0; } static void __exit led_exit(void){ iounmap(IMX6U_CCM_CCGR1); iounmap(SW_MUX_GPIO1_IO04); iounmap(SW_PAD_GPIO1_IO04); iounmap(GPIO1_GDIR); iounmap(GPIO1_DR); kobject_put(led_kobj); } module_init(led_init); module_exit(led_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("couvrir"); MODULE_DESCRIPTION("led module"); MODULE_ALIAS("led module");
|
测试流程:
1 2 3 4 5 6 7 8 9
| insmod kobject_led.ko
查看/sys/文件夹,存在led_kobject的目录项。
查看/sys/led_kobject的属性文件。
然后就是echo和cat命令的使用。
rmmod kobject_led
|
