Linux设备模型的核心是使用Bus、Class、Device、Driver 四个核心数据结构,将大量的、不同功能的硬件设备(以及驱动该硬件设备的方法),以树状结构的形式,进行归纳、抽象,从而方便Kernel的统一管理。学习平台mt8768,内核版本kernel-4.9
一、kobject
kobject 它是构建设备模型的根基,它使设备模型下能在 /sys/ 下以目录层次的形式呈现 、 动态管理着所属对象的生命周期 ,以及提供了与用户空间进行信息交互的属性文件(attribute) . 文章将着重这几个方面进行描述.
温馨提示 本文的目录 和文件 是两个不同的概念,别搞混了。
目录 : 可以打开里面可以存放目录和文件
文件 : 不可以打开只能读写
1、相关数据结构
1) kobject
核心结构kobject,牢牢记住每一个注册到内核的 kobject 就是一个目录
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 struct kobject { const char *name; struct list_head entry ; struct kobject *parent ; struct kset *kset ; struct kobj_type *ktype ; struct sysfs_dirent *sd ; struct kref kref ; 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 ; };
2) kobj_type
用户空间和内核进行交互的窗口之一 ,通过这个结构中包含的属性链表指向的属性文件和属性操作函数进行交互。
1 2 3 4 5 6 7 8 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); };
3) attribute
牢牢记住,每一个注册到内核的 attribute 都是它所属的 kobj 目录下的一个属性文件 。
1 2 3 4 5 6 7 8 9 10 11 --include/linux/sysfs.h struct attribute { const char *name; struct module *owner ; mode_t mode; };
内核定义的读写权限
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 #define S_IRWXU 00700 #define S_IRUSR 00400 #define S_IWUSR 00200 #define S_IXUSR 00100 #define S_IRWXG 00070 #define S_IRGRP 00040 #define S_IWGRP 00020 #define S_IXGRP 00010 #define S_IRWXO 00007 #define S_IROTH 00004 #define S_IWOTH 00002 #define S_IXOTH 00001 #define S_IRWXUGO (S_IRWXU|S_IRWXG|S_IRWXO) #define S_IALLUGO (S_ISUID|S_ISGID|S_ISVTX|S_IRWXUGO) #define S_IRUGO (S_IRUSR|S_IRGRP|S_IROTH) #define S_IWUGO (S_IWUSR|S_IWGRP|S_IWOTH) #define S_IXUGO (S_IXUSR|S_IXGRP|S_IXOTH)
注:kobject是目录,attribute是文件。
4) sysfs_ops
属性文件操作函数,当 cat 属性文件时,会调用 kobj->ktype->sysfs_ops->show
,当 echo 属性文件时调用 kobj->ktype->sysfs_ops->store
函数
1 2 3 4 5 6 --include/linux/sysfs.h struct sysfs_ops { ssize_t (*show)(struct kobject *, struct attribute *,char *); ssize_t (*store)(struct kobject *,struct attribute *,const char *, size_t ); };
2、kobject 的创建与初始化
1) kobject_create
该函数动态申请一个 kobject 结构,然后调用 kobject_init 对内部成员进行初始化,并且使用 dynamic_kobj_ktype 作为默认的 ktype
1 2 3 4 5 6 7 8 9 10 11 12 13 14 kernel-4.9 /lib/kobject.c struct kobject *kobject_create (void ) { struct kobject *kobj ; kobj = kzalloc(sizeof (*kobj), GFP_KERNEL); if (!kobj) return NULL ; kobject_init(kobj, &dynamic_kobj_ktype); return kobj; }
2) kobject_init
该函数初始化 kobj 的 ktype 以及内部成员
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 void kobject_init (struct kobject *kobj, struct kobj_type *ktype) { char *err_str; if (!kobj) { err_str = "invalid kobject pointer!" ; goto error; } if (!ktype) { err_str = "must have a ktype to be initialized properly!\n" ; goto error; } if (kobj->state_initialized) { printk(KERN_ERR "kobject (%p): tried to init an initialized " "object, something is seriously wrong.\n" , kobj); dump_stack(); } kobject_init_internal(kobj); kobj->ktype = ktype; return ; error: printk(KERN_ERR "kobject (%p): %s\n" , kobj, err_str); dump_stack(); }
3) kobject_init_internal
该函数初始化引用计数,entry 链表以及状态位。
1 2 3 4 5 6 7 8 9 10 11 static void kobject_init_internal (struct kobject *kobj) { if (!kobj) return ; kref_init(&kobj->kref); INIT_LIST_HEAD(&kobj->entry); kobj->state_in_sysfs = 0 ; kobj->state_add_uevent_sent = 0 ; kobj->state_remove_uevent_sent = 0 ; kobj->state_initialized = 1 ; }
kobject的创建与初始化基本也就反复用这三个接口了。
3、kobject 的注册
1) kobject_add
设置 kobj 的 name 以及 parent 并将 kobject 注册进入内核
1 2 3 4 5 6 7 8 9 10 11 12 int kobject_add (struct kobject *kobj, struct kobject *parent, const char *fmt, va_list vargs) { int retval; retval = kobject_set_name_vargs(kobj, fmt, vargs); if (retval) { printk(KERN_ERR "kobject: can not set name properly!\n" ); return retval; } kobj->parent = parent; return kobject_add_internal(kobj); }
2) kobject_add_internal
将 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 static int kobject_add_internal (struct kobject *kobj) { int error = 0 ; struct kobject *parent ; if (!kobj) return -ENOENT; if (!kobj->name || !kobj->name[0 ]) { WARN(1 , "kobject: (%p): attempted to be registered with empty " "name!\n" , kobj); return -EINVAL; } parent = kobject_get(kobj->parent); if (kobj->kset) { if (!parent) parent = kobject_get(&kobj->kset->kobj); kobj_kset_kobj_kset_leavejoin(kobj); kobj->parent = parent; } pr_debug("kobject: '%s' (%p): %s: parent: '%s', set: '%s'\n" , kobject_name(kobj), kobj, __func__, parent ? kobject_name(parent) : "<NULL>" , kobj->kset ? kobject_name(&kobj->kset->kobj) : "<NULL>" ); error = create_dir(kobj); if (error) { kobj_kset_leave(kobj); kobject_put(parent); kobj->parent = NULL ; if (error == -EEXIST) pr_err("%s failed for %s with -EEXIST, don't try to register things with the same name in the same directory.\n" , __func__, kobject_name(kobj)); else pr_err("%s failed for %s (error: %d parent: %s)\n" , __func__, kobject_name(kobj), error, parent ? kobject_name(parent) : "'none'" ); } else kobj->state_in_sysfs = 1 ; return error; }
为了方便理解我在这里附上 create_dir 、 sysfs_create_dir_ns 以及 populate_dir 的源码
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 static int create_dir (struct kobject *kobj) { const struct kobj_ns_type_operations *ops ; int error; error = sysfs_create_dir_ns(kobj, kobject_namespace(kobj)); if (error) return error; error = populate_dir(kobj); if (error) { sysfs_remove_dir(kobj); return error; } return 0 ; } int sysfs_create_dir_ns (struct kobject *kobj, const void *ns) { struct kernfs_node *parent , *kn ; BUG_ON(!kobj); if (kobj->parent) parent = kobj->parent->sd; else parent = sysfs_root_kn; if (!parent) return -ENOENT; kn = kernfs_create_dir_ns(parent, kobject_name(kobj), S_IRWXU | S_IRUGO | S_IXUGO, kobj, ns); if (IS_ERR(kn)) { if (PTR_ERR(kn) == -EEXIST) sysfs_warn_dup(parent, kobject_name(kobj)); return PTR_ERR(kn); } kobj->sd = kn; return 0 ; } static int populate_dir (struct kobject *kobj) { struct kobj_type *t = get_ktype(kobj); struct attribute *attr ; int error = 0 ; int i; if (t && t->default_attrs) { for (i = 0 ; (attr = t->default_attrs[i]) != NULL ; i++) { error = sysfs_create_file(kobj, attr); if (error) break ; } } return error; }
kobjcet的注册主要完成了下面三件事情
判断父节点是否存在,如果存在增加父节点引用计数,判断是否存在 kset 如果存在则链接进 kset ,如果 kset 存在且父节点不存在则使用 Kset->kobj 作为父节点 ,增加 kset 点引用计数
调用 create_dir 为 kobj 创建目录和属性文件,在 create_dir 中调用 sysfs_create_dir_ns 为 kobject 创建目录,创建时会判断如果父节点为 NULL 则使用 sysfs_root_kn 作为父节点 ,即直接在 sys/ 目录下创建当前目录,在 create_dir 中调用 populate_dir 遍历属性文件链表创建默认属性文件
创建成功则设置 state_in_sysfs 为 1
内核也提供了一些组合API
1 2 3 4 5 struct kobject *kobject_create_and_add (const char *name, struct kobject *parent) int kobject_init_and_add (struct kobject *kobj, struct kobj_type *ktype, struct kobject *parent, const char *fmt, ...)
上面的 api 这么多,可以根据需要灵活选择来创建并注册 kobj,我也总结了两条很简单的规则:
如果你的 kobject 不需要嵌入到更大的数据结构 则使用kobject_create_and_add
反之如果你的 kobject 需要嵌入到更大的数据结 构则使用kobject_init_and_add
为什么这么选择呢这涉及到后文提到的对对象生命周期管理的内容,这里只需记住这两条规则就行了。
3) 编程实验 1
实验很简单,我们只需要再内核中创建一个名为 my_kobject 的目录,并不需要将 kobject 嵌入到其他数据结构因此选择使用 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 23 24 25 26 27 28 29 #include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> #include <linux/string.h> #include <linux/sysfs.h> #include <linux/stat.h> #include <linux/slab.h> #include <linux/kobject.h> static struct kobject *my_kobj ;static int my_kobject_init (void ) { my_kobj = kobject_create_and_add("my_kobject" , NULL ); return 0 ; } static void my_kobject_exit (void ) { kobject_del(my_kobj); kfree(my_kobj); } module_init(my_kobject_init); module_exit(my_kobject_exit); MODULE_AUTHOR("baron" ); MODULE_LICENSE("GPL" );
验证结果
1 2 3 4 5 6 7 8 9 10 k85v1_64:/cache # ls /sys/ block bootinfo bus class dev devices firmware fs kernel module mtk_rgu power k85v1_64 : /cache #k85v1_64:/cache # insmod my_kobject.ko k85v1_64:/cache # ls /sys/ block bootinfo bus class dev devices firmware fs kernel module mtk_rgu my_kobject power //加载后生成 my_kobject 目录 k85v1_64 : /cache #k85v1_64:/cache # rmmod my_kobject.ko k85v1_64:/cache # ls /sys/ block bootinfo bus class dev devices firmware fs kernel module mtk_rgu power //卸载后移除 my_kobject 目录
上面这种方式内核也用的挺多的,例如我们熟悉的 /sys/dev 、/sys/dev/char 、/sys/dev/block 等都是用这个方式创建的。
4) 编程实验 2
在sys/下创建一个叫做 my_dir 的目录,这里我们将 kobject 嵌入到我们自己创建的结构中,于是选择 kobject_init_and_add 来创建目录。
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 #include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> #include <linux/string.h> #include <linux/sysfs.h> #include <linux/stat.h> #include <linux/slab.h> #include <linux/kobject.h> struct my_dir { char * name; int data; struct kobject kobj ; }; struct my_dir* my_dir_create (char * name) { struct my_dir * my_dirp ; my_dirp = kzalloc(sizeof (*my_dirp), GFP_KERNEL); if (!my_dirp) return NULL ; my_dirp->name = name; return my_dirp; } static struct my_dir * my_dirp ;static int my_dir_init (void ) { my_dirp = my_dir_create("my_dir" ); kobject_init_and_add(&my_dirp->kobj, NULL , NULL , "%s" , my_dirp->name); return 0 ; } static void my_dir_exit (void ) { kobject_del(&my_dirp->kobj); kfree(my_dirp); } module_init(my_dir_init); module_exit(my_dir_exit); MODULE_AUTHOR("baron" ); MODULE_LICENSE("GPL" );
验证结果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 k85v1_64:/ # ls sys/ block/ bus/ dev/ firmware/ kernel/ mtk_rgu/ bootinfo/ class / devices / fs / module / power / k85v1_64 : / # ls sys/ block bootinfo bus class dev devices firmware fs kernel module mtk_rgu power k85v1_64 : / #k85v1_64:/ # cd cache/ k85v1_64:/cache # insmod my_kobject.ko k85v1_64:/cache # ls /sys/ block bootinfo bus class dev devices firmware fs kernel module mtk_rgu my_dir power //加载后生成 my_dir 目录 k85v1_64 : /cache # 2 |k85v1_64:/cache # cd /sys/my_dir/ k85v1_64:/sys/my_dir # ls k85v1_64:/sys/my_dir # k85v1_64:/sys/my_dir # cd .. k85v1_64:/sys # rmmod my_kobject.ko k85v1_64:/sys # ls block bootinfo bus class dev devices firmware fs kernel module mtk_rgu power //卸载后移除 my_dir 目录
看起来这种方式更加复杂,但实际上我们的 bus、 device、 device_driver 等都是使用这个方式,使用这个方式的优点见后文 “对象生命周期管理” 以及 “用户空间与内核信息交互” 。
4、在 sys/ 下组织出目录层次
object 的核心功能之一,利用 kobject.parent 组织出文件的目录层次,前面 kobject 的注册已经分析的很清楚了这里就不再赘述了,内核还提供了链接文件的创建接口。
1 2 int __must_check sysfs_create_link (struct kobject *kobj, struct kobject *target, const char *name) ;
编程实验
这个实验很简单,在/sys/目录下创建一个目录 father 然后在这个目录下创建两个子文件 son1 和 son2,再在 son1 下创建一个链接到 son2 的链接文件 link_to_son2。 只是单纯的展示层次目录关系,因此无需将kobject嵌入到更大的数据结构,采用 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 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 #include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> #include <linux/string.h> #include <linux/sysfs.h> #include <linux/stat.h> #include <linux/slab.h> #include <linux/kobject.h> MODULE_AUTHOR("baron" ); MODULE_LICENSE("GPL" ); static struct kobject * father ;static struct kobject * son1 ;static struct kobject * son2 ;static int my_kobject_init (void ) { father = kobject_create_and_add("father" , NULL ); son1 = kobject_create_and_add("son1" , father); son2 = kobject_create_and_add("son2" , father); sysfs_create_link(son1, son2, "link_to_son2" ); return 0 ; } static void my_kobject_exit (void ) { kobject_del(father); kfree(father); kobject_del(son1); kfree(son1); kobject_del(son2); kfree(son2); } module_init(my_kobject_init); module_exit(my_kobject_exit);
验证结果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 k85v1_64:/cache # insmod my_kobject.ko k85v1_64:/cache # k85v1_64:/cache # cd /sys/ k85v1_64:/sys # ls block bootinfo bus class dev devices father firmware fs kernel module mtk_rgu power //创建出的 father k85v1_64 : /sys # cd father/ k85v1_64:/sys/father # ls son1 son2 k85v1_64:/sys/father # cd son1/ k85v1_64:/sys/father/son1 # ls link_to_son2 k85v1_64:/sys/father/son1 # ls -la total 0 drwxr-xr-x 2 root root 0 2021 -01 -11 06 :40 . drwxr-xr-x 4 root root 0 2021 -01 -11 06 :40 .. lrwxrwxrwx 1 root root 0 2021 -01 -11 06 :41 link_to_son2 -> ../son2 k85v1_64:/sys/father/son1 #
5、kobj 对象生命周期管理
kobject 还有一个非常强大的功能就是管理所嵌入的对象的生命周期 ,而<color=brown>引用计数 kref 则是它管理所嵌入对象生命周期的核心。对于kerf内核提供了两个下面函数来进行操作。
1) kobject_get
增加 kobj 引用计数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 struct kobject *kobject_get (struct kobject *kobj) { if (kobj) { if (!kobj->state_initialized) WARN(1 , KERN_WARNING "kobject: '%s' (%p): is not " "initialized, yet kobject_get() is being " "called.\n" , kobject_name(kobj), kobj); kref_get(&kobj->kref); } return kobj; }
2) kobject_put
1 2 3 4 5 6 7 8 9 10 void kobject_put (struct kobject *kobj) { if (kobj) { if (!kobj->state_initialized) WARN(1 , KERN_WARNING "kobject: '%s' (%p): is not " "initialized, yet kobject_put() is being " "called.\n" , kobject_name(kobj), kobj); kref_put(&kobj->kref, kobject_release); } }
调用 kref_put 减少引用计数,同时传入回调函数 kobject_release,该回调函数在引用计数为0时调用。
1 2 3 4 5 6 7 8 9 10 11 int kref_put (struct kref *kref, void (*release)(struct kref *kref)) { WARN_ON(release == NULL ); WARN_ON(release == (void (*)(struct kref *))kfree); if (atomic_dec_and_test(&kref->refcount)) { release(kref); return 1 ; } return 0 ; }
减少引用计数,当 kref 为 0 时调用传入的 release 回调函数,即前面的 kobject_release 函数
1 2 3 4 static void kobject_release (struct kref *kref) { kobject_cleanup(container_of(kref, struct kobject, kref)); }
kobject_put 传入的回调函数,使用 container_of 函数获取到包含 kref 的 kobjec 结构地址,并传入 kobject_cleanup
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 static void kobject_cleanup (struct kobject *kobj) { struct kobj_type *t = get_ktype(kobj); const char *name = kobj->name; pr_debug("kobject: '%s' (%p): %s, parent %p\n" , kobject_name(kobj), kobj, __func__, kobj->parent); if (t && !t->release) pr_debug("kobject: '%s' (%p): does not have a release() " "function, it is broken and must be fixed.\n" , kobject_name(kobj), kobj); if (kobj->state_add_uevent_sent && !kobj->state_remove_uevent_sent) { pr_debug("kobject: '%s' (%p): auto cleanup 'remove' event\n" , kobject_name(kobj), kobj); kobject_uevent(kobj, KOBJ_REMOVE); } if (kobj->state_in_sysfs) { pr_debug("kobject: '%s' (%p): auto cleanup kobject_del\n" , kobject_name(kobj), kobj); kobject_del(kobj); } if (t && t->release) { pr_debug("kobject: '%s' (%p): calling ktype release\n" , kobject_name(kobj), kobj); t->release(kobj); } if (name) { pr_debug("kobject: '%s': free name\n" , name); kfree_const(name); } }
判断 kobj 的 release 函数是否存在,如果在 sys 中存在 kobj 则调用 kobject_del 删除 kobj, 如果 release 存在则调用 release 函数,该函数需要自己实现,如果是用 kobject_create 创建的 kobj,则会使用默认 dynamic_kobj_ktype 中的 release。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 void kobject_del (struct kobject *kobj) { struct kernfs_node *sd ; if (!kobj) return ; sd = kobj->sd; sysfs_remove_dir(kobj); sysfs_put(sd); kobj->state_in_sysfs = 0 ; kobj_kset_leave(kobj); kobject_put(kobj->parent); kobj->parent = NULL ; }
删除sys目录相关文件,减少parent引用计数,并调用 kobj_kset_leave函数从 kset list 中移除这个kobject
1 2 3 4 5 6 7 8 9 10 11 static void kobj_kset_leave (struct kobject *kobj) { if (!kobj->kset) return ; spin_lock(&kobj->kset->list_lock); list_del_init(&kobj->entry); spin_unlock(&kobj->kset->list_lock); kset_put(kobj->kset); }
可以看到 kobject_put 的实现比较复杂,但总的来说它也就完成了下面几件事情
减少 kobject 引用计数,当 kobject 引用计数为 0 时调用 kobject->ktype->release
函数 。
向用户空间发送 uevent 事件 KOBJ_REMOVE
调用 kobject_del 删除 sys 目录相关文件,从属于的 kset 链表中删除该 kobj 成员,减少 kset 引用计数,减少 parent 的引用计数(这里分别减少了paren t的和所属的 kset 的引用计数)
其中我们需要实现的也就是这个回调函数 kobject->ktype->release ,但实际上内核提供的设备模型已经都实现好了,例如 bus 总线
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 static void bus_release (struct kobject *kobj) { struct subsys_private *priv = to_subsys_private(kobj); struct bus_type *bus = priv->bus; kfree(priv); bus->p = NULL ; } static struct kobj_type bus_ktype = { .sysfs_ops = &bus_sysfs_ops, .release = bus_release, }; int bus_register (struct bus_type *bus) { ... priv->subsys.kobj.ktype = &bus_ktype; ... }
其他的 device,device_driver,等凡是内核提供的结构基本内核都帮我们设计好了它的 relase 函数
3) 优化 my_dir
有了上面知识了之后我们也可以用其来优化我们前面创建的 my_dir, 给我们的 my_dir 增加自动释放自身数据结构的功能。
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 #include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> #include <linux/string.h> #include <linux/sysfs.h> #include <linux/stat.h> #include <linux/slab.h> #include <linux/kobject.h> struct my_dir { char * name; int data; struct kobject kobj ; }; struct my_dir* my_dir_create (char * name) { struct my_dir * my_dirp ; my_dirp = kzalloc(sizeof (*my_dirp), GFP_KERNEL); if (!my_dirp) return NULL ; my_dirp->name = name; return my_dirp; } static struct my_dir * my_dirp ;void my_dir_release (struct kobject *kobj) { struct my_dir * my_dirp = container_of(kobj, struct my_dir, kobj); printk("my dir release\n" ); kfree(my_dirp); } struct kobj_type my_dir_type = { .release = my_dir_release, }; static int my_dir_init (void ) { my_dirp = my_dir_create("my_dir" ); kobject_init_and_add(&my_dirp->kobj, &my_dir_type, NULL , "%s" , my_dirp->name); return 0 ; } static void my_dir_exit (void ) { kobject_put(&my_dirp->kobj); } module_init(my_dir_init); module_exit(my_dir_exit); MODULE_AUTHOR("baron" ); MODULE_LICENSE("GPL" );
验证结果
1 2 3 4 5 k85v1_64:/cache # insmod my_kobject.ko k85v1_64:/cache # ls /sys/ block bootinfo bus class dev devices firmware fs kernel module mtk_rgu my_dir power //这里创建出 my_dir k85v1_64 : /cache # rmmod my_kobject.ko
此同时内核打印出下面 log
1 [ 52.897412 ] <7 >.(7 )[2526 :rmmod] my dir release
在查看时发现目录已经被删除
1 2 3 k85v1_64:/cache # ls /sys/ block bootinfo bus class dev devices firmware fs kernel module mtk_rgu power //mydir 被移除 k85v1_64 : /cache #
这里用了一个非常巧妙的方式实现了,利用 kobject 释放其所嵌入的更大的数据结构的功能,从而实现对 my_dir 生命周期的管理,于是可以得到下面这个结论: 凡是需要做对象生命周期管理的对象,都可以通过内嵌kobject来实现需求 该结论来自窝窝科技的文章
6、用户空间与内核信息交互
kobject 的核心功能之一, 能实现用户空间与内核空间的信息交互 ,该功能非常, 非常, 非常重要, 是驱动开发中最常用的手段之一. 我们知道每一个注册的 kobject 都会在 /sys 中以目录的形式呈现,也就是 bus 等数据结构可以利用嵌入 kobject 可以使它显示在 /sys 中。而 attribute 又会以文件的形式出现在目录中, 通过这些属性文件, 我们就能够获取/修改内核中的变量,字符串等信息 。在Linux内核中,attribute 分为普通的 attribute 和二进制 attribute,这里只记录普通的,二进制的没研究,后面有机会研究了再补充(希望渺茫).
1) 属性文件调用逻辑
在 fs/sysfs/file.c 文件下查看相关的逻辑代码
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 static const struct sysfs_ops *sysfs_file_ops (struct kernfs_node *kn) { struct kobject *kobj = kn->parent->priv; if (kn->flags & KERNFS_LOCKDEP) lockdep_assert_held(kn); return kobj->ktype ? kobj->ktype->sysfs_ops : NULL ; } static ssize_t sysfs_kf_read (struct kernfs_open_file *of, char *buf, size_t count, loff_t pos) { const struct sysfs_ops *ops = sysfs_file_ops(of->kn); struct kobject *kobj = of->kn->parent->priv; ssize_t len; ... len = ops->show(kobj, of->kn->priv, buf); ... return min_t (ssize_t , count, len); } static ssize_t sysfs_kf_write (struct kernfs_open_file *of, char *buf, size_t count, loff_t pos) { const struct sysfs_ops *ops = sysfs_file_ops(of->kn); struct kobject *kobj = of->kn->parent->priv; if (!count) return 0 ; return ops->store(kobj, of->kn->priv, buf, count); }
处理过程很简单在 cat/echo 属性文件时(读/写属性文件写数据时)
1, 先调用 sysfs_file_ops 获取到kobj->ktype->sysfs_ops 指针
2, 调用对应内核的 show/store 函数。
从这里可以得出两条结论:
对于用户空间来讲,只负责把数据丢给内核的 store 以及从内核的 show 函数获取数据 ,至于 store 的数据用来做什么和 show 获取到数据表示什么意思则由内核决定。
如果从属的 kobject(就是 attribute 文件所在的目录)没有 ktype,或者没有 ktype->sysfs_ops 指针,是不允许它注册任何 attribute 的
2) 属性文件的创建以及删除
内核也提供了创建属性文件的 api
1 2 3 4 5 6 7 8 include/linux/sysfs.h int sysfs_create_file (struct kobject *kobj, struct attribute *attr) ; static inline void sysfs_remove_file (struct kobject *kobj, const struct attribute *attr) int sysfs_create_files (struct kobject *kobj, const struct attribute **ptr) void sysfs_remove_files (struct kobject *kobj, const struct attribute **attr) ;
3) 在 my_dir 下创建属性文件
动手实践一下,在 my_dir 下创建两个属性文件
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 #include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> #include <linux/string.h> #include <linux/sysfs.h> #include <linux/stat.h> #include <linux/slab.h> #include <linux/kobject.h> struct my_dir { char * name; int data; struct kobject kobj ; }; struct my_dir* my_dir_create (char * name) { struct my_dir * my_dirp ; my_dirp = kzalloc(sizeof (*my_dirp), GFP_KERNEL); if (!my_dirp) return NULL ; my_dirp->name = name; return my_dirp; } static struct my_dir * my_dirp ;void my_dir_release (struct kobject *kobj) { struct my_dir * my_dirp = container_of(kobj, struct my_dir, kobj); printk("my dir release\n" ); kfree(my_dirp); } ssize_t my_dir_show (struct kobject *kobj, struct attribute *attr,char *buf) { printk("my dir show attr->name : %s\n" , attr->name); sprintf (buf, "%s\n" , attr->name); return strlen ((char *)attr->name) +2 ; } ssize_t my_dir_store (struct kobject *kobj,struct attribute *attr,const char *buf, size_t count) { printk("my dir store : %s\n" , buf); return count; } struct sysfs_ops my_sysfs_ops = { .show = my_dir_show, .store = my_dir_store, }; struct kobj_type my_dir_type = { .release = my_dir_release, .sysfs_ops = &my_sysfs_ops, }; struct attribute my_dir_attr1 = { .name = "my_dir_attr1" , .mode = S_IRWXUGO, }; struct attribute my_dir_attr2 = { .name = "my_dir_attr2" , .mode = S_IRWXUGO, }; static int my_dir_init (void ) { my_dirp = my_dir_create("my_dir" ); kobject_init_and_add(&my_dirp->kobj, &my_dir_type, NULL , "%s" , my_dirp->name); sysfs_create_file(&my_dirp->kobj, &my_dir_attr1); sysfs_create_file(&my_dirp->kobj, &my_dir_attr2); return 0 ; } static void my_dir_exit (void ) { kobject_put(&my_dirp->kobj); } module_init(my_dir_init); module_exit(my_dir_exit); MODULE_AUTHOR("baron" ); MODULE_LICENSE("GPL" );
验证结果
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 k85v1_64:/cache # insmod my_kobject.ko k85v1_64:/cache # k85v1_64:/cache # cd /sys/ k85v1_64:/sys # ls block bootinfo bus class dev devices firmware fs kernel module mtk_rgu my_dir power //查看生成了 my_dir k85v1_64 : /sys # cd my_dir/ k85v1_64:/sys/my_dir # ls my_dir_attr1 my_dir_attr2 k85v1_64:/sys/my_dir # k85v1_64:/sys/my_dir # echo 123 > my_dir_attr1 [ 848.043148 ] <0 >.(0 )[5617 :sh]my dir store : 123 k85v1_64:/sys/my_dir # cat my_dir_attr1 my_dir_attr1 [ 1002.452204 ] <7 >.(7 )[8065 :cat]my dir show attr->name : my_dir_attr1 k85v1_64:/sys/my_dir # k85v1_64:/sys/my_dir # echo 222 > my_dir_attr2 [ 1028.696923 ] <1 >.(1 )[5617 :sh]my dir store : 222 k85v1_64:/sys/my_dir # cat my_dir_attr2 my_dir_attr2 [ 1033.116307 ] <7 >.(7 )[8397 :cat]my dir show attr->name : my_dir_attr2
4) 优化属性文件操作
上面的例子我们虽然创建了属性文件,也能操作属性文件,但是两个属性文件最终都是调用的同一个 show/store 函数,即同一个 kobject 下的所有的属性文件使用同一个属性操作函数 。而这样明显属性文件就失去了它的独立性。
怎么实现属性文件自己的 show/store ?
我们可以将 attribute 嵌入到更大的数据结构中,该数据结构包含真正的 show/store 函数然后使用 my_dir_type 中的 show/store 函数作为中转函数,利用 container_of 调用属性文件真正的 show/store 函数.
于是我们调整代码架构将通用的部分提取出来作为 my_kobject_core.c 这部分代码如下:
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 #include "my_kobject_core.h" void my_dir_release (struct kobject *kobj) { struct my_dir * my_dirp = container_of(kobj, struct my_dir, kobj); printk("my dir release\n" ); kfree(my_dirp); } ssize_t my_dir_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(kobj, my_attr, buf); return ret; } ssize_t my_dir_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(kobj, my_attr, buf, count); return ret; } struct sysfs_ops my_sysfs_ops = { .show = my_dir_show, .store = my_dir_store, }; struct kobj_type my_dir_type = { .release = my_dir_release, .sysfs_ops = &my_sysfs_ops, }; struct my_dir* my_dir_regiseter (char * name) { struct my_dir * my_dirp ; my_dirp = kzalloc(sizeof (*my_dirp), GFP_KERNEL); if (!my_dirp) return NULL ; my_dirp->name = name; kobject_init_and_add(&my_dirp->kobj, &my_dir_type, NULL , "%s" , my_dirp->name); return my_dirp; } EXPORT_SYMBOL_GPL(my_dir_regiseter); void my_dir_unregiseter (struct my_dir* my_dirp) { if (my_dirp) kobject_put(&my_dirp->kobj); } EXPORT_SYMBOL_GPL(my_dir_unregiseter); MODULE_AUTHOR("baron" ); MODULE_LICENSE("GPL" );
我们将公共的部分放在头文件 my_kobject_core.h 中
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 #ifndef _KOBJECT_CORE_H_ #define _KOBJECT_CORE_H_ #include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> #include <linux/string.h> #include <linux/sysfs.h> #include <linux/stat.h> #include <linux/slab.h> #include <linux/kobject.h> struct my_dir { char * name; int date; struct kobject kobj ; }; struct my_attribute { struct attribute attr ; ssize_t (*show)(struct kobject *kobj, struct my_attribute *attr, char *buf); ssize_t (*store)(struct kobject *kobj, struct my_attribute *attr, const char *buf, size_t count); }; extern struct my_dir* my_dir_regiseter (char * name) ;extern void my_dir_unregiseter (struct my_dir* my_dirp) ;#endif
在我们想要创建目录 的时候就调用 my_dir_regiseter
来创建 my_dir,想要创建属性文件 的时候调用 sysfs_create_file
来创建。 于是在我们真正在 my_kobject.c 中创建 my_dir 以及其属性文件如下。
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 #include "my_kobject_core.h" static struct my_dir * my_dirp ;ssize_t my_dir_attr1_show (struct kobject *kobj, struct my_attribute *attr, char *buf) { struct my_dir * my_dirp = container_of(kobj, struct my_dir, kobj); printk("%s\n" ,attr->attr.name); sprintf (buf, "%s : %d\n" , attr->attr.name, my_dirp->date); return strlen (buf); } ssize_t my_dir_attr1_store (struct kobject *kobj, struct my_attribute *attr, const char *buf, size_t count) { int i = 0 ; int tmp = 0 ; struct my_dir * my_dirp = container_of(kobj, struct my_dir, kobj); for (i=0 ; i<count; i++) { if (buf[i] > '0' && buf[i] < '9' ){ tmp = 10 *tmp + (buf[i] - '0' ); } } my_dirp->date = tmp; printk("%s store : my_dirp->date = %d, buf = %s\n" ,attr->attr.name, my_dirp->date, buf); return count; } ssize_t my_dir_attr2_show (struct kobject *kobj, struct my_attribute *attr, char *buf) { struct my_dir * my_dirp = container_of(kobj, struct my_dir, kobj); printk("%s\n" ,attr->attr.name); sprintf (buf, "%s : %d\n" , attr->attr.name, my_dirp->date); return strlen (buf); } ssize_t my_dir_attr2_store (struct kobject *kobj, struct my_attribute *attr, const char *buf, size_t count) { int i = 0 ; int tmp = 0 ; struct my_dir * my_dirp = container_of(kobj, struct my_dir, kobj); for (i=0 ; i<count; i++) { if (buf[i] > '0' && buf[i] < '9' ) tmp = 10 *tmp + (buf[i] - '0' ); } my_dirp->date = tmp; printk("%s store : my_dirp->date = %d, buf = %s\n" ,attr->attr.name, my_dirp->date, buf); return count; } struct my_attribute my_dir_attr1 = { .attr ={ .name = "my_dir_attr1" , .mode = S_IRWXUGO, }, .show = my_dir_attr1_show, .store = my_dir_attr1_store, }; struct my_attribute my_dir_attr2 = { .attr ={ .name = "my_dir_attr2" , .mode = S_IRWXUGO, }, .show = my_dir_attr2_show, .store = my_dir_attr2_store, }; static int my_dir_init (void ) { my_dirp = my_dir_regiseter("my_dir" ); sysfs_create_file(&my_dirp->kobj, &my_dir_attr1.attr); sysfs_create_file(&my_dirp->kobj, &my_dir_attr2.attr); return 0 ; } static void my_dir_exit (void ) { my_dir_unregiseter(my_dirp); } module_init(my_dir_init); module_exit(my_dir_exit); MODULE_AUTHOR("baron" ); MODULE_LICENSE("GPL" );
验证结果:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 k85v1_64:/cache # insmod my_kobject_core.ko k85v1_64:/cache # insmod my_kobject.ko k85v1_64:/cache # k85v1_64:/cache # k85v1_64:/cache # cd /sys/my_dir/ k85v1_64:/sys/my_dir # ls my_dir_attr1 my_dir_attr2 k85v1_64:/sys/my_dir # echo 11 > my_dir_attr1 [ 5223.566440 ] <2 >.(2 )[23263 :sh]my_dir_attr1 store : my_dirp->date = 11 , buf = 11 k85v1_64:/sys/my_dir # k85v1_64:/sys/my_dir # cat my_dir_attr1 my_dir_attr1 : 11 k85v1_64:/sys/my_dir # k85v1_64:/sys/my_dir # echo 22 > my_dir_attr2 [ 5248.493173 ] <3 >.(3 )[23263 :sh]my_dir_attr2 store : my_dirp->date = 22 , buf = 22 k85v1_64:/sys/my_dir # k85v1_64:/sys/my_dir # cat my_dir_attr2 my_dir_attr2 : 22 k85v1_64:/sys/my_dir #
上面的逻辑实现比前面的代码要复杂一点点,可以花点时间看一下,这个方式的牛逼之处在于通过 kobject 我们将我们自己创建的数据结构 struct my_dir 开放到用户空间,以目录的形式呈现出来,同时通过属性文件用户空间能够获取和修改 my_dir.date 这个属于my_dir的成员变量。内核的 bus、device、device_driver 等设备模型不过是在这个基础之上增加了一些其他功能,如设备和驱动的匹配等。
二 、kset
kset 本身包含 kobject 结构,因此拥有上述 kobject 的所有特性 ,除此之外, kset 具有管理 kobject 的功能 以及提供热插拔功能 . kset 的数据结构如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 struct kset { struct list_head list ; spinlock_t list_lock; struct kobject kobj ; const struct kset_uevent_ops *uevent_ops ; }
可以看出主要多出了两个数据结构 list 和 kset_uevent_ops,在前面的 kobject_add 分析中可以知道,只要是 kobject 属于某个 kset 那么都会被链接到所属的 kset 中的 list 链表。 也就是 kset 具有管理 kobject 的功能 ,举个栗子,例如: 当 kernel 关机时, 会在 device_shutdown 中利用 devices_kset->list 找到该链表上的所有设备,并做相应的操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 void device_shutdown (void ) { struct device *dev , *parent ; spin_lock(&devices_kset->list_lock); while (!list_empty(&devices_kset->list )) { dev = list_entry(devices_kset->list .prev, struct device, kobj.entry); ...... } spin_unlock(&devices_kset->list_lock); }
我们所熟知的热插拔机制的功能也是由 kset 提供的,热插拔指的是当一个设备加入系统,内核如何通知用户空间。感兴趣可以看看这篇文章:http://bbs.chinaunix.net/thread-3678367-1-1.html
热插拔大致的可以分为两个部分组成,内核部分和用户空间部分,而 kset则负责将事件发送到用户空间,而发送的方式则是 uevent。 大致的流程如下
外设接入设备,设备产生中断
内核响应中断,调用 device_add 添加新设备
在device_add中会调用 kobject_uevent 将事件通知给用户空间
由于热插拔的内容相对复杂可以单独成文,而且也不影响对设备模型的理解,因此这里只提一下它的功能,关于热插拔更加详细的内容后面认真学习之后再整理出一篇文章。
1、 kset的创建与注册
1) kset_create
动态获取一个 kset 内存空间,初始化 uevent_ops ,初始化 parent_kobj ,初始化一个内核默认的 kset_ktype ,初始化 kset 中的 kset 为NULL
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 static struct kset *kset_create (const char *name, const struct kset_uevent_ops *uevent_ops, struct kobject *parent_kobj) { struct kset *kset ; int retval; kset = kzalloc(sizeof (*kset), GFP_KERNEL); if (!kset) return NULL ; retval = kobject_set_name(&kset->kobj, "%s" , name); if (retval) { kfree(kset); return NULL ; } kset->uevent_ops = uevent_ops; kset->kobj.parent = parent_kobj; kset->kobj.ktype = &kset_ktype; kset->kobj.kset = NULL ; return kset; }
2) kset_init
初始化kset的kobj成员,初始化list链表
1 2 3 4 5 6 7 8 9 10 void kset_init (struct kset *k) { kobject_init_internal(&k->kobj); INIT_LIST_HEAD(&k->list ); spin_lock_init(&k->list_lock); }
3) kset_register
将 kset 注册进入内核
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 int kset_register (struct kset *k) { int err; if (!k) return -EINVAL; kset_init(k); err = kobject_add_internal(&k->kobj); if (err) return err; kobject_uevent(&k->kobj, KOBJ_ADD); return 0 ; }
3) kset_create_and_add
动态创建一个kset结构,并将其注册,其实就是 kset_create 和 kset_register 的组合
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 struct kset *kset_create_and_add (const char *name, const struct kset_uevent_ops *uevent_ops, struct kobject *parent_kobj) { struct kset *kset ; int error; kset = kset_create(name, uevent_ops, parent_kobj); if (!kset) return NULL ; error = kset_register(kset); if (error) { kfree(kset); return NULL ; } return kset; }
kset 的创建与注册的函数的选择和前面的kobject一样:
当 kset 需要嵌入到更大的数据结构时 使用 kset_register
当 kset 不需嵌入到更大的数据结构时 使用 kset_create_and_add
2、卸载kset
1) kset_unregister
从卸载一个keset
1 2 3 4 5 6 7 8 9 10 11 void kset_unregister (struct kset *k) { if (!k) return ; kobject_del(&k->kobj); kobject_put(&k->kobj); }
2) kset_put
减少kset的kobj成员引用计数
1 2 3 4 static inline void kset_put (struct kset *k) { kobject_put(&k->kobj); }
三、bus 总线
Linux 认为总线是CPU和一个或多个设备之间信息交互的通道 。就设备模型而言,所有设备都是通过总线连接的,这样的总线有两类 :
第一类是 i2c、spi、usb 等这类总线,这类总线有实际的物理总线 ,它本身就有总线这个概念,我们直接根据功能将其抽象出来成一个个单独的总线。
第二类就是虚拟总线,如i2c控制器、GPIO控制器、UART控制器等,cpu 直接通过寄存器或地址与之进行通信。但是呢我们又想将他们统一到设备模型,因此 platform 这类虚拟总线 就诞生了。
1、数据结构
1) bus_type
每一个 bus_type 都代表一个 bus
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 struct bus_type { const char *name; const char *dev_name; struct device *dev_root ; struct device_attribute *dev_attrs ; const struct attribute_group **bus_groups ; const struct attribute_group **dev_groups ; const struct attribute_group **drv_groups ; int (*match)(struct device *dev, struct device_driver *drv); int (*uevent)(struct device *dev, struct kobj_uevent_env *env); int (*probe)(struct device *dev); int (*remove)(struct device *dev); void (*shutdown)(struct device *dev); int (*online)(struct device *dev); int (*offline)(struct device *dev); int (*suspend)(struct device *dev, pm_message_t state); int (*resume)(struct device *dev); int (*num_vf)(struct device *dev); const struct dev_pm_ops *pm ; const struct iommu_ops *iommu_ops ; struct subsys_private *p ; struct lock_class_key lock_key ; };
2) subsys_private
这个结构被用在两个地方 class 和 bus,在不同的位置有不同的含义,当它出现在 bus下用它描述 bus 在 /sys 中的层次结构
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 struct subsys_private { struct kset subsys ; struct kset *devices_kset ; struct list_head interfaces ; struct mutex mutex ; struct kset *drivers_kset ; struct klist klist_devices ; struct klist klist_drivers ; struct blocking_notifier_head bus_notifier ; unsigned int drivers_autoprobe:1 ; struct bus_type *bus ; struct kset glue_dirs ; struct class *class ; };
2、bus 根目录的初始化
buses_init 在 driver_init 中被调用,用于初始化 bus 的根目录,以后所有的 bus 总线均为该目录的子目录。
1 2 3 4 5 6 7 8 9 10 11 12 int __init buses_init (void ) { bus_kset = kset_create_and_add("bus" , &bus_uevent_ops, NULL ); if (!bus_kset) return -ENOMEM; system_kset = kset_create_and_add("system" , NULL , &devices_kset->kobj); if (!system_kset) return -ENOMEM; return 0 ; }
buses_init 会创建以下目录
3、bus 总线的注册
该函数向内核注册一个 bus 总线
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 int bus_register (struct bus_type *bus) { int retval; struct subsys_private *priv ; struct lock_class_key *key = &bus->lock_key; priv = kzalloc(sizeof (struct subsys_private), GFP_KERNEL); if (!priv) return -ENOMEM; priv->bus = bus; bus->p = priv; BLOCKING_INIT_NOTIFIER_HEAD(&priv->bus_notifier); retval = kobject_set_name(&priv->subsys.kobj, "%s" , bus->name); if (retval) goto out; priv->subsys.kobj.kset = bus_kset; priv->subsys.kobj.ktype = &bus_ktype; priv->drivers_autoprobe = 1 ; retval = kset_register(&priv->subsys); if (retval) goto out; retval = bus_create_file(bus, &bus_attr_uevent); if (retval) goto bus_uevent_fail; priv->devices_kset = kset_create_and_add("devices" , NULL , &priv->subsys.kobj); if (!priv->devices_kset) { retval = -ENOMEM; goto bus_devices_fail; } priv->drivers_kset = kset_create_and_add("drivers" , NULL , &priv->subsys.kobj); if (!priv->drivers_kset) { retval = -ENOMEM; goto bus_drivers_fail; } INIT_LIST_HEAD(&priv->interfaces); __mutex_init(&priv->mutex, "subsys mutex" , key); klist_init(&priv->klist_devices, klist_devices_get, klist_devices_put); klist_init(&priv->klist_drivers, NULL , NULL ); retval = add_probe_files(bus); if (retval) goto bus_probe_files_fail; retval = bus_add_groups(bus, bus->bus_groups); if (retval) goto bus_groups_fail; pr_debug("bus: '%s': registered\n" , bus->name); return 0 ; bus_groups_fail: remove_probe_files(bus); bus_probe_files_fail: kset_unregister(bus->p->drivers_kset); bus_drivers_fail: kset_unregister(bus->p->devices_kset); bus_devices_fail: bus_remove_file(bus, &bus_attr_uevent); bus_uevent_fail: kset_unregister(&bus->p->subsys); out: kfree(bus->p); bus->p = NULL ; return retval; }
bus_register 会创建以下节点:
1 2 3 4 5 6 /sys/bus/xxx /sys/bus/xxx/uevent /sys/bus/xxx/devices ----- bus下注册的dev将出现在这个目录 /sys/bus/xxx/drivers ----- bus下注册的drv将出现在这个目录 /sys/bus/xxx/drivers_probe /sys/bus/xxx/drivers_autoprobe
bus_register 创建并初始化 priv->klist_devices 和 priv->klist_drivers 这两条由该总线维护的非常重要的两条链表,该总线下的 dev 和 drv 会分别链入这两条链表。
4、编程实验创建自己的总线
在内核中创建一个名为 my_bus 的总线
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 #include <linux/device.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> #include <linux/string.h> #include <linux/sysfs.h> #include <linux/stat.h> MODULE_AUTHOR("baron" ); MODULE_LICENSE("GPL" ); int my_bus_probe (struct device *dev) { printk("my_bus_probe\n" ); return 0 ; } int my_bus_match (struct device *dev, struct device_driver *drv) { printk("my_bus_match\n" ); return 0 ; } int my_bus_remove (struct device *dev) { printk("my_bus_remove\n" ); return 0 ; } struct bus_type my_bus = { .name = "my_bus" , .probe = my_bus_probe, .match = my_bus_match, .remove = my_bus_remove, }; static int my_bus_init (void ) { bus_register(&my_bus); return 0 ; } static void my_bus_exit (void ) { bus_unregister(&my_bus); } module_init(my_bus_init); module_exit(my_bus_exit);
模块验证
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 XF-E97:/sys/bus # ls amba container gpio iio scsi spi clockevents cpu hid mmc sdio usb clocksource event_source i2c platform snd_seq workqueue XF-E97:/sys/bus # XF-E97:/sys/bus # insmod /cache/my_bus.ko XF-E97:/sys/bus # XF-E97:/sys/bus # ls amba container gpio iio platform snd_seq workqueue clockevents cpu hid mmc scsi spi clocksource event_source i2c my_bus sdio usb XF-E97:/sys/bus # cd my_bus/ XF-E97:/sys/bus/my_bus # ls devices drivers drivers_autoprobe drivers_probe uevent XF-E97:/sys/bus/my_bus #
该模块创建下面节点
1 2 3 4 5 6 /sys/bus/my_bus /sys/bus/my_bus/uevent /sys/bus/my_bus/devices /sys/bus/my_bus/drivers /sys/bus/my_bus/drivers_probe /sys/bus/my_bus/drivers_autoprobe
4、创建属性文件
内核也提供了在 bus 总线下创建属性文件的接口
1 2 3 4 int bus_create_file (struct bus_type *bus, struct bus_attribute *attr) void bus_remove_file (struct bus_type *bus, struct bus_attribute *attr)
四、device 设备
device 用于抽象系统中所有的硬件设备,描述它的名字、属性、从属的 Bus、从属的 Class 等信息。
1、数据结构
1) device
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 struct device { struct device *parent ; struct device_private *p ; struct kobject kobj ; const char *init_name; const struct device_type *type ; struct mutex mutex ; struct bus_type *bus ; struct device_driver *driver ; void *platform_data; void *driver_data; struct dev_links_info links ; struct dev_pm_info power ; struct dev_pm_domain *pm_domain ; #ifdef CONFIG_GENERIC_MSI_IRQ_DOMAIN struct irq_domain *msi_domain ; #endif #ifdef CONFIG_PINCTRL struct dev_pin_info *pins ; #endif #ifdef CONFIG_GENERIC_MSI_IRQ struct list_head msi_list ; #endif #ifdef CONFIG_NUMA int numa_node; #endif const struct dma_map_ops *dma_ops ; u64 *dma_mask; u64 coherent_dma_mask; unsigned long dma_pfn_offset; struct device_dma_parameters *dma_parms ; struct list_head dma_pools ; struct dma_coherent_mem *dma_mem ; #ifdef CONFIG_DMA_CMA struct cma *cma_area ; #endif struct dev_archdata archdata ; struct device_node *of_node ; struct fwnode_handle *fwnode ; dev_t devt; u32 id; spinlock_t devres_lock; struct list_head devres_head ; struct klist_node knode_class ; struct class *class ; const struct attribute_group **groups ; void (*release)(struct device *dev); struct iommu_group *iommu_group ; struct iommu_fwspec *iommu_fwspec ; bool offline_disabled:1 ; bool offline:1 ; bool of_node_reused:1 ; };
2) device_private
1 2 3 4 5 6 7 8 struct device_private { struct klist klist_children ; struct klist_node knode_parent ; struct klist_node knode_driver ; struct klist_node knode_bus ; struct list_head deferred_probe ; struct device *device ; };
2、device 根目录的初始化
device文件目录初始化函数,在 driver_init 中被调用,内核初始化时被调用。
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 int __init devices_init (void ) { devices_kset = kset_create_and_add("devices" , &device_uevent_ops, NULL ); if (!devices_kset) return -ENOMEM; dev_kobj = kobject_create_and_add("dev" , NULL ); if (!dev_kobj) goto dev_kobj_err; sysfs_dev_block_kobj = kobject_create_and_add("block" , dev_kobj); if (!sysfs_dev_block_kobj) goto block_kobj_err; sysfs_dev_char_kobj = kobject_create_and_add("char" , dev_kobj); if (!sysfs_dev_char_kobj) goto char_kobj_err; return 0 ; char_kobj_err: kobject_put(sysfs_dev_block_kobj); block_kobj_err: kobject_put(dev_kobj); dev_kobj_err: kset_unregister(devices_kset); return -ENOMEM; }
devices_init 初始化了 device 的基本根目录,如下
1 2 3 4 /sys/devices /sys/dev /sys/dev/block /sys/dev/char
bus总线管理着该总线下的所有设备和驱动,他们分别位于/sys/bus/xxx/device
和/sys/bus/xxx/driver
下
3、device的注册
1) device_register
使用 device_regster 注册 device ,这里以源码的形式分析。
1 2 3 4 5 int device_register (struct device *dev) { device_initialize(dev); return device_add(dev); }
2) device_initialize
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 void device_initialize (struct device *dev) { dev->kobj.kset = devices_kset; kobject_init(&dev->kobj, &device_ktype); INIT_LIST_HEAD(&dev->dma_pools); mutex_init(&dev->mutex); lockdep_set_novalidate_class(&dev->mutex); spin_lock_init(&dev->devres_lock); INIT_LIST_HEAD(&dev->devres_head); device_pm_init(dev); set_dev_node(dev, -1 ); #ifdef CONFIG_GENERIC_MSI_IRQ INIT_LIST_HEAD(&dev->msi_list); #endif }
从它的初始化可以看出,所有通过device_register注册的dev的dev->kobj->list,都将挂接在 devices_kset->list上 (在kobj_add注册时链接) ,如果没有父设备则设备将使用 devices_kset 作为父设备目录节点 ,因此可以得出下面结论:
所有通过 device_regster 创建的设备都应该是/sys/devices/的子目录
3) device_add
这个函数将 device 注册进 bus,同时它也做了很多事情非常多的事情,具体做了什么事情我们跟着源码来看一下。
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 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 int device_add (struct device *dev) { struct device *parent ; struct kobject *kobj ; struct class_interface *class_intf ; int error = -EINVAL; struct kobject *glue_dir = NULL ; dev = get_device(dev); if (!dev) goto done; if (!dev->p) { error = device_private_init(dev); if (error) goto done; } if (dev->init_name) { dev_set_name(dev, "%s" , dev->init_name); dev->init_name = NULL ; } if (!dev_name(dev) && dev->bus && dev->bus->dev_name) dev_set_name(dev, "%s%u" , dev->bus->dev_name, dev->id); if (!dev_name(dev)) { error = -EINVAL; goto name_error; } pr_debug("device: '%s': %s\n" , dev_name(dev), __func__); parent = get_device(dev->parent); kobj = get_device_parent(dev, parent); if (IS_ERR(kobj)) { error = PTR_ERR(kobj); goto parent_error; } if (kobj) dev->kobj.parent = kobj; if (parent && (dev_to_node(dev) == NUMA_NO_NODE)) set_dev_node(dev, dev_to_node(parent)); error = kobject_add(&dev->kobj, dev->kobj.parent, NULL ); if (error) { glue_dir = get_glue_dir(dev); goto Error; } if (platform_notify) platform_notify(dev); error = device_create_file(dev, &dev_attr_uevent); if (error) goto attrError; error = device_add_class_symlinks(dev); if (error) goto SymlinkError; error = device_add_attrs(dev); if (error) goto AttrsError; error = bus_add_device(dev); if (error) goto BusError; error = dpm_sysfs_add(dev); if (error) goto DPMError; device_pm_add(dev); if (MAJOR(dev->devt)) { error = device_create_file(dev, &dev_attr_dev); if (error) goto DevAttrError; error = device_create_sys_dev_entry(dev); if (error) goto SysEntryError; devtmpfs_create_node(dev); } if (dev->bus) blocking_notifier_call_chain(&dev->bus->p->bus_notifier, BUS_NOTIFY_ADD_DEVICE, dev); kobject_uevent(&dev->kobj, KOBJ_ADD); bus_probe_device(dev); if (parent) klist_add_tail(&dev->p->knode_parent, &parent->p->klist_children); if (dev->class) { mutex_lock(&dev->class->p->mutex); klist_add_tail(&dev->knode_class, &dev->class->p->klist_devices); list_for_each_entry(class_intf, &dev->class->p->interfaces, node) if (class_intf->add_dev) class_intf->add_dev(dev, class_intf); mutex_unlock(&dev->class->p->mutex); } done: put_device(dev); return error; SysEntryError: if (MAJOR(dev->devt)) device_remove_file(dev, &dev_attr_dev); DevAttrError: device_pm_remove(dev); dpm_sysfs_remove(dev); DPMError: bus_remove_device(dev); BusError: device_remove_attrs(dev); AttrsError: device_remove_class_symlinks(dev); SymlinkError: device_remove_file(dev, &dev_attr_uevent); attrError: kobject_uevent(&dev->kobj, KOBJ_REMOVE); glue_dir = get_glue_dir(dev); kobject_del(&dev->kobj); Error: cleanup_glue_dir(dev, glue_dir); parent_error: put_device(parent); name_error: kfree(dev->p); dev->p = NULL ; goto done; }
4) get_device_parent
这个函数用于获取 dev->kobj->parent
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 static struct kobject *get_device_parent (struct device *dev, struct device *parent) { if (dev->class) { struct kobject *kobj = NULL ; struct kobject *parent_kobj ; struct kobject *k ; #ifdef CONFIG_BLOCK if (sysfs_deprecated && dev->class == &block_class) { if (parent && parent->class == &block_class) return &parent->kobj; return &block_class.p->subsys.kobj; } #endif if (parent == NULL ) parent_kobj = virtual_device_parent(dev); else if (parent->class && !dev->class->ns_type) return &parent->kobj; else parent_kobj = &parent->kobj; mutex_lock(&gdp_mutex); spin_lock(&dev->class->p->glue_dirs.list_lock); list_for_each_entry(k, &dev->class->p->glue_dirs.list , entry) if (k->parent == parent_kobj) { kobj = kobject_get(k); break ; } spin_unlock(&dev->class->p->glue_dirs.list_lock); if (kobj) { mutex_unlock(&gdp_mutex); return kobj; } k = class_dir_create_and_add(dev->class, parent_kobj); mutex_unlock(&gdp_mutex); return k; } if (!parent && dev->bus && dev->bus->dev_root) return &dev->bus->dev_root->kobj; if (parent) return &parent->kobj; return NULL ; }
class_dir_create_and_add
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 static struct kobject* class_dir_create_and_add (struct class *class, struct kobject *parent_kobj) { struct class_dir *dir ; int retval; dir = kzalloc(sizeof (*dir), GFP_KERNEL); if (!dir) return ERR_PTR(-ENOMEM); dir->class = class ; kobject_init(&dir->kobj, &class_dir_ktype); dir->kobj.kset = &class->p->glue_dirs; retval = kobject_add(&dir->kobj, parent_kobj, "%s" , class->name); if (retval < 0 ) { kobject_put(&dir->kobj); return ERR_PTR(retval); } return &dir->kobj; }
get_device_parent函数需要分为情况讨论
设备属于某个class且parent为空
创建 /sys/devices/virtual目录,并将其作为父目录parent_kobj
遍历 dev->class->p->glue_dirs 下的所有kobj, 如果存在 parent_kobj,则增加其引用计数并直接返回 parent_kobj
否则在parent_kobj下创建一个dir->kobj (/sys/dev/vitual/dir->kobj->name),使用传入的class->name 作为其dir->kobj->name, 并将class->p->glue_dirs作为其kset。并返回dir->kobj作为parent_kobj,这种情况会曾加如下目录
1 2 3 /sys/devices/virtual /sys/devices/virtual/class -> name
设备属于某个class且parent不为空
如果存在dev->class->ns_type
则直接返回 parent_kobj (这种情况不增加目录)
如果上述不存在,则遍历 dev->class->p->glue_dirs
下的所有 kobj, 如果存在parent_kobj,则增加其引用计数并直接返回 parent_kobj
否则在 parent_kobj 下创建一个dir->kobj,使用传入的 class->name 作为其 dir->kobj->name
, 并将 class->p->glue_dirs
作为其 kset。并返回 dir->kobj
作为parent_kobj,这种情况会增加如下目录
1 ..../parent_kobj->name/class -> name
设备属不属于某个class
如果 parent 为空则且 dev->bus->dev_root
不为空则使用 dev->bus->dev_root->kobj
作为父节点,否则直接返回 parent->kobj
5) device_add_class_symlinks
该函数会创建 class 与 dev 之间的连接,首先在传入的 dev 所在目录创建三个链接文件,of_node、subsystem、device,同时在所属的 class 目录下创建指向 dev 目录的链接文件,dev->init_name
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 static int device_add_class_symlinks (struct device *dev) { struct device_node *of_node = dev_of_node(dev); int error; if (of_node) { error = sysfs_create_link(&dev->kobj, &of_node->kobj,"of_node" ); if (error) dev_warn(dev, "Error %d creating of_node link\n" ,error); } if (!dev->class) return 0 ; error = sysfs_create_link(&dev->kobj, &dev->class->p->subsys.kobj, "subsystem" ); if (error) goto out_devnode; if (dev->parent && device_is_not_partition(dev)) { error = sysfs_create_link(&dev->kobj, &dev->parent->kobj, "device" ); if (error) goto out_subsys; } #ifdef CONFIG_BLOCK if (sysfs_deprecated && dev->class == &block_class) return 0 ; #endif error = sysfs_create_link(&dev->class->p->subsys.kobj, &dev->kobj, dev_name(dev)); if (error) goto out_device; return 0 ; out_device: sysfs_remove_link(&dev->kobj, "device" ); out_subsys: sysfs_remove_link(&dev->kobj, "subsystem" ); out_devnode: sysfs_remove_link(&dev->kobj, "of_node" ); return error; }
6) device_add_attrs
该函数用于在 dev 下创建所属的 class->dev_groups
, 所属的 type->groups
和dev->groups
指向的属性文件以及属性文件 online
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 static int device_add_attrs (struct device *dev) { struct class *class = dev->class ; const struct device_type *type = dev->type; int error; if (class) { error = device_add_groups(dev, class->dev_groups); if (error) return error; } if (type) { error = device_add_groups(dev, type->groups); if (error) goto err_remove_class_groups; } error = device_add_groups(dev, dev->groups); if (error) goto err_remove_type_groups; if (device_supports_offline(dev) && !dev->offline_disabled) { error = device_create_file(dev, &dev_attr_online); if (error) goto err_remove_dev_groups; } return 0 ; err_remove_dev_groups: device_remove_groups(dev, dev->groups); err_remove_type_groups: if (type) device_remove_groups(dev, type->groups); err_remove_class_groups: if (class) device_remove_groups(dev, class->dev_groups); return error; }
7) bus_add_device
这个函数将 device 注册进入bus
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 int bus_add_device (struct device *dev) { struct bus_type *bus = bus_get(dev->bus); int error = 0 ; if (bus) { pr_debug("bus: '%s': add device %s\n" , bus->name, dev_name(dev)); error = device_add_attrs(bus, dev); if (error) goto out_put; error = device_add_groups(dev, bus->dev_groups); if (error) goto out_id; error = sysfs_create_link(&bus->p->devices_kset->kobj, &dev->kobj, dev_name(dev)); if (error) goto out_groups; error = sysfs_create_link(&dev->kobj, &dev->bus->p->subsys.kobj, "subsystem" ); if (error) goto out_subsys; klist_add_tail(&dev->p->knode_bus, &bus->p->klist_devices); } return 0 ; out_subsys: sysfs_remove_link(&bus->p->devices_kset->kobj, dev_name(dev)); out_groups: device_remove_groups(dev, bus->dev_groups); out_id: device_remove_attrs(bus, dev); out_put: bus_put(dev->bus); return error; }
bus_add_device 会创建以下节点
1 2 /sys/bus/xxx/devices/dev->name ----> ..../dev->name ..../dev->name/subsystem ----> /sys/bus/xxx
8) bus_probe_device
如果drivers_autoprobe为1,即可以自动匹配则调用 device_initial_probe(dev)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 void bus_probe_device(struct device *dev) { struct bus_type *bus = dev->bus; struct subsys_interface *sif; if (!bus) return; //如果设置了 bus->p->drivers_autoprobe 则进行匹配 if (bus->p->drivers_autoprobe) device_initial_probe(dev); mutex_lock(&bus->p->mutex); list_for_each_entry(sif, &bus->p->interfaces, node) if (sif->add_dev) sif->add_dev(dev, sif); mutex_unlock(&bus->p->mutex); }
device_initial_probe
1 2 3 4 void device_initial_probe (struct device *dev) { __device_attach(dev, true ); }
__device_attach
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 static int __device_attach(struct device *dev, bool allow_async){ int ret = 0 ; device_lock(dev); if (dev->driver) { if (device_is_bound(dev)) { ret = 1 ; goto out_unlock; } ret = device_bind_driver(dev); if (ret == 0 ) ret = 1 ; else { dev->driver = NULL ; ret = 0 ; } } else { struct device_attach_data data = { .dev = dev, .check_async = allow_async, .want_async = false , }; if (dev->parent) pm_runtime_get_sync(dev->parent); ret = bus_for_each_drv(dev->bus, NULL , &data, __device_attach_driver); if (!ret && allow_async && data.have_async) { dev_dbg(dev, "scheduling asynchronous probe\n" ); get_device(dev); async_schedule(__device_attach_async_helper, dev); } else { pm_request_idle(dev); } if (dev->parent) pm_runtime_put(dev->parent); } out_unlock: device_unlock(dev); return ret; }
device_bind_driver
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 int device_bind_driver (struct device *dev) { int ret; ret = driver_sysfs_add(dev); if (!ret) driver_bound(dev); else if (dev->bus) blocking_notifier_call_chain(&dev->bus->p->bus_notifier, BUS_NOTIFY_DRIVER_NOT_BOUND, dev); return ret; }
__device_attach_driver
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 static int __device_attach_driver(struct device_driver *drv, void *_data){ struct device_attach_data *data = _data; struct device *dev = data->dev; bool async_allowed; int ret; if (dev->driver) return -EBUSY; ret = driver_match_device(drv, dev); if (ret == 0 ) { return 0 ; } else if (ret == -EPROBE_DEFER) { dev_dbg(dev, "Device match requests probe deferral\n" ); driver_deferred_probe_add(dev); } else if (ret < 0 ) { dev_dbg(dev, "Bus failed to match device: %d" , ret); return ret; } async_allowed = driver_allows_async_probing(drv); if (async_allowed) data->have_async = true ; if (data->check_async && async_allowed != data->want_async) return 0 ; return driver_probe_device(drv, dev); }
driver_probe_device
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 int driver_probe_device (struct device_driver *drv, struct device *dev) { int ret = 0 ; if (!device_is_registered(dev)) return -ENODEV; pr_debug("bus: '%s': %s: matched device %s with driver %s\n" , drv->bus->name, __func__, dev_name(dev), drv->name); if (dev->parent) pm_runtime_get_sync(dev->parent); pm_runtime_barrier(dev); ret = really_probe(dev, drv); pm_request_idle(dev); if (dev->parent) pm_runtime_put(dev->parent); return ret; }
really_probe
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 137 138 139 140 141 142 static int really_probe (struct device *dev, struct device_driver *drv) { int ret = -EPROBE_DEFER; int local_trigger_count = atomic_read (&deferred_trigger_count); bool test_remove = IS_ENABLED(CONFIG_DEBUG_TEST_DRIVER_REMOVE) && !drv->suppress_bind_attrs; #ifdef CONFIG_MTPROF unsigned long long ts = 0 ; #endif if (defer_all_probes) { dev_dbg(dev, "Driver %s force probe deferral\n" , drv->name); driver_deferred_probe_add(dev); return ret; } atomic_inc (&probe_count); pr_debug("bus: '%s': %s: probing driver %s with device %s\n" , drv->bus->name, __func__, drv->name, dev_name(dev)); WARN_ON(!list_empty(&dev->devres_head)); re_probe: dev->driver = drv; ret = pinctrl_bind_pins(dev); if (ret) goto pinctrl_bind_failed; if (driver_sysfs_add(dev)) { printk(KERN_ERR "%s: driver_sysfs_add(%s) failed\n" , __func__, dev_name(dev)); goto probe_failed; } if (dev->pm_domain && dev->pm_domain->activate) { ret = dev->pm_domain->activate(dev); if (ret) goto probe_failed; } if (dev->bus->probe) { TIME_LOG_START(); ret = dev->bus->probe(dev); TIME_LOG_END(); bootprof_probe(ts, dev, drv, (unsigned long )dev->bus->probe); if (ret) goto probe_failed; } else if (drv->probe) { TIME_LOG_START(); ret = drv->probe(dev); TIME_LOG_END(); bootprof_probe(ts, dev, drv, (unsigned long )drv->probe); if (ret) goto probe_failed; } if (test_remove) { test_remove = false ; if (dev->bus->remove) dev->bus->remove(dev); else if (drv->remove) drv->remove(dev); devres_release_all(dev); driver_sysfs_remove(dev); dev->driver = NULL ; dev_set_drvdata(dev, NULL ); if (dev->pm_domain && dev->pm_domain->dismiss) dev->pm_domain->dismiss(dev); pm_runtime_reinit(dev); goto re_probe; } pinctrl_init_done(dev); if (dev->pm_domain && dev->pm_domain->sync) dev->pm_domain->sync(dev); driver_bound(dev); ret = 1 ; pr_debug("bus: '%s': %s: bound device %s to driver %s\n" , drv->bus->name, __func__, dev_name(dev), drv->name); goto done; probe_failed: if (dev->bus) blocking_notifier_call_chain(&dev->bus->p->bus_notifier, BUS_NOTIFY_DRIVER_NOT_BOUND, dev); pinctrl_bind_failed: devres_release_all(dev); driver_sysfs_remove(dev); dev->driver = NULL ; dev_set_drvdata(dev, NULL ); if (dev->pm_domain && dev->pm_domain->dismiss) dev->pm_domain->dismiss(dev); pm_runtime_reinit(dev); switch (ret) { case -EPROBE_DEFER: dev_dbg(dev, "Driver %s requests probe deferral\n" , drv->name); driver_deferred_probe_add(dev); if (local_trigger_count != atomic_read (&deferred_trigger_count)) driver_deferred_probe_trigger(); break ; case -ENODEV: case -ENXIO: pr_debug("%s: probe of %s rejects match %d\n" , drv->name, dev_name(dev), ret); break ; default : printk(KERN_WARNING "%s: probe of %s failed with error %d\n" , drv->name, dev_name(dev), ret); } ret = 0 ; done: atomic_dec (&probe_count); wake_up(&probe_waitqueue); return ret; }
4、device_register总结
1) 检查设备名的合法性
从代码可以看出对于 dev 来说名字是一个非常重要的参数,首先使用 init_name 作为dev->kobj 的名字同时将 init_name 设置为空,如果 init_name 初始为空则使用 “bus->dev_nam + dev->id” 作为dev->kobj 的名字,如果设备没有设置名字则直接返回错误。
2)在sys/创建文件目录的层次关系的创建
下面列出所有可能出现的情况
设备的 bus 为空,class 为空,parent 为空
1 2 3 4 5 6 7 8 /sys/devices/xxx /sys/devices/xxx/power /sys/devices/xxx/uevent /sys/devices/xxx/of_node /sys/devices/xxx/dev /sys/dev/char /"major+minor" ---> /sys/devices/xxx /dev/xxx
设备的 bus 为空,class 为空,parent 不为空
1 2 3 4 5 6 7 8 /sys/devices/.../xxx->parent/xxx /sys/devices/.../xxx->parent/xxx/power /sys/devices/.../xxx->parent/xxx/uevent /sys/devices/.../xxx->parent/xxx/of_node /sys/devices/.../xxx->parent/xxx/dev /sys/dev/char /"major+minor" ---> /sys/devices/.../xxx->parent/xxx /dev/xxx
设备的 bus 不为空,class 为空,parent 为空
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 /sys/devices/xxx /sys/devices/xxx/power /sys/devices/xxx/uevent /sys/devices/xxx/of_node /sys/bus/xxx->bus/devices/xxx --->/sys/devices/xxx /sys/devices/xxx/subsystem ---> /sys/bus/xxx->bus /sys/devices/xxx/dev /sys/dev/char /"major+minor" ---> /sys/devices/xxx /dev/xxx /sys/devices/xxx/driver ---> /sys/bus/drivers/xxx->driver/ /sys/bus/drivers/xxx->driver/xxx ---> /sys/devices/xxx
设备的bus不为空,class为空,parent不为空
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 /sys/devices/.../xxx->parent/xxx /sys/devices/.../xxx->parent/xxx/power /sys/devices/.../xxx->parent/xxx/uevent /sys/devices/.../xxx->parent/xxx/of_node /sys/bus/xxx->bus/devices/xxx --->/sys/devices/.../xxx->parent/xxx /sys/devices/.../xxx->parent/xxx/subsystem ---> /sys/bus/xxx->bus /sys/devices/.../xxx->parent/xxx/dev /sys/dev/char /"major+minor" ---> /sys/devices/.../xxx->parent/xxx /dev/xxx /sys/devices/.../xxx->parent/xxx/driver ---> /sys/bus/drivers/xxx->driver/ /sys/bus/drivers/xxx->driver/xxx ---> /sys/devices/.../xxx->parent/xxx
设备的 bus 为空,class 不为空,parent 为空
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 /sys/devices/virtual /sys/devices/virtual/dir->kobj /sys/devices/virtual/dir->kobj/xxx /sys/devices/virtual/dir->kobj/xxx/uevent /sys/devices/virtual/dir->kobj/xxx/of_node /sys/devices/virtual/dir->kobj/xxx/subsystem ---> /sys/class /xxx ->class /xxx /sys /class /dev -> class /xxx ---> /sys/devices/virtual/dir->kobj/xxx/sys/devices/xxx/dev /sys/dev/char /"major+minor" ---> /sys/devices/virtual/dir->kobj/xxx /dev/xxx
设备的 bus 为空,class 不为空,parent 不为空
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 /sys/devices/.../xxx->parent/dir->kobj/ /sys/devices/.../xxx->parent/dir->kobj/xxx /sys/devices/.../xxx->parent/dir->kobj/xxx/uevent /sys/devices/.../xxx->parent/dir->kobj/xxx/of_node /sys/devices/.../xxx->parent/dir->kobj/xxx/subsystem ---> /sys/class /xxx ->class /sys /devices /xxx -> parent/dir->kobj/xxx/device ---> dev->parent->kobj /sys/class /dev ->class /xxx ---> /sys/devices/xxx->parent/dir->kobj/xxx /sys/devices/xxx/dev /sys/dev/char /"major+minor" ---> /sys/devices/xxx->parent/dir->kobj/xxx /dev/xxx
为什么没有同时出现一个设备同时属于 bus 和 class 的情况,通过整理 device_add 创建的目录层次可以发现,bus(class)下的设备都会在注册的的时候在设备目录创建 subsystem ,这个链接文件指向 bus(class),同时也会在 bus(class) 目录创建指向设备的链接文件。在 device_add_class_symlinks 函数和 bus_add_device 函数中都会在当前设备下创建 “subsystem” 这个属性文件 ,很明显存在着冲突,这得出一个结论:内核在向 bus 注册设备的时候,如果设备同属于 class 和 bus 时,设备是无法注册的
3)和挂接的bus上的所有drv进行匹配
匹配流程如下
1 2 3 4 5 6 7 8 9 10 11 12 bus_probe_device---> device_initial_probe(dev) ---> //如果bus->p->drivers_autoprobe被置位则调用这个函数 ---> __device_attach(dev, true); ---> bus_for_each_drv(dev->bus, NULL, &data, __device_attach_driver); //对于该bus上的每一个drv都调用__device_attach_driver函数 ---> __device_attach_driver(drv, data) ---> driver_match_device(drv, dev); ---> drv->bus->match(dev, drv) ---> driver_probe_device(drv, dev) //如果匹配成功则调用这个函数 ---> really_probe(dev, drv); ---> dev->driver = drv; //将匹配成功的驱动赋值给对应的设备 ---> dev->bus->probe(dev) //默认调用这个 ---> drv->probe(dev) //如果没有设置 dev->bus->probe 函数,则调用这
dev 在注册的时候会和所挂接 bus上 的所有 drv 进行匹配,即调用 drv->bus->match(dev, drv) 函数,如果匹配成功则调用所在总线上的 probe 函数,即 dev->bus->probe(dev) 函数 , 从这里也可以看出匹配的规则是灵活的,它由总线决定,由总线来决定设备和驱动的匹配规则 , 比如 platform 总线就规定了5种匹配规则,这里只是提一下,后面的platform设备详述。
4)建立与字符设备的联系
常用的字符设备就是通过设备号 与设备模型建立联系的,当我们在设备模型中注册一个 dev 时,如果存在设备号则会在/dev下创建对应的设备文件 ,我们可以通过这个文件的设备号,在已经注册的字符设备链表中查询到我们已经注册的字符设备。
5、创建我们自己的设备
我们之前已经创建了一个 my_bus 总线,现在在这个基础之上再创建一个 my_dev
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 #include <linux/device.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> #include <linux/string.h> #include <linux/sysfs.h> #include <linux/stat.h> extern struct bus_type my_bus ;struct device my_dev = { .init_name = "my_dev" , .bus = &my_bus, }; static int my_device_init (void ) { device_register(&my_dev); return 0 ; } static void my_device_exit (void ) { device_unregister(&my_dev); } module_init(my_device_init); module_exit(my_device_exit); MODULE_AUTHOR("baron" ); MODULE_LICENSE("GPL" );
验证结果
1 2 3 4 5 6 7 8 9 XF-X2:/sys/bus/my_bus/devices # ls XF-X2:/sys/bus/my_bus/devices # XF-X2:/sys/bus/my_bus/devices # insmod /cache/my_device.ko XF-X2:/sys/bus/my_bus/devices # XF-X2:/sys/bus/my_bus/devices # ls my_dev XF-X2:/sys/bus/my_bus/devices # cd my_dev/ XF-X2:/sys/bus/my_bus/devices/my_dev # ls power subsystem uevent
6、在dev下创建属性文件
属性文件作为用户空间和内核空间交互的常用手段之一,它的重要性不言而喻。 device_register 在 device_initialize 中会将 ktype 初始化为 device_ktype
1 2 3 4 5 6 7 8 9 10 static struct kobj_type device_ktype = { .release = device_release, .sysfs_ops = &dev_sysfs_ops, .namespace = device_namespace, }; static const struct sysfs_ops dev_sysfs_ops = { .show = dev_attr_show, .store = dev_attr_store, };
可以看到内核已经实现了 device_release 函数当 dev 引用计数为 0 时会自动释放掉自己。同样的内核也实现了属性文件中间层的函数 dev_attr_show 和 dev_attr_store
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 static ssize_t dev_attr_show (struct kobject *kobj, struct attribute *attr, char *buf) { struct device_attribute *dev_attr = to_dev_attr(attr); struct device *dev = kobj_to_dev(kobj); ssize_t ret = -EIO; if (dev_attr->show) ret = dev_attr->show(dev, dev_attr, buf); if (ret >= (ssize_t )PAGE_SIZE) { print_symbol("dev_attr_show: %s returned bad count\n" , (unsigned long )dev_attr->show); } return ret; } static ssize_t dev_attr_store (struct kobject *kobj, struct attribute *attr, const char *buf, size_t count) { struct device_attribute *dev_attr = to_dev_attr(attr); struct device *dev = kobj_to_dev(kobj); ssize_t ret = -EIO; if (dev_attr->store) ret = dev_attr->store(dev, dev_attr, buf, count); return ret; } 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); };
这个框架看起来是不是很熟悉,其实前面我们自己在my_dir就已经实现了这个框架了,这里只是换了个壳而已,像前面的 bus,以及后面的 driver 等模型,内核已经帮我们实现了相关框架,我们只需要直接用就行了。创建属性结构的方法太麻烦了,没关系内核也为我们封装了快速创建并初始化 device_attribute 结构的宏了
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 #define DEVICE_ATTR(_name, _mode, _show, _store) \ struct device_attribute dev_attr_##_name = __ATTR(_name, _mode, _show, _store) #define DEVICE_ATTR_RW(_name) \ struct device_attribute dev_attr_##_name = __ATTR_RW(_name) #define DEVICE_ATTR_RO(_name) \ struct device_attribute dev_attr_##_name = __ATTR_RO(_name) #define DEVICE_ATTR_WO(_name) \ struct device_attribute dev_attr_##_name = __ATTR_WO(_name) #define DEVICE_ULONG_ATTR(_name, _mode, _var) \ struct dev_ext_attribute dev_attr_##_name = \ { __ATTR(_name, _mode, device_show_ulong, device_store_ulong), &(_var) } #define DEVICE_INT_ATTR(_name, _mode, _var) \ struct dev_ext_attribute dev_attr_##_name = \ { __ATTR(_name, _mode, device_show_int, device_store_int), &(_var) } #define DEVICE_BOOL_ATTR(_name, _mode, _var) \ struct dev_ext_attribute dev_attr_##_name = \ { __ATTR(_name, _mode, device_show_bool, device_store_bool), &(_var) } #define DEVICE_ATTR_IGNORE_LOCKDEP(_name, _mode, _show, _store) \ struct device_attribute dev_attr_##_name = \ __ATTR_IGNORE_LOCKDEP(_name, _mode, _show, _store)
创建出了 device_attribute 结构,再调用 device_attribute 函数就可以在当前设备下快速创建属性文件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 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; } EXPORT_SYMBOL_GPL(device_create_file);
编程实现
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 #include <linux/device.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> #include <linux/string.h> #include <linux/sysfs.h> #include <linux/stat.h> extern struct bus_type my_bus ;extern struct class *my_class ;struct device my_dev = { .init_name = "my_dev" , .bus = &my_bus, }; ssize_t my_attr_show (struct device *dev, struct device_attribute *attr, char *buf) { printk("%s\n" ,attr->attr.name); sprintf (buf, "%s\n" , attr->attr.name); return strlen ((char *)attr->attr.name) +2 ; } ssize_t my_attr_store (struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { printk("%s store : %s\n" ,attr->attr.name, buf); return count; } DEVICE_ATTR(my_attr_test, 0664 , my_attr_show, my_attr_store); static int my_device_init (void ) { device_register(&my_dev); device_create_file(&my_dev,&dev_attr_my_attr_test); return 0 ; } static void my_device_exit (void ) { device_unregister(&my_dev); } module_init(my_device_init); module_exit(my_device_exit); MODULE_AUTHOR("baron" ); MODULE_LICENSE("GPL" );
验证结果
1 2 3 4 5 6 7 8 XF-X2:/sys/bus/my_bus/devices/my_dev # cat my_attr_test my_attr_test [ 501.905519 ] <6 >.(6 )[2845 :cat]my_attr_test XF-X2:/sys/bus/my_bus/devices/my_dev # echo 123>my_attr_test [ 545.227562 ] <4 >.(4 )[2821 :sh]my_attr_test store : 123
五、device_driver 驱动
Linux设备模型用 Driver 抽象硬件设备的驱动程序,它包含设备初始化、电源管理相关的接口实现。而Linux内核中的驱动开发,基本都围绕该抽象进行(实现所规定的接口函数)。
1、数据结构
1) device_driver
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 struct device_driver { const char *name; struct bus_type *bus ; struct module *owner ; const char *mod_name; bool suppress_bind_attrs; enum probe_type probe_type ; const struct of_device_id *of_match_table ; const struct acpi_device_id *acpi_match_table ; int (*probe) (struct device *dev); int (*remove) (struct device *dev); void (*shutdown) (struct device *dev); int (*suspend) (struct device *dev, pm_message_t state); int (*resume) (struct device *dev); const struct attribute_group **groups ; const struct dev_pm_ops *pm ; struct driver_private *p ; };
2) driver_private
1 2 3 4 5 6 7 struct driver_private { struct kobject kobj ; struct klist klist_devices ; struct klist_node knode_bus ; struct module_kobject *mkobj ; struct device_driver *driver ; };
2、driver的注册
相较于device的注册,driver的注册就比较简单
在总线上查找drv,判断drv是否已经注册进bus,防止重复注册
调用bus_add_driver将drv注册进入bus
创建属性文件 drv->groups
向上层发送uevent事件,KOBJ_ADD
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 int driver_register (struct device_driver *drv) { int ret; struct device_driver *other ; BUG_ON(!drv->bus->p); if ((drv->bus->probe && drv->probe) || (drv->bus->remove && drv->remove) || (drv->bus->shutdown && drv->shutdown)) printk(KERN_WARNING "Driver '%s' needs updating - please use " "bus_type methods\n" , drv->name); other = driver_find(drv->name, drv->bus); if (other) { printk(KERN_ERR "Error: Driver '%s' is already registered, " "aborting...\n" , drv->name); return -EBUSY; } ret = bus_add_driver(drv); if (ret) return ret; ret = driver_add_groups(drv, drv->groups); if (ret) { bus_remove_driver(drv); return ret; } kobject_uevent(&drv->p->kobj, KOBJ_ADD); return ret; }
bus_add_driver
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 int bus_add_driver (struct device_driver *drv) { struct bus_type *bus ; struct driver_private *priv ; int error = 0 ; bus = bus_get(drv->bus); if (!bus) return -EINVAL; pr_debug("bus: '%s': add driver %s\n" , bus->name, drv->name); priv = kzalloc(sizeof (*priv), GFP_KERNEL); if (!priv) { error = -ENOMEM; goto out_put_bus; } klist_init(&priv->klist_devices, NULL , NULL ); priv->driver = drv; drv->p = priv; priv->kobj.kset = bus->p->drivers_kset; error = kobject_init_and_add(&priv->kobj, &driver_ktype, NULL , "%s" , drv->name); if (error) goto out_unregister; klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers); if (drv->bus->p->drivers_autoprobe) { if (driver_allows_async_probing(drv)) { pr_debug("bus: '%s': probing driver %s asynchronously\n" , drv->bus->name, drv->name); async_schedule(driver_attach_async, drv); } else error = driver_attach(drv); if (error) goto out_unregister; } } module_add_driver(drv->owner, drv); error = driver_create_file(drv, &driver_attr_uevent); if (error) { printk(KERN_ERR "%s: uevent attr (%s) failed\n" , __func__, drv->name); } error = driver_add_groups(drv, bus->drv_groups); if (error) { printk(KERN_ERR "%s: driver_create_groups(%s) failed\n" , __func__, drv->name); } if (!drv->suppress_bind_attrs) { error = add_bind_files(drv); if (error) { printk(KERN_ERR "%s: add_bind_files(%s) failed\n" , __func__, drv->name); } } return 0 ; out_unregister: kobject_put(&priv->kobj); drv->p = NULL ; out_put_bus: bus_put(bus); return error; }
3、driver_register 总结
1) 在sys/创建对应节点
bus_add_driver 会创建下面节点
1 2 3 4 5 6 /sys/bus/xxx/drivers/drv->name /sys/bus/xxx/drivers/drv->name/uevent /sys/bus/xxx/drivers/drv->name/unbind /sys/bus/xxx/drivers/drv->name/bind
2) 匹配 bus 总线上的设备
除了创建节点之外,如果可以自动 probe ,如果可以,则遍历 klist_devices,对其中的每一个 dev 都调用 drv->bus->match(dev, drv)
函数,如果匹配成功则调用 really_probe(drv) ,在这个函数中默认先调用函数 dev->bus->probe(dev)
,如果没有设置 dev->bus->probe
函数,则调用该函数,调用流程如下
1 2 3 4 5 6 7 8 9 bus_add_driver----> driver_attach----> 无论如何最终都会调用这个函数 bus_for_each_dev----> __driver_attach----> driver_match_device----> drv->bus->match(dev, drv) really_probe----> dev->bus->probe(dev) drv->probe(dev)
4、注册我们自己的驱动
在我们创建的 bus 上注册我们自己的驱动
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 #include <linux/device.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> #include <linux/string.h> #include <linux/sysfs.h> #include <linux/stat.h> MODULE_AUTHOR("baron" ); MODULE_LICENSE("GPL" ); extern struct bus_type my_bus ;int my_drv_probe (struct device *dev) { printk("my_drv_probe\n" ); return 0 ; } int my_drv_remove (struct device *dev) { printk("my_drv_remove\n" ); return 0 ; } struct device_driver my_drv = { .name = "my_drv" , .bus = &my_bus, .probe = my_drv_probe, .remove = my_drv_remove, }; static int my_drv_init (void ) { driver_register(&my_drv); return 0 ; } static void my_drv_exit (void ) { driver_unregister(&my_drv); } module_init(my_drv_init); module_exit(my_drv_exit);
验证结果
1 2 3 4 5 6 7 8 9 10 11 XF-X2:/sys/bus/my_bus/drivers # ls XF-X2:/sys/bus/my_bus/drivers # XF-X2:/sys/bus/my_bus/drivers # insmod /cache/my my_bus.ko my_device.ko my_driver.ko XF-X2:/sys/bus/my_bus/drivers # insmod /cache/my_driver.ko XF-X2:/sys/bus/my_bus/drivers # ls my_drv XF-X2:/sys/bus/my_bus/drivers # XF-X2:/sys/bus/my_bus/drivers # cd my_drv/ XF-X2:/sys/bus/my_bus/drivers/my_drv # ls bind uevent unbind
5、在driver目录下创建属性文件
和前面的 device 下创建属性文件相同,这里只给接口不再赘述
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 struct driver_attribute { struct attribute attr ; ssize_t (*show)(struct device_driver *driver, char *buf); ssize_t (*store)(struct device_driver *driver, const char *buf, size_t count); }; #define DRIVER_ATTR(_name, _mode, _show, _store) \ struct driver_attribute driver_attr_##_name = __ATTR(_name, _mode, _show, _store) #define DRIVER_ATTR_RW(_name) \ struct driver_attribute driver_attr_##_name = __ATTR_RW(_name) #define DRIVER_ATTR_RO(_name) \ struct driver_attribute driver_attr_##_name = __ATTR_RO(_name) #define DRIVER_ATTR_WO(_name) \ struct driver_attribute driver_attr_##_name = __ATTR_WO(_name) extern int __must_check driver_create_file (struct device_driver *driver, const struct driver_attribute *attr) ;extern void driver_remove_file (struct device_driver *driver, const struct driver_attribute *attr) ;
六、class 类
class 用于管理同类的设备,常常被我们用来给上层开辟一个属性节点,多用于查看,修改对应的设备信息。
1、数据结构
1) class
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 struct class { const char *name; struct module *owner ; struct class_attribute *class_attrs ; const struct attribute_group **dev_groups ; struct kobject *dev_kobj ; int (*dev_uevent)(struct device *dev, struct kobj_uevent_env *env); char *(*devnode)(struct device *dev, umode_t *mode); void (*class_release)(struct class *class); void (*dev_release)(struct device *dev); int (*suspend)(struct device *dev, pm_message_t state); int (*resume)(struct device *dev); int (*shutdown)(struct device *dev); const struct kobj_ns_type_operations *ns_type ; const void *(*namespace)(struct device *dev); const struct dev_pm_ops *pm ; struct subsys_private *p ; };
2) subsys_private
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 struct subsys_private { struct kset subsys ; struct kset *devices_kset ; struct list_head interfaces ; struct mutex mutex ; struct kset *drivers_kset ; struct klist klist_devices ; struct klist klist_drivers ; struct blocking_notifier_head bus_notifier ; unsigned int drivers_autoprobe:1 ; struct bus_type *bus ; struct kset glue_dirs ; struct class *class ; };
2、函数接口
1) classes_init
1 2 3 4 5 6 7 int __init classes_init (void ) { class_kset = kset_create_and_add("class" , NULL , NULL ); if (!class_kset) return -ENOMEM; return 0 ; }
2)class_create
用于在内核中创建一个 class
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 #define class_create(owner, name) \ ({ \ static struct lock_class_key __key; \ __class_create(owner, name, &__key); \ }) struct class *__class_create (struct module *owner , const char *name , struct lock_class_key *key ) { struct class *cls ; int retval; cls = kzalloc(sizeof (*cls), GFP_KERNEL); if (!cls) { retval = -ENOMEM; goto error; } cls->name = name; cls->owner = owner; cls->class_release = class_create_release; retval = __class_register(cls, key); if (retval) goto error; return cls; error: kfree(cls); return ERR_PTR(retval); } EXPORT_SYMBOL_GPL(__class_create);
a. __class_register
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 int __class_register(struct class *cls, struct lock_class_key *key){ struct subsys_private *cp ; int error; pr_debug("device class '%s': registering\n" , cls->name); cp = kzalloc(sizeof (*cp), GFP_KERNEL); if (!cp) return -ENOMEM; klist_init(&cp->klist_devices, klist_class_dev_get, klist_class_dev_put); INIT_LIST_HEAD(&cp->interfaces); kset_init(&cp->glue_dirs); __mutex_init(&cp->mutex, "subsys mutex" , key); error = kobject_set_name(&cp->subsys.kobj, "%s" , cls->name); if (error) { kfree(cp); return error; } if (!cls->dev_kobj) cls->dev_kobj = sysfs_dev_char_kobj; #if defined(CONFIG_BLOCK) if (!sysfs_deprecated || cls != &block_class) cp->subsys.kobj.kset = class_kset; #else cp->subsys.kobj.kset = class_kset; #endif cp->subsys.kobj.ktype = &class_ktype; cp->class = cls; cls->p = cp; error = kset_register(&cp->subsys); if (error) { kfree(cp); return error; } error = add_class_attrs(class_get(cls)); class_put(cls); return error; } EXPORT_SYMBOL_GPL(__class_register);
b. device_create
在 class 下创建对应的设备,返回创建的设备结构。
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 struct device *device_create (struct class *class, struct device *parent, dev_t devt, void *drvdata, const char *fmt, ...) { va_list vargs; struct device *dev ; va_start(vargs, fmt); dev = device_create_vargs(class, parent, devt, drvdata, fmt, vargs); va_end(vargs); return dev; } EXPORT_SYMBOL_GPL(device_create); struct device *device_create_vargs (struct class *class, struct device *parent, dev_t devt, void *drvdata, const char *fmt, va_list args) { return device_create_groups_vargs(class, parent, devt, drvdata, NULL , fmt, args); } EXPORT_SYMBOL_GPL(device_create_vargs); static struct device *device_create_groups_vargs (struct class *class, struct device *parent, dev_t devt, void *drvdata, const struct attribute_group **groups, const char *fmt, va_list args) { struct device *dev = NULL ; int retval = -ENODEV; if (class == NULL || IS_ERR(class)) goto error; dev = kzalloc(sizeof (*dev), GFP_KERNEL); if (!dev) { retval = -ENOMEM; goto error; } device_initialize(dev); dev->devt = devt; dev->class = class ; dev->parent = parent; dev->groups = groups; dev->release = device_create_release; dev_set_drvdata(dev, drvdata); retval = kobject_set_name_vargs(&dev->kobj, fmt, args); if (retval) goto error; retval = device_add(dev); if (retval) goto error; return dev; error: put_device(dev); return ERR_PTR(retval); }
device_add 函数在前面已经有很详细的分析,这里不赘述,不过这里再补充说明一点,device_create 会在 /dev/ 目录下创建设备节点是因为设备有设备号,只要是调用 device_add 注册的设备,同时有设备号就会在 /dev/ 下创建设备节点
3) 属性操作接口
属性文件是 class 重点内容,我们多用 class 来给上层提供接口,属性文件创建接口如下。
1 2 3 4 5 6 7 8 #define CLASS_ATTR(_name, _mode, _show, _store) \ struct class_attribute class_attr_##_name = __ATTR(_name, _mode, _show, _store) #define CLASS_ATTR_RW(_name) \ struct class_attribute class_attr_##_name = __ATTR_RW(_name) #define CLASS_ATTR_RO(_name) \ struct class_attribute class_attr_##_name = __ATTR_RO(_name)
1 static inline int __must_check class_create_file (struct class *class, const struct class_attribute *attr)
在计算机中有这样一类设备,它们通过各自的设备控制器,直接和 CPU 连接,CPU 可以通过常规的寻址操作访问它们(或者说访问它们的控制器)。这种连接方式,并不属于传统意义上的总线连接。但设备模型应该具备普适性,因此 Linux 就虚构了一条 Platform Bus ,供这些设备挂靠。
1、数据结构
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 struct platform_device { const char *name; int id; bool id_auto; struct device dev ; u32 num_resources; struct resource *resource ; const struct platform_device_id *id_entry ; char *driver_override; struct mfd_cell *mfd_cell ; struct pdev_archdata archdata ; };
1 2 3 4 5 6 7 8 9 10 struct platform_driver { int (*probe)(struct platform_device *); int (*remove)(struct platform_device *); void (*shutdown)(struct platform_device *); int (*suspend)(struct platform_device *, pm_message_t state); int (*resume)(struct platform_device *); struct device_driver driver ; const struct platform_device_id *id_table ; bool prevent_deferred_probe; };
3) resource
1 2 3 4 5 6 7 8 struct resource { resource_size_t start; resource_size_t end; const char *name; unsigned long flags; unsigned long desc; struct resource *parent , *sibling , *child ; };
4) of_dev_auxdata
1 2 3 4 5 6 struct of_dev_auxdata { char *compatible; resource_size_t phys_addr; char *name; void *platform_data; };
platform 总线是内核提供的虚拟总线,它个构建依赖于前面的,bus,device,driver设备模型。首先内核提供了一个名字叫 “platform” 的默认总线,它是一个全局结构并且被 EXPORT_SYMBOL_GPL 导出,如下
1 2 3 4 5 6 7 8 struct bus_type platform_bus_type = { .name = "platform" , .dev_groups = platform_dev_groups, .match = platform_match, .uevent = platform_uevent, .pm = &platform_dev_pm_ops, }; EXPORT_SYMBOL_GPL(platform_bus_type);
除此之外内核也提供了该总线下的一个设备,名字叫做 “platform_bus”
1 2 3 4 struct device platform_bus = { .init_name = "platform" , }; EXPORT_SYMBOL_GPL(platform_bus);
这是一个设备结构,并不是bus,虽然他的名字叫做 platform_bus , 我也不知道为啥叫这个名字,第一次读我就以为这是个bus。它作为基本设备,它注册之后内核将会创建出如下节点
platform_bus 的结构只初始化了一个名字,为什么要注册一个只有名字的设备在这里,我想是为了方便管理,将它作为以后 platform 设备的父设备,以后只要是 platform 设备,都将出现在 /sys/devices/platform
下 ,一眼就能找出那些是 platform 设备。上述的 platform_bus_type 和 platform_bus 是在platform_bus_init 中被注册的,它被driver_init调用,即在内核启动的时候被创建。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 int __init platform_bus_init (void ) { int error; early_platform_cleanup(); error = device_register(&platform_bus); if (error) return error; error = bus_register(&platform_bus_type); if (error) device_unregister(&platform_bus); of_platform_register_reconfig_notifier(); return error; }
以后但凡是注册在 platform 总线上的设备都叫做 platform 设备,注册在该总线上的驱动叫做 platform 驱动
内核在开机时创建了 platform 总线,同时也提供了该总线相关操作函数
使用这个函数注册一个 platform 设备
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 int platform_device_register (struct platform_device *pdev) { int ret; #ifdef CONFIG_MTPROF unsigned long long ts = 0 ; #endif TIME_LOG_START(); device_initialize(&pdev->dev); arch_setup_pdev_archdata(pdev); ret = platform_device_add(pdev); TIME_LOG_END(); bootprof_pdev_register(ts, pdev); return ret; }
真正的注册函数是 platform_device_add
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 int platform_device_add (struct platform_device *pdev) { int i, ret; if (!pdev) return -EINVAL; if (!pdev->dev.parent) pdev->dev.parent = &platform_bus; pdev->dev.bus = &platform_bus_type; switch (pdev->id) { 设置 pdev->dev->init_name default : dev_set_name(&pdev->dev, "%s.%d" , pdev->name, pdev->id); break ; case PLATFORM_DEVID_NONE: dev_set_name(&pdev->dev, "%s" , pdev->name); break ; case PLATFORM_DEVID_AUTO: ret = ida_simple_get(&platform_devid_ida, 0 , 0 , GFP_KERNEL); if (ret < 0 ) goto err_out; pdev->id = ret; pdev->id_auto = true ; dev_set_name(&pdev->dev, "%s.%d.auto" , pdev->name, pdev->id); break ; } for (i = 0 ; i < pdev->num_resources; i++) { struct resource *p , *r = &pdev->resource[i]; if (r->name == NULL ) r->name = dev_name(&pdev->dev); p = r->parent; if (!p) { if (resource_type(r) == IORESOURCE_MEM) p = &iomem_resource; else if (resource_type(r) == IORESOURCE_IO) p = &ioport_resource; } if (p && insert_resource(p, r)) { dev_err(&pdev->dev, "failed to claim resource %d\n" , i); ret = -EBUSY; goto failed; } } pr_debug("Registering platform device '%s'. Parent at %s\n" , dev_name(&pdev->dev), dev_name(pdev->dev.parent)); ret = device_add(&pdev->dev); if (ret == 0 ) return ret; failed: if (pdev->id_auto) { ida_simple_remove(&platform_devid_ida, pdev->id); pdev->id = PLATFORM_DEVID_AUTO; } while (--i >= 0 ) { struct resource *r = &pdev->resource[i]; if (r->parent) release_resource(r); } err_out: return ret; }
该函数设置设备的父设备为platform_bus,以后凡是挂接在 platform 总线上的设备都将使用 platform_bus 作为它的父设备。即所有的设备都将在下面目录生成
1 /sys/devices/platform/xxx
同样内核也提供了 platform 驱动的注册函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 #define platform_driver_register(drv) \ __platform_driver_register(drv, THIS_MODULE) int __platform_driver_register(struct platform_driver *drv, struct module *owner) { drv->driver.owner = owner; drv->driver.bus = &platform_bus_type; drv->driver.probe = platform_drv_probe; drv->driver.remove = platform_drv_remove; drv->driver.shutdown = platform_drv_shutdown; //将驱动注册进总线 return driver_register(&drv->driver); } EXPORT_SYMBOL_GPL(__platform_driver_register);
1, 可以看出 drv 的,probe,rmove 函数被分别初始化为 platform_drv_probe,platform_drv_remove。最后调用 driver_register 将驱动注册进总线
2, 对于 platform 设备的注册最后会调用,device_add,它最终会遍历platform_bus_type上所有的 drv,并对每一个 drv 调用 platform_match函数。
3, 而对于platform_driver 的注册会调用 driver_register,它最终会遍历 platform_bus_type 上左右的d ev,对每一个dev都调用platform_match 函数。
像这种交叉遍历的方式在内核中很常见,input子系统中也使用这样的方式。也就是无论如何只要总线上有设备,驱动注册的时候就会去与之匹配,同理总线上有驱动,设备注册时就会去与之匹配。
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 static int platform_match (struct device *dev, struct device_driver *drv) { struct platform_device *pdev = to_platform_device(dev); struct platform_driver *pdrv = to_platform_driver(drv); if (pdev->driver_override) return !strcmp (pdev->driver_override, drv->name); if (of_driver_match_device(dev, drv)) return 1 ; if (acpi_driver_match_device(dev, drv)) return 1 ; if (pdrv->id_table) return platform_match_id(pdrv->id_table, pdev) != null; return (strcmp (pdev->name, drv->name) == 0 ); }
从这个函数可以看出 platform 设备的匹配方式有 5 种,按照优先级如下:
如果设置了driver_override,则匹配和driver_override相同名字的设备,它的优先级最高
使用设备树方式匹配,这是目前比较常用的方式之一
电源相关方式匹配
如果设置了id_table, 则与id_table中的名字进行匹配
最后比较驱动和设备的名称,也是比较常用的方式之一
其中比较常用的是设备树和设备驱动名称进行匹配,下面详细分析一下设备树匹配流程。
1 2 3 4 static inline int of_driver_match_device (struct device *dev, const struct device_driver *drv) { return of_match_device(drv->of_match_table, dev) != NULL ; }
1 2 3 4 5 6 const struct of_device_id *of_match_device (const struct of_device_id *matches, const struct device *dev) { if ((!matches) || (!dev->of_node)) return NULL ; return of_match_node(matches, dev->of_node); }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 static const struct of_device_id *__of_match_node (const struct of_device_id *matches , const struct device_node *node ) { const struct of_device_id *best_match = NULL ; int score, best_score = 0 ; if (!matches) return NULL ; for (; matches->name[0 ] || matches->type[0 ] || matches->compatible[0 ]; matches++) { score = __of_device_is_compatible(node, matches->compatible, matches->type, matches->name); if (score > best_score) { best_match = matches; best_score = score; } } return best_match; }
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 static int __of_device_is_compatible(const struct device_node *device, const char *compat, const char *type, const char *name) { struct property *prop ; const char *cp; int index = 0 , score = 0 ; if (compat && compat[0 ]) { prop = __of_find_property(device, "compatible" , NULL ); for (cp = of_prop_next_string(prop, NULL ); cp; cp = of_prop_next_string(prop, cp), index++) { if (of_compat_cmp(cp, compat, strlen (compat)) == 0 ) { score = INT_MAX/2 - (index << 2 ); break ; } } if (!score) return 0 ; } if (type && type[0 ]) { if (!device->type || of_node_cmp(type, device->type)) return 0 ; score += 2 ; } if (name && name[0 ]) { if (!device->name || of_node_cmp(name, device->name)) return 0 ; score++; } return score; }
再来看看 of_device_id 这个结构
1 2 3 4 5 6 struct of_device_id { char name[32 ]; char type[32 ]; char compatible[128 ]; const void *data; };
我们用到的是 compatible 作为匹配的对象,从结构看最对大支持的长度为128个字节。从上述代码可以看出匹配的过程就是匹配 drv.id->compatible 和 dts中的 compatible 节点比较,例如: hall 的 dts 的节点如下
1 2 3 hall: hall{ compatible = "mediatek,hall-gpio-int" ; };
在驱动中如下配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 #ifdef CONFIG_OF static const struct of_device_id hall_switch_of_match [] = { {.compatible = "mediatek,hall-gpio-int" }, {}, }; #endif static struct platform_driver hall_driver = { .probe = hall_probe, .suspend = hall_suspend, .resume = hall_resume, .remove = ln4913_remove, .driver = { .name = "ln4913_Driver" , .of_match_table = hall_switch_of_match, }, };
从这里可以看出匹配其实是会遍历 hall_switch_of_match 数组中的 compatible 描述,也就是说一个驱动可以尝试匹配多个设备,直到匹配到为止。由前面的分析可知一旦匹配成功,就会调用 really_probe 函数
1 2 3 ---> really_probe ---> dev->bus->probe(dev) ---> drv->probe(dev)
来看看 platform 总线提供的 probe 函数
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 static int platform_drv_probe (struct device *_dev) { struct platform_driver *drv = to_platform_driver(_dev->driver); struct platform_device *dev = to_platform_device(_dev); int ret; ret = of_clk_set_defaults(_dev->of_node, false ); if (ret < 0 ) return ret; ret = dev_pm_domain_attach(_dev, true ); if (ret != -EPROBE_DEFER) { if (drv->probe) { ret = drv->probe(dev); if (ret) dev_pm_domain_detach(_dev, true ); } else { ret = 0 ; } } if (drv->prevent_deferred_probe && ret == -EPROBE_DEFER) { dev_warn(_dev, "probe deferral not supported\n" ); ret = -ENXIO; } return ret; }
可以看到其实最终调用了platform_driver结构中的probe函数。除此之外匹配id也是常用的方式,匹配代码如下
1 2 3 4 5 6 7 8 9 10 11 12 13 static const struct platform_device_id *platform_match_id ( const struct platform_device_id *id, struct platform_device *pdev) { while (id->name[0 ]) { if (strcmp (pdev->name, id->name) == 0 ) { pdev->id_entry = id; return id; } id++; } return NULL ; }
1) 用名字进行匹配
注册platform_device
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 #include <linux/device.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> #include <linux/string.h> #include <linux/sysfs.h> #include <linux/stat.h> #include <linux/platform_device.h> MODULE_AUTHOR("baron" ); MODULE_LICENSE("GPL" ); struct platform_device my_platform_dev = { .name = "my_platform" , }; static int my_platform_dev_init (void ) { platform_device_register(&my_platform_dev); return 0 ; } static void my_platform_dev_exit (void ) { platform_device_register(&my_platform_dev); } module_init(my_platform_dev_init); module_exit(my_platform_dev_exit);
注册platform_driver
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 #include <linux/device.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> #include <linux/string.h> #include <linux/sysfs.h> #include <linux/stat.h> #include <linux/platform_device.h> MODULE_AUTHOR("baron" ); MODULE_LICENSE("GPL" ); int my_platform_probe (struct platform_device *pdev) { printk("my_platform_probe\n" ); return 0 ; } int my_platform_remove (struct platform_device *pdev) { printk("my_platform_remove\n" ); return 0 ; } struct platform_driver my_platform_driver = { .probe = my_platform_probe, .remove = my_platform_remove, .driver = { .name = "my_platform" , }, }; static int my_platform_drv_init (void ) { platform_driver_register(&my_platform_driver); return 0 ; } static void my_platform_drv_exit (void ) { platform_driver_unregister(&my_platform_driver); } module_init(my_platform_drv_init); module_exit(my_platform_drv_exit);
验证结果
1 2 3 4 5 tb8768p1_64_bsp:/cache # insmod my_platform_drive.ko tb8768p1_64_bsp:/cache # insmod my_platform_device.ko [ 2111.422598 ] <6 >.(5 )[3265 :insmod]my_platform_probe
2) 用设备树方式匹配
dts增加配置
1 2 3 my_platform_dts: my_platform_dts { compatible = "mediatek,my_platform" ; };
注册platform_driver
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 #include <linux/device.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> #include <linux/string.h> #include <linux/sysfs.h> #include <linux/stat.h> #include <linux/platform_device.h> MODULE_AUTHOR("baron" ); MODULE_LICENSE("GPL" ); int my_platform_probe (struct platform_device *pdev) { printk("my_platform_probe\n" ); return 0 ; } int my_platform_remove (struct platform_device *pdev) { printk("my_platform_remove\n" ); return 0 ; } #ifdef CONFIG_OF static const struct of_device_id my_platform_match [] = { {.compatible = "mediatek,my_platform" }, {}, }; #endif struct platform_driver my_platform_driver = { .probe = my_platform_probe, .remove = my_platform_remove, .driver = { .name = "my_platform" , #ifdef CONFIG_OF .of_match_table = my_platform_match, #endif }, }; static int my_platform_drv_init (void ) { { platform_driver_register(&my_platform_driver); return 0 ; } static void my_platform_drv_exit (void ) { platform_driver_unregister(&my_platform_driver); } module_init(my_platform_drv_init); module_exit(my_platform_drv_exit);
验证结果
1 2 3 4 XF-X2:/cache # insmod my_platform_drive.ko [ 1018.455974 ] <4 >.(7 )[3165 :insmod]my_platform_probe
对于当前的内核,我们一般不会主动去创建一个 platform 设备,我们往往通过设备树的方式添加 platform 设备。 例如 i2c 设备的 dts 如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 / { i2c0: i2c@11007000 { compatible = "mediatek,i2c" ; id = <0 >; reg = <0 0x11007000 0 0x1000 >, <0 0x11000080 0 0x80 >; interrupts = <GIC_SPI 81 IRQ_TYPE_LEVEL_LOW>; clocks = <&infracfg_ao INFRACFG_AO_I2C0_CG>, <&infracfg_ao INFRACFG_AO_AP_DMA_CG>; clock-names = "main" , "dma" ; clock-div = <5 >; mediatek,hs_only; mediatek,skip_scp_sema; }; };
该 dts 将会在内核中被解析为一个设备名为 11007000.i2c 的 platform 设备, 解析的规则是什么,跟着源码看一下
1 2 3 4 5 6 7 8 9 10 11 12 static int __init of_platform_default_populate_init (void ) { struct device_node *node ; ...... of_platform_default_populate(NULL , NULL , NULL ); ...... return 0 ; } arch_initcall_sync(of_platform_default_populate_init);
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 * {} * }; * */ int of_platform_default_populate (struct device_node *root, const struct of_dev_auxdata *lookup, struct device *parent) { return of_platform_populate(root, of_default_bus_match_table, lookup, parent); }
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 int of_platform_populate (struct device_node *root, const struct of_device_id *matches, const struct of_dev_auxdata *lookup, struct device *parent) { struct device_node *child ; int rc = 0 ; root = root ? of_node_get(root) : of_find_node_by_path("/" ); if (!root) return -EINVAL; pr_debug("%s()\n" , __func__); pr_debug(" starting at: %pOF\n" , root); for_each_child_of_node(root, child) { rc = of_platform_bus_create(child, matches, lookup, parent, true ); if (rc) { of_node_put(child); break ; } } of_node_set_flag(root, OF_POPULATED_BUS); of_node_put(root); return rc; }
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 static int of_platform_bus_create (struct device_node *bus, const struct of_device_id *matches, const struct of_dev_auxdata *lookup, struct device *parent, bool strict) { const struct of_dev_auxdata *auxdata ; struct device_node *child ; struct platform_device *dev ; const char *bus_id = NULL ; void *platform_data = NULL ; int rc = 0 ; if (strict && (!of_get_property(bus, "compatible" , NULL ))) { pr_debug("%s() - skipping %pOF, no compatible prop\n" , __func__, bus); return 0 ; } if (of_node_check_flag(bus, OF_POPULATED_BUS)) { pr_debug("%s() - skipping %pOF, already populated\n" , __func__, bus); return 0 ; } auxdata = of_dev_lookup(lookup, bus); if (auxdata) { bus_id = auxdata->name; platform_data = auxdata->platform_data; } if (of_device_is_compatible(bus, "arm,primecell" )) { of_amba_device_create(bus, bus_id, platform_data, parent); return 0 ; } dev = of_platform_device_create_pdata(bus, bus_id, platform_data, parent); if (!dev || !of_match_node(matches, bus)) return 0 ; for_each_child_of_node(bus, child) { pr_debug(" create child: %pOF\n" , child); rc = of_platform_bus_create(child, matches, lookup, &dev->dev, strict); if (rc) { of_node_put(child); break ; } } of_node_set_flag(bus, OF_POPULATED_BUS); return rc; }
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 static struct platform_device *of_platform_device_create_pdata ( struct device_node *np, const char *bus_id, void *platform_data, struct device *parent) { struct platform_device *dev ; if (!of_device_is_available(np) || of_node_test_and_set_flag(np, OF_POPULATED)) return NULL ; dev = of_device_alloc(np, bus_id, parent); if (!dev) goto err_clear_flag; dev->dev.bus = &platform_bus_type; dev->dev.platform_data = platform_data; of_msi_configure(&dev->dev, dev->dev.of_node); if (of_device_add(dev) != 0 ) { platform_device_put(dev); goto err_clear_flag; } return dev; err_clear_flag: of_node_clear_flag(np, OF_POPULATED); return NULL ; }
6) of_device_alloc
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 struct platform_device *of_device_alloc (struct device_node *np, const char *bus_id, struct device *parent) { struct platform_device *dev ; int rc, i, num_reg = 0 , num_irq; struct resource *res , temp_res ; dev = platform_device_alloc("" , PLATFORM_DEVID_NONE); if (!dev) return NULL ; while (of_address_to_resource(np, num_reg, &temp_res) == 0 ) num_reg++; num_irq = of_irq_count(np); if (num_irq || num_reg) { res = kzalloc(sizeof (*res) * (num_irq + num_reg), GFP_KERNEL); if (!res) { platform_device_put(dev); return NULL ; } dev->num_resources = num_reg + num_irq; dev->resource = res; for (i = 0 ; i < num_reg; i++, res++) { rc = of_address_to_resource(np, i, res); WARN_ON(rc); } if (of_irq_to_resource_table(np, res, num_irq) != num_irq) pr_debug("not all legacy IRQ resources mapped for %s\n" , np->name); } dev->dev.of_node = of_node_get(np); dev->dev.fwnode = &np->fwnode; dev->dev.parent = parent ? : &platform_bus; if (bus_id) dev_set_name(&dev->dev, "%s" , bus_id); else of_device_make_bus_id(&dev->dev); return dev; } EXPORT_SYMBOL(of_device_alloc);
7) of_device_make_bus_id
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 static void of_device_make_bus_id (struct device *dev) { struct device_node *node = dev->of_node; const __be32 *reg; u64 addr; while (node->parent) { reg = of_get_property(node, "reg" , NULL ); if (reg && (addr = of_translate_address(node, reg)) != OF_BAD_ADDR) { dev_set_name(dev, dev_name(dev) ? "%llx.%s:%s" : "%llx.%s" , (unsigned long long )addr, node->name, dev_name(dev)); return ; } dev_set_name(dev, dev_name(dev) ? "%s:%s" : "%s" , kbasename(node->full_name), dev_name(dev)); node = node->parent; } }
8) 总结
只要dts中的节点有 compatible 属性,将会在内核中将该节点转换为 platform 设备,该设备将出现在/sys/devices/platform/
下
如果有设备节点有 reg 属性则使用addr.node->name
作为该设备的名字
如果设备节点没有 reg 属性则使用 node->full_name
作为设备名,同时设置node = node->parent