Linux设备驱动模型之platform总线深入浅出
- 格式:doc
- 大小:105.00 KB
- 文档页数:30
Linux驱动之platform总线详解⽬录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 为例:从图可以看出每⼀种平台下都有⼀套主机驱动和⼀套设备驱动,因为每个平台的 I2C 控制器不同,所以这个主机驱动得每个平台配⼀个⾃⼰的,但⼤家所⽤的 MPU6050 是⼀样的,所以完全可以就共⽤⼀套设备驱动代码。
完善后框架如下:当然,这只是对于 I2C 下的 MPU6050 这个设备,实际情况下,I2C 下肯定会挂载很多设备,根据这个思路,我们可以得到框架为:⽽在实际开发中,I2C 主机驱动半导体⼚家会编写好,设备驱动也由设备⼚家编写好,我们只需要提供设备信息即可,如设备接到那个 I2C 接⼝上,I2C 速度为多少。
这样就相当于把设备信息从设备驱动中剥离出来,⽽设备驱动也会⽤标准⽅法去获取设备信息(如从设备树中获取设备信息)。
这样就相当于驱动只负责驱动,设备(信息)只负责设备,想办法将两者进⾏匹配即可,来做这个匹配⼯作的就是总线,这就构成了 Linux 中的总线-驱动-设备模型。
Linux在2.6版本引入了设备驱动模型,设备驱动模型负责统一实现和维护一些特性,诸如:热插拔、对象生命周期、用户空间和驱动空间的交互等基础设施1.设备驱动模型基本概念设备驱动模型主要包含:类(class)、总线(bus)、设备(device)、驱动(driver),它们的本质都是内核中的几种数据结构的“实例”∙类的本质是class结构体类型,各种不同的类其实就是class的各种实例∙总线的本质是bus_type结构体类型,各种不同的总线其实就是bus_type的各种实例∙设备的本质是device结构体类型,各种不同的设备其实就是device的各种实例∙驱动的本质是device_driver结构体类型,各种不同的驱动其实就是device_driver的各种实例2.sysfs基本概念sysfs其实就是/sys目录,其主要作用就是:展示设备驱动模型中各组件的层次关系,并将各组件的本体——内核中的数据结构以文件形式呈现,方便用户层查看及操作3./sys目录结构与设备驱动模型∙/sys目录结构很好的展示了驱动设备模型,如图:∙注意:同一个设备可能在/sys中存在多个设备文件,比如一颗led的设备文件可能在/sys/bus/platform/devices/led1,同时还有一个在/sys/class/leds/led1。
虽然他们都是同一颗led的设备文件,但是他们的来源、机制、原理都是不同的,不能混为一谈4.各组件的特性与联系∙kobject:设备驱动模型各实例的最基本单元,提供一些公用型服务如:提供该实例在sysfs中的操作方法(show和store);提供在sysfs中以文件形式存在的属性,其实就是应用接口;提供各个实例的层次架构,让sysfs中弄出目录结构。
设备驱动模型中每个实例内部都会包含一个kobject∙总线、设备、驱动,这三者有着密切的联系。
在内核中,设备和驱动是分开注册的,注册设备的时候,并不需要驱动已经存在,而驱动被注册的时候,也不需要对应的设备已经被注册。
详解Linux2.6内核中基于platform机制的驱动模型【摘要】本文以Linux 2.6.25 内核为例,分析了基于platform总线的驱动模型。
首先介绍了Platform总线的基本概念,接着介绍了platform device和platform driver的定义和加载过程,分析了其与基类device 和driver的派生关系及在此过程中面向对象的设计思想。
最后以ARM S3C2440中I2C控制器为例介绍了基于platform总线的驱动开发流程。
【关键字】platform_bus, platform_device, resource , platform_driver, file_operations目录1 何谓platform bus? 22 device和platform_device 33 device_register和platform_device_register 54 device_driver和platform driver 85 driver_register 和platform_driver_register 106 bus、device及driver三者之间的关系177 哪些适用于plarform驱动?188 基于platform总线的驱动开发流程188.1 初始化platform_bus 198.2 定义platform_device 228.3 注册platform_device 228.4 定义platform_driver 288.5 注册platform_driver 298.6 操作设备321 何谓platform bus?Linux系统中许多部分对设备是如何链接的并不感兴趣,但是他们需要知道哪些类型的设备是可以使用的。
设备模型提供了一种机制来对设备进行分类,在更高的功能层面上描述这些设备,并使得这些设备对用户空间可见。
因此从2.6内核开始引入了设备模型。
深⼊解析LinuxPlatform_device及驱动[导读] 前⽂分析了Linux设备驱动的驱动模型,本⽂来聊聊Platform_driver/Platform_device这个类。
做嵌⼊式Linux的驱动,这个也是绕不开的,所以来学习分析总结⼀下。
上⽂阅读:注:代码分析基于linux-5.4.31为什么有Platform_driver前⽂谈到的总线驱动模型(注这个图是照着bootlin的⽂档绘制的):同时,根据代码分析其基础数据结构框架关系如下(UML关系并不严谨,仅为理解⽅便):可见驱动程序的模型分层有⼀层总线基础层,那么对于嵌⼊式开发领域⽽⾔,有很多SOC芯⽚内置了各种外设,并⽐如LCD,UART、audio、摄像头⼝等等,并没有总线。
为了统⼀驱动架构抽象,所以引⼊了platform bus这个虚拟的总线模型。
做过嵌⼊式开发的⼈应该都有体会,这类设备在嵌⼊式系统中⾮常多,所以在研究具体某类设备的驱动开发之前,有必要研究platform 设备的驱动模型。
在强调⼀下这个是统⼀在总线驱动模型这个体系内的。
驱动模型的实现定义在./include/linux/platform_device.h中,来梳理⼀下这些数据结构间的关系:platform_device ⽤于抽象平台设备platform_driver ⽤于抽象匹配平台设备对应的驱动程序通过继承演化关系分析,platform_device/platform_driver 仍然统⼀于总线驱动模型,只是虚拟出来了⼀条platform bus这样⼀条虚拟总线。
platform_bus在哪⾥实现的呢?该模块的实现位于./driver/base/platform.c中struct device platform_bus = {.init_name = "platform",};platform.c导出了⼀系列内核全局操作接⼝集:EXPORT_SYMBOL_GPL(platform_bus);EXPORT_SYMBOL_GPL(__platform_driver_register);EXPORT_SYMBOL_GPL(__platform_driver_probe);EXPORT_SYMBOL_GPL(platform_get_resource_byname);EXPORT_SYMBOL_GPL(platform_get_irq_byname);....那么既然这条总线并不存在,往往并不能实现设备枚举、热插拔等功能。
Linux设备驱动之platform
根据Linux设备模型可知,一个现实的Linux设备和驱动通常都需要挂接在一种总线上,对于本身依附于PCI、USB等的设备而言,这自然不是问题,但是在嵌入式系统里面,SoC系统中集成的独立的外设控制器、挂接在SoC 内存空间的外设等却不依附于此类总线。
基于这一背景,Linux设计了一种虚拟的总线,称为platform总线,相应的设备称为platform_device,而驱动称为platform_driver。
设计目的
兼容设备模型
使得设备被挂接在一个总线上,因此,符合Linux 2.6 的设备模型。
其结果是,配套的sysfs 结点、设备电源管理都成为可能。
BSP和驱动隔离
在BSP中定义platform设备和设备使用的资源、设备的具体配置信息。
而在驱动中,只需要通过通用API去获取资源和数据,做到了板相关代码和驱动代码的分离,使得驱动具有更好的可扩展性和跨平台性。
软件架构
内核中Platform设备有关的实现位于include/linux/platform_device.h和drivers/base/platform.c两个文件中,它的软件架构如下:
由图片可知,Platform设备在内核中的实现主要包括三个部分:
Platform Bus,基于底层bus模块,抽象出一个虚拟的Platform bus,用于挂载Platform设备;
Platform Device,基于底层device模块,抽象出Platform Device,用于表示Platform设备;Platform Driver,基于底层device_driver模块,抽象出Platform Driver,用于驱动Platform 设备。
platform 总线、设备与驱动2013-09-04 17:38:36分类:LINUXLinux2.6的设备驱动模型中,关心总线、设备和驱动3个实体,总线将设备和驱动绑定。
在系统每注册一个设备的时候,会寻找与之匹配的驱动;相反,在系统每次注册一个驱动的时候,会寻找与之匹配的设备,而匹配由总线完成。
Linux发明了一种虚拟的总线,称为platform总线,相应的设备称为platform_device,而驱动称为platform_driver。
platform_device结构体:点击(此处)折叠或打开1.struct platform_device {2.const char * name;3. u32 id;4. struct device dev;5. u32 num_resources;6. struct resource * resource;7.};platform_driver结构体:点击(此处)折叠或打开1.struct platform_driver {2.int(*probe)(struct platform_device *);3.int(*remove)(struct platform_device *);4. void (*shutdown)(struct platform_device *);5.int(*suspend)(struct platform_device *, pm_message_t state);6.int(*suspend_late)(struct platform_device *, pm_message_t state);7.int(*resume_early)(struct platform_device *);8.int(*resume)(struct platform_device *);9. struct device_driver driver;10.};系统中为platform总线定义了一个bus_type的实例platform_bus_type:点击(此处)折叠或打开1.struct bus_type platform_bus_type ={ ="platform",3..dev_attrs = platform_dev_attrs,4..match = platform_match,5..uevent = platform_uevent,6..suspend = platform_suspend,7..suspend_late = platform_suspend_late,8..resume_early = platform_resume_early,9..resume= platform_resume,10.};下面就以2.6.22.6内核和DM9000为例,介绍下platform总线、设备与驱动在代码中的实现,DM9000驱动部分,依照《嵌入式Linux应用开发完全手册》进行配置,配置如下:arch\arm\plat-s3c24xx\common-smdk.c文件中添加,对应的是platform_device这个结构体:点击(此处)折叠或打开1.#include <linux/dm9000.h>2.3.static struct resource s3c_dm9k_resource[]={4.[0]={5..start = S3C2410_CS4,//ADDR2 = 0 ,发送地址时使用这个地址6..end= S3C2410_CS4 + 3,7..flags = IORESOURCE_MEM,8.},9.[1]={10..start = S3C2410_CS4 + 4,//ADDR2 = 1 ,发送数据时使用这个地址11..end= S3C2410_CS4 + 4 + 3,12..flags = IORESOURCE_MEM,13.},14.[2]={15..start = IRQ_EINT7,//IRQ_DM900016..end= IRQ_EINT7,//IRQ_DM900017..flags = IORESOURCE_IRQ,18.}19.20.};21.22.static struct dm9000_plat_data s3c_dm9k_platdata ={23..flags = DM9000_PLATF_16BITONLY,//数据总线宽度为1624.};25.26.static struct platform_device s3c_device_dm9k ={ ="dm9000",28..id = 0,29..num_resources = ARRAY_SIZE(s3c_dm9k_resource),30..resource = s3c_dm9k_resource,31..dev ={32..platform_data =&s3c_dm9k_platdata,33.}34.};35.36.37.static struct platform_device __initdata *smdk_devs[]={38.……39.&s3c_device_dm9k,40.};那么很显然有platform_device就对应应该有个platform_driver,其在dm9000.c文件中:点击(此处)折叠或打开1.static struct platform_driver dm9000_driver ={2..driver ={ ="dm9000",4..owner = THIS_MODULE,5.},6..probe = dm9000_probe,7..remove = dm9000_drv_remove,8..suspend = dm9000_drv_suspend,9..resume= dm9000_drv_resume,10.};那么具体去加载platform_device和platform_driver到函数是什么呢?在内核代码中可以找到:点击(此处)折叠或打开1.static int __init dm9000_init(void)2.{3.……4. return platform_driver_register(&dm9000_driver);/* search board and register */5.}6.module_init(dm9000_init);7.8.void __init smdk_machine_init(void)9.{10.……11. platform_add_devices(smdk_devs, ARRAY_SIZE(smdk_devs));12.……13.}有一点需要注明:对platform_device的定义通常是在BSP的板文件中实现,在板文件中,platform_device归纳为一个数组,最终通过platform_add_devices(struct platform_device **devs, int num)函数统一注册。
随着系统结构演化越来越复杂,Linux内核对设备描述衍生出一般性的抽象描述,形成一个分层体系结构,从而引入了设备驱动模型。
这样描述还是不够让人理解,来看一下这些需求就好理解些:•Linux内核可以在各种体系结构和硬件平台上运行,因此需要最大限度地提高代码在平台之间的可重用性。
•分层实现也实现了软件工程的高内聚-低耦合的设计思想。
低耦合体现在对外提供统一的抽象访问接口,高内聚将相关度紧密的集中抽象实现。
•Linux内核驱动程序模型是先前在内核中使用的所有不同驱动程序模型的统一。
它旨在通过将一组数据和操作整合到全局可访问的数据结构中,来扩展基于基础总线来桥接设备驱动程序。
传统的驱动模型为它们所控制的设备实现了某种类似于树的结构(有时只是一个列表)。
不同类型的总线之间没有任何一致性。
驱动模型抽象了啥当前驱动程序模型为描述总线和总线下可能出现的设备提供了一个通用的、统一的模型。
统一总线模型包括一组所有总线都具有的公共属性和一组公共回调,如总线探测期间的设备发现、总线关闭、总线电源管理等。
通用的设备和桥接接口反映了现代计算机的目标:即执行无缝设备“即插即用”,电源管理和热插拔的能力。
特别是,英特尔和微软规定的模型(即ACPI)可确保与x86兼容的系统上几乎任何总线上的几乎所有设备都可以在此范式下工作。
当然,虽然大多数总线都支持其中大多数操作,但并不是每条总线都能够支持所有此类操作。
那么哪些通用需求被抽象出来了呢?•电源系统和系统关机,对于电源管理与系统关机对于设备相关的操作进行抽象实现。
关机为什么要被抽象出来管理,比如设备操作正在进行此时系统收到关机指令,那么在设备模型层就会遍历系统设备硬件,确保系统正确关机。
•用户空间访问:sysfs虚拟文件系统实现与设备模型对外的访问抽象,这也是为什么说Linux 设备也是文件的由来。
实际从软件架构层面看,这其实是一个软件桥接模块,抽象出统一用户访问接口,桥接了设备驱动。
Linux驱动中的platform从Linux2.6 内核起,引入一套新的驱动管理和注册机制:platform_device和platform_driver 。
Linux 中大部分的设备驱动,都可以使用这套机制,设备用platform_device 表示;驱动用platform_driver 进行注册。
Linux platform driver 机制和传统的device driver 机制(即:通过driver_register 函数进行注册)相比,一个十分明显的优势在于platform 机制将设备本身的资源注册进内核,由内核统一管理,在驱动程序中用使用这些资源时,通过platform device 提供的标准接口进行申请并使用。
platform 是一个虚拟的地址总线,相比PCI、USB,它主要用于描述SOC 上的片上资源。
platform 所描述的资源有一个共同点:在CPU 的总线上直接取址。
平台设备会分到一个名称(用在驱动绑定中)以及一系列诸如地址和中断请求号(IRQ)之类的资源。
platform 总线下驱动的开发步骤是:1、设备需要实现的结构体是:platform_device 。
1)初始化resource 结构变量2)初始化platform_device 结构变量3)向系统注册设备:platform_device_register。
以上三步,必须在设备驱动加载前完成,即执行platform_driver_register()之前,原因是驱动注册时需要匹配内核中所有已注册的设备名。
platform_driver_register()中添加device 到内核最终还是调用的device_add 函数。
Platform_device_add 和device_add 最主要的区别是多了一步insert_resource(p, r),即将platform 资源(resource)添加进内核,由内核统一管理。
LinuxPlatform驱动模型(⼆)_驱动⽅法在和中我们讨论了设备信息的写法,本⽂主要讨论平台总线中另外⼀部分-驱动⽅法,将试图回答下⾯⼏个问题:1. 如何填充platform_driver对象?2. 如何将驱动⽅法对象注册到平台总线中?正⽂前的⼀点罗嗦写驱动也有⼀段时间了,可以发现,其实驱动本质上只做了两件事:向上提供接⼝,向下控制硬件,当然,这⾥的向上并不是直接提供接⼝到应⽤层,⽽是提供接⼝给内核再由内核间接的将我们的接⼝提供给应⽤层。
⽽写驱动也是有⼀些套路可寻的,拿到⼀个硬件,我们⼤体可以按照下⾯的流程写⼀个驱动:1. 确定驱动架构:根据硬件连接⽅式结合分层/分离思想设计驱动的基本结构2. 确定驱动对象:内核中的⼀个驱动/设备就是⼀个对象,1.定义,2.初始化,3.注册,4.注销3. 向上提供接⼝:根据业务需要确定提供cdev/proc/sysfs哪种接⼝4. 向下控制硬件:1.查看原理图确定引脚和控制逻辑,2.查看芯⽚⼿册确定寄存器配置⽅式,3.进⾏内存映射,4.实现控制逻辑认识驱动⽅法对象内核⽤platform_driver结构来表⽰⼀个驱动⽅法对象//include/linux/device.h173 struct platform_driver {174 int (*probe)(struct platform_device *);175 int (*remove)(struct platform_device *);176 void (*shutdown)(struct platform_device *);177 int (*suspend)(struct platform_device *, pm_message_t state);178 int (*resume)(struct platform_device *);179 struct device_driver driver;180 const struct platform_device_id *id_table;181 bool prevent_deferred_probe;182 };在这个结构中,我们主要关⼼以下⼏个成员struct platform_driver--174-->探测函数,如果驱动匹配到了⽬标设备,总线会⾃动回调probe函数,必须实现,下⾯详细讨论。
Linux总线、设备、驱动模型与设备树
1.总线、设备、驱动模型
本着⾼内聚、低耦合的原则,Linux 把设备驱动模型分为了总线、设备和驱动三个实体,这三个实体在内核⾥的职责分别如下:
设备和驱动向总线进⾏注册,总线负责把设备和对应的驱动绑定起来。
驱动通过总线 API 接⼝platform_get_resource()取得板级设备信息,这样驱动和设备之间就实现了⾼内聚、低耦合的设计,
⽆论设备怎么换,驱动都可以岿然不动。
代码架构如下图所⽰:
2、设备树
引⼊设备树之前,关于硬件设备的描述信息⼀般放在⼀个个类似 arch/xxx/mach-xxx/board-xxx.c 的⽂件中,
这些代码中除了描述的设备信息不同,其代码逻辑都是⼀样的。
我们有理由,把这些设备端的信息,⽤⼀个⾮ C 的脚本语⾔来描述,这个脚本⽂件,就是 Device Tree(设备树)。
设备树是⼀种 dts ⽂件,它⽤简单的语法描述每个板⼦上的所有设备,以及这些设备的连接信息。
设备树⽂件存储在⽬录 arch/xxx/boot/dts 中,每⼀个 board 对应⼀个 dts ⽂件。
引⼊设备树之后,⼤量重复的 C 代码(arch/xxx/mach-xxx/board-xxx.c)被去除——驱动的归于驱动 C 代码,设备的归于设备树脚本⽂件。
arch/arm/mach-xxx/board-a.c 这样的⽂件永远地进⼊了历史的故纸堆,换个板⼦,只要换个 Device Tree ⽂件就好。
代码架构如下图所⽰:。
Platform设备驱动模型linux设备驱动模型中,三个重要的实体,总线、设备、驱动。
其中的概念和意义不再强调,网上还有书上都有很多。
这里只强调两点:1)引入platform使得设备被挂在在总线上,符合了linux2.6内核的设备模型。
2)隔离BSP和驱动。
在BSP中定义的platform设备和设备使用的资源、设备的具体配置信息,而在驱动中,只需要通过通用API去获取资源和数据,做到了板相关代码和驱动代码的分离,使得驱动具有更好的可扩展性和跨平台性。
几个重要的结构体:platform_bus_type是platform总线定义了一个bus_type的实例。
这部分有系统内部为我们完成,我们不需要去做特殊的定义或者初始化。
点击(此处)折叠或打开1struct bus_type platform_bus_type ={ ="platform", //总线名称3.dev_attrs = platform_dev_attrs,4.match =platform_match,//匹配函数,该函数确定了platform_device和platform_driver之间如何匹配5.uevent = platform_uevent, //用于添加环境变量6.pm = PLATFORM_PM_OPS_PTR,7};8EXPORT_SYMBOL_GPL(platform_bus_type);910int __init platform_bus_init(void) //总线的初始化11{12int error;1314 early_platform_cleanup();1516error= device_register(&platform_bus);17if(error)18 return error;19error= bus_register(&platform_bus_type);20if(error)21 device_unregister(&platform_bus);22 return error;23}24总线也是一种设备,要先注册总线设备,在使用总线设备完成总线注册。
Linux设备模型之platform总线-一:前言Platform总线是kernel中最近加入的一种虚拟总线.在近版的2.6kernel中,很多驱动都用platform改写了.只有在分析完platform总线之后,才能继续深入下去分析.在分析完sysfs和设备驱动模型之后,这部份应该很简单了.闲言少叙.步入正题.GO.GO!以下的源代码分析是基于2.6.25的.二:platform概貌在分析源代码之前,先在内核代码中找一个platform架构的驱动程序.下面以i8042芯片的驱动为例进行分析.在linux-2.6.25/drivers/input/serio/i8042.c的intel 8042的初始化入口中,有以下代码分段: static int __init i8042_init(void){……err = platform_driver_register(&i8042_driver);if (err)goto err_platform_exit;i8042_platform_device = platform_device_alloc("i8042", -1);if (!i8042_platform_device) {err = -ENOMEM;goto err_unregister_driver;}err = platform_device_add(i8042_platform_device);if (err)goto err_free_device;……}我们在上面的程序片段中看到,驱动程序先注册了一个platform device.然后又添加了一个platform device.这里就涉及到了platform的两个最主要的操作,一个设备驱动注册,一个设备注册.要了解platform总线的来龙去脉.得从它的初始化开始.三:platform初始化Platform总线的初始化是在linux-2.6.25/drivers/base/platform.c中的platform_bus_init()完成的,代码如下:int __init platform_bus_init(void){int error;error = device_register(&platform_bus);if (error)return error;error = bus_register(&platform_bus_type);if (error)device_unregister(&platform_bus);return error;}上面代码中调用的子函数在<<linux设备模型深探>>中已经分析过了.这段初始化代码创建了一个名为“platform”的设备.后续platform的设备都会以此为parent.在sysfs中表示为:所有platform类型的设备都会添加在platform_bus所代码的目录下.即/sys/devices/platform 下面.接着,这段初始化代码又创建了一个名为“platform”的总线.platform_bus_type的定义如下:struct bus_type platform_bus_type = {.name = "platform",.dev_attrs = platform_dev_attrs,.match = platform_match,.uevent = platform_uevent,.suspend = platform_suspend,.suspend_late = platform_suspend_late,.resume_early = platform_resume_early,.resume = platform_resume,};我们知道,在bus_type中包含了诸如设备与驱动匹配(mach)、hotplug(uevent)事件等很多重要的操作.这些操作在分析platform设备注册与platform驱动注册的时候依次分析.四:platform device注册在intel 8042的驱动代码中,我们看到注册一个platform device分为了两部分,一部份是创建一个platform device结构,另一部份是将其注册到总线中.先来看第一个接口.struct platform_device *platform_device_alloc(const char *name, int id){struct platform_object *pa;pa = kzalloc(sizeof(struct platform_object) + strlen(name), GFP_KERNEL);if (pa) {strcpy(pa->name, name);pa-> = pa->name;pa->pdev.id = id;device_initialize(&pa->pdev.dev);pa->pdev.dev.release = platform_device_release;}return pa ? &pa->pdev : NULL;}这段代码主要初始化了封装在struct platform_object中的struct device.Struct platform_object结构定义如下:struct platform_object {struct platform_device pdev;char name[1];};在定义中,定义name为一个长度.所以,才有了platform_device_alloc()中分配struct platform_object的时候多加了名称的长度:pa = kzalloc(sizeof(struct platform_object) + strlen(name), GFP_KERNEL);struct device结构如下:struct platform_device {const char * name;int id;struct device dev;u32 num_resources;struct resource * resource;};在这个结构里封装了struct device.struct resource. struct resource这个结构在此之前没有接触过,这个结构表示设备所拥有的资源.即I/O端口或者是I/O映射内存.platform_device_add()代码分段分析如下:int platform_device_add(struct platform_device *pdev){int i, ret = 0;if (!pdev)return -EINVAL;if (!pdev->dev.parent)pdev->dev.parent = &platform_bus;pdev->dev.bus = &platform_bus_type;初始化设备的parent为platform_bus.初始化驱备的总线为platform_bus_type.回忆platform初始化的时候.这两个东东这里终于派上用场了.if (pdev->id != -1)snprintf(pdev->dev.bus_id, BUS_ID_SIZE, "%s.%d", pdev->name,pdev->id);elsestrlcpy(pdev->dev.bus_id, pdev->name, BUS_ID_SIZE);设置设备struct device 的bus_id成员.留心这个地方,在以后还需要用到这个的.for (i = 0; i < pdev->num_resources; i++) {struct resource *p, *r = &pdev->resource[i];if (r->name == NULL)r->name = pdev->dev.bus_id;p = r->parent;if (!p) {if (r->flags & IORESOURCE_MEM)p = &iomem_resource;else if (r->flags & IORESOURCE_IO)p = &ioport_resource;}if (p && insert_resource(p, r)) {printk(KERN_ERR"%s: failed to claim resource %d\n",pdev->dev.bus_id, i);ret = -EBUSY;goto failed;}}如果设备指定了它所拥有的资源,将这些资源分配给它.如果分配失败,则失败退出.从代码中可以看出.如果struct resource的flags域被指定为IORESOURCE_MEM.则所表示的资源为I/O映射内存.如果指定为IORESOURCE_IO.则所表示的资源为I/O端口.pr_debug("Registering platform device '%s'. Parent at %s\n",pdev->dev.bus_id, pdev->dev.parent->bus_id);ret = device_add(&pdev->dev);if (ret == 0)return ret;failed:while (--i >= 0)if (pdev->resource[i].flags & (IORESOURCE_MEM|IORESOURCE_IO))release_resource(&pdev->resource[i]);return ret;}device_add()已经很熟悉了吧.没错,它就是将设备注册到指定的bus_type.在分析linux设备模型的时候,曾说过.调用device_add()会产生一个hotplug事件.platform device的hotplug与一般的device事件相比.它还要它所属bus_type的uevent().对这个流程不熟悉的可以参照<< linux设备模型深探>>.platform device所属的bus_type为platform_bus_type.它的uevent()接口代码如下:static int platform_uevent(struct device *dev, struct kobj_uevent_env *env){struct platform_device *pdev = to_platform_device(dev);add_uevent_var(env, "MODALIAS=platform:%s", pdev->name);return 0;}上面的代码很简单,它就是在环境变量中添加一项MODALIAS.五:platform driver的注册在intel 8024的驱动代码中看到.platform driver注册的接口为: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;if (drv->suspend)drv->driver.suspend = platform_drv_suspend;if (drv->resume)drv->driver.resume = platform_drv_resume;return driver_register(&drv->driver);}struct platform_driver主要封装了struct device_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 (*suspend_late)(struct platform_device *, pm_message_t state);int (*resume_early)(struct platform_device *);int (*resume)(struct platform_device *);struct device_driver driver;};在这个接口里,它指定platform driver的所属总线.如果在struct platform_driver指定了各项接口的操作,就会为struct device_driver中的相应接口赋值.不要被上面的platform_drv_XXX吓倒了,它们其实很简单,就是将struct device转换为struct platform_device和struct platform_driver.然后调用platform_driver中的相应接口函数最后, platform_driver_register()将驱动注册到总线上.这样,总线上有设备,又有驱动,就会进行设备与匹配的过程,调用的相应接口为:bus ->match --- > bus->probe/driver->probe (如果总线的probe操作不存在,就会调用设备的probe接口).这样,我们又回到platform_bus_type中的各项操作了,调用总线的match函数,检查驱动和设备是否匹配。
【Linux笔记】总线设备驱动模型Linux内核中更是存在着更多的分离、分层思想的代码,platform平台设备驱动就是用了这样的思想。
本篇笔记我们一起来学习一下platform驱动框架。
对于 Linux 这样一个成熟、庞大、复杂的操作系统,代码的重用性非常重要,否则的话就会在Linux 内核中存在大量无意义的重复代码。
为了能提高驱动代码的重用性,因此引入了platform。
下面以一个例子来说明传统的设动转变至Linux 总线、驱动和设备模式(来源:《正点原子Linux驱动开发指南》):最后一张图就是Linux下的总线(bus)、驱动(driver)和设备(device)模型。
但是在SOC 中有些外设是没有总线这个概念的,但是又要使用总线、驱动和设备模型该怎么办呢?为了解决此问题, Linux 提出了 platform_bus 这个虚拟总线,相应的就有 platform_driver 和 platform_device。
总线设备驱动模型1、platform_busplatform总线实例(在文件drivers/base/platform.c 中):这里我们重点关注匹配函数platform_match:我们暂且先知道其有几种匹配方法。
2、platform_driverplatform驱动结构体(在文件include/linux/platform_device.h 中):platform_driver 结构体中的driver成员所属的结构体类型为(在文件include/linux/device.h 中):这里体现出了C语言的面向对象的思想,其中driver相当于基类,platform_driver继承了这个基类。
struct device_driver 结构体中(在文件include/linux/device.h中)的name成员在上面提到的匹配函数platform_match里用到,即:platform_driver 结构体中的id_table成员所属的结构体类型为(在文件include/linux/mod_devicetable.h 中):注册/卸载函数:platform 驱动注册函数:int platform_driver_register(struct platform_driver *driver);platform 驱动卸载函数:void platform_driver_unregister(struct platform_driver *drv);3、platform_device描述设备有两种方法:一种是用用设备树来指定,另一种是直接用platform_device结构体来指定。
了解Linux平台总线的驱动设备模型一定不会错platform总线是一种虚拟的总线,相应的设备则为platform_device,而驱动则为platform_driver。
Linux 2.6的设备驱动模型中,把I2C、RTC、LCD等都归纳为platform_device。
总线将设备和驱动绑定,在系统每注册一个设备的时候,会寻找与之匹配的驱动;相反的,在系统每注册一个驱动的时候,会寻找与之匹配的设备,而匹配由总线完成。
Linux2.6系统中定义了一个bus_type的实例platform_bus_type[cpp] view plain copyprint?struct bus_type platform_bus_type = {.name = "platform",.dev_attrs = platform_dev_attrs,.match = platform_match, //设备和驱动使用match函数来判断是否匹配.uevent = platform_uevent,.pm = PLATFORM_PM_OPS_PTR,};[cpp] view plain copyprint?/* 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);/* match against the id table first */if (pdrv->id_table)return platform_match_id(pdrv->id_table, pdev) != NULL;/* fall-back to driver name match */return (strcmp(pdev->name, drv->name) == 0);}platform_match函数首先判断是否由id_table,如果有则使用id_table来进行匹配,否则,。
Linux设备驱动模型之platform总线深入浅出
在Linux2.6以后的设备驱动模型中,需关心总线,设备和驱动这三种实体,总线将设备和驱动绑定。
在系统每注册一个设备的时候,会寻找与之匹配的驱动;相反,在系统每注册一个驱动的时候,会寻找与之匹配的设备,而匹配由总线完成。
对于依附在USB、PCI、I2C、SPI等物理总线来这些都不是问题。
但是在嵌入式系统里面,在Soc系统中集成的独立外设控制器,挂接在Soc内存空间的外设等却不依附在此类总线。
基于这一背景,Linux发明了一种总线,称为platform。
相对于USB、PCI、I2C、SPI等物理总线来说,platform总线是一种虚拟、抽象出来的总线,实际中并不存在这样的总线。
platform总线相关代码:driver\base\platform.c 文件相关结构体定义:include\linux\platform_device.h 文件中
platform总线管理下最重要的两个结构体是platform_device和platform_driver 分别表示设备和驱动在Linux中的定义如下一:platform_driver
//include\linux\platform_device.h 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;//设备驱动结构};
1
2
3
4
5
6
7
8。