linux设备驱动模型的底层架构与组织方式

目录

一、linux设备驱动模型简介

1、什么是设备驱动模型

2、优点:

二、设备驱动模型的底层架构

1、kobject

2、kobj_type

3、kset

一些概念:

引用计数

Kobject的添加

三、总线式设备驱动组织方式

1、总线(bus)

驱动框架中的总线式设计

2、设备(device)

3、驱动(driver)

4、类


一、linux设备驱动模型简介

1、什么是设备驱动模型

它是linux内核为了管理硬件上的设备和对应的驱动制定的一套软件体系

  • (1)类class、总线bus、设备device、驱动driver(对应4个结构体来描述)
  • (2)kobject(结构体,类似于一个基类)和对象生命周期 (kobject实现了一种自我管理的机制)
  • (3)sysfs(一种虚拟文件系统,使内核空间 和 用户空间建立了一种映射关系(在用户空间通过sysfs文件系统可以去操作内核空间,可以cat去读他的值,echo去写他的值等))
  • (4)udev(实现内核空间 和 用户空间信息的同步)

设备驱动模型负责统一实现和维护一些特性,诸如:电源管理、热插拔、对象生命周期、用户空间和驱动空间的交互等基础设施

2、优点

(1)代码重用。将对象抽象为总线、驱动、设备三种,各司其职。同一总线的多个驱动使用相同的总线对象。同一驱动可以关联驱动多个设备。
(2)通过sysfs文件系统,清晰了展示内核驱动模型中的层次关系。同时sysfs文件系统还提供了方便的同用户控件交互的接口。

二、设备驱动模型的底层架构

1、kobject

定义在linux_kobject.h中。

在内核中由struct kobject表示。通过这个数据结构使所有设备在底层都具有统一的接口,kobject提供基本的对象管理,它与sysfs文件系统紧密关联,每个在内核中注册的kobject对象都对应于sysfs文件系统中的一个目录。Kobject是组成设备模型的基本结构。

	
struct kobject {
 
    const char		*name;//kobject的名字,且作为一个目录的名字
    struct list_head	entry;//连接下一个kobject结构(平行层的连接)
    struct kobject		*parent;//指向父亲kobject结构体,如果父设备存在(上下层的挂接)
    struct 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;/*标记:已发出KOBJ_ADD uevent*/
    unsigned int state_remove_uevent_sent:1;/*标记:已发出的KOBJ_REMOVE uevent*/
    unsigned int uevent_suppress:1;/*标记:禁止发出uevent*/
};

(1)各种对象最基本单元,提供一些公用型服务如:对象引用计数、维护对象链表、对象上锁、对用户空间的表示等
(2)设备驱动模型中的各种对象其内部都会包含一个kobject,从而可以使用kobject的功能和服务。
(3)地位相当于面向对象体系架构中的总基类

上层结构例如device,device_driver,bus_type都嵌入了一个kobject,这相当于面向对象程序设计机制中的继承。

当kobject被嵌入到其他结构体中时,该结构体便拥有了kobject提供的标准功能。更重要的一点是,嵌入kobject结构体可以成为对象层次架构中的一部分。比如cdev结构体就可以通过其父进程指针cdev->kobj->parent和链表 cdev->kobj->entry来插入到对象层次结构中。

2、kobj_type

使用该kobject设备的共同属性
struct kobj_type {
	void (*release)(struct kobject *kobj);//释放kobject和其占用的函数
	const struct sysfs_ops *sysfs_ops;//操作读和写的方法
	struct attribute **default_attrs;//属性数组的方法(比如led的亮度属性等等)
	const struct kobj_ns_type_operations *(*child_ns_type)(struct kobject *kobj);
	const void *(*namespace)(struct kobject *kobj);
};

操作属性数组的方法
struct sysfs_ops {
	ssize_t	(*show)(struct kobject *, struct attribute *,char *);//读属性操作函数(cat方法)
	ssize_t	(*store)(struct kobject *,struct attribute *,const char *, size_t);
    //写属性操作函数(echo方法)
};

属性数组
struct attribute {
	const char		*name;  //属性的名字
	struct module		*owner;//指向用于该属性的模块,已经不常使用
	mode_t			mode;  //属性读写权限
};

(1)很多书中简称为ktype,每一个kobject都需要绑定一个ktype来提供相应功能
(2)关键点1:sysfs_ops,提供该对象在sysfs中的操作方法(show和store),show()函数用于读取一个属性到用户空间,store()函数将属性写入内核中。(属性文件的操作方法)
(2)关键点2:attribute,提供在sysfs中以文件形式存在的属性,其实就是应用接口(属性文件)

3、kset

注意,kset是kobject的上级,kobject是包含在kset里面的,kset有个指针会指向kobject

struct kset {
	struct list_head list;
	spinlock_t list_lock;//自旋锁,有其表示可以上锁
	struct kobject kobj;
	const struct kset_uevent_ops *uevent_ops;
};

	kset是kobject对象的集合体。把它看成一个容器,可将所有相关的kobject对象,比如“
全部的块设备”置于同一位置。

	kset可把kobject集中到一个集合中,而ktype描述相关类型kobject所共有的特性,它
们之间的重要区别在于:具有相同ktype的kobject可以分到不同的kset中(同一个kset中的
kobject是否具有相同的ktype呢)。kobject的kset指针指向相应的kset集合。

(1)kset的主要作用是做顶层kobject的容器类
(2)kset的主要目的是将各个kobject(代表着各个对象)组织出目录层次架构
(3)可以认为kset就是为了在sysfs中弄出目录,从而让设备驱动模型中的多个对象能够有层次有逻辑性的组织在一起

一些概念:

引用计数

kobject的主要功能之一就是为我们提供了一个统一的引用计数系统。初始化后,kobject的引用计数设置为1。只要引用计数不为0(表示有多个对象在引用该对象),那么该对象就会继续保留在内存中,也可以说是被“钉”住了,任何包含对象引用的代码首先要增加该对象的引用计数,当代码结束后则减少它的引用计数。当引用计数减为0时(此时相当于close),对象便可以被销毁,同时相关内存也都被释放。

增加一个引用计数可通过kobject_get()函数完成:
struct kobject * kobject_get(struct kobject *kobj);
该函数正常情况下返回一个指向kobject的指针,如果失败,则返回NULL指针。

减少引用计数通过kobject_put()完成:
void kobject_put(struct kobject *kobj);

Kobject的添加

仅仅初始化一个kobject是不能自动将其导出到sysfs中的,想要把kobject导入sysfs,需要用到函数kobject_add():

int kobject_add(struct kobject *kobj, struct kobject *parent, const char *fmt, ...);

 kobject在sysfs中的位置取决于kobject在对象层次结构中的位置。如果kobject的父指针被设置,那么在sysfs中kobject将被映射为其父目录下的子目录,如果parent没有设置,那么kobject将被映射到kset->kobj中的子目录。两者都未设置,映射为 sysfs下的根级目录。

    如果一个kset被关联到一个kobject,那么在kobject_add()调用中该kobject的父节点可被设置为NULL(就是kobject_add()的第二个参数设置为NULL),并且,该kobject的父节点就是那个kset本身。

三、总线式设备驱动组织方式

1、总线(bus)

驱动框架中的总线式设计

在这里插入图片描述

 总线分为usb/pci/iic等多种总线,其中的某个总线又可划分为设备和驱动两部分,当添加一个USB设备时,会将其划分给USB总线,寻找相应的驱动。

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;  //总线的私有数据
};

bus_type结构体中:bus_type_private 与 bus_attribute

总线私有数据

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);//属性写函数
};

内核中 usb总线的定义


在这里插入图片描述


内核中ac97总线的定义


在这里插入图片描述

2、设备(device)

在Linux设备驱动模型中,每一个设备都有一个device结构体来描述。device结构体包含了该设备所具备的通用信息,对于驱动开发人员,当遇到新设备时,需要定义一个新的设备结构体,将device作为新结构体的成员,这样就可以在新结构体中定义新设备的一些信息,而设备通用的信息就使用device结构体来表示。

device中的大多数函数被内核使用,驱动开发人员不用关注

(1)struct device是硬件设备在内核驱动框架中的抽象
(2)device_register用于向内核驱动框架注册一个设备

int __must_check device_register(struct device *dev);

(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、驱动(driver)

(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;
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值