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 | 属性文件 |
对象间的关系 | 符号链接 |
2.核心数据结构
设备驱动模式由kobject、kset、subsystem这三个核心数据结构组成。
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 %}