广告
返回顶部
首页 > 资讯 > 操作系统 >Linux驱动之platform总线详解
  • 241
分享到

Linux驱动之platform总线详解

Linux驱动中的platform总线分析 2022-06-04 23:06:19 241人浏览 薄情痞子
摘要

目录1、platfORM 总线简介1.1、linux 驱动的分离和分层思想1.1.1、Linux 驱动的分离1.2、platform 平台驱动模型2、platform 框架2.1、platform 总线2.2、platf

目录
  • 1、platfORM 总线简介
    • 1.1、linux 驱动的分离和分层思想
      • 1.1.1、Linux 驱动的分离
    • 1.2、platform 平台驱动模型
    • 2、platform 框架
      • 2.1、platform 总线
        • 2.2、platform 驱动
          • 2.2.1、platform 驱动定义
          • 2.2.2、platform 驱动注册
        • 2.3、platform 设备
          • 2.3.1、platform 设备定义
        • 2.4、platform 匹配过程
        • 3、总结

          1、platform 总线简介

          1.1、Linux 驱动的分离和分层思想

          1.1.1、Linux 驱动的分离

          先讲 Linux 驱动的分离,Linux 操作系统支持在各类 CPU 上运行,因为每一种 CPU 对设备的驱动不一样,这样就造成了 Linux 内核中积累了大量代码,并且这些代码关于同一设备的描述大致相同,这就使得内核代码很冗余。以 CPU 通过 I2C 控制 MPU6050 为例:

          Linux驱动之platform总线详解

          从图可以看出每一种平台下都有一套主机驱动和一套设备驱动,因为每个平台的 I2C 控制器不同,所以这个主机驱动得每个平台配一个自己的,但大家所用的 MPU6050 是一样的,所以完全可以就共用一套设备驱动代码。完善后框架如下:

          Linux驱动之platform总线详解

          当然,这只是对于 I2C 下的 MPU6050 这个设备,实际情况下,I2C 下肯定会挂载很多设备,根据这个思路,我们可以得到框架为:

          Linux驱动之platform总线详解

          而在实际开发中,I2C 主机驱动半导体厂家会编写好,设备驱动也由设备厂家编写好,我们只需要提供设备信息即可,如设备接到那个 I2C 接口上,I2C 速度为多少。这样就相当于把设备信息从设备驱动中剥离出来,而设备驱动也会用标准方法去获取设备信息(如从设备树中获取设备信息)。这样就相当于驱动只负责驱动,设备(信息)只负责设备,想办法将两者进行匹配即可,来做这个匹配工作的就是总线,这就构成了 Linux 中的 总线-驱动-设备 模型。结构图如下:

          Linux驱动之platform总线详解

          1.2、platform 平台驱动模型

          上面我们讲做设备驱动的分离,得到 总线-驱动-设备 模型,这个总线就是我平常所说的 I2C、SPI、USB 等总线。但问题是有些设备是不需要通过某一跟总线的,这是就引入了 platform 总线。

          这里需要注意的是,platform 总线是区别于 USB、SPI、I2C 这些总线的虚拟总线。说它虚拟是因为 SoC 与一些外设如 LED、定时器、蜂鸣器是通过内存的寻址空间来进行寻址的,所以 CPU 与这些设备通信压根就不需要总线,那么硬件上也就没有这样一个总线。但内核有对这些设备做统一管理的需求,所以就对这些直接通过内存寻址的设备虚拟了一条 platform 总线,所有直接通过内存寻址的设备都映射到这条虚拟总线上。

          platform 总线的优点:

          1、通过 platform 总线,可以遍历所有挂载在platform 总线上的设备;

          2、实现设备和驱动的分离,通过 platform 总线,设备和驱动是分开注册的,因为有probe 函数,可以随时检测与设备匹配的驱动,匹配成功就会把这个驱动向内核注册;

          3、一个驱动可供同类的几个设备使用,这个功能的实现是因为驱动注册过程中有一个遍历设备的操作。

          2、platform 框架

          2.1、platform 总线

          Linux 内核用 bus_type 结构体来表示总线,我们所用的 I2C、SPI、USB 都是用这个结构体来定义的。该结构体如下:

          
          
           
          struct bus_type {
              const char *name;                                  
              const char *dev_name; 
              struct device *dev_root;
              struct device_attribute *dev_attrs;
              const struct attribute_group **bus_groups;         
              const struct attribute_group **dev_groups;         
              const struct attribute_group **drv_groups;         
           
              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 (*online)(struct device *dev);
              int (*offline)(struct device *dev);
              int (*suspend)(struct device *dev, pm_message_t state);
              int (*resume)(struct device *dev);
              const struct dev_pm_ops *pm;
              const struct iommu_ops *iommu_ops;
              struct subsys_private *p;
              struct lock_class_key lock_key;
          };

          platform 总线是 bus_type 类型的常量,之所以说它是常量是因为这个变量已经被 Linux 内核赋值好了,其结构体成员对应的函数也已经在内核里面写好。

          定义如下:

          
          
           
          struct bus_type platform_bus_type = {
              .name = "platform",
              .dev_groups = platform_dev_groups,
              .match = platform_match,       
              .uevent = platform_uevent,
              .pm = &platform_dev_pm_ops,
          };

          platform_bus_type 中的 platform_match 就是我们前面所说的做驱动和设备匹配的函数,该函数定义如下:

          
          
           
          static int 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);
           
              
              if (pdev->driver_override)
                  return !strcmp(pdev->driver_override, drv->name);
           
              
              if (of_driver_match_device(dev, drv))
                  return 1;
           
              
              if (acpi_driver_match_device(dev, drv))
                  return 1;
           
              
              if (pdrv->id_table)
                  return platform_match_id(pdrv->id_table, pdev) != NULL;
           
              
              return (strcmp(pdev->name, drv->name) == 0);
          }

          这个匹配函数什么时候用,在哪里用,我们不妨先留一个悬念。

          2.2、platform 驱动

          2.2.1、platform 驱动定

          platform 驱动用结构体 platform_driver 来表示,该结构体内容为:

          
          
           
          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 (*resume)(struct platform_device *);
              struct device_driver driver;                
              const struct platform_device_id *id_table;  
              bool prevent_deferred_probe;
          };

          platform_driver 中const struct platform_device_id *id_table 是 id_table 表,在 platform 总线匹配驱动和设备时id_table 表匹配法时使用的,这个id_table 表其实是一个数组,里面的每个元素类型都为platform_device_id,platform_device_id 是一个结构体,内容如下:

          
          struct platform_device_id {
              char name[PLATFORM_NAME_SIZE];
              kernel_ulong_t driver_data;
          };

          platform_driver 中driver 是一个驱动基类,相当于驱动具有的最基础的属性,在不同总线下具有的属性则存放在platform_driver 结构体下。

          驱动基类结构体device_driver 内容为:

          
          
           
          struct device_driver {
              const char *name;                               
              struct bus_type *bus;
              struct module *owner;
              const char *mod_name; 
              bool suppress_bind_attrs; 
              const struct of_device_id *of_match_table;      
              const struct acpi_device_id *acpi_match_table;
              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);
              const struct attribute_group **groups;
              const struct dev_pm_ops *pm;
              struct driver_private *p;
          };

          driver 中 of_match_table 也是一个匹配表,这个匹配表是 platform 总线给驱动和设备做匹配时使用设备树匹配时用的,也是一个数组,数组元素都为of_device_id 类型,该类型结构体如下:

          
          
           
          struct of_device_id {
              char name[32];
              char type[32];
              char compatible[128];   
              const void *data;
          };

          2.2.2、platform 驱动注册

          用platform_driver 结构体定义好 platform 驱动后,用 platform_driver_reGISter 函数向 Linux 内核注册 platform 驱动,函数大致流程如下:

          
          platform_driver_register (drv)
              -> __platform_driver_register
                  -> drv->driver.probe = platform_drv_probe;       
                  -> driver_registe (&drv->driver)                 
                      -> ...... 
                          -> drv->driver->probe                    
                              -> platform_drv_probe
                                  -> drv->probe                    

          上面的分析中从 driver_register (&drv->driver) 到 drv->driver->probe 这一步我们用省略号代替了,现在来做一下分析:

          
          driver_register(&drv->driver)
              -> bus_add_driver                             
                  -> driver_attach 
                      -> bus_for_each_dev                    
                          -> __driver_attach                
                              -> driver_match_device        
                                  -> 调用bus下的match匹配函数
                              -> driver_probe_device        
                                  -> really_probe 
                                      -> drv->probe         

          根据driver_register 函数流程,我们就知道了总线的 match 匹配函数会在这里遍历使用,这就回答了我们之前留下的一个问题:总线 match 函数在哪里用,一旦匹配成功就会进入到驱动的 probe 函数。

          根据 platform_driver_register 函数流程,我们可以得出一个结论:向 Linux 内核注册 platform driver 过程里面会有一个遍历驱动和设备匹配的过程,匹配成功后最终会执行 platform driver 的 probe 函数,过程中 的驱动基类 driver 的 probe 函数和 platform_drv_probe 函数都是达到这个目的的中转函数而已。

          值得注意的是,最终会执行的 platform driver 的 probe 函数是由我们来写的,所以主动权又回到我们手里。

          2.3、platform 设备

          2.3.1、platform 设备定义

          如果我们用的 Linux 版本支持设备树,那就在设备树中去描述设备,如果不支持设备树,就要定义好 platform 设备。这里我们需要考虑的一个点是,总线下的匹配函数 match 在做匹配时是先设备树匹配,然后 id_table 表匹配,然后才是 name 字段匹配。支持设备树时,直接在设备树节点里面改设备信息,内核启动时会自动遍历设备树节点,匹配成功就会自动生成一个 platform_device,给下一步来使用。不是设备树的话,这个 platform_device 就是由开发者来写。

          这里我们先不用设备树,自己来定义 platform 设备。platform 设备用 platform_device 结构体来表示,该结构体定义如下:

          
          
           
          struct platform_device {
              const char *name;                   
              int id; 
              bool id_auto;
              struct device dev;
              u32 num_resources; 
              struct resource *resource;
              const struct platform_device_id *id_entry;
              char *driver_override; 
              
              struct mfd_cell *mfd_cell;
              
              struct pdev_archdata archdata;
          };

          2.4、platform 匹配过程

          platform 总线对驱动和设备的匹配过程其实上面零零碎碎也已经讲的差不多了,现在我们汇总起来在过一遍。

          前面也说过,总线下的驱动和设备的匹配是通过总线下的 match 函数来实现的,不同的总线对应的 match 函数肯定不一样,这个我们不用管,内核都会写好。我们所用的 platform 总线对应的 match 函数是 platform_match 函数,分析一下这个函数:

          
          platform_match
              -> of_driver_match_device          
              -> acpi_driver_match_device        
              -> platform_match_id               
              -> strcmp(pdev->name, drv->name)   

          通过对上面匹配函数的一个简单分析,我们知道匹配函数做匹配的顺序是先匹配设备树,然后匹配 id_table 表,然后才是暴力匹配 name 字段。对于支持设备树的 Linux 版本,我们一上来做设备树匹配就完事。不支持设备树时,我们就得定义 platform 设备,再用 id_tabale 表或 name 匹配,一般情况下都是选用 name 匹配。

          现在我们来具体看一下设备树条件下的匹配过程:

          
          of_driver_match_device     
              -> of_match_device (drv->of_match_table, dev)    
                  -> of_match_node  
                      -> __of_match_node
                          -> __of_device_is_compatible
                              -> __of_find_property(device, "compatible", NULL)   

          看上面的分析我们就知道了这个匹配过程最终是驱动基类的 of_match_table 里的 compatible 去设备树节点里面的compatible 属性作比较。这个就是把设备树与 platform 总线串起来的一个机理,从而实现了在设备树对应节点里面写设备信息,驱动另外单独写的目的,也就是我们前面讲的驱动分离。

          3、总结

          在具体的开发过程中我们并不需要真的去写一个 platform 总线模型,内核中都已经给我们定义好了。我们对 platform 总线模型的分析主要是搞清楚如何将驱动和设备匹配的,即当我们插入设备是如何找到对应驱动或插入驱动如何找到对应设备的,并最终调用 probe 函数。其实不管是先有驱动后有设备、还是先有设备后有驱动,最终匹配成功后第一件事都是执行驱动的 probe 函数,所以我们尽可放心的忽略中间曲折的情感纠葛,直接把注意力放在最终的 probe 函数。

          到此这篇关于Linux驱动之platform总线详解的文章就介绍到这了,更多相关Linux驱动platform总线内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

          --结束END--

          本文标题: Linux驱动之platform总线详解

          本文链接: https://www.lsjlt.com/news/21503.html(转载时请注明来源链接)

          有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

          本篇文章演示代码以及资料文档资料下载

          下载Word文档到电脑,方便收藏和打印~

          下载Word文档
          猜你喜欢
          • Linux驱动之platform总线详解
            目录1、platform 总线简介1.1、linux 驱动的分离和分层思想1.1.1、Linux 驱动的分离1.2、platform 平台驱动模型2、platform 框架2.1、platform 总线2.2、platf...
            99+
            2022-06-04
            Linux驱动中的platform总线分析
          • 如何解析Linux驱动中的platform总线
            如何解析Linux驱动中的platform总线,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。1、platform 总线简介1.1、Linux 驱动的分离和分层思...
            99+
            2023-06-22
          • 详解linux 驱动编写(sd卡驱动)
            随着sd卡的流行,sd卡在嵌入式设备上使用的场景也越来越多。那下面我们可以看一下,linux驱动框架上是怎么处理sd卡驱动的? 1、代码目录地址 drivers/mmc 2、基本结构 从mmc的代码结构可以看得出...
            99+
            2022-06-04
            linux sd卡驱动 linux驱动编写 linux sd卡
          • linux串口驱动详解
            Linux串口驱动是用于控制和管理串口设备的软件模块。它提供了一组函数和数据结构,使得应用程序可以通过操作这些函数和数据结构来与串口...
            99+
            2023-09-13
            linux
          • 详解linux lcd驱动编写
            有些嵌入式设备是不需要lcd的,比如路由器。但是,还有些设备是需要lcd显示内容的,比如游戏机、测试仪、智能手表等等。所以,今天我们就看看lcd驱动在linux上是怎么进行的。 1、代码目录 drivers/vide...
            99+
            2022-06-04
            linux lcd驱动 linux lcd驱动框架
          • 详解linux dma驱动编写
            linux下面的驱动虽然什么样的情形都有,但是dma驱动却并不少见。dma可以有很多的好处,其中最重要的功能就是能够帮助我们将数据搬来搬去,这个时候cpu就由时间去做别的事情了,提高了设备效率。 1、dma驱动在什么地方...
            99+
            2022-06-04
            linux dma驱动
          • 详解linux pwm驱动编写
            pwm方波可以用来控制很多的设备,比如它可以被用来控制电机。简单来说,就是单位时间内的方波越多,那么电机的转速就会越快;反之就越慢。通过这个特性,soc就可以轻松地利用pwm对外设进行自动控制。所以,今天的主题就是pwm...
            99+
            2022-06-04
            linux pwm驱动 linux驱动编写 linux pwm
          • Linux平台总线驱动设备模型是什么
            这篇文章主要介绍“Linux平台总线驱动设备模型是什么”,在日常操作中,相信很多人在Linux平台总线驱动设备模型是什么问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Linux平台总线驱动设备模型是什么”的疑...
            99+
            2023-06-16
          • 详解领域驱动设计之事件驱动与CQRS
            目录一、前言:从物流详情开始二、领域事件2.1、建模领域事件2.2、领域事件代码解读2.3、领域事件的存储2.3.1、单独的EventStore2.3.2、与业务数据一起存储2.4、...
            99+
            2022-11-12
          • 详解linux驱动编写(入门)
            在我离职之前,工作内容几乎不涉及到驱动方面的知识。我所要做的内容就是把客户对设备的请求拆分成一个一个的接口,调用驱动的设置进行配置就可以了。当然,至于驱动下面是怎么实现那就要根据具体情况而定了。比如说,有的驱动是芯片厂商...
            99+
            2022-06-04
            linux驱动编写 linux驱动
          • 详解linux 摄像头驱动编写
            对于现代嵌入式设备,特别是手机来说,摄像头是很重要的一个设备。很多同学买手机,一看颜值,第二就看摄像头拍照如何。所以,从某个角度来说,摄像头是各个厂家主打的应用功能。那么,linux是如何支持摄像头的,我们可以来看一下?...
            99+
            2022-06-04
            linux 摄像头驱动
          • 详解linux 看门狗驱动编写
            看门狗是linux驱动的一个重要环节。某些特殊的设备,有时候需要放在一些环境恶劣的地方,比如电信设备。但是,任何软件都不可能100%没有bug。如何保证软件在遇到严重bug、死机的时候也能正常运行呢,那么看门狗就是有效的...
            99+
            2022-06-04
            linux 看门狗驱动 linux 看门狗
          • 详解linux usb host驱动编写入门
            usb协议是一个复杂的协议,目前涉及到的版本就有usb1.0, usb2.0, usb3.0。大家如果打开kernel usb host目录,就会发现下面包含了ohci,uhci,ehci,xhci,whci等多种形式的...
            99+
            2022-06-04
            linux usb host驱动 linux usb驱动 linux usb host
          • 详解linux电源管理驱动编写
            对于嵌入式设备来说,合适的电源管理,不仅可以延长电池的寿命,而且可以省电,延长设备运行时间,在提高用户体验方面有很大的好处。所以,各个soc厂家在这方面花了很多的功夫。下面,我们可以看看linux是如何处理电源管理驱动的...
            99+
            2022-06-04
            linux 电源管理驱动 linux 电源管理 命令 linux 电源管理
          • linux命令详解之挂载光驱的方法
            linux的硬件设备在/dev目录下,光驱也是其中。/dev/cdrom表示光驱,挂载光驱的方法如下(以root身份): mkdir /mnt/cdrommount -t auto -o ro /d...
            99+
            2022-06-04
            详解 光驱 命令
          • 详解Spring注解驱动开发之属性赋值
            一、@Value注解 在Person的属性上使用@Value注解指定注入值 public class Person { @Value("#{20-2}") ...
            99+
            2022-11-12
          • vue篇之事件总线EventBus使用示例详解
            目录正文父子组件通讯原则EventBus的简介如何使用EventBus初始化发送事件接收事件移除事件监听者全局EventBus创建全局EventBus总结正文 许多现代JavaScr...
            99+
            2022-11-13
          • Vue组件之事件总线和消息发布订阅详解
            目录简介事件总线消息的发布订阅总结简介 主要介绍事件总线的定义和编写方法和Vue是如何实现消息的订阅与发布的。 事件总线 事件总线是组件间通信的一种方式,适用于任意组件间的通信,比如...
            99+
            2022-11-13
          • linux驱动开发中常用函数copy_from_user open read write详解
            目录linux驱动常用函数(copy_from_user open read write)1.open2.read3.write4.copy_to_user5.copy_from_u...
            99+
            2022-11-12
          • C语言驱动开发之内核使用IO/DPC定时器详解
            本章将继续探索驱动开发中的基础部分,定时器在内核中同样很常用,在内核中定时器可以使用两种,即IO定时器,以及DPC定时器,一般来说IO定时器是DDK中提供的一种,该定时器可以为间隔为...
            99+
            2023-05-14
            C语言内核使用IO/DPC定时器 C语言 内核 IO/DPC定时器 C语言 内核 定时器
          软考高级职称资格查询
          编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
          • 官方手机版

          • 微信公众号

          • 商务合作