文章目录
设备驱动模型
相关介绍
简单理解起来,设备驱动模型主要作用有两点:
- 在sysfs下形成树状层次结构,然后由sysfs文件系统作为设备入口。(以Kobject/Kset为主)
- 实现device、bus、driver的统一管理,方便设备与驱动的match和probe。
框架大概是这样的:
Sysfs |
---|
Kobject/Kset |
device–bus–driver |
platform … SPI I2C |
/sys 文件系统下的目录结构
/sys/devices | 对所有设备的分层次表达 |
---|---|
/sys/dev | 维护一个按字符设备和块设备的主次号码(major:minor)链接到真实的设备(/sys/devices下)的符号链接文件 |
/sys/bus | 这是内核设备按总线类型分层放置的目录结构, devices 中的所有设备都是连接于某种总线之下,在这里的每一种具体总线之下可以找到每一个具体设备的符号链接,它也是构成 Linux 统一设备模型的一部分; |
/sys/class | 这是按照设备功能分类的设备模型,如系统所有输入设备都会出现在 /sys/class/input 之下,而不论它们是以何种总线连接到系统。 |
/sys/block | 这里是系统中当前所有的块设备所在 ,由于历史遗留问题一直存在,但在2.6.22开始已经被标记过时,为了向后兼容才保留着,但其内容已经链接到 /sys/devices/ 中真实设备的符号链接文件 。 |
/sys/firmware | 这里是系统加载固件机制的对用户空间的接口,关于固件有专用于固件加载的一套API,在附录 LDD3 一书中有关于内核支持固件加载机制的更详细的介绍; |
/sys/fs | 这里按照设计是用于描述系统中所有文件系统,包括文件系统本身和按文件系统分类存放的已挂载点 |
/sys/module | 这里有系统中所有模块的信息,不论这些模块是以内联(inlined)方式编译到内核映像文件(vmlinuz)中还是编译为外部模块(ko文件),都可能会出现在 /sys/module 中 |
/sys/power | 这里是系统中电源选项,这个目录下有几个属性文件可以用于控制整个机器的电源状态,如可以向其中写入控制命令让机器关机、重启等。 |
不同的硬件平台上的设备是多种多样的,为了使设备都能抽象出来进行统一管理,Linux内核定义了一个kobject结构体,/sys下的每个目录都是通过kobject来抽象的,而kset即是一个特殊的kobject,也是同类型的kobject的一个集合,kobject和sysfs自然的绑定在一起,这样能方便组织驱动中的各种联系。
在Linux设备驱动模型中,主要需要的基本结构为
类型 | 内容 | 数据结构 | /sys目录 |
---|---|---|---|
设备(Devices) | 设备是此模型中最基本的类型,以设备本身的连接按层次组织 | strust device | /sys/devices/* |
设备驱动(Device Drivers) | 在一个系统中安装多个相同设备,只需要一份驱动程序的支持 | struct device_driver | /sys/bus/pci/drivers/* |
总线(Bus) | 在整个总线级别对此总线上连接的所有设备进行管理 | struct bus_type | /sys/bus/* |
设备类别(Device Classes) | 这是按照功能进行分类组织的设备层次树;如 USB 接口和 PS/2 接口的鼠标都是输入设备,都会出现在 /sys/class/input/ 下 | struct class | /sys/class/* |
kobject/kset
从内核实现sysfs层次结构时所使用的数据结构角度来入手分析,Linux 统一设备模型又是以两种基本数据结构(Kobject/Kset)进行树型和链表型结构组织的。
/******************************************************************************
* Kobject是基本的数据类型,每个Kobject都会在"/sys/“文件系统中以目录的形式出现。
* struct kref 内含一个 atomic_t 类型用于引用计数, parent 是单个指向父节点的指针, entry
* 用于父 kset 以链表头结构将 kobject 结构维护成双向链表;
*******************************************************************************/
struct kobject {
const char *name; //Kobject名字
struct list_head entry; //用于把kobject添加到Kset中的list_head
struct kobject *parent;//指向parent kobject,以此形成结构
struct kset *kset; //该kobject属于的kset,可以设置为NULL。如果存在,且还未制定parent,会默认kset为parent
struct kobj_type *ktype; //该Kobject属于的kobj_type,每个Kobject必须有一个ktype。
struct kernfs_node *sd; /* sysfs directory entry */
struct kref kref; //用于原子操作的引用计数。
#ifdef CONFIG_DEBUG_KOBJECT_RELEASE
struct delayed_work release;
#endif
unsigned int state_initialized:1;//表示是否初始化
unsigned int state_in_sysfs:1; //指示该Kobject是否已在sysfs中呈现,以便在自动注销时从sysfs中移除。
unsigned int state_add_uevent_sent:1;
unsigned int state_remove_uevent_sent:1;
unsigned int uevent_suppress:1; //如果该字段为1,则表示忽略所有上报的uevent事件。
};
/******************************************************************************
* kset类型是属于特定子系统的一组特定类型的kobjects。
* KSET定义了一组Kobjects。它们可以是不同的“类型”,但总体上,这些Kobjects都希望被分组在一
* 起,并以相同的方式操作。KSET用于定义Kobjects中发生的属性回调和其他常见事件。
*******************************************************************************/
struct kset {
struct list_head list; //该kset所有kobjects的list
spinlock_t list_lock; //
struct kobject kobj; //嵌入的kobject
const struct kset_uevent_ops *uevent_ops; //kset操作函数
} ;
这三个kobjects同属于一个kset,在没有指定parent的情况下,默认为它们的parent为kset内嵌的kobject。
kobj_type
有一些kobject下会有一些属性,在 /sys 下表现形式就是文件,通过这些文件可以向用户提供一些接口操作。
/* /include/linux/kobject.h */
struct kobj_type {
void (*release)(struct kobject *kobj); //释放kobject函数接口
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);
};
/* /include/linux/sysfs.h */
struct attribute {
const char *name;
umode_t mode;
#ifdef CONFIG_DEBUG_LOCK_ALLOC
bool ignore_lockdep:1;
struct lock_class_key *key;
struct lock_class_key skey;
#endif
};
attribute, 属性。它以文件的形式输出到sysfs的目录当中。在kobject对应的目录下面。文件名就是name。文件读写的方法对应于kobj type中的sysfs ops。
小结
在Linux统一设备模型中,核心内容就是使用Bus、Class、Device、Driver四个核心数据结构,将大量不同功能的硬件设备以及驱动,以树状结构进行归纳、抽象,从而进行统一管理这也是kobject产生的原因。
目前为止,Kobject在设备模型中主要提供的功能为:
- 通过parent指针,可以将所有Kobject以层次结构的形式组合起来。
- 使用一个引用计数 kref,来记录Kobject被引用的次数,并在引用次数变为0时把它释放(这是Kobject诞生时的唯一功能)。
- 和sysfs虚拟文件系统配合,将每一个Kobject及其特性,以文件的形式,开放到用户空间。
由于kobject可以自动释放,使得kobject必须是动态分配的。而kobject大多数时候是内嵌在大型的数据结构中(如Kset、device_driver等),因此这些大型数据结构也是需要动态分配、动态释放的,释放的时机是内嵌的Kobject释放的时候。但是Kobject的释放是由Kobject模块自动完成的(在引用计数为0时),那么怎么一并释放包含自己的大型数据结构呢?
这时Kobj_type就派上用场了。我们知道,Kobj_type中的release回调函数负责释放Kobject(甚至是包含Kobject的数据结构)的内存空间,那么Kobj_type及其内部函数,是由谁实现呢?是由上层数据结构所在的模块!因为只有它,才清楚Kobject嵌在哪个数据结构中,并通过Kobject指针以及自身的数据结构类型,找到需要释放的上层数据结构的指针,然后释放它。
由此可知,每一个内嵌Kobject的数据结构,例如kset、device、device_driver等等,都要实现一个Ktype,并定义其中的回调函数。同理,sysfs相关的操作也一样,必须经过ktype的中转,因为sysfs看到的是Kobject,而真正的文件操作的主体,是内嵌Kobject的上层数据结构!
bus、device和driver的数据类型
/******************************************************************************
* bus_type 设备总线类型
*******************************************************************************/
struct bus_type {
const char *name; // 总线名称
const char *dev_name; // 用于子系统枚举像("foo%d",dev->id)这样的的设备
struct device *dev_root; // 用作父级的默认设备
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);
// 回调函数,用于匹配 device和driver
int (*uevent)(struct device *dev, struct kobj_uevent_env *env);
// 回调函数,在device发生添加、移除或者其他动作时候调用,用来修改环境变量
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);
int (*dma_configure)(struct device *dev);
const struct dev_pm_ops *pm;
const struct iommu_ops *iommu_ops;
struct subsys_private *p; //私有数据,内有kset、klist
struct lock_class_key lock_key;
bool need_parent_lock;
};
/* /drivers/base/base.h */
struct subsys_private {
struct kset subsys; //在bus_type中代表bus本身,它下面可以包含其它的kset或者其它的kobject;
struct kset *devices_kset; //bus下面所有的device
struct list_head interfaces; //用于保存该bus下所有的interface。
struct mutex mutex