一,linux设备驱动模型简介
1、什么是设备驱动模型
设备驱动模型其实是Linux内核为了管理硬件上的设备和对应的驱动制定的一套软件体系。那么其实设备驱动模型是一个比较抽象、比较广的一个概念,一两句话是很难说清楚的,类(class)、总线(bus)、设备(device)、驱动(driver)、mdev(自动创建设备节点和设备类)sysfs等都属于设备驱动模型的范畴。
(1)类class、总线bus、设备device、驱动driver
(2)kobject和对象生命周期
(3)sysfs
(4)udev
2、为什么需要设备驱动模型
(1)早期内核(2.4之前)没有统一的设备驱动模型,但照样可以用
(2)2.6版本中正式引入设备驱动模型,目的是在设备越来越多,功耗要求等新特性要求的情况下让驱动体系更易用、更优秀。
(3)设备驱动模型负责统一实现和维护一些特性,诸如:电源管理、热插拔、对象生命周期、用户空间和驱动空间的交互等基础设施
(4)设备驱动模型目的是简化驱动程序编写,但是客观上设备驱动模型本身设计和实现很复杂。
3、优点如下:
(1)代码重用。将对象抽象为总线、驱动、设备三种,各司其职。同一总线的多个驱动使用相同的总线对象。同一驱动可以关联驱动多个设备。
(2)通过sysfs文件系统,清晰了展示内核驱动模型中的层次关系。同时sysfs文件系统还提供了方便的同用户控件交互的接口。
二,设备驱动模型的底层架构
1、kobject
Kobject是代表驱动模型中的一个对象。总线、驱动、设备都继承了它(在结构体中包含kobject)。每个kobject在sysfs中表现为一个目录。
struct kobject {
const char *name;//kobject的名字,且作为一个目录的名字
struct list_head entry;//连接下一个kobject结构
struct kobject *parent;//指向父亲kobject结构体,如果父设备存在
struct kset *kset; //指向kset集合
struct kobj_type *ktype; //指向kobject的属性描述符
struct sysfs_dirent *sd; //对应sysfs的文件目录
struct kref kref; //kobject的引用计数
unsigned int state_initialized:1; //表示该kobject是否初始化
unsigned int state_in_sysfs:1; //表示是否加入sysfs中
unsigned int state_add_uevent_sent:1;
unsigned int state_remove_uevent_sent:1;
unsigned int uevent_suppress:1;
};
(1)各种对象最基本单元,提供一些公用型服务如:对象引用计数、维护对象链表、对象上锁、对用户空间的表示等
(2)设备驱动模型中的各种对象其内部都会包含一个kobject
(3)地位相当于面向对象体系架构中的总基类
2、kobj_type
使用该kobject设备的共同属性
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);
};
属性数组
struct attribute {
const char *name; //属性的名字
struct module *owner;//指向用于该属性的模块,已经不常使用
mode_t mode; //属性读写权限
};
操作属性数组的方法
struct sysfs_ops
{
ssize t (*show) (struct kobject *, struct attribute *, char *);/*读属性操作函数*/
ssize t (*store) (struct kobject *,struct attribute *,const char *buf, size_t count);/*写属性操作函数*/
}
(1)很多书中简称为ktype,每一个kobject都需要绑定一个ktype来提供相应功能
(2)关键点1:sysfs_ops,提供该对象在sysfs中的操作方法(show和store),show()函数用于读取一个属性到用户空间,store()函数将属性写入内核中。
(2)关键点2:attribute,提供在sysfs中以文件形式存在的属性,其实就是应用接口
3、kset
struct kset {
struct list_head list; //连接链表
spinlock_t list_lock; //链表的自旋锁
struct kobject kobj; //内嵌的kobject结构体,说明kset本身也是一个目录
const struct kset_uevent_ops *uevent_ops; //热插拔事件
};
(1)kset的主要作用是做顶层kobject的容器类
(2)kset的主要目的是将各个kobject(代表着各个对象)组织出目录层次架构
(3)可以认为kset就是为了在sysfs中弄出目录,从而让设备驱动模型中的多个对象能够有层次有逻辑性的组织在一起
三,总线式设备驱动组织方式
1、总线
驱动框架中的总线式设计
bus_type结构体,关键是match函数和uevent函数
struct bus_type {
const char *name; //总线类型名
struct bus_attribute *bus_attrs; //总线属性和导出到sysfs中的方法
struct device_attribute *dev_attrs; //设备属性和导出到sysfs中的方法
struct driver_attribute *drv_attrs; //驱动程序属性和导出到sysfs中的方法
//匹配函数,检验参数2中的驱动是否支持参数1中的设备
int (*match)(struct device *dev, struct device_driver *drv);
int (*uevent)(struct device *dev, struct kobj_uevent_env *env);
int (*probe)(struct device *dev); //探测设备
int (*remove)(struct device *dev); //移除设备
void (*shutdown)(struct device *dev); //关闭函数
int (*suspend)(struct device *dev, pm_message_t state);//改变设备供电状态,使其节能
int (*resume)(struct device *dev); //恢复供电状态,使其正常工作
const struct dev_pm_ops *pm; //关于电源管理的操作符
struct bus_type_private *p; //总线的私有数据
};
内核中 usb总线的定义
内核中ac97总线的定义:
总线私有数据
struct bus type private {
struct kset subsys;/*代表该bus子系统,里面的kobj是该bus的主kobj,也就是最顶层*/
struct kset *drivers kset;/*挂接到该总线上的所有驱动集合*/
struct kset *devices kset;/*挂接到该总线上的所有设备集合*/
struct klist klist devices;/*所有设备的列表,与devices kset中的1ist相同*/
struct klist klist drivers;/*所有驱动程序的列表,与drivers_kset中的1ist相同*/
struct blocking notifier head bus notifier;/**/
unsigned int drivers autoprobe:1;/*设置是否在驱动注册时, 自动探测(probe)设备*/
struct bus type *bus;/*回指包含自己的总线*/
}
总线属性
struct bus_attribute {
struct attribute attr;//总线属性
ssize_t (*show)(struct bus_type *bus, char *buf); //属性读函数
ssize_t (*store)(struct bus_type *bus, const char *buf, size_t count);//属性写函数
};
2、设备
在Linux设备驱动模型中,每一个设备都由一个device结构体来描述。device结构体包含了设备所具有的一些通用信息。对于驱动开发人员来说,当遇到新设备时,需要定义一个新的设备结构体,将device作为新结构体的成员。这样就可以在新结构体中定义新设备的一些信息,而设备通用的信息就使用device结构体来表示。使用device结构体的另一个好处是,可以通过device轻松地将新设备加入设备驱动模型的管理中。
device中的大多数函数被内核使用,驱动开发人员不用关注
(1)struct device是硬件设备在内核驱动框架中的抽象
(2)device_register用于向内核驱动框架注册一个设备
(3)通常device不会单独使用,而是被包含在一个具体设备结构体中,如struct usb_device
struct device {
struct klist_klist children; 连接子设备的链表
struct device *parent; 指向父设备的指针
struct kobject kobj; 内嵌的kobject结构体
char bus_id[BUS ID SIZE]; 连接到总线上的位置
unsigned uevent suppress:1; 是否支持热插拔事件
const char init_name; 设备的初始化名字
struct device_type *type; 设备相关的特殊处理函数
struct bus_type *bus; 指向连接的总线指针
struct device_driver *driver; 指向该设备的驱动程序
void *driver data; 指向驱动程序私有数据的指针
struct dev_pm info power; 电源管理信息
dev t deyt; 设备号
struct class *class; 指向设备所属类
struct attribute_group **groups; 设备的组属性
void (*release) (struct device *dev);释放设备描述符的回调函数
}
设备属性
struct device_attribute {
struct attribute attr;
ssize_t (*show)(struct device *dev, struct device_attribute *attr,
char *buf);
ssize_t (*store)(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count);
};
用来在devic目录下创建一个属性文件
int device_create_file(struct device *dev, //创建
const struct device_attribute *attr)
void device_remove_file(struct device *dev, //删除
const struct device_attribute *attr)
3、驱动
(1)struct device_driver是驱动程序在内核驱动框架中的抽象
(2)关键元素1:name,驱动程序的名字,很重要,经常被用来作为驱动和设备的匹配依据
(3)关键元素2:probe,驱动程序的探测函数,用来检测一个设备是否可以被该驱动所管理
struct device_driver {
const char *name;//设备驱动程序的名字
struct bus_type *bus;//指向驱动属于的总线
struct module *owner;//设备驱动自身模块
const char *mod_name; /* used for built-in modules */
bool suppress_bind_attrs; /* disables bind/unbind via sysfs */
#if defined(CONFIG_OF)
const struct of_device_id *of_match_table;
#endif
int (*probe) (struct device *dev);//探测设备的方法,并检测设备驱动可以控制哪些设备
int (*remove) (struct device *dev);//移除设备调用的方法
void (*shutdown) (struct device *dev);//关闭设备的方法
int (*suspend) (struct device *dev, pm_message_t state);//设备处于低功耗的方法
int (*resume) (struct device *dev);//恢复正常的方法
const struct attribute_group **groups;//属性组
const struct dev_pm_ops *pm;//电源管理
struct driver_private *p;//设备驱动私有数据
};
4、类
(1)相关结构体:struct class 和 struct class_device
(2)udev的使用离不开class
(3)class的真正意义在于作为同属于一个class的多个设备的容器。也就是说,class是一种人造概念,目的就是为了对各种设备进行分类管理。当然,class在分类的同时还对每个类贴上了一些“标签”,这也是设备驱动模型为我们写驱动提供的基础设施。
struct class {
const char *name;
struct module *owner;
struct class_attribute *class_attrs;
struct device_attribute *dev_attrs;
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 kobj_ns_type_operations *ns_type;
const void *(*namespace)(struct device *dev);
const struct dev_pm_ops *pm;
struct class_private *p;
};
参考博客:Linux设备驱动模型