设备驱动模型

CSDN仅用于增加百度收录权重,排版未优化,日常不维护。请访问:www.hceng.cn 查看、评论。
本博文对应地址: https://hceng.cn/2017/03/18/设备驱动模型/#more
最近在看设备驱动模型,记录下分析的结构。


设备驱动模型提供了硬件的抽象,内核可以使用该抽象完成很多硬件重复的工作,这样很多重复的代码就不需要重复编写和调试了。这些抽象包括:电源管理、即插即用设备的支持、与用户空间的通信。

1.sysfs文件系统

sysfs文件系统是Linux众多文件系统中的一个,在Linux中每个文件系统都要自己的特殊的用途。Liunx设备驱动模型由大量的数据结构和算法组成,数据结构之间通过指针相互关联,构成树形或者网状关系。显示这种关系最好的利用树形结构的文件系统,同时还要显示内核中一些关于设备、驱动和驱动的信息,因此内核开发者创造sysfs这种新文件系统来实现上述的要求。
  sysfs是一个只存在于内存的文件系统,内核通过这个文件系统将系统信息导出到用户空间,同时用户空间的数据也能通过它传到内核中,实现设置驱动程序的状态和属性。
  sysfs文件系统是内核对象(kobject)、属性(kobj_type)及它们互相关系的一种表现机制。内核与sysfs的关系如下:

Linux内核中的结构sysfs中的结构
kobject目录
kobj_type属性文件
对象间的关系符号链接
![](https://blog-image-1257972744.cos.ap-chongqing.myqcloud.com/hceng/blog_image/170318/1.jpg)

2.核心数据结构

设备驱动模式由kobjectksetsubsystem这三个核心数据结构组成。

2.1 kobject

在Linux系统中,kobject结构体是组成设备驱动模型的基本结构,提供最基本的设备对象管理能力,每个在内核注册的kobject对象都对应sysfs文件系统中的一个目录。

  kobject结构体:
{% codeblock lang:c %}
struct kobject {
const char name; / 对应sysfs的目录名 /
struct list_head entry; /
kobjetct双向链表 ,指向下一个kobject结构*/
struct kobject parent; / 指向kset中的kobject,相当于指向父目录 */
struct kset *kset; /*指向所属的kset */
struct kobj_type *ktype; /指向kobject的类型描述符/
struct sysfs_dirent *sd; /对应的sysfs文件目录/
struct kref kref; /kobject引用计数,为0释放/
unsigned int state_initialized:1; /kobject是否已初始化,1初0未/
unsigned int state_in_sysfs:1 ; /kobject是否注册到sysfs/
unsigned int state_add_uevent_sent:1;
unsigned int state_remove_uevent_sent:1;
unsigned int uevent_suppress:1;
};
{% endcodeblock %}
  kobject操作函数:

2.2 kset

kobject通过kset组织层次化,kest是拥有相同类型的kobject的集合。
  kset结构体:
{% codeblock lang:c %}
struct kset {
struct list_head list; /* 连接所包含的kobject对象的链表头部 /
spinlock_t list_lock; /
添加/删除kobject时使用的自旋锁 */
struct kobject kobj; /*所有属于该kset的kobject的parent均指向这 */
const struct kset_uevent_ops uevent_ops; / 热拔插事件函数集 */
};
{% endcodeblock %}
  kset操作函数:

kest和kobject关系:

2.3 注册kobject到sysfs中的实例

{% codeblock lang:c [kobject_test.c] https://github.com/hceng/learn/blob/master/kobject_test/kobject_test.c%}
/*
目的:在/sys目录下添加一个名为kobject_test的目录名,
并在该目录下添加一个kobject_test_attr的文件,这个文件为属性文件;
功能:koject_test_show()函数显示属性的值;
kobject_test_store()函数向属性中写入一个值;
*/
#include <linux/device.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/string.h>
#include <linux/sysfs.h>
#include <linux/stat.h>

/声明释放kobject结构体函数/
void kobject_test_release(struct kobject *kobject);

/读属性的函数/
ssize_t kobject_test_show(struct kobject *kobject, struct attribute *attr, char *buf);

/写属性的函数/
ssize_t kobject_test_store(struct kobject *kobject, struct attribute *attr, const char *buf, size_t count);

/定义了一个名为kobject_test,可以读写的属性/
struct attribute test_attr = {
.name = “kobject_test_attr”,
.mode = S_IRWXUGO,
};

/该object只有一个属性/
static struct attribute *def_attrs[] = {
&test_attr,
NULL,
};

/操作函数/
struct sysfs_ops obj_test_sysops = {
.show = kobject_test_show,
.store = kobject_test_store,
};

/kobject属性/
struct kobj_type ktype = {
.release = kobject_test_release,
.sysfs_ops = &obj_test_sysops,
.default_attrs = def_attrs,
};

/释放kobject结构体函数/
void kobject_test_release(struct kobject *kobject)
{
printk(“kobject_test: kobject_test_release(). \n”);
}

/该函数用于读取一个属性的名字/
ssize_t kobject_test_show(struct kobject *kobject, struct attribute *attr, char *buf)
{
printk(“call kobject_test_show(). \n”);
printk(“attrname:%s.\n”, attr->name);
sprintf(buf, “%s\n”, attr->name);
return strlen(attr->name)+2;
}

/该函数用于写入一个属性的值/
ssize_t kobject_test_store(struct kobject *kobject, struct attribute *attr, const char *buf, size_t count)
{
printk(“call kobject_test_store(). \n”);
printk(“write: %s\n”, buf);
strcpy(attr->name, buf);
return count;
}

/入口函数/
struct kobject kobj;
static int kobject_test_init()
{
printk(“kobject test_init().\n”);
kobject_init_and_add(&kobj, &ktype, NULL, “kobject_test”);
return 0;
}

/出口函数/
static int kobject_test_exit()
{
printk(“kobject test exit.\n”);
kobject_del(&kobj);
return 0;
}

module_init(kobject_test_init);
module_exit(kobject_test_exit);
MODULE_AUTHOR(“hceng”);
MODULE_LICENSE(“Dual BSD/GPL”);
{% endcodeblock %}

{% codeblock lang:c [kobject_app.c] https://github.com/hceng/learn/blob/master/kobject_test/kobject_app.c%}

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <poll.h>
#include <signal.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>

/*
kobject_test
*/
int fd;

int main(int argc, char **argv)
{
fd = open("/dev/kobject_test", O_RDWR);
if (fd < 0)
{
printf(“can’t open!\n”);
return -1;
}

return 0;

}
{% endcodeblock %}

调用关系:

3.三大组件

设备驱动模型中有三个重要的组件:总线(bus_type)、设备(device)、驱动(driver),三者关系如下:

  • bus_type通过扫描设备链表驱动链表,使用mach方法查找匹配的设备和驱动,然后将struct device中的*driver设置为匹配的驱动;
  • 将struct device_driver中的device设置为匹配的设备,这就完成了将总线、设备和驱动3者之间的关联;
  • bus_type只有很少的成员必须提供初始化,大部分由设备模型核心控制;
  • 内核提供许多函数实现bus_type的注册注销等操作,新注册的总线可以再**/sys/bus目录**下看到;

3.1总线

总线数据结构bus_type:
{% codeblock lang:c %}
struct bus_type {
const char name; / 总线类型名 */
struct bus_attribute bus_attrs; / 总线的属性、方法 */
struct device_attribute dev_attrs; / 设备属性,为每个加入总线的设备建立属性链表 */
struct driver_attribute drv_attrs; / 驱动属性,为每个加入总线的驱动建立属性链表 */

/* 驱动与设备匹配函数:当一个新设备或者驱动被添加到这个总线时,这个方法会被调用一次或多次,
若指定的驱动程序能够处理指定的设备,则返回非零值。
必须在总线层使用这个函数, 因为那里存在正确的逻辑,核心内核不知道如何为每个总线类型匹配设备和驱动程序 */
int (*match)(struct device *dev, struct device_driver *drv);
/在为用户空间产生热插拔事件之前,这个方法允许总线添加环境变量(参数和 kset 的uevent方法相同)/
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 subsys_private *p;                                /* 一个很重要的域,包含了device链表和drivers链表 */

};
{% endcodeblock %}    

  相关操作函数:

3.2设备

设备数据结构device:
{% codeblock lang:c %}
struct device {
struct device parent; / 父设备,总线设备指定为NULL */
struct device_private p; / 包含设备链表,driver_data(驱动程序要使用数据)等信息 */
struct kobject kobj;
const char init_name; / 初始默认的设备名,但@device_add调用之后又重新设为NULL */
struct device_type type;
struct mutex mutex; /
mutex to synchronize calls to its driver */
struct bus_type bus; / type of bus device is on */
struct device_driver driver; / which driver has allocated this device */
void platform_data; / Platform specific data, device core doesn’t touch it */
struct dev_pm_info power;

#ifdef CONFIG_NUMA
int numa_node; /* NUMA node this device is close to */
#endif
u64 dma_mask; / dma mask (if dma’able device) */
//Like dma_mask, but for alloc_coherent mappings as not all hardware supports 64 bit addresses
//for consistentallocations such descriptors.
u64 coherent_dma_mask;
struct device_dma_parameters dma_parms;
struct list_head dma_pools; /
dma pools (if dma’ble) */
struct dma_coherent_mem dma_mem; / internal for coherent mem override /
/
arch specific additions */
struct dev_archdata archdata;
#ifdef CONFIG_OF
struct device_node *of_node;
#endif

 dev_t devt;	 /* dev_t, creates the sysfs "dev" 设备号 */
 spinlock_t devres_lock;
 struct list_head devres_head;
 struct klist_node knode_class;
 struct class *class;
 const struct attribute_group **groups; 	/* optional groups */

 void (*release)(struct device *dev); 

};
{% endcodeblock %}  

3.3驱动

驱动数据结构driver·:
{% codeblock lang:c %}
struct device_driver {
const char name; / 驱动名称,在sysfs中以文件夹名出现 */
struct bus_type bus; / 驱动属于的总线,总线上可以有很多设备 */
struct module owner; / 设备驱动自身模块 */
const char mod_name; / 驱动模块名字 /
bool suppress_bind_attrs; /
disables bind/unbind via sysfs */

const struct of_device_id  *of_match_table;
 /*检测设备方法,并检测设备驱动可以控制那些设备*/
 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;	 /* 定义device_driver中的私有数据类型 */

};
{% endcodeblock %}  

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值