热门标签 | 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与设备资源进行隔开,同时也为设备树在内核的推广提供了一个较好的方式。


推荐阅读
  • Python爬虫中使用正则表达式的方法和注意事项
    本文介绍了在Python爬虫中使用正则表达式的方法和注意事项。首先解释了爬虫的四个主要步骤,并强调了正则表达式在数据处理中的重要性。然后详细介绍了正则表达式的概念和用法,包括检索、替换和过滤文本的功能。同时提到了re模块是Python内置的用于处理正则表达式的模块,并给出了使用正则表达式时需要注意的特殊字符转义和原始字符串的用法。通过本文的学习,读者可以掌握在Python爬虫中使用正则表达式的技巧和方法。 ... [详细]
  • 本文介绍了在处理不规则数据时如何使用Python自动提取文本中的时间日期,包括使用dateutil.parser模块统一日期字符串格式和使用datefinder模块提取日期。同时,还介绍了一段使用正则表达式的代码,可以支持中文日期和一些特殊的时间识别,例如'2012年12月12日'、'3小时前'、'在2012/12/13哈哈'等。 ... [详细]
  • This article discusses the efficiency of using char str[] and char *str and whether there is any reason to prefer one over the other. It explains the difference between the two and provides an example to illustrate their usage. ... [详细]
  • 本文介绍了设计师伊振华受邀参与沈阳市智慧城市运行管理中心项目的整体设计,并以数字赋能和创新驱动高质量发展的理念,建设了集成、智慧、高效的一体化城市综合管理平台,促进了城市的数字化转型。该中心被称为当代城市的智能心脏,为沈阳市的智慧城市建设做出了重要贡献。 ... [详细]
  • c语言\n不换行,c语言printf不换行
    本文目录一览:1、C语言不换行输入2、c语言的 ... [详细]
  • 本文介绍了解决二叉树层序创建问题的方法。通过使用队列结构体和二叉树结构体,实现了入队和出队操作,并提供了判断队列是否为空的函数。详细介绍了解决该问题的步骤和流程。 ... [详细]
  • 本文介绍了C函数ispunct()的用法及示例代码。ispunct()函数用于检查传递的字符是否是标点符号,如果是标点符号则返回非零值,否则返回零。示例代码演示了如何使用ispunct()函数来判断字符是否为标点符号。 ... [详细]
  • Python正则表达式学习记录及常用方法
    本文记录了学习Python正则表达式的过程,介绍了re模块的常用方法re.search,并解释了rawstring的作用。正则表达式是一种方便检查字符串匹配模式的工具,通过本文的学习可以掌握Python中使用正则表达式的基本方法。 ... [详细]
  • 《数据结构》学习笔记3——串匹配算法性能评估
    本文主要讨论串匹配算法的性能评估,包括模式匹配、字符种类数量、算法复杂度等内容。通过借助C++中的头文件和库,可以实现对串的匹配操作。其中蛮力算法的复杂度为O(m*n),通过随机取出长度为m的子串作为模式P,在文本T中进行匹配,统计平均复杂度。对于成功和失败的匹配分别进行测试,分析其平均复杂度。详情请参考相关学习资源。 ... [详细]
  • 本文详细介绍了Spring的JdbcTemplate的使用方法,包括执行存储过程、存储函数的call()方法,执行任何SQL语句的execute()方法,单个更新和批量更新的update()和batchUpdate()方法,以及单查和列表查询的query()和queryForXXX()方法。提供了经过测试的API供使用。 ... [详细]
  • [大整数乘法] java代码实现
    本文介绍了使用java代码实现大整数乘法的过程,同时也涉及到大整数加法和大整数减法的计算方法。通过分治算法来提高计算效率,并对算法的时间复杂度进行了研究。详细代码实现请参考文章链接。 ... [详细]
  • 3.223.28周学习总结中的贪心作业收获及困惑
    本文是对3.223.28周学习总结中的贪心作业进行总结,作者在解题过程中参考了他人的代码,但前提是要先理解题目并有解题思路。作者分享了自己在贪心作业中的收获,同时提到了一道让他困惑的题目,即input details部分引发的疑惑。 ... [详细]
  • 本文讨论了clone的fork与pthread_create创建线程的不同之处。进程是一个指令执行流及其执行环境,其执行环境是一个系统资源的集合。在调用系统调用fork创建一个进程时,子进程只是完全复制父进程的资源,这样得到的子进程独立于父进程,具有良好的并发性。但是二者之间的通讯需要通过专门的通讯机制,另外通过fork创建子进程系统开销很大。因此,在某些情况下,使用clone或pthread_create创建线程可能更加高效。 ... [详细]
  • JDK源码学习之HashTable(附带面试题)的学习笔记
    本文介绍了JDK源码学习之HashTable(附带面试题)的学习笔记,包括HashTable的定义、数据类型、与HashMap的关系和区别。文章提供了干货,并附带了其他相关主题的学习笔记。 ... [详细]
  • 本文介绍了Oracle存储过程的基本语法和写法示例,同时还介绍了已命名的系统异常的产生原因。 ... [详细]
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社区 版权所有