热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

LINUXPLATFORM总线、设备及驱动接口分析

前面几章我们分析了LINUX设备驱动模型,说明了总线注册、设备注册、驱动注册以及它们之间是如何绑定的。借助之前的分析基础,本文以platform模块为例

 前面几章我们分析了LINUX设备驱动模型,说明了总线注册、设备注册、驱动注册以及它们之间是如何绑定的。借助之前的分析基础,本文以platform模块为例,介绍系统中如何创建总线、设备以及驱动的实例。

 

platform总线是一个虚拟的总线,该模块的出现实现了资源与驱动的隔离。一般没有驱动程序

仅实现platform-driver,该模型一般用于各具体的设备驱动模块(如i2c、spi、pcie、gpio、watchdog等),这样即可通过platform-device将各具体设备驱动相关的资源信息(如中断号、寄存器地址等)从驱动中抽离出来,使得驱动模块更通用。

如我们实现一个I/O引脚模拟jtag引脚的字符驱动程序,在不使用platform模块时,那我们在

该字符驱动文件中定义使用的具体I/O值,这就使得该字符设备驱动不通用了(若下一个硬件版本修改了I/O引脚值,是否需要修改该字符设备驱动呢?)。若借助platform模块实现该字符设备驱动,则可以达到字符设备驱动的通用性,我们将引脚信息通过platform-device传递,而在platform-driver的probe接口中,我们进行字符设备驱动的创建,并传递I/O引脚的信息。通过platform-driver、cdev-add这两个模块,就使我们的I/O模拟jtag引脚的字符设备驱动更加通用,不需要依赖硬件开发板的改变而更新驱动。

platform模块与linux设备树的结合,真的是linux内核的一大特点,将之前linux板级文件更改

需要重新编译内核的步骤彻底摆除了。在linux系统内核中,只需要在内核启动时解析设备树文件,并调用platform的接口,为设备树中的节点创建platform设备,然后在具体的platform设备驱动加载时,解析注册的platform-device设备的资源信息,获取这些资源信息,并进行具体模块驱动的注册(misc_register、i2c_add_numbered_adapter、input_register_device、pci_register_driver等)。

 

本次platform模块,我们依旧从如下几个方面分析:

  1. 相关结构体分析及关联图说明
  2. Platform bus定义
  3. platform模块初始化
  4. platform总线相关接口分析
  5. platform设备相关接口分析
  6. platform驱动相关接口分析

 

相关结构体分析及关联图说明

        既然platform模块的实现时继承自device-bus-driver模型,因此其device、driver结构体定义应该也是包含device-driver、device类型的成员变量,从而实现继承功能。此处我们从platform模块的功能入手,来说明platform相关结构体。

针对platform模块而言相关的结构体包括struct platform_driver、struct platform_device、struct platform_device_info 、struct platform_device_id 、struct resource等。

struct platform_driver

该结构体可以理解为struct device_driver的子类,该结构体在包含device_driver类型变量的

基础上,还定义了probe、remove、suspend等函数指针(有一点点类似于面向对象的多态,通过调用device_driver->probe,最终调用platform_driver->probe),而id_table主要用于说明该驱动可匹配的设备名称,用于platform的device-driver匹配时使用。

该结构体的定义如下,

 

struct platform_driver {/*驱动的probe、remove等,在在device-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 (*resume)(struct platform_device *);/*可以将device_driver理解为基类,则platform_driver为子类*/struct device_driver driver;/*标识该驱动所识别的device信息(主要是根据name值进行platform device-driver的绑定)*/const struct platform_device_id *id_table;};

 

   相对来说就是包裹了device_driver类型的变量,同时增加该driver支持设备的id_table,以及针对device_driver的probe、remove、suspend、resume接口的自定义内容(类似于面向对象中子类重写父类的方法)。

 

 

 

 

struct platform_device

该结构体用于表征一个platform device,其也内嵌了struct device类型的变量,以便继承

device类型的特性,同时完成与driver、bus的关联。该结构体还包含了资源类型的变量,以便传递给platform驱动,由驱动进行申请资源,同时也实现了资源与驱动的隔离。

 

struct platform_device {const char *name;int id;bool id_auto;struct device dev;u32 num_resources;/*该platform设备对应的资源,通过该变量实现设备资源与设备驱动的抽离,使驱动更加通用*/struct resource *resource;/*该变量为对应driver中的id_table。针对不支持设备树的linux,则通过id_table中的name并与platform->name进行匹配;针对支持设备树的linux,则会根据device->of_node与device_driver->of_match_table中的参数匹配。设备驱动模型通过device_driver、device->of_node实现了对设备树的支持。*/const struct platform_device_id *id_entry;/* MFD cell pointer */struct mfd_cell *mfd_cell;/* arch specific additions */struct pdev_archdata archdata;
};

struct resource结构体

在上面说明中,我们知道platform模块很好的将资源与驱动抽离,因此必然存在表示资源的结

构体变量,且与platform_device关联。该结构体的定义如下,其可以表示寄存器地址及区间,也可以表示中断信息等。

/*资源信息,包含寄存器地址空间、中断等*/
struct resource {resource_size_t start;resource_size_t end;const char *name;unsigned long flags;struct resource *parent, *sibling, *child;
};

struct platform_device_info

该结构体用于描述一个platform device的信息,根据该结构体变量传递的信息,可以创建一个platform device。该结构体主要和platform device的创建有关,和platform device-bus-driver的关联倒是不大。

/*该结构体主要用于描述待一个platform device的信息,包括该platform-device对应的parent-device
该platform-device的名称、该platform-device所包含的资源、dma信息等,根据该
信息可以创建一个platform device*/
struct platform_device_info {struct device *parent;struct acpi_dev_node acpi_node;const char *name;int id;const struct resource *res;unsigned int num_res;const void *data;size_t size_data;u64 dma_mask;
};

 

platform bus-device-driver的关联

    以上即是platform模块相关的结构体,主要说明了一个platfom 模块如何使用linux设备驱动模型中的结构体及函数指针实现具体的总线模块(可以理解为面向对象中的多态)。下面我们基于之前对linux设备驱动模型的学习基础,将platform device、driver与设备驱动模块的关联描述一下(我们之前在设备驱动模型中已经介绍过,此处还是给出它们三者的关联图,希望对设备驱动模型不熟悉的人也可以快速理解)。

 

如下图所示,platform-device、platform-driver借助其中的device类型以及device_driver

类型的成员变量,实现bus_type、device、device_driver等设备模型关联图的建立,从而可以借助设备驱动模型中定义的接口,实现设备驱动的绑定等功能。下图在细节上可能需要斟酌,但对于描述platform-device、platform-driver、bus_type、device、device_driver、kobject等的关联来说,也已经足够了,基本上理解了下图间结构体的关联,基本上也可以理解platform 总线注册与否、driver注册与注销、device的注册与注销的实现了。(关于设备驱动模型、sysfs文件系统等内容的实现,我已经在之前的文档中分析过,需要了解的童鞋可查看我之前的分析文档)。

 

platform总线的定义

platform总线的定义如下,platform总线没有定义probe接口,因此在进行device-driver的绑定时,将直接调用device类型变量的probe接口进行探测操作。

其中也定义了platform设备的默认device属性,针对platform类型的设备,均会定义一个默

认属性,即在每一个platform设备对应的目录下,均会创建一个名为“modalias”的文件。

而match接口platform_match,则定义了platform模块device-driver匹配的通用接口,该接

口中包含了针对支持设备树以及不支持设备树的匹配方式:

若不支持设备树,则根据id_table中的变量进行device-driver的匹配;

若支持设备树,则借助device->of_node与device_driver->of_match_table中的参数进行device-driver的匹配验证。

而uevent接口则主要将该platform总线的uevent事件,当该总线上增加设备或驱动时,会调用该接口增加该platform相关的event变量,并发送给应用层。

总线名称为platform,默认的设备属性为platform_dev_attrs*/
struct bus_type platform_bus_type = {.name = "platform",.dev_attrs = platform_dev_attrs,.match = platform_match,.uevent = platform_uevent,.pm = &platform_dev_pm_ops,
};
static struct device_attribute platform_dev_attrs[] = {__ATTR_RO(modalias),__ATTR_NULL,
};

Platform模块的初始化

针对platform模型的初始化接口platform_bus_init,包括了platform总线的注册、platforom总线对应设备的注册等。其函数实现如下:

int __init platform_bus_init(void)
{int error;early_platform_cleanup();error = device_register(&platform_bus);if (error)return error;error = bus_register(&platform_bus_type);if (error)device_unregister(&platform_bus);return error;
}

该函数其实就是调用device_register、bus_register实现了platform总线注册以及总线对应设备的注册(关于这几个接口在之前的设备驱动模型分析中已经说明过,此处不赘述)。

 

 

platform设备注册

platform设备的注册,主要也就是调用device_register接口,将platform device注册到platform总线上,并试图完成platform device-driver的绑定操作。

针对platform设备注册的接口包括platform_driver_register、platform_device_add等,其中platform_register就是调用device_initialize、platform_device_add实现对device的初始化及注册操作,而针对platform_device_add接口,其实现流程如下,在完成针对platform相关的设备名称、依附总线、所包含资源的链接外,即调用device_add实现设备的注册操作。

针对device_add接口,我们在之前已经分析过,主要完成device-bus的绑定(包括对应kobject之间的关联、属性创建、uevent变量的添加、class模块的关联等等),此处我们仅细说下device-driver的绑定以及设备探测相关的内容,如下图所示,通过一系统的函数调用,最终通过调用接口driver_probe_device,实现设备的探测,通过调用device_driver->probe完成对设备的探测操作(此处没有画出是调用bus->probe,因为platform总线没有实现probe接口),而driver->probe最终是调用platform_driver->probe,实现对platform设备的探测操作。

 

Platform 驱动注册

platform驱动的注册接口为platform_driver_register,该接口的定义如下,主要是设置该驱动所依附的总线为platform_bus_type,同时设置该platform的probe、remove、shutdown接口(若platform driver设置了这几个函数指针,则通过将platform_driver->driver对应的接口为platform_drv_probe、platform_drv_remove、platform_drv_shutdown等,而这几个接口最终还是会调用platform_driver中的具体接口,如上图所示)。

然后调用driver_register进行驱动的注册,所谓驱动注册主要也就是完成driver-bus的绑定(包括kobject相关的绑定、属性文件的创建、uevent设置等等),并调用bus_add_driver,实现driver-device的绑定,如上图所示,针对platform_driver->driver->probe指针,要么为空,要么为platform_drv_probe,而在platform_drv_probe接口中,则调用platform_driver->probe,调用关系如上图中虚线箭头所指。

int platform_driver_register(struct platform_driver *drv)
{drv->driver.bus = &platform_bus_type;if (drv->probe)drv->driver.probe = platform_drv_probe;if (drv->remove)drv->driver.remove = platform_drv_remove;if (drv->shutdown)drv->driver.shutdown = platform_drv_shutdown;return driver_register(&drv->driver);
}

 

Platform device、driver的注销

关于它们注销的接口,分别为platform_driver_unregister、platform_device_unregister,与上述的注册接口实现的功能刚好相反,也就是调用device_del、driver_unregister实现注销操作,此处不再分析device_del、driver_unregister。此处主要说明一下注销时detach的过程。

不管是设备的注销还是驱动的注销,通过一系统的系统调用,最终会调用drv->remove接口,实现remove操作,而remove接口即为platform_drv_remove,该接口会调用具体platform_driver->remove接口进行实现的remove操作,调用关系如下图虚线箭头所指。

以上即是detach的过程。

 

 

 

本文主要介绍了platform的概念以及platform总线存在的意义、platform模块相关的结构体、platform总线的注册、platform设备的注册与注销、platform驱动的注册与注销等。platform总线的出现,有效的将driver与设备资源进行隔开,同时也为设备树在内核的推广提供了一个较好的方式。


推荐阅读
  • 主调|大侠_重温C++ ... [详细]
  • 目录一、salt-job管理#job存放数据目录#缓存时间设置#Others二、returns模块配置job数据入库#配置returns返回值信息#mysql安全设置#创建模块相关 ... [详细]
  • 本题来自WC2014,题目编号为BZOJ3435、洛谷P3920和UOJ55。该问题描述了一棵不断生长的带权树及其节点上小精灵之间的友谊关系,要求实时计算每次新增节点后树上所有可能的朋友对数。 ... [详细]
  • Linux环境下C语言实现定时向文件写入当前时间
    本文介绍如何在Linux系统中使用C语言编程,实现在每秒钟向指定文件中写入当前时间戳。通过此示例,读者可以了解基本的文件操作、时间处理以及循环控制。 ... [详细]
  • Linux环境下进程间通信:深入解析信号机制
    本文详细探讨了Linux系统中信号的生命周期,从信号生成到处理函数执行完毕的全过程,并介绍了信号编程中的注意事项和常见应用实例。通过分析信号在进程中的注册、注销及处理过程,帮助读者理解如何高效利用信号进行进程间通信。 ... [详细]
  • 本文深入探讨了MySQL中常见的面试问题,包括事务隔离级别、存储引擎选择、索引结构及优化等关键知识点。通过详细解析,帮助读者在面对BAT等大厂面试时更加从容。 ... [详细]
  • 本文详细介绍了如何解压并安装MySQL集群压缩包,创建用户和组,初始化数据库,配置环境变量,并启动相关服务。此外,还提供了详细的命令行操作步骤和常见问题的解决方案。 ... [详细]
  • CentOS 6.8 上安装 Oracle 10.2.0.1 的常见问题及解决方案
    本文记录了在 CentOS 6.8 系统上安装 Oracle 10.2.0.1 数据库时遇到的问题及解决方法,包括依赖库缺失、操作系统版本不兼容、用户权限不足等问题。 ... [详细]
  • 丽江客栈选择问题
    本文介绍了一道经典的算法题,题目涉及在丽江河边的n家特色客栈中选择住宿方案。两位游客希望住在色调相同的两家客栈,并在晚上选择一家最低消费不超过p元的咖啡店小聚。我们将详细探讨如何计算满足条件的住宿方案总数。 ... [详细]
  • Linux系统中Java程序Too Many Open Files问题的深入解析与解决方案
    本文详细分析了在Linux环境下运行的Java应用程序中可能出现的“Too many open files”异常现象,探讨其成因及解决方法。该问题通常出现在高并发文件访问或大量网络连接场景下,对系统性能和稳定性有较大影响。 ... [详细]
  • 本文介绍如何使用MFC和ADO技术调用SQL Server中的存储过程,以查询指定小区在特定时间段内的通话统计数据。通过用户界面选择小区ID、开始时间和结束时间,系统将计算并展示小时级的通话量、拥塞率及半速率通话比例。 ... [详细]
  • 深入解析SpringMVC核心组件:DispatcherServlet的工作原理
    本文详细探讨了SpringMVC的核心组件——DispatcherServlet的运作机制,旨在帮助有一定Java和Spring基础的开发人员理解HTTP请求是如何被映射到Controller并执行的。文章将解答以下问题:1. HTTP请求如何映射到Controller;2. Controller是如何被执行的。 ... [详细]
  • 本题要求在一组数中反复取出两个数相加,并将结果放回数组中,最终求出最小的总加法代价。这是一个经典的哈夫曼编码问题,利用贪心算法可以有效地解决。 ... [详细]
  • 软件工程课堂测试2
    要做一个简单的保存网页界面,首先用jsp写出保存界面,本次界面比较简单,首先是三个提示语,后面是三个输入框,然 ... [详细]
  • 本文详细探讨了 PHP 中常见的 '未定义索引' 错误,包括其原因、解决方案及最佳实践。通过实例和代码片段,帮助开发者更好地理解和处理这一常见问题。 ... [详细]
author-avatar
孤独游侠1976_127
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有