Linux SPI总线和设备驱动架构之SPI数据传输的队列化

咱们知道,SPI数据传输能够有两种方式:同步方式和异步方式。所谓同步方式是指数据传输的发起者必须等待本次传输的结束,期间不能作其它事情,用代码来解释就是,调用传输的函数后,直到数据传输完成,函数才会返回。而异步方式则正好相反,数据传输的发起者无需等待传输的结束,数据传输期间还能够作其它事情,用代码来解释就是,调用传输的函数后,函数会马上返回而不用等待数据传输完成,咱们只需设置一个回调函数,传输完成后,该回调函数会被调用以通知发起者数据传送已经完成。同步方式简单易用,很适合处理那些少许数据的单次传输。可是对于数据量大、次数多的传输来讲,异步方式就显得更加合适。数据结构

 

对于SPI控制器来讲,要支持异步方式必需要考虑如下两种情况:架构

1. 对于同一个数据传输的发起者,既然异步方式无需等待数据传输完成便可返回,返回后,该发起者能够马上又发起一个message,而这时上一个message尚未处理完。框架

 

2.  对于另一个不一样的发起者来讲,也有可能同时发起一次message传输请求。异步

 

队列化正是为了为了解决以上的问题,所谓队列化,是指把等待传输的message放入一个等待队列中,发起一个传输操做,其实就是把对应的message按前后顺序放入一个等待队列中,系统会在不断检测队列中是否有等待传输的message,若是有就不停地调度数据传输内核线程,嵌入式物联网智能硬件等系统学习提高企鹅意义气呜呜吧久零就易,逐个取出队列中的message进行处理,直到队列变空为止。SPI通用接口层为咱们实现了队列化的基本框架。async

 

spi_transfer的队列化函数

 

回顾一下通用接口层的介绍,对协议驱动来讲,一个spi_message是一次数据交换的原子请求,而spi_message由多个spi_transfer结构组成,这些spi_transfer经过一个链表组织在一块儿,咱们看看这两个数据结构关于spi_transfer链表的相关字段:学习

 

[cpp] view plain copythis

1. struct spi_transfer {  线程

2.         ......  接口

3.         const void      *tx_buf;  

4.         void            *rx_buf;  

5.         ......  

6.   

7.         struct list_head transfer_list;  

8. };  

9.   

10. struct spi_message {  

11.         struct list_head        transfers;  

12.           

13.         struct spi_device       *spi;  

14.         ......          

15.         struct list_head        queue;  

16.         ......  

17. };  

 

可见,一个spi_message结构有一个链表头字段:transfers,而每一个spi_transfer结构都包含一个链表头字段:transfer_list,经过这两个链表头字段,全部属于此次message传输的transfer都会挂在spi_message.transfers字段下面。咱们能够经过如下API向spi_message结构中添加一个spi_transfer结构:

[cpp] view plain copy

1. static inline void  

2. spi_message_add_tail(struct spi_transfer *t, struct spi_message *m)  

3. {  

4.         list_add_tail(&t->transfer_list, &m->transfers);  

5. }  

 

通用接口层会以一个message为单位,在工做线程中调用控制器驱动的transfer_one_message回调函数来完成spi_transfer链表的处理和传输工做,关于工做线程,咱们留在后面讨论。

 

spi_message的队列化

 

一个或者多个协议驱动程序能够同时向控制器驱动申请多个spi_message请求,这些spi_message也是以链表的形式被过在表示控制器的spi_master结构体的queue字段下面:

[cpp] view plain copy

1. struct spi_master {  

2.         struct device   dev;  

3.         ......  

4.         bool                            queued;  

5.         struct kthread_worker           kworker;  

6.         struct task_struct              *kworker_task;  

7.         struct kthread_work             pump_messages;  

8.         spinlock_t                      queue_lock;  

9.         struct list_head                queue;  

10.         struct spi_message              *cur_msg;  

11.         ......  

12. }  

如下的API能够被协议驱动程序用于发起一个message传输操做:

[cpp] view plain copy

1. extern int spi_async(struct spi_device *spi, struct spi_message *message);  

spi_async函数是发起一个异步传输的API,它会把spi_message结构挂在spi_master的queue字段下,而后启动专门为spi传输准备的内核工做线程,由该工做线程来实际处理message的传输工做,由于是异步操做,因此该函数会马上返回,不会等待传输的完成,这时,协议驱动程序(多是另外一个协议驱动程序)能够再次调用该API,发起另外一个message传输请求,结果就是,当工做线程被唤醒时,spi_master下面可能已经挂了多个待处理的spi_message结构,工做线程会按先进先出的原则来逐个处理这些message请求,每一个message传送完成后,对应spi_message结构的complete回调函数就会被调用,以通知协议驱动程序准备下一帧数据。这就是spi_message的队列化。工做线程唤醒时,spi_master、spi_message和spi_transfer之间的关系能够用下图来描述

 

队列以及工做线程的初始化

 

经过Linux SPI总线和设备驱动架构之三:SPI控制器驱动这篇文章,SPI控制器驱动在初始化时,会调用通用接口层提供的API:spi_register_master,来完成控制器的注册和初始化工做,和队列化相关的字段和工做线程的初始化工做正是在该API中完成的。我先把该API的调用序列图贴出来:

 

若是spi_master设置了transfer回调函数字段,表示控制器驱动不许备使用通用接口层提供的队列化框架,有关队列化的初始化就不会进行,不然,spi_master_initialize_queue函数就会被调用:

[cpp] view plain copy

1. /* If we're using a queued driver, start the queue */  

2.         if (master->transfer)  

3.                 dev_info(dev, "master is unqueued, this is deprecated\n");  

4.         else {  

5.                 status = spi_master_initialize_queue(master);  

6.                 if (status) {  

7.                         device_del(&master->dev);  

8.                         goto done;  

9.                 }  

10.         }  

 

咱们固然不但愿本身实现一套队列化框架,因此,若是你在实现一个新的SPI控制器驱动,请记住,不要在你打控制器驱动中实现并赋值spi_master结构的transfer回调字段!进入spi_master_initialize_queue函数看看:

[cpp] view plain copy

1. static int spi_master_initialize_queue(struct spi_master *master)  

2. {  

3.         ......  

4.         master->queued = true;  

5.         master->transfer = spi_queued_transfer;  

6.         if (!master->transfer_one_message)  

7.                 master->transfer_one_message = spi_transfer_one_message;  

8.   

9.         /* Initialize and start queue */  

10.         ret = spi_init_queue(master);  

11.         ......  

12.         ret = spi_start_queue(master);  

13.         ......  

14. }  

相关文章
相关标签/搜索