首页 理论教育深入剖析Linux内核与设备驱动:解析SPI总线驱动框架

深入剖析Linux内核与设备驱动:解析SPI总线驱动框架

【摘要】:SPI总线框架对这两类设备进行管理,具体的框架如图7-6所示。通常的SPI驱动都是在内核实现并在内核态执行的,所以就不对spidev进行详细的分析。SPI总线框架整体的功能就是这样,接下来看看具体各部分的实现。在SPI总线框架中相应的实体是spi_board_info,内容如下:其中的主要信息是与总线信号相关的属性。

1.总线相关以及核心框架

同I2C总线相同,先来了解SPI总线的实现。在介绍系统初始化的时候,整个SPI的初始化函数spi_init定义为postcore_initcall,这样可保证SPI总线在SPI设备或者驱动注册之前进行注册,从而确保总线的功能。初始化中包含如下语句:

978-7-111-49426-3-Chapter07-62.jpg

这里注册了总线类型,其中bus_register设置总线自动probe功能。而spi_bus_type作为了解整个总线框架的入口,详细定义如下:

978-7-111-49426-3-Chapter07-63.jpg

这里重点的接口是match。下面详细分析该接口:

978-7-111-49426-3-Chapter07-64.jpg

从分析中可见,主要就是进行名字匹配。这里并没有像I2C验证设备类型,这是为什么呢?难道总线控制器不是设备吗?总线控制器还是设备,但是SPI的实现方式不同,在初始化函数spi_init中有如下代码:

978-7-111-49426-3-Chapter07-65.jpg

这里创建了class功能类,总线控制器作为class设备进行管理,而match只是匹配相同bus的设备,这样总线控制器设备就不会进行match操作了。总线进行匹配的设备是spi_de-vice,这就是总线连接设备的管理实体。

在spi_bus_type中并没有看到probe接口,probe操作是match之后就需要进行的操作,主要有设备特殊资源的探测、申请以及初始化的操作。SPI总线是如何实现该功能的呢?下面来看看对应的spi_driver注册的操作:

978-7-111-49426-3-Chapter07-66.jpg

这里直接将接口赋值给驱动的接口,在设备模型的统一操作中如果找不到总线相关的接口就会执行driver的接口,这样问题就解决了。与I2C比较又是不同的实现方式。系统提供了统一的框架,具体的实现方法还是仁者见仁的。

SPI总线框架对这两类设备进行管理,具体的框架如图7-6所示。

978-7-111-49426-3-Chapter07-67.jpg

图7-6 LinuxSPI总线框架

从图7-6中可见,SPI设备主要是为内核中不同的功能模块服务的,当不同功能模块的驱动需要使用SPI设备进行操作时,通常会在具体的管理实体中嵌入spi_device,实际的总线操作通过spi_device来实现。图中右侧可见特殊的spidev,这是对应用开放的接口设备,SPI通过CS信号进行设备管理,而相应的CS排列主要属于板级信息,所以不适合像I2C总线一样将总线控制器对应用开放(应用程序操作设备需要对不同板子进行修改)。将CS信息对应用屏蔽,只开放具体的设备,这就是spidev的作用,应用只是针对具体的设备进行操作,即设备的应用层驱动(驱动在用户态执行),要在应用层使用该功能需要在注册设备时将spi_board_info中的modalias设置为spidev,这样就可以在应用层实现相应设备的驱动。通常的SPI驱动都是在内核实现并在内核态执行的,所以就不对spidev进行详细的分析。

SPI总线框架整体的功能就是这样,接下来看看具体各部分的实现。

2.总线控制器相关

SPI总线框架对总线控制器的管理主要是通过i2c_adapter来实现的,其详细分析如下:

978-7-111-49426-3-Chapter07-68.jpg

关于setup接口,框架为设备进行操作提供了统一的接口spi_setup,其中会对信号属性、传输字长、总线频率等进行匹配和设置。

这里所有的接口都是与设备总线控制器相关的,通过这些接口设备的总线控制器实现总线操作。

SPI总线框架为总线控制器驱动开发提供了相关的接口,具体如下:

978-7-111-49426-3-Chapter07-69.jpg

978-7-111-49426-3-Chapter07-70.jpg

下面详细看看spi_alloc_master的具体实现过程:

978-7-111-49426-3-Chapter07-71.jpg

这里还为设备分配了所需的空间,设备不需要进行相应的操作,只要通过spi_master_get_devdata来获得相应的指针即可。

spi_register_master注册了总线控制器,就相当于建立了总线,相应的会将总线中的设备创建并注册。这里主要通过如下操作完成相应功能:

978-7-111-49426-3-Chapter07-72.jpg

这里是实现总线设备发现相关功能,根据板级的信息注册spi_device。

总线的主要功能(包括总线传输事务、总线设备发现)就都在代表SPI总线的SPI总线控制器部分有了完整的支持。(www.chuimin.cn)

3.总线设备相关

对SPI总线设备的管理,SPI总线框架提供了spi_device来完成该功能,详细内容如下:

978-7-111-49426-3-Chapter07-73.jpg

从相关属性来看,主要是master及其与总线信号相关的属性。下面来看看与之关联driver的详细信息:

978-7-111-49426-3-Chapter07-74.jpg

从spi_driver的内容分析可见,其主要负责总线相关以及电源管理相关的操作,操作接口中并不涉及实际的信息传输,可以说该驱动同样是处理总线和设备模型相关的事务,并不涉及功能的事务。功能型事务涉及操作的具体内容是在功能型驱动中通过SPI的接口实现的。而功能型的事务需要有总线设备的信息。这就要求功能型驱动管理实体中能够包含spi_device的信息。这个桥梁是与SPI总线事务相关的,所以该工作自然就由spi_driver来实现,具体的接口就是probe,这样各种实体就关联起来。以ads7846_probe接口为例,ads7846是SPI总线接口的触屏控制器,在功能类型中对应于输入设备,相应的spi_driver_的probe接口即ads7846_probe中有如下代码:

978-7-111-49426-3-Chapter07-75.jpg

978-7-111-49426-3-Chapter07-76.jpg

从中可见,spi device、触屏设备管理实体以及input device建立了关联,这样从功能型设备到总线设备就形成了一个整体,可以进行完整的操作。只有总线设备与功能管理实体关联后才能完成功能性的工作。

无论spi_device还是spi_driver都属于SPI总线框架管理的运行时实体,而SPI总线设备通常是直接焊接在板子上面,这些固定的信息同样需要进行表示,在创建动态管理的设备实体时需要这些信息。在SPI总线框架中相应的实体是spi_board_info,内容如下:

978-7-111-49426-3-Chapter07-77.jpg

其中的主要信息是与总线信号相关的属性。

对SPI总线设备的创建,框架提供了相应的函数接口,具体如下:

978-7-111-49426-3-Chapter07-78.jpg

从中可见,要创建总线设备需要spi_master和spi_board_info,对于特定总线设备有spi_board_info信息,这就需要明确与spi_master关联。

创建总线设备有两种方法:

方法一是通过struct spi_master *spi_busnum_to_master(u16 busnum);来获得指定的spi_master,然后调用spi_new_device来创建总线设备。

方法二是早期的初始化阶段调用spi_register_board_info(struct spi_board_info const *info,unsignedn);函数注册某个SPI总线上的所有设备信息,在总线控制器spi_master会在注册时遍历这些信息为总线上的设备创建spi_device。

方法一主要用于总线设备信息初始化晚于spi_master初始化的情况,如使用SPI总线的V4L2子设备;而方法二则用于较早初始化的情况。

4.总线传输接口

总线传输是完成功能型事务的基础。SPI总线传输是由spi_message和spi_transfer共同进行管理的,下面来看一下详细内容:

978-7-111-49426-3-Chapter07-79.jpg

978-7-111-49426-3-Chapter07-80.jpg

可见SPI总线设备的总线传输是以spi_transfer为单位的,可以进行批处理,将一次批处理所有的spi_transfer,并形成链表通过spi_message进行管理。而spi_message中会与具体的spi_device关联,这样某个SPI总线设备的总线传输需要的所有信息就可以通过spi_message进行管理。

通常设备进行SPI传输的操作流程如下:

978-7-111-49426-3-Chapter07-81.jpg

功能型驱动可以根据自身的需要设置spi_transfer,读写操作信息都设置在一起,然后通过spi_message_add_tail加入spi_message链表中;在spi_message准备好之后,就可以通过spi_sync进行实际的传输操作。具体如下:

978-7-111-49426-3-Chapter07-82.jpg

这样设备就同步等待操作完成,具体的实现是通过__spi_sync来完成的,细节如下:

978-7-111-49426-3-Chapter07-83.jpg

978-7-111-49426-3-Chapter07-84.jpg

可见主要操作是由总线控制器驱动完成的,而在总线框架层则是等待completion同步操作完成后返回,这样就可保证整个传输的操作是以同步方式实现的。