5、数据结构spi_message

spi_message表示spi消息,由多个spi_transfer段组成。spi_message用来原子的执行spi_transfer表示的一串数组传输请求。这个传输队列是原子的,这意味着在这个消息完成之前不会有其它消息占用总线。消息的执行总是按照FIFO的顺序。向底层提交spi_message的代码要负责管理它的内存空间。未显示初始化的内存需要使用0来初始化。

struct spi_message {
        struct list_head        transfers;
        struct spi_device       *spi;// 传输的目的设备
        unsigned                is_dma_mapped:1;// 如果为真,此次调用提供dma和cpu虚拟地址
        /* REVISIT:  we might want a flag affecting the behavior of the
         * last transfer ... allowing things like "read 16 bit length L"
         * immediately followed by "read L bytes".  Basically imposing
         * a specific message scheduling algorithm.
         *
         * Some controller drivers (message-at-a-time queue processing)
         * could provide that as their default scheduling algorithm.  But
         * others (with multi-message pipelines) could need a flag to
         * tell them about such special cases.
         */
        /* completion is reported through a callback */
        void                    (*complete)(void *context);// 异步调用完成后的回调函数
        void                    *context;// 回调函数的参数
        unsigned                frame_length;
        unsigned                actual_length;// 实际传输的数据长度
        int                     status;// 该消息的发送结果,成功被置0,否则是一个负的错误码
        /* for optional use by whatever driver currently owns the
         * spi_message ...  between calls to spi_async and then later
         * complete(), that's the spi_master controller driver.
         */
        struct list_head        queue;
        void                    *state;

};

6、数据结构spi_board_info

spi_device的板级信息用spi_board_info结构体描述,该结构体记录着SPI外设使用的主机控制序号、片选信号、数据比特率、SPI传输模式等。ARM Linux3.x之后的内核在改为设备树之后,不再需要在arch/arm/mach-xxx中编码SPI的板级信息了,倾向于在SPI控制器节点下填写子节点。

struct spi_board_info {
        /* the device name and module name are coupled, like platform_bus;
         * "modalias" is normally the driver name.
         *
         * platform_data goes to spi_device.dev.platform_data,
         * controller_data goes to spi_device.controller_data,
         * irq is copied too
         */
        char            modalias[SPI_NAME_SIZE];
        const void      *platform_data;
        void            *controller_data;
        int             irq;
        /* slower signaling on noisy or low voltage boards */
        u32             max_speed_hz;

        /* bus_num is board specific and matches the bus_num of some
         * spi_master that will probably be registered later.
         *
         * chip_select reflects how this chip is wired to that master;
         * it's less than num_chipselect.
         */
        u16             bus_num;
        u16             chip_select;

        /* mode becomes spi_device.mode, and is essential for chips
         * where the default of SPI_CS_HIGH = 0 is wrong.
         */
        u16             mode;

        /* ... may need additional spi_device chip config data here.
         * avoid stuff protocol drivers can set; but include stuff
         * needed to behave without being bound to a driver:
         *  - quirks like clock rate mattering when not selected
         */

};

7、数据结构spi_bitbang

spi_bitbang是具体的负责信息传输的数据结构。

struct spi_bitbang {
spinlock_t lock;
u8 busy;//忙标志
u8 use_dma;
u8 flags; /* extra spi->mode support */
struct spi_master *master;
/* setup_transfer() changes clock and/or wordsize to match settings
* for this transfer; zeroes restore defaults from spi_device.
*/
int (*setup_transfer)(struct spi_device *spi,
struct spi_transfer *t);// 对数据传输进行设置

void (*chipselect)(struct spi_device *spi, int is_on);// 控制片选
#define BITBANG_CS_ACTIVE 1 /* normally nCS, active low */
#define BITBANG_CS_INACTIVE 0

/* txrx_bufs() may handle dma mapping for transfers that don't
* already have one (transfer.{tx,rx}_dma is zero), or use PIO
*/
int (*txrx_bufs)(struct spi_device *spi, struct spi_transfer *t);//实际的数据传输函数

/* txrx_word[SPI_MODE_*]() just looks like a shift register */
u32 (*txrx_word[4])(struct spi_device *spi,
unsigned nsecs,
u32 word, u8 bits);

};

五、SPI驱动的软件架构

在内核SPI驱动的软件架构中,进行了合理的分层和抽象,如下图所示:


1、SPI控制器驱动程序

SPI控制器不用关心设备的具体功能,它只负责把上层协议驱动准备好的数据按SPI总线的时序要求发送给SPI设备,同时把从设备收到的数据返回给上层的协议驱动,因此,内核把SPI控制器的驱动程序独立出来。SPI控制器驱动负责控制具体的控制器硬件,诸如DMA和中断操作等,因为多个上层的协议驱动可能会通过控制器请求数据传输操作,所以,SPI控制器驱动同时也要负责对这些请求进行队列管理,保证先进先出原则。

2、SPI通用接口封装层

为简化SPI驱动程序编写工作,同时也为了降低协议驱动程序和控制器驱动程序的耦合度,Linux内核把控制器驱动和协议驱动的一些通用操作封装成标准的接口,加上一些通用的逻辑处理操作,组成了SPI通用接口封装层。好处是,对于控制器驱动程序,只要实现标准的接口回调API,并把它注册到通用接口层即可,无需直接和协议层驱动程序进行交互。而对于协议层驱动来说,只需通过通用接口层提供的API即可完成设备和驱动的注册,并通过通用接口层的API完成数据的传输,无需关注SPI控制器驱动的实现细节。

3、SPI协议驱动程序

控制器驱动程序并不清楚和关注设备的具体功能,SPI设备的具体功能是由SPI协议驱动程序完成的,SPI协议驱动程序了解设备的功能和通信数据的协议格式。向下,协议驱动通过通用接口层和控制器交换数据,向上,协议驱动通常会根据设备的具体功能和内核的其它子系统进行交互,例如,和MTD层交互以便把SPI接口的存储设备实现为某个文件系统,和TTY子系统交互把SPI设备实现为一个TTY设备,和网络子系统交互以便把一个SPI设备实现为一个网络设备,等等。如果是一个专有的SPI设备,也可以按照设备的协议要求,实现自己的专有协议驱动。

4、SPI通用设备驱动程序

有时候,考虑到连接在SPI控制器上的设备的可变性,在内核没有配备相应的协议驱动程序,对于这种情况,内核准备了通用的SPI设备驱动程序,该通用设备驱动程序向用户空间提供了控制SPI控制的控制接口,具体的协议控制和数据传输工作交由用户空间根据具体的设备来完成,在这种方式中,只能采用同步的方式和SPI设备进行通信,通常用于一些数据量较少的简单SPI设备。


Logo

更多推荐