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函数会调用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值