随着linux系统越来越成熟,设备驱动加入了一些新的特性,之前独立的设备驱动已经无法胜任这种工作,于是linux系统找出一种方式,让各种设备及其驱动程序有效的联系起来,形成一个“群体”,这就是设备驱动模型。
设备驱动模型最基本的元素包括:sysfs, kobject, kset。
一. sysfs是2.6版本之后加入的一个文件系统接口,它把内核里面的各种kobject, kset 联系起来,通过文件接口的方式跟外界的沟通与互动,实现用户空间与内核空间之间数据对象的交互。sysfs取代proc文件系统的部分功能,实现ioctl的功能。如果用老的设备文件的方式来控制设备,需要先open设备文件,再调用ioctl来发控制命令,需要一个完整的应用程序来实现,现在有了sysfs,一个简单的shell命令,比如cat 或 echo 命令就能完成ioctl的功能。
sysfs的初始化发生在linux系统启动阶段,调用的代码如下:
int __init sysfs_init()
{
err = register_filesystem(&sysfs_fs_type);
if(!err)
sysfs_mount = kern_mount(&sysfs_fs_type);
els
goto out_err;
}
sysfs 文件系统是居于RAM实现的文件系统,内核编译时需要指定 CONFIG_SYSFS 选项。sysfs的标准挂载点是"/sys"目录,挂载命令为:mount -t sysfs sysfs /sys
所有内核层面对sysfs文件树的操作,都显示在用户空间的"/sys”目录下。
二. koject是一个内核对象,kset是同类型的kobject对象的一个集合,kobject与kset是设备驱动模型最基本的数据单位,它们在内部的联系构成了一个设备的驱动模型。
1. kobject的数据结构定义:
struct kobject {
const char *name; //内核对象名称,在sysfs文件系统中作为一个目录,即在目录"/sys"下的一个子目录。
struct list_head entry; //将一系列的内核对象构成链表。
struct kobject *parent; //该内核对象的上层内核对象节点,构成内核对象的层次关系。
struct kset *kset; //该内核对象所属的kset对象指针,它容纳同类型的kobject 对象。
struct kobj_type *ktype; //该内核对象一组sysfs文件系统相关的操作函数和属性,不同类型的内核对象有不同的ktype。
struct sysfs_dirent *sd; //该内核对象在sysfs文件系统中对应的目录项的实例。
struct kref kref; //表示该内核对象的引用计数,核心数据是一个原子型变量,通过该成员追踪内核对象的生命周期。
};
kobject 数据结构最通用的用法是嵌入在某一个设备的对象数据结构中,比如字符设备cdev中就嵌入了一个kobject成员。
struct cdev {
struct kobject kobj;
struct module *owner;
const struct file_operations *ops;
struct list_head list;
dev_t dev;
unsigned int count;
};
2. kobject 常用操作函数
(1). int kobject_set_name(struct kobject *kobj, const char *fmt, ...); //设定kobject中name成员。
(2). void kobject_init(struct kobject *kobj, struct kobj_type *ktype); //初始化一个内核对象的数据结构。
(3). int kobject_add(struct kobject *kobj, struct kobject *parent, const char *fmt, ...); //建立kobject的层次关系,并在sysfs文件系统中创建一个目录。
(4). int kobject_init_and_add(struct kobject *kobj, struct kobj_type *ktype, struct kobject *parent, const char *fmt, ...);//实际是把kobject_init与kobject_add合在一起。
(5). struct kobject *kobject_create(void); //分配并初始化一个kobject内核对象。
(6). struct kobject *kobject_create_and_add(const char *name, struct kobject *parent); //先分配并初始化一个内核对象,然后调用kobject_add在sysfs中创建新目录
(7). void kobject_del(struct kobject *kobj); //在sysfs文件系统文件树中把内核对象对应的目录删掉,如果该对象属于某kset的话,将其从kset链表中删除。
3. kobject 的类型属性
数据结构:
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);
};
release是一个函数指针,重点的是sysfs_ops, 它的结构定义为:
struct sysfs_ops {
ssize_t (*show)(struct kobject *, struct attribute *,char *);
ssize_t (*store)(struct kobject *,struct attribute *,const char *, size_t);
const void *(*namespace)(struct kobject *, const struct attribute *);
};
sysfs_ops实际就是对struct attribute对象的操作。
三. kset
1. kset 就是一组kobject的集合,是kobject的容器。kset本身也是一个内核对象,所以需要内嵌一个kobject对象,数据结构为:
struct kset {
struct list_head list; //将其中的kobject对象构建成链表。
spinlock_t list_lock; //对链表进行访问时用来作为互斥保护用的自旋锁。
struct kobject kobj; //当前内核对象的kobject变量。
const struct kset_uevent_ops *uevent_ops; //一组函数指针,kobject发生变化时调用这些函数通知用户空间。
};
2. 主要操作函数
(1). void kset_init(struct kset *k); //初始化一个kset对象。
(2). int kset_register(struct kset *k); //初始化并向系统注册一个kset对象。
(3). struct kset *kset_create_and_add(const char *name, const struct kset_uevent_ops *uevent_ops, struct kobject *parent_kobj);//动态产生一个kset对象并加入到sysfs文件系统。
(4). void kset_unregister(struct kset *k); //将k指向的kset从系统中注销。
四. 例子实现代码
1. 包含头文件
#include <linux/kobject.h>
#include <linux/sysfs.h>
2. 定义相关变量
static struct kobject *parent;
static sturct kobject *child;
static struct kset *c_kset;
static unsigned long flag=1;
3. 属性操作函数
static ssize_t att_show(struct kobject *kobj, struct attribute *attr, char *buf)
{
size_t count=0;
count += sprintf(&buf[count], "%lu\n", flag);
return count;
}
static ssize_t att_store(struct kobject *kobj, struct attribute *attr, const char *buf, size_t count)
{
flag = buf[0] - '0';
switch(flag)
{
case 0:
kobject_uevent(kobj, KOBJ_ADD);
break;
case 1:
kobject_uevent(kobj, KOBJ_REMOVE);
break;
}
return count;
}
4. 属性对象、sysfs_ops及kobj_type对象
static struct attribute cld_att= {
.name = "cldatt",
.mode = S_IRUGO | S_IWUSR,
};
static const struct sysfs_ops att_ops= {
.show = att_show,
.store = att_store,
};
static sturct kobj_type cld_ktype= {
.sysfs_ops = &att_ops,
};
5. 初始化函数,创建并向系统加入kobject, kset
static int kobj_demo_init()
{
int err;
parent = kobject_create_and_add("pa_obj", NULL);
child = kzalloc(sizeof(*child), GFP_KERNEL);
if(!child)
return PTR_ERR(child);
c_kset = kset_create_and_add("c_kset", NULL, parent);
if(!c_kset)
return -1;
child->kset = c_kset;
err = sysfs_create_file(child, &cld_att);
return err;
}
6. 注销函数
static void kobj_demo_exit(void)
{
sysfs_remove_file(child, &cld_att);
kset_unregister(c_kset);
kobject_del(child);
kobject_del(parent);
}