platform设备驱动框架搭建分析

之前接触到的字符设备驱动是非常单纯的Linux字符设备驱动,他不具备工程中Linux驱动中的设备与驱动分离思想设备驱动的分层思想,不具备“总线-设备-驱动”模型的概念。接下来通过分析platform设备驱动模型的搭建过程来看看Linux的设备驱动模型究竟是怎样的?

platform驱动模型搭建:

(1)platform核心层:为设备层和驱动层提供注册接口、为设备层和驱动层的匹配提供标准

①搭建总线框架:

总线类结构体:

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); //#####
	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 (*suspend_late)(struct device *dev, pm_message_t state);
	int (*resume_early)(struct device *dev);
	int (*resume)(struct device *dev);
	struct dev_pm_ops *pm;
	struct bus_type_private *p; //看到这个private就有点C++类中的限定域关键字了,这个类的私有成员
};

总线类实例化:platform总线

struct bus_type platform_bus_type = {
	.name		= "platform",
	.dev_attrs	= platform_dev_attrs,
	.match		= platform_match, //关键成员
	.uevent		= platform_uevent,
	.pm		= PLATFORM_PM_OPS_PTR,
};

注册platform总线过程:

platform_bus_init()
{
	.....
	error =  bus_register(&platform_bus_type);//注册platform总线的核心工作
	.....
}
bus_register(struct bus_type *bus)
{
	//创建bus的属性文件
    retval = bus_create_file(bus, &bus_attr_uevent);
    ......
	//在/sys/bus/bus->name目录下创建devices目录
	priv->devices_kset = kset_create_and_add("devices", NULL,&priv->subsys.kobj);
	....
	//在/sys/bus/bus->name目录下创建drivers目录
	priv->drivers_kset = kset_create_and_add("drivers", NULL,&priv->subsys.kobj);
	//初始化总线设备\总线驱动链表
	klist_init(&priv->klist_devices, klist_devices_get, klist_devices_put);
	klist_init(&priv->klist_drivers, NULL, NULL);
}

核心层的功绩:初始化了klist_devices和klist_drivers两个链表,没有谈到如何判断设备和驱动匹配?“.match=platform_match”有初始化,但是什么时候被调用?

当一个驱动挂接到该总线的时候,该总线的match方法被调用。同样的,当一个设备挂接到该总线时,platform_match也会被调用。也就是说核心层只提供匹配的方法!不会帮他们去匹配,这人生大事要他们自己去完成!

这就好办了,都是挂接到总线上的时候,往后分析时肯定会遇到,先暂时放着,先看看他的实现:

platform_match(struct device *dev, struct device_driver *drv)
{
	struct platform_device *pdev = to_platform_device(dev);
	struct platform_driver *pdrv = to_platform_driver(drv);

	/* match against the id table first */
	if (pdrv->id_table) //看看drv的id_table中是否有现成匹配的设备记录
		return platform_match_id(pdrv->id_table, pdev) != NULL;

	/* fall-back to driver name match */
	return (strcmp(pdev->name, drv->name) == 0); /* match成功,strcmp返回0,语句逻辑返回1 */
}

②为设备层提供注册API、提供自动匹配接口函数

设备基类:

struct device {
	struct device			*parent;
	struct device_private	*p;
	struct kobject 			kobj;
	const char				*init_name; 	/* initial name of the device 这个就是传统的bus_id,具体到每一个设备之后当做默认值 */
	struct device_type		*type;
	......
	struct bus_type			*bus;			/* type of bus device is on */
	struct device_driver 	*driver;		/* which driver has allocated this device */
	void					*driver_data;	/* data private to the driver */
	void					*platform_data;	/* Platform specific data, device core doesn't touch it */
	......
	void					(*release)(struct device *dev);
};

派生类:platform设备

struct platform_device {
	const char		*name;
	int				id;  // 硬件设备的象征/代表
	struct device	dev; // 由此继承基类
	u32				num_resources;
	struct resource	* resource;//这个驱动使用的资源
	struct platform_device_id	*id_entry;
};

注册platform设备函数调用关系:

platform_device_register(struct platform_device *pdev)

platform_device_add(struct platform_device *pdev)

pdev->dev.bus = &platform_bus_type;

device_add(&pdev->dev);

bus_attach_device(struct device *dev)

device_attach(dev);

bus_for_each_drv(dev->bus, NULL, dev, __device_attach);

bus_for_each_drv()函数的实现:

bus_for_each_drv(struct bus_type *bus, struct device_driver *start,
		     void *data, int (*fn)(struct device_driver *, void *))
{
	......
	while ((drv = next_driver(&i)) && !error)
		error = fn(drv, data);
	......
}

分析:

首先关心他的最后一个形参(*fn),他在注册platform_device时最终被重定向到__device_attach()函数,回调函数的使用在内核源码里边屡见不鲜!因为它可以减少很多重复的代码。

现在分析的焦点转移到__device_attach函数:

__device_attach(struct device_driver *drv, void *data)
{
	struct device *dev = data;
	if (!driver_match_device(drv, dev))
		return 0;
	return driver_probe_device(drv, dev); //match成功就执行这个函数,他最终调用really_probe()函数
}
driver_match_device(struct device_driver *drv,struct device *dev)
{
	return drv->bus->match ? drv->bus->match(dev, drv) : 1; //看到这一句,上面留下的疑问就解决了:原来核心层留下的匹配判断标准match接口就是在这里被调用的!!!好爽!^_^
}
really_probe(struct device *dev, struct device_driver *drv)
{
......
	if (dev->bus->probe) //如果bus_type结构里边的probe成员有定义就优先调用他的
	{
		ret = dev->bus->probe(dev);
		if (ret)
			goto probe_failed;
	}
	else if (drv->probe) //没有就调用匹配到的drv结构里边的probe成员函数
	{
		ret = drv->probe(dev);
		if (ret)
			goto probe_failed;
	}
	driver_bound(dev);//bound是绑定的意思,即将match成功的设备加入驱动的设备链表
	......
}

③为驱动层提供API、提供自动匹配接口函数

驱动基类:

struct device_driver {
	const char		*name;
	struct bus_type		*bus;
	struct module		*owner;
	const char 		*mod_name;	/* used for built-in modules */
	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);
	struct attribute_group **groups;
	struct dev_pm_ops *pm;
	struct driver_private *p;
};

驱动派生类:

struct platform_driver {
	int (*probe)(struct platform_device *); //通常这个函数要自己去实现
	int (*remove)(struct platform_device *);
	void (*shutdown)(struct platform_device *);
	int (*suspend)(struct platform_device *, pm_message_t state);
	int (*suspend_late)(struct platform_device *, pm_message_t state);
	int (*resume_early)(struct platform_device *);
	int (*resume)(struct platform_device *);
	struct device_driver driver;  //继承基类
	struct platform_device_id *id_table;
};

注册platform_driver驱动结构体函数执行流程:

platform_driver_register(struct platform_driver *drv)
{
	/*下面进行一系列的判断,如果派生的platform_driver中没有对特有成员进行初始化,设置成默认的 */
	drv->driver.bus = &platform_bus_type;   //指向这个驱动所属的bus类型:platform
	if (drv->probe)  //有重定向
		drv->driver.probe = platform_drv_probe;
	if (drv->remove) //有重定向
		drv->driver.remove = platform_drv_remove;
	......
	return driver_register(&drv->driver); 【进入分析】
	//注册的关键材料是platform_driver->driver->bus:关键是为了注册总线的类型platform_bus_type
}
driver_register(struct device_driver *drv)
{
	......
	struct device_driver *other;
	......
	other = driver_find(drv->name, drv->bus); //在该总线上查找是否有该设备驱动名对应的驱动
	if (other) { //如果设备已经存在对应的驱动就:出错,驱动已经存在
		put_driver(other);
		printk(KERN_ERR "Error: Driver '%s' is already registered, "
			"aborting...\n", drv->name);
		return -EEXIST;
	}
	bus_add_driver(drv);  /* 在总线上添加这个驱动,成功的话最终结果:在bus/platform/drivers目录下面生成“name”对应的目录 ,并且会生成 bind  module  uevent  unbind 四个文件*/
	......
}

继续深入分析:

bus_add_driver(struct device_driver *drv)

driver_attach(drv);  /* 试图将驱动和设备绑定起来 */

bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);//到这里可以非常明显的发现和设备层做的事情非常相似,几乎是对称出现

/* 对总线上的每一个设备都会拿来执行__driver_attach,他在这里被用作回调函数,看看是否匹配,这个函数和__device_attach函数做的事情基本一样这里就不再累述了*/

(2)设备层:主要工作就是把核心层提供的API用起来

1.设置好platform_device结构体成员:主要是name、resource、num_resources、id、dev->release、

2.通过platform_device_register()把这个结构体链入核心层的klist_devices链表

(3)驱动层:同样是把核心层提供的接口函数用起来

1.设置好platform_driver结构体成员:probe、remove、driver->name

2.通过platform_driver_register()函数把这个结构体链入核心层的klist_drivers链表

3.实现probe成员函数

4.通常最后才去完成probe函数用到的材料,一般是file_operation结构体成员,这样应用层就可以通过这个接口来操作设备

时间: 2024-10-11 02:57:46

platform设备驱动框架搭建分析的相关文章

spi驱动框架全面分析,从master驱动到设备驱动

内核版本:linux2.6.32.2 硬件资源:s3c2440 参考:  韦东山SPI视频教程 内容概括: 1.I2C 驱动框架回顾 2.SPI 框架简单介绍 3.master 驱动框架 3.1 驱动侧 3.2 设备侧 4.SPI 设备驱动框架 4.1 设备册 4.2 驱动侧 5.设备驱动程序实例 1.I2C 驱动框架回顾 在前面学习 I2C 驱动程序的时候我们知道,I2C 驱动框架分为两层,一层是控制器驱动程序 i2c_adapter,它一般是由芯片厂商写好的,主要提供一个 algorithm

Linux设备驱动框架设计

引子 Linux操作系统的一大优势就是支持数以万计的芯片设备,大大小小的芯片厂商工程师都在积极地向Linux kernel提交设备驱动代码.能让这个目标得以实现,这背后隐藏着一个看不见的技术优势:Linux内核提供了一套易于扩展和维护的设备驱动框架.Linux内核本身提供一套设备驱动模型,此模型提供了Linux内核对设备的一般性抽象描述,包括设备的电源管理.对象生命周期管理.用户空间呈现等等.在设备模型的帮助下,设备驱动开发工程师从设备的一般性抽象中解脱出来.但是每个设备的具体功能实现还需要大量

Linux 设备驱动开发 —— platform设备驱动应用实例解析

前面我们已经学习了platform设备的理论知识Linux 设备驱动开发 -- platform 设备驱动 ,下面将通过一个实例来深入我们的学习. 一.platform 驱动的工作过程 platform模型驱动编程,platform 驱动只是在字符设备驱动外套一层platform_driver 的外壳. 在一般情况下,2.6内核中已经初始化并挂载了一条platform总线在sysfs文件系统中.那么我们编写platform模型驱动时,需要完成两个工作: a -- 实现platform驱动 架构就

【linux设备模型】之platform设备驱动

一.platform总线.设备和驱动 platform是一种虚拟总线,对应的设备称为platform_device,对应的驱动称为platform_driver. platform_device定义在<linux/platform_device.h>中: 1 struct platform_device { 2 const char * name; 3 int id; 4 struct device dev; 5 u32 num_resources; 6 struct resource * r

PLATFORM设备驱动

字符设备,杂项设备虽然简单,但是在工程中,比如SDK中,通常都使用platform设备驱动来实现硬件驱动,为什么呢?先看看platform设备驱动的结构: platform由两部分组成:设备--platform_device和驱动--platform_driver.它们之间通过platform总线来绑定,这个我们不需要关心,内核里面的东西. platform总线是一个虚拟的总线,跟其他总线(比如:I2C,SPI,USB,PCIE)一样,当系统注册一个设备(platform_device)的时候,

Linux字符设备驱动框架

字符设备是Linux三大设备之一(另外两种是块设备,网络设备),字符设备就是字节流形式通讯的I/O设备,绝大部分设备都是字符设备,常见的字符设备包括鼠标.键盘.显示器.串口等等,当我们执行ls -l /dev的时候,就能看到大量的设备文件,c就是字符设备,b就是块设备,网络设备没有对应的设备文件.编写一个外部模块的字符设备驱动,除了要实现编写一个模块所需要的代码之外,还需要编写作为一个字符设备的代码. 驱动模型 Linux一切皆文件,那么作为一个设备文件,它的操作方法接口封装在struct fi

块设备驱动框架详解

一.正确的理解块设备驱动的概念 1.块设备和字符设备的差异 (1)块和字符是两种不同的访问设备的策略 (2)同一个设备可以同时支持块和字符两种访问策略 (3)设备本身的物理特性决定了哪一种访问策略更适合 (4)块设备本身驱动层支持缓冲区,而字符设备驱动层没有缓冲 (5)块设备驱动最适合存储设备 2.块设备驱动的特点 (1)字符设备只能顺序访问(如串口发送数据顺序),而块设备可以随机访问(不连续块访问). (2)传统的机械式块设备(如硬盘.DVD)虽然可以随机访问,但是连续访问效率更高,因此块设备

SylixOS字符设备驱动框架

1.概述 本文档主要介绍SylixOS中字符设备驱动框架,适用于在SylixOS集成开发环境下进行字符设备驱动开发的学习. 注:文中xxx是指具体设备名,编写对应驱动时,自行命名(如RTC.COMPASS等). 2.SylixOS字符设备驱动简介 字符设备是指只能以字节为单位进行读写的设备,读取数据需按照先后顺序,不能随机读取设备内存中某一数据.常见的字符设备如:鼠标.键盘.串口等. 在SylixOS中,每个字符设备都会在/dev目录下对应一个设备文件,用户程序可通过设备文件(或设备节点)来使用

块设备驱动框架分析(二)

参考:块设备驱动之一  块设备驱动之二  块设备驱动之三 总结上一篇的块设备驱动的步骤: 1. 分配gendisk: alloc_disk static struct gendisk * ramblock_disk = alloc_disk(16); /* 次设备号个数: 分区个数+1 */2. 设置2.1 分配/设置队列: // 它提供读写能力static struct request_queue  * ramblock_queue = blk_init_queue(do_ramblock_r