设备模型的引入
在Linux系统中存在成千上万的驱动代码,试想一下:每一个驱动对应着一个硬件设备,那样每增加一个硬件设备就需要重新修改驱动的所操作的硬件地址去适配这个硬件,这样有两个麻烦:
1.对于驱动的适配修改增加了工作量。
2.不利于后期统一维护,这样在大规模的系统中增加了不必要的工作量。
所以就引入了驱动模型的概念,这样减少了驱动的适配的工作量。其主要思想是把硬件设备和驱动分离的设计概念。
主要有两种:
1.总线-设备-驱动模型
2.类模型
本章主要讲解类模型的实现以及类模型中的设备接口和类接口还有class_interface接口。
类的概念
在设备模型中引入面向对象的概念,这样可以最大限度地抽象共性,减少驱动开发过程中的重复劳动,降低工作量。类本质上是把设备添加和类下面的设备驱动注册分离,其主要数据是在class->class_private§->class_devices 和class->class_private§->class_interfaces存放,前者存放的是设备,后者存放的是类接口。
类的数据结构:
struct class{
const char *name; //类的名字主要显示在/class/xxxx下面,在类注册时候拷贝到class->p->class_subsys.kobj.name下面
struct module *owner; //所属模块
struct class_attribute *class_attrs; //类属性,主要/sys/class/xxx类下面创建一些文件
struct device_attribute *dev_attrs; //设备属性,主要在设备kobj下面创建文件
struct kobject *dev_kobj;
int (*dev_uevent)(struct device *dev, struct kobj_uevent_env *env);
char *(*devnode)(struct device *dev, mode_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);
const struct dev_pm_ops *pm;
//类私有的数据
struct class_private *p;
}
struct class_private {
struct kset class_subsys; //类子系统的kset
struct klist class_devices; //类下所挂载的设备
struct list_head class_interfaces; //类下所注册的interface 接口
struct kset class_dirs; //主要存放着kobj/kset之间的关系
struct mutex class_mutex;
struct class *class; //指向自身类
};
其数据结构如下表示:
类的工作流程
1. 类注册
类注册过程中主要实现以下工作:
主要如下:
1. 初始化类下面的class->class_private数据结构
2. 把类名拷贝到class_private->class_sybsys的kset下
3. 类的dev_kobj指针指向初始化(指向块kobject还是字符设备KOBJECT还是其他)
2. 向类中添加设备:
以下工作流程是向勒种添加设备的工作流程
3.向类中添加接口:
以下是向类中添加类接口的操作,操作完成之后要遍历类下挂载所有的class_devices操作,对devices接口进行注册。
以上为类接口的一些基本操作。下面讲解一下类的相关函数。
类操作的相关函数。
class_register/class_unregister
int __class_register(struct class *cls, struct lock_class_key *key)
{
struct class_private *cp;
int error;
pr_debug("device class '%s': registering\n", cls->name); //显示类名字
cp = kzalloc(sizeof(*cp), GFP_KERNEL); //申请class_private内存
if (!cp)
return -ENOMEM;
klist_init(&cp->class_devices, klist_class_dev_get, klist_class_dev_put); //设备链表初始化
INIT_LIST_HEAD(&cp->class_interfaces); //类接口链表初始化
kset_init(&cp->class_dirs); //初始化cp->class_dirs
__mutex_init(&cp->class_mutex, "struct class mutex", key); //加锁操作
/***把类名字拷贝到CP->class_subsys.kobj下****/
error = kobject_set_name(&cp->class_subsys.kobj, "%s", cls->name);
if (error) {
kfree(cp);
return error;
}
//如果默认传入的dev_kojbect为空给一个字符设备的kobject
/* set the default /sys/dev directory for devices of this class */
if (!cls->dev_kobj)
cls->dev_kobj = sysfs_dev_char_kobj;
#if defined(CONFIG_SYSFS_DEPRECATED) && defined(CONFIG_BLOCK)
/* let the block class directory show up in the root of sysfs */
if (cls != &block_class)
cp->class_subsys.kobj.kset = class_kset;
#else
cp->class_subsys.kobj.kset = class_kset;
#endif
// /sys/class/xx类名称下面的ktype
cp->class_subsys.kobj.ktype = &class_ktype;
cp->class = cls; //指向class
cls->p = cp;
// 注册类下面的kset
error = kset_register(&cp->class_subsys);
if (error) {
kfree(cp);
return error;
}
// 生成/sys/class/xxxx下面的文件
error = add_class_attrs(class_get(cls));
class_put(cls);
return error;
}
device_add
只是向class->p->class_devices添加设备并且遍历class->p->class_interfaces链表,对于注册进入的device 执行class_intf->add_dev
class_interface_register
只是向class->p->class_interfaces添加class_interface并且遍历,对于每一个已经注册的struct device执行class_intf->add_dev操作