Linux设备模型分析之(一):设备模型核心

Linux设备模型分析之(一):设备模型核心
Linux设备模型分析之(二):设备模型的基石
Linux设备模型分析之(三):sysfs
Linux设备模型分析之(四):class
Linux设备模型分析之(五):uevent

设备模型其实是Linux内核为了管理硬件上的设备和对应的驱动制定的一套软件体系。

Linux设备模型的核心是使用bus、class、device、driver四个核心数据结构,将大量的、不同功能的硬件设备以及驱动,以树状结构的形式,进行归纳、抽象,从而方便内核的统一管理。

用device和driver两个数据结构,分别从“有什么用”和“怎么用”两个角度描述硬件设备。

bus,即总线,如下图:
在这里插入图片描述
SoC系统中有spi,i2c,usb等实体总线用于外设的连接,而针对集成在SoC中的外设控制器,Linux内核提供一种虚拟总线platform用于这些外设控制器的连接,此外platform总线也可用于没有实体总线的外设。bus还管理着device与driver的匹配。

class:它主要是集合具有相似功能或属性的设备,这样就可以抽象出一套可以在多个设备之间共用的数据结构和接口函数。

设备模型的核心

device和driver是Linux驱动开发的基本概念。驱动开发,就是要开发指定的driver以驱动指定的设备。一个driver可以支持多个设备(同类型设备)。

bus,在设备驱动模型中,它管理着deivce与driver的匹配。总线有设备链表和驱动链表,注册设备或驱动到内核时,会将他们挂入对应的链表,并负责去完成匹配,进而回调驱动的probe函数。

内核使用struct bus_type结构体描述一个总线,定义如下:

struct bus_type {

  	//总线的name
	const char		*name;
	......

 
  	//当任何属于该总线的设备或者驱动注册到内核时,内核都会调用该接口,判断设备与驱动是否匹配
	int (*match)(struct device *dev, struct device_driver *drv);
   
  
	//当设备与驱动匹配配对成功后,调用probe函数做进一步处理
	int (*probe)(struct device *dev);

	//注销设备或驱动会调用remove函数
	int (*remove)(struct device *dev);

	......

	struct subsys_private *p;
	
};

struct subsys_private {
	......
	struct klist klist_devices;  //设备链表
	struct klist klist_drivers;  //驱动链表
	......
};

内核实现的一些总线,如platform_bus_type、i2c_bus_type、spi_bus_type等。

/* drivers/base/platform.c */
struct bus_type platform_bus_type = {
	.name		= "platform",
	.dev_groups	= platform_dev_groups,
	.match		= platform_match,
	.uevent		= platform_uevent,
	.pm		= &platform_dev_pm_ops,
};

/* drivers/i2c/i2c-core.c */
struct bus_type i2c_bus_type = {
	.name		= "i2c",
	.match		= i2c_device_match,
	.probe		= i2c_device_probe,
	.remove		= i2c_device_remove,
	.shutdown	= i2c_device_shutdown,
};

/* drivers/spi/spi.c */
struct bus_type spi_bus_type = {
	.name		= "spi",
	.dev_groups	= spi_dev_groups,
	.match		= spi_match_device,
	.uevent		= spi_uevent,
};

struct device结构体,描述一个设备,定义如下:

struct device {

	......

	struct device_private	*p;
  
  	//该设备的name
	const char		*init_name; 
  
	......

	//设备所连接的总线
	struct bus_type	*bus;		

	//与该设备匹配的驱动
	struct device_driver *driver;	
	
	......
  
	//如果设备是通过设备树实例的化的,那么指向对应的设备节点
	struct device_node	*of_node; 
	struct fwnode_handle	*fwnode; /* firmware device node */
  
 
	//设备号
	dev_t			devt;	

	......

  	//该设备属于哪个类
	struct class		*class;

	......

};


struct device_private {
	.......
	struct klist_node knode_driver;  //用于挂入驱动的设备链表
	struct klist_node knode_bus;     //用于挂入总线的设备链表
	......
};

struct device_driver结构体,描述一个设备驱动,定义如下:

struct device_driver {
  
	//驱动的名称
	const char		*name;

  	//驱动所连接的总线
	struct bus_type		*bus;

	......

	//匹配表
	const struct of_device_id	*of_match_table; 
	

	//设备与驱动匹配时,会调用probe回调
	int (*probe) (struct device *dev);
	
	int (*remove) (struct device *dev);

	......
 
	struct driver_private *p;
};

struct driver_private {
	......
	struct klist klist_devices;  //与该驱动匹配的设备会挂入该链表
	struct klist_node knode_bus; //用于挂入总线的驱动链表
	......
};

来个图:
在这里插入图片描述

device与driver的匹配

注册设备的流程:
在这里插入图片描述

注册驱动的流程:
在这里插入图片描述

来个示例,内核版本4.9.88,安装驱动时,要先安装vir_bus.ko。
vir_bus.c:

#include <linux/acpi.h>
#include <linux/err.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>




static int vir_device_probe(struct device *dev)
{
	//调用驱动的probe函数
	if(dev->driver && dev->driver->probe)
			return dev->driver->probe(dev);
	
	return -1;
}


static const struct of_device_id *vir_match_id(
			const struct of_device_id *id,
			struct device *dev)
{
	while (id->name[0]) {
		if (strcmp(dev->kobj.name, id->name) == 0) {
			return id;
		}
		id++;
	}
	return NULL;
}

//该函数返回1,表示匹配
static int vir_device_match(struct device *dev, struct device_driver *drv)
{
	//id表匹配
	if (drv->of_match_table)
		return vir_match_id(drv->of_match_table, dev) != NULL;
	
	//直接比较驱动与设备的name
	return (strcmp(dev->kobj.name, drv->name) == 0);
}

struct bus_type vir_bus = {
	.name		= "vir_bus",
	.match		= vir_device_match,
	.probe		= vir_device_probe,
};
EXPORT_SYMBOL_GPL(vir_bus);




static int vir_bus_init(void)
{
	int ret = 0;
	//注册总线
	ret = bus_register(&vir_bus);
	printk(KERN_ALERT"vir_bus_init!\n");
	if(ret < 0 )
	{
		printk(KERN_ALERT"bus_register failure!\n");
		return ret;
	}
		
	
	return 0;
	
}



static void vir_bus_exit(void)
{
	bus_unregister(&vir_bus);
	
}




module_init(vir_bus_init);
module_exit(vir_bus_exit);
MODULE_LICENSE("GPL");

vir_driver.c:

#include <linux/acpi.h>
#include <linux/err.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>



extern struct bus_type vir_bus;


static const struct of_device_id vir_id_table[] = {
	{.name = "vir_device"},
	{}
	
};			

int vir_probe(struct device *dev)
{
	printk(KERN_ALERT"vir_probe!\n");
	
	return 0;
}


struct device_driver vir_driver = {
	.owner = THIS_MODULE,
	.name = "vir_driver", 
	.bus = &vir_bus,  //绑定自己定义的总线
	.of_match_table = vir_id_table,
	.probe = vir_probe,
};






static int vir_driver_init(void)
{
	int ret;
	printk(KERN_ALERT"vir_driver_init!\n");
	
	ret = driver_register(&vir_driver);
	
	if(ret)
	{
		printk(KERN_ALERT"vir_driver_register failure!\n");
		return ret;
	}
	
	return 0;
	
}



static void vir_driver_exit(void)
{
	driver_unregister(&vir_driver);
	
}




module_init(vir_driver_init);
module_exit(vir_driver_exit);
MODULE_LICENSE("GPL");

vir_device.c:

#include <linux/acpi.h>
#include <linux/err.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>




extern struct bus_type vir_bus;


struct device vir_device = {
	.init_name = "vir_device", 
	.bus = &vir_bus,  //绑定自己定义的总线
};





static int vir_device_init(void)
{
	int ret = device_register(&vir_device);
	printk(KERN_ALERT"vir_device_init!\n");
	if(ret)
	{
		printk(KERN_ALERT"device_register failure!\n");
		return ret;
	}
	
	return 0;
	
}



static void vir_device_exit(void)
{
	device_del(&vir_device);
	
}




module_init(vir_device_init);
module_exit(vir_device_exit);
MODULE_LICENSE("GPL");

Makefile:

KERNELBUILD :=/lib/modules/$(shell uname -r)/build
default:
	make -C $(KERNELBUILD) M=$(shell pwd) modules
clean:
	rm -rf *.o *.ko *.mod.c .*.cmd *.markers *.order *.symvers .tmp_versions *.mod
	
	
obj-m   += vir_bus.o vir_device.o vir_driver.o

没什么意外的话,vir_driver的porbe函数会调用。

  • 2
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
LINUX设备驱动第三版_ 前言 第一章 设备驱动程序简介 设备驱动程序的作用 内核功能划分 设备和模块的分类 安全问题 版本编号 许可证条款 加入内核开发社团 本书概要 第二章 构造和运行模块 设置测试系统 Hello World模块 核心模块与应用程序的对比 编译和装载 内核符号表 预备知识 初始化和关闭 模块参数 在用户空间编写驱动程序 快速参考 第三章 字符设备驱动程序 scull的设计 主设备号和次设备号 一些重要的数据结构 字符设备的注册 open和release scull的内存使用 read和write 试试新设备 快速参考 第四章 调试技术 内核中的调试支持 通过打印调试 通过查询调试 通过监视调试 调试系统故障 调试器和相关工具 第五章 并发和竞态 scull的缺陷 并发及其管理 信号量和互斥体 completion 自旋锁 锁陷阱 除了锁之外的办法 快速参考 第六章 高级字符驱动程序操作 ioctl 阻塞型I/O poll和select 异步通知 定位设备 设备文件的访问控制 快速参考 第七章 时间、延迟及延缓操作 度量时间差 获取当前时间 延迟执行 内核定时器 tasklet 工作队列 快速参考 第八章 分配内存 kmalloc函数的内幕 后备高速缓存 get_free_page和相关函数 vmalloc及其辅助函数 per-CPU变量 获取大的缓冲区 快速参考 第九章 与硬件通信 I/O端口和I/O内存 使用I/O端口 I/O端口示例 使用I/O内存 快速参考 第十章 中断处理 准备并口 安装中断处理例程 实现中断处理例程 顶半部和底半部 中断共享 中断驱动的I/O 快速参考 第十一章 内核的数据类型 使用标准C语言类型 为数据项分配确定的空间大小 接口特定的类型 其他有关移植性的问题 链表 快速参考 第十二章 PCI驱动程序 PCI接口 ISA回顾 PC/104和PC/104+ 其他的PC总线 SBus NuBus 外部总线 快速参考 第十三章 USB驱动程序 USB设备基础 USB和Sysfs USB urb 编写USB驱动程序 不使用urb的USB传输 快速参考 第十四章 Linux设备模型 kobject、kset和子系统 低层sysfs操作 热插拔事件的产生 总线、设备驱动程序 类 各环节的整合 热插拔 处理固件 快速索引 第十五章 内存映射和DMA Linux的内存管理 mmap设备操作 执行直接I/O访问 直接内存访问 快速参考 第十六章 块设备驱动程序 注册 块设备操作 请求处理 其他一些细节 快速参考 第十七章 网络驱动程序 snull设计 连接到内核 net_device结构细节 打开和关闭 数据包传输 数据包的接收 中断处理例程 不使用接收中断 链路状态的改变 套接字缓冲区 MAC 地址解析 定制 ioctl 命令 统计信息 组播 其他知识点详解 快速参考 第十八章 TTY驱动程序 小型TTY驱动程序 tty_driver函数指针 TTY线路设置 ioctls proc和sysfs对TTY设备的处理 tty_driver结构详解 tty_operations结构详解 tty_struct结构详解 快速参考 参考书目 9112405-1_o.jpg (85.53 KB, 下载次数: 50)
Linux 设备模型是指 Linux 操作系统中对硬件设备进行管理的一种机制。与传统的设备驱动程序相比,Linux 设备模型设备抽象为设备树的形式,使得内核可以更加高效地管理和操作各种设备。 关于 Linux 设备模型的书籍,以下是一些值得推荐的: 1. 《Linux设备驱动开发详解》:这本书详细介绍了 Linux 设备模型的原理和实践,包含了驱动程序的开发流程、设备树的编写、设备模型中的核心概念等内容。 2. 《Linux内核设计与实现》:这是一本比较经典的 Linux 内核开发书籍,其中有章节专门讲解了设备模型的设计和实现原理,可以帮助读者全面理解 Linux 设备模型的运作机制。 3. 《Linux设备驱动》:这本书从设备编程的角度出发,介绍了 Linux 设备模型的基本概念、驱动程序的开发方法,以及在设备管理中的一些常见问题和解决方案。 除了书籍之外,网络上也有很多相关的文档和教程可以供参考。Linux 官方文档中有关于设备模型的详细说明,还有一些开源社区提供的指南和教程,也可以帮助读者更深入地了解和学习 Linux 设备模型的知识。 总的来说,熟悉 Linux 设备模型对于从事 Linux 驱动程序开发和系统调优等领域的人来说是非常重要的。通过阅读相关书籍和文档,可以帮助读者更好地理解和应用 Linux 设备模型的概念和原理,提高自己的实际开发能力。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值