完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
本帖最后由 q2113435929 于 2015-10-16 11:22 编辑
UT4412BV03开发板学习linux设备驱动模型(一) 设备驱动模型概述: 设备驱动模型比较复杂,linux系统将设备和驱动归结到设备驱动模型中来管理,设备驱动模型的提出解决了以前编写驱动时没有统一的方法的局面,设备驱动模型给各种驱动程序提供了很多辅助性的函数,这些函数经过严格测试,可以很大程度上提高驱动开发人员的工作效率。 设备驱动模型中用到的几个核心的数据结构,分别是kobjeck,kset,subsystem,这些结构体使设备驱动模型组成了一个层次结构,该层次结构将驱动,设备,总线等联系起来,形成了一个完整的设备驱动。 kobject结构体提供了一个最基本的设备对象管理能力,每一个在内核中注册的kobject对象都对应于sysfs文件中的一个目录。 对应头文件:#include 对应源文件:kobjeck.c 1.kobject结构体 struct kobject { const char *name; //kobject结构体的名字,作为一个目录显示在sysfs文件系统中 struct list_head entry; //链接下一个kobject结构体 struct kobject *parent; //指向本结构体的指针 struct kset *kset; //指向kset结构体的指针 struct kobj_type *ktype; //指向kobj_type结构体的指针,设备属性结构体 struct sysfs_dirent *sd; // struct kref kref; //表示该对象的引用计数, 内核提供增加和减少引用计数的kobject_get();kobject_put();当计数为0时,该对象的所有资源被释放 unsigned int state_initialized:1; //表示kobject结构体是否初始化过,1表示初始化过,0表示未初始化过。 unsigned int state_in_sysfs:1; //表示kobject结构体是否已注册到sysfs文件系统中 unsigned int state_add_uevent_sent:1; // unsigned int state_remove_uevent_sent:1; // unsigned int uevent_suppress:1 }; Kobject通过kset组织成层次化的结构,kset中包含了kobject集合,像驱动程序一样放在/sys/driver目录下,目录driver是一个kset对象,包含系统中的驱动程序对应的目录,驱动程序的目录由kobject表示。Kset结构体的定义如下: 用到的头文件#include struct kset { struct list_head list; //链接所包含的的kobject对象的链表的首部 spinlock_t list_lock; //维护list链表的自旋锁 struct kobject kobj; //内嵌的kobject结构体,说明kset本身也是一个目录 struct kset_uevent_ops *uevent_ops; //热插拔事件 }; 每一个kobject对象都有一些属性,这些属性由kobj_type结构体表示,最开始,内核开发者考虑将包含在kobject结构体中,后来考虑到同类设备会具有相同的属性,所以将属性隔离开来。由kobj_type表示。 用到的头文件#include struct kobj_type { void (*release)(struct kobject *kobj); //释放kobject结构体占用的资源,该函数用驱动开发者去实现 struct sysfs_ops *sysfs_ops; struct attribute **default_attrs; }; 用到的头文件#include struct attribute { const char *name; struct module *owner; mode_t mode; }; struct sysfs_ops { ssize_t (*show)(struct kobject *, struct attribute *,char *); //写属性操作函数 show函数用于读取一个属性到用户空间,读取成功返回读取到的字节数 ssize_t (*store)(struct kobject *,struct attribute *,const char *, size_t); //写属性操作函数, store函数用于将属性写入内核 }; 用到的头文件#include struct sysfs_dirent { atomic_t s_count; atomic_t s_active; struct sysfs_dirent *s_parent; struct sysfs_dirent *s_sibling; const char *s_name; union { struct sysfs_elem_dir s_dir; struct sysfs_elem_symlink s_symlink; struct sysfs_elem_attr s_attr; struct sysfs_elem_bin_attr s_bin_attr; }; unsigned int s_flags; ino_t s_ino; umode_t s_mode; struct iattr *s_iattr; }; 二. 操作kobject结构体用的函数 1.kobject结构体初始化函数 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) { /* do not error out as sometimes we can recover */ 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): %sn", kobj, err_str); dump_stack(); } 2.初始化kobject结构体内部成员的函数 static void kobject_init_internal(struct kobject *kobj) { if (!kobj) return; //kobj为空指针时退出程序 kref_init(&kobj->kref); //增加kobject引用计数 INIT_LIST_HEAD(&kobj->entry); // 初始化kobject链表 kobj->state_in_sysfs = 0; // 表示kobject结构体还没注册到sysfs文件系统中 kobj->state_add_uevent_sent = 0; // 始终初始化为0 kobj->state_remove_uevent_sent = 0; // 始终初始化为0 kobj->state_initialized = 1; // 表示kobject结构体以始化过 } 3.此函数用于初始化和加载kobject到内核中 此函数完成两个功能: 1.调用kobject_init(kobj, ktype);对kobject函数进行初始化 2.调用kobject_add_varg(kobj, parent, fmt, args);将kobject加入设备驱动模型中 int kobject_init_and_add(struct kobject *kobj, struct kobj_type *ktype, struct kobject *parent, const char *fmt, ...) { va_list args; int retval; kobject_init(kobj, ktype); va_start(args, fmt); retval = kobject_add_varg(kobj, parent, fmt, args); va_end(args); return retval; } 4.表示增加kobject结构体的引用计数 truct kobject *kobject_get(struct kobject *kobj) { if (kobj) kref_get(&kobj->kref); return kobj; } 5.表示减少kobject结构体的引用计数, 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); } } 6.表示用于设置kobject结构体名字的函数 int kobject_set_name(struct kobject *kobj, const char *fmt, ...) { va_list vargs; int retval; va_start(vargs, fmt); retval = kobject_set_name_vargs(kobj, fmt, vargs); va_end(vargs); return retval; } int kobject_rename(struct kobject *kobj, const char *new_name); 7.将kobject加入设备驱动模型中的函数 static int kobject_add_varg(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); } 8.用于向设备驱动模型中添加kobjct结构体的函数 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); /* join kset if set, use it as parent if we do not already have one */ if (kobj->kset) { if (!parent) parent = kobject_get(&kobj->kset->kobj); kobj_kset_join(kobj); kobj->parent = parent; } pr_debug("kobject: '%s' (%p): %s: parent: '%s', set: '%s'n", kobject_name(kobj), kobj, __func__, parent ? kobject_name(parent) : " kobj->kset ? kobject_name(&kobj->kset->kobj) : " error = create_dir(kobj); if (error) { kobj_kset_leave(kobj); kobject_put(parent); kobj->parent = NULL; /* be noisy on error issues */ if (error == -EEXIST) printk(KERN_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 printk(KERN_ERR "%s failed for %s (%d)n", __func__, kobject_name(kobj), error); dump_stack(); } else kobj->state_in_sysfs = 1; return error; } 9.从设备驱动模型中删除一个kobject对象 void kobject_del(struct kobject *kobj) { if (!kobj) return; sysfs_remove_dir(kobj); //从文件系统中删除kobj对象 obj->state_in_sysfs = 0; //表示该kobj没有在sysfs中 kobj_kset_leave(kobj); // kobject_put(kobj->parent); //减少父目录的引用计数 kobj->parent = NULL; //将父目录设为空 } 三. 操作kobj_type结构体用的函数和结构体 struct kobj_type { void (*release)(struct kobject *kobj); //释放kobject结构体占用的资源,该函数用驱动开发者去实现 struct sysfs_ops *sysfs_ops; //操作下一个属性数组的方法 struct attribute **default_attrs; // 属性数组 }; 以下的函数和结构体在#include struct attribute { const char *name; // 属性的名字,对应某个目录下的属性文件 struct module *owner; // 指向拥有该属性的模块 mode_t mode; // 属性读写权限 S_IRUGO:表示可读,S_IWUGO:表示可写,S_IRWXUGO:表示可读可写 }; struct sysfs_ops { ssize_t (*show)(struct kobject *, struct attribute *,char *); //写属性操作函数 show函数用于读取一个属性到用户空间,读取成功返回读取到的字节数 ssize_t (*store)(struct kobject *,struct attribute *,const char *, size_t); //写属性操作函数, store函数用于将属性写入内核 }; 10.非默认属性的建立函数 static inline int sysfs_create_file(struct kobject *kobj, const struct attribute *attr) { return 0; } 11.非默认属性的移除函数 static inline void sysfs_remove_file(struct kobject *kobj, const struct attribute *attr) { } 四.设备驱动模型(注册kobject到sysfs) 1.设备驱动模型用到的结构体 struct kset { struct list_head list; //链接所包含的kobject对象的链表首部 spinlock_t list_lock; //维护list链表的自旋锁 struct kobject kobj; //指向kobject结构体的变量,说明kset也是一个目录 struct kset_uevent_ops *uevent_ops; //热插拔事件用到的函数 }; 2.热插拔事件用到的函数 一个热插拔事件是从内核空间发送到用户空间的通知,表明系统某些部分的配置已经发生变化,例如,当U盘插入到USB系统时,会产生一个热插拔事件,内核会捕捉到这个事件并调用用户空间的/***in/hostplug程序,改程序通过加载驱动程序来响应U盘的插入动作。 当内核调用kobject_add()和kobject_del()函数时,会产生热插拔事件。并执行kset_uevent_ops结构体中定义的函数。 struct kset_uevent_ops { int (*filter)(struct kset *kset, struct kobject *kobj); // 通过此函数可以决定是否向用户空间发送事件产生信号,如果filter返回0,表示不产生事件,如果filter返回1,表示产生事件, const char *(*name)(struct kset *kset, struct kobject *kobj); //若用户空间的热插拔程序需要知道只系统的名字时调用此函数,该函数返回给用户空间程序一个字符窜数据 int (*uevent)(struct kset *kset, struct kobject *kobj, struct kobj_uevent_env *env); }; 操作kset用到的函数 Kset_init用来初始化kset对象的成员,其中最重要的是初始化kset.kobj成员,使用上面介绍的kobject_init_internal()函数 void kset_init(struct kset *k) { kobject_init_internal(&k->kobj); //初始化kset.kobj成员 INIT_LIST_HEAD(&k->list); //初始化链接kobject的链表 spin_lock_init(&k->list_lock); //初始化自旋锁,该锁用于对kobject的添加和删除等操作 } 4.该函数用于完成系统对kset的注册 int kset_register(struct kset *k); 5.该函数用于完成系统对kset的注销 void kset_unregister(struct kset *k) { if (!k) return; kobject_put(&k->kobj); } 6.kest的引用计数用到的函数,kset的引用计数由kobj成员来实现。 增加引用计数用到的函数 static inline struct kset *kset_get(struct kset *k) { return k ? to_kset(kobject_get(&k->kobj)) : NULL; } 7.减少引用计数用到的函数 static inline void kset_put(struct kset *k) { kobject_put(&k->kobj); } 通过上面的学习,主要是让大家去了解linux设备驱动模型这么一个概念,了解linux设备驱动模型中常用的一些函数,及设备驱动模型的整体构架,以及设备驱动模型在sysfs文件系统中的显示,熟悉设备驱动模型的编程方法,设备驱动模型是linux中比较难得部分,没有几年的编程经验,一般建议初学者简单的了解设备驱动模型,工作几年后再去详细的分析这部分的代码。 |
|
相关推荐
|
|
飞凌嵌入式ElfBoard ELF 1板卡-CAN编程示例之开发板测试
654 浏览 0 评论
该问题是用APP给芯海科技的CST92F25芯片发指令是出现的
2242 浏览 1 评论
771 浏览 0 评论
1530 浏览 1 评论
2276 浏览 1 评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-11-22 13:27 , Processed in 0.640170 second(s), Total 66, Slave 48 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号