20150225 IMX257设备驱动模型之Kset
2015-02-25 17:00 李海沿
前面我们实现了一个简单的kobject模型,结果就是成功的在 sysfs的/sys/目录下创建一个kobject的目录,目录下面有attr属性文件,很简单,对kobject的操作其实就是把那四个结构体搞懂就习惯了。博文参考地址:http://www.cnblogs.com/lihaiyan/p/4299390.html
下面我们来实现设备驱动模型的kset,kset其实和kobject 是差不多的,唯一的一个驱动就是kset目录下面可以包含kobject目录,而kobject目录下面不能包含目录,只能包含属性文件,为了更好理解,可以说kobject就是kset的简化版。
好了,下面我们正式开始来实现设备驱动模型kset。
一、kset介绍
1. kset结构体
Struct kset{ Struct list_head list; //连接所包含的kobject对象的链表首部 Spinlock_t list_lock; //维护list链表的自旋锁 Struct kobject kobj; //内嵌的kobject结构体,说明kset本身也是一个目录 Struct kset_uevent_ops *uevent_ops; //热插拔事件 } |
从上面的结构体可以得知:
kset 包含kobject 和 一个kset_uevent_ops 热插拔事件结构体
2. 热插拔事件结构体 kset_uevent_ops
热插拔事件意思就是当kset目录下有任何变动,包括目录的移动,增加目录或者属性文件等操作。
当系统配置发生变化时,如添加kset到系统或移动kobject,一个通知会从内核空间发送到用户空间,这就是热插拔事件。热插拔事件会导致用户空间中的处理程序(如udev,mdev)被调用,这些处理程序会通过加载驱动程序,创建设备节点等来响应热插拔事件。
结构体定义为:
struct kset_uevnt_ops{ int (*filter)(struct kset *kset,struct kobject *kobj); const char *(*name)(struct kset *kset, struct kobject *kobj ); int (*uevent)(struct kset *kset,struct kobject *kobj,struct kobj_uevent *env); } |
功能:
filter 过滤事件,决定是否产生事件,如果返回0,将不产生事件。
name向用户空间传递一个合适的字符串
uevent通过环境变量传递任何热插拔脚本需要的信息,他会在(udev或mdev)调用之前,提供添加环境变量的机会。
3. kset操作函数
void kset_init(struct kset *kset); int kset_add(struct kset *kset); int kset_register(struct kset *kset); void kset_unregister(struct kset *kset); |
二、程序分析
1. 定义kset结构体
2. 定义热插拔事件结构体
函数实现:
3. 在init初始化函数中
如图所示,
第45行: kobject_set_name 是设定kset中的kobject的名字为kset_p
第46行: 关联kset与热插拔时间处理函数
第47行: 注册kset, 此时会在sys目录下创建一个名字为kobject的名字的文件夹
第50行: 设置kset_p的父目录(父kset)为kset_p(注意这一行)
4. 在exit函数中,卸载kset
5.总体流程分析
1.kset_test_init初始化kset
2.kobject_set_name(&kset_p.kobj,"kset_p");
3.创建一个struct kset kset_p,目录名字是kset_p
4.注册kset_register(&kset_p);
5.添加一个目录kset_p
6.kobject_set_name(&kset_c.kobj,"kset_c");
7.创建一个struct kset kset_c,目录名字是kset_c
8.kset_c.kobj.kset = &kset_p; kset_c的父目录是kset_p,也就是讲kset_p下有一个目录kset_c
9.注册kset_register(&kset_c);
10.添加一个目录kset_c
11.当在kset_p下增加一个kset_c目录,发生热插拔事件,调用uevent_ops
12.kset_filter被调用,返回0,不传递事件,返回1,传递
13.kset_name打印名字
14.kset_uevent打印相关的信息
6.编译测试
如图所示:
在 sys/目录下 ,产生一个 kset_p 的目录,子目录下还有一个kset_c
然后再看打印信息,可以发现分别调用调用了热插拔时间的三个函数。
注意 ACTION后面 为add,意思就是添加了一个kset目录
目录的路径为DEVPATH = /kset_p/kset_c
我们再观察卸载驱动时的变化
可以发现,前面ACTION = remove 意思是移除一个kset目录
7.总结
kset和kobject的不同之处就是kset下面可以再次包含kset目录,kobject下只能包含属性文件,
创建kset目录时,首先第一步是定义kset结构体,第二步是实现热插拔的结构体及其相应的函数与,第三步就是在初始化函数中对kset的kobject命名,关联热插拔事件,最后就是注册kset,第四步是当我们不用kset时自然就是卸载kset了。很简单吧。
搞定这个了,下一步我们就开始 总线-设备-驱动模型 之旅了,
附上驱动程序:
1 #include <linux/kernel.h> 2 #include <linux/init.h> 3 #include <linux/string.h> 4 #include <linux/sysfs.h> 5 #include <linux/stat.h> 6 #include <linux/kobject.h> 7 #include <linux/module.h> 8 #include <linux/device.h> 9 10 //定义两个kset结构体 11 struct kset kset_p; 12 struct kset kset_c; 13 14 static int kset_filter(struct kset *kset,struct kobject *kobj){ 15 printk("Filter: kobj %s. ",kobj->name); 16 return 1; 17 } 18 19 const char *kset_name(struct kset *kset, struct kobject *kobj){ 20 static char buf[20]; 21 printk("Name: kobj %s. ",kobj->name); 22 return buf; 23 } 24 25 static int kset_uevent(struct kset *kset, struct kobject *kobj,struct kobj_uevent_env *env){ 26 int i = 0; 27 printk("uevent : kobj %s. ",kobj->name); 28 29 while(i < env->envp_idx){ //打印相关的信息 30 printk("%s . ",env->envp[i]); 31 i++; 32 } 33 return 0; 34 } 35 36 //定义kset发生热插拔(目录下有任何变动)时,调用的函数 37 struct kset_uevent_ops uevent_ops = { 38 .filter = kset_filter, 39 .name = kset_name, 40 .uevent = kset_uevent, 41 }; 42 43 static int kset_test1_init(void){ 44 printk("<0>kset test init. "); 45 kobject_set_name(&kset_p.kobj,"kset_p"); 46 kset_p.uevent_ops = &uevent_ops; //关联热插拔事件处理函数 47 kset_register(&kset_p); //注册 48 49 kobject_set_name(&kset_c.kobj,"kset_c"); 50 kset_c.kobj.kset = &kset_p; //执行kset_c的父目录为kset_p 51 kset_register(&kset_c); //注册 52 53 return 0; 54 } 55 56 static int kset_test1_exit(void){ 57 printk("<0>kset test exit. "); 58 kset_unregister(&kset_p); //kset卸载 59 kset_unregister(&kset_c); 60 return 0; 61 } 62 63 module_init(kset_test1_init); 64 module_exit(kset_test1_exit); 65 66 MODULE_AUTHOR("Lover雪儿"); 67 MODULE_LICENSE("Dual BSD/GPL");