platform总线驱动程序

从这篇文章开始讲解驱动程序,Linux内核中有很多总线驱动,但都是由bus_type总线内管理,在/sys/bus目录下能够看到该内核定义了那些系统总线,常见的有platform、iic、spi、input等。内核中的总线与实际物理总线不一样,这里的总线是虚拟的,仅仅是一系列链表管理的对象,我们称这个管理链表为驱动总线。本文分析platform总线,这个总线在驱动结构中比较常用。

平台定义与注册如下图所示。

platform总线驱动程序

 

注意留意类型定义的match函数,后面讲解。

 

Platform总线有platform_device和platform_driver两部分组成,dev部分用来记录设备的具体信息,比如寄存器地址,内存地址,中断管脚,时钟频率;drv部分是真正的驱动代码,该代码与具体的硬件信息无关,是抽象出来的代码,也是通用的代码。当需要编写led驱动时,如果有100个led就需要100个驱动,当使用platform总线后只需要使用一个drv驱动,唯一更改的是dev部分,这样对整个代码编写来说效率更高。当一个drv匹配到了一个dev后会调用drv里面的probe函数,该函数才是真正实现驱动代码的地方。如何匹配在后文讲解。匹配示意图如下。

platform总线驱动程序

先分析platform_device模块类型定义如下。

platform总线驱动程序

 

Name:是驱动名字,可以随便取 ,也是作为match的标准之一。

num_resources:resource资源的个数

resource:资源

id_entry:支持哪些dev,也是match标准之一

device中有3个成员比较常用

of_node:用于match匹配之一选项

platform_data:私人数据存放指针

driver_data:也是数据存放指针,操作需要通过函数接口,是内核使用的指针,在使用设备树时,解析的私人数据会放在这个指针下。后期驱动程序讲解中会经常跟这几个指针打交道,现在先做到心中有数。

 

                                                                                   

platform_driver结构定义如下:

platform总线驱动程序

Probe:match成功后会调用的函数

Remove:dev与drv分离时调用函数,做清理工作

剩下几个函数是关于休眠、电源管理等相关操作函数

Id_table:作为match匹配函数之一。

Drvier结构有如下几个重要成员:

Name:驱动名字,作为match选项之一。

of_match_table:match匹配选项之一。

注意图中有一个container_of函数,该函数 在内核中用法比较巧妙,是什么含义建议读者自行百度。

基本数据结构做了讲解,下面看看dev和drv如何做匹配的,匹配函数如下。当drv驱动或者dev驱动被加载platform总线都会调用match函数去检测是否有对应的dev或者drv(注意这里drv和dev的加载顺序是无关紧要的)。

platform总线驱动程序

从图中可以清晰看到有很多种匹配方式,常用的有三种方式,这里不会讲解很详细,主要讲解思路,具体代码实现可以看linux源代码。

of_driver_match_device函数实际是匹配platform_driver->drv->of_match_table  和platform_dev->dev->of_node。

platform_match_id函数实际是匹配pdrv->id_table 和pdev->name

最后匹配的pdev->name 和pdrv->name

注册加载Platform_device驱动程序资源如下:

platform总线驱动程序

注册加载Platform_device驱动程序方式如下:

platform总线驱动程序

 

加载platform_driver部分驱动程序如下:

platform总线驱动程序

platform_driver_register(&hello_drv);实现注册。

 

新内核可能对一些函数做了进一步的封装,封装方式如下。

Platfrom注册于注销函数封装到一个函数:

platform总线驱动程序platform总线驱动程序

 

这里代码使用的是回调函数调用方式,回调函数也就是函数指针的一种特殊方式,代码中还使用了宏联合剂,这些需要读者自行百度。

有些驱动不使用module_init做初始化函数,做了一些变形。

platform总线驱动程序

 

Platfrom总线流程基本讲解完毕,实验中提供的源码比较简单,阅读本文时可以参考Linux内核源码,probe函数怎么编写下文讲解,drv中如何得到dev中定义的资源也在下文中讲解。这里需要注意在3.x内核中已经不使用platform_device,因为这个造成了太多冗余代码。一个设备就需要一个dev驱动,dev驱动代码是相当庞大的,现在采用基本是dtb设备树。