platform设备驱动框架搭建分析

2024-06-11 16:08

本文主要是介绍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类型:platformif (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结构体成员,这样应用层就可以通过这个接口来操作设备

这篇关于platform设备驱动框架搭建分析的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1051647

相关文章

修改若依框架Token的过期时间问题

《修改若依框架Token的过期时间问题》本文介绍了如何修改若依框架中Token的过期时间,通过修改`application.yml`文件中的配置来实现,默认单位为分钟,希望此经验对大家有所帮助,也欢迎... 目录修改若依框架Token的过期时间修改Token的过期时间关闭Token的过期时js间总结修改若依

本地搭建DeepSeek-R1、WebUI的完整过程及访问

《本地搭建DeepSeek-R1、WebUI的完整过程及访问》:本文主要介绍本地搭建DeepSeek-R1、WebUI的完整过程及访问的相关资料,DeepSeek-R1是一个开源的人工智能平台,主... 目录背景       搭建准备基础概念搭建过程访问对话测试总结背景       最近几年,人工智能技术

C#使用DeepSeek API实现自然语言处理,文本分类和情感分析

《C#使用DeepSeekAPI实现自然语言处理,文本分类和情感分析》在C#中使用DeepSeekAPI可以实现多种功能,例如自然语言处理、文本分类、情感分析等,本文主要为大家介绍了具体实现步骤,... 目录准备工作文本生成文本分类问答系统代码生成翻译功能文本摘要文本校对图像描述生成总结在C#中使用Deep

5分钟获取deepseek api并搭建简易问答应用

《5分钟获取deepseekapi并搭建简易问答应用》本文主要介绍了5分钟获取deepseekapi并搭建简易问答应用,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需... 目录1、获取api2、获取base_url和chat_model3、配置模型参数方法一:终端中临时将加

Redis主从/哨兵机制原理分析

《Redis主从/哨兵机制原理分析》本文介绍了Redis的主从复制和哨兵机制,主从复制实现了数据的热备份和负载均衡,而哨兵机制可以监控Redis集群,实现自动故障转移,哨兵机制通过监控、下线、选举和故... 目录一、主从复制1.1 什么是主从复制1.2 主从复制的作用1.3 主从复制原理1.3.1 全量复制

Redis主从复制的原理分析

《Redis主从复制的原理分析》Redis主从复制通过将数据镜像到多个从节点,实现高可用性和扩展性,主从复制包括初次全量同步和增量同步两个阶段,为优化复制性能,可以采用AOF持久化、调整复制超时时间、... 目录Redis主从复制的原理主从复制概述配置主从复制数据同步过程复制一致性与延迟故障转移机制监控与维

Redis连接失败:客户端IP不在白名单中的问题分析与解决方案

《Redis连接失败:客户端IP不在白名单中的问题分析与解决方案》在现代分布式系统中,Redis作为一种高性能的内存数据库,被广泛应用于缓存、消息队列、会话存储等场景,然而,在实际使用过程中,我们可能... 目录一、问题背景二、错误分析1. 错误信息解读2. 根本原因三、解决方案1. 将客户端IP添加到Re

Mycat搭建分库分表方式

《Mycat搭建分库分表方式》文章介绍了如何使用分库分表架构来解决单表数据量过大带来的性能和存储容量限制的问题,通过在一对主从复制节点上配置数据源,并使用分片算法将数据分配到不同的数据库表中,可以有效... 目录分库分表解决的问题分库分表架构添加数据验证结果 总结分库分表解决的问题单表数据量过大带来的性能

Java汇编源码如何查看环境搭建

《Java汇编源码如何查看环境搭建》:本文主要介绍如何在IntelliJIDEA开发环境中搭建字节码和汇编环境,以便更好地进行代码调优和JVM学习,首先,介绍了如何配置IntelliJIDEA以方... 目录一、简介二、在IDEA开发环境中搭建汇编环境2.1 在IDEA中搭建字节码查看环境2.1.1 搭建步

Redis主从复制实现原理分析

《Redis主从复制实现原理分析》Redis主从复制通过Sync和CommandPropagate阶段实现数据同步,2.8版本后引入Psync指令,根据复制偏移量进行全量或部分同步,优化了数据传输效率... 目录Redis主DodMIK从复制实现原理实现原理Psync: 2.8版本后总结Redis主从复制实