UT4412BV03开发板学习Linux设备驱动模型(一)
UT4412BV03开发板学习linux设备驱动模型(一)
设备驱动模型概述:
设备驱动模型比较复杂,linux系统将设备和驱动归结到设备驱动模型中来管理,设备驱动模型的提出解决了以前编写驱动时没有统一的方法的局面,设备驱动模型给各种驱动程序提供了很多辅助性的函数,这些函数经过严格测试,可以很大程度上提高驱动开发人员的工作效率。
设备驱动模型中用到的几个核心的数据结构,分别是kobjeck,kset,subsystem,这些结构体使设备驱动模型组成了一个层次结构,该层次结构将驱动,设备,总线等联系起来,形成了一个完整的设备驱动。
kobject结构体提供了一个最基本的设备对象管理能力,每一个在内核中注册的kobject对象都对应于sysfs文件中的一个目录。
对应头文件:#include<linux/kobject.h>
对应源文件: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 <linux/kobject.h>
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 <linux/kobject.h>
struct kobj_type {
void (*release)(struct kobject *kobj); //释放kobject结构体占用的资源,该函数用驱动开发者去实现
struct sysfs_ops *sysfs_ops;
struct attribute **default_attrs;
};
用到的头文件#include <linux/sysfs.h>
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 <linux/sysfs.h>
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): %s\n", 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) : "<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;
/* 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 <sysfs.h>中
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系统时,会产生一个热插拔事件,内核会捕捉到这个事件并调用用户空间的/sbin/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中比较难得部分,没有几年的编程经验,一般建议初学者简单的了解设备驱动模型,工作几年后再去详细的分析这部分的代码。