Linux SPI bus and device-driven architecture of the four: SPI data transmission of the queue of __linux

Source: Internet
Author: User

We know that SPI data transfer can be in two ways: synchronous and asynchronous. The so-called synchronization means that the initiator of the data transmission must wait for the end of this transmission, can not do other things, in code to explain that, after the transfer function is called, until the data transfer completes, the function will return. The asynchronous approach is the opposite, the initiator of the data transmission without waiting for the end of the transmission, data transmission can also do other things, with the code to explain that the function of the transfer, the function will return immediately without waiting for data transmission complete, we just set a callback function, after the transmission is completed, The callback function is invoked to notify the initiator that the data transfer has been completed. The synchronization method is simple and easy to handle, and is ideal for single transmission of small amounts of data. However, the asynchronous method is more suitable for the transmission with large data and more times.

For SPI controllers, the following two conditions must be considered to support asynchronous methods:
For the initiator of the same data transmission, since the asynchronous method returns without waiting for the data to be completed, the initiator can immediately initiate a message and the previous message has not been processed. For another different initiator, it is possible to initiate a message transfer request at the same time. /*****************************************************************************************************/
Statement: This Bo content by Http://blog.csdn.net/droidphone Original, reproduced please indicate the source, thank you.
/*****************************************************************************************************/

Queuing is precisely in order to solve the above problem, the so-called queue, refers to the message waiting for transmission into a waiting queue, initiating a transmission operation, in fact, the corresponding message in order to put in a waiting queue, The system will continuously detect whether there is a message waiting for transmission in the queue, if there is a non-stop scheduling of data transmission kernel thread, the queue is taken out of the message processing, until the queue becomes empty. The common interface layer of SPI implements the basic framework of queueing.

Spi_transfer A review of the General Interface layer Introduction, for the protocol driver, a spi_message is a data exchange of the atomic request, and Spi_message consists of multiple spi_transfer structure, these Spi_ Transfer through a list of linked lists, let's look at the related fields of the two data structures about the Spi_transfer list:

struct Spi_transfer {
        ...
        const void      *tx_buf;
        void            *rx_buf;
        ......

        struct List_head transfer_list;

struct Spi_message {
        struct list_head        transfers;
        
        struct Spi_device       *spi;
        ......        
        struct List_head        queue;
        ......
};
Visible, a spi_message structure has a chain header field: transfers, and each Spi_transfer structure contains a list header field: Transfer_list, through the two linked header fields, All transfer that belong to this message transmission will be hung under the Spi_message.transfers field. We can add a spi_transfer structure to the spi_message structure through the following APIs:
static inline void
spi_message_add_tail (struct spi_transfer *t, struct spi_message *m)
{
        List_add_tail (&t->transfer_list, &m->transfers);
}

The common interface layer, in a message, calls the controller-driven Transfer_one_message callback function in the worker thread to complete the processing and transmission of the spi_transfer linked list, and we'll stay behind to discuss the work threads. Spi_message of the queue

One or more protocol drivers can request multiple Spi_message requests to the controller at the same time, and these spi_message are also in the form of a linked list under the Queue field representing the controller's spi_master structure:

struct Spi_master {
        struct device   dev;
        ......
        BOOL                            queued;
        struct Kthread_worker           kworker;
        struct task_struct              *kworker_task;
        struct kthread_work             pump_messages;
        spinlock_t                      Queue_lock;
        struct List_head                queue;
        struct Spi_message              *cur_msg;
        ......
}
The following APIs can be used by the protocol driver to initiate a message transfer operation:

extern int Spi_async (struct spi_device *spi, struct spi_message);
The Spi_async function is an API that initiates an asynchronous transmission, which hangs the spi_message structure under the Spi_master queue field, and then initiates a kernel worker thread specially prepared for the SPI transfer, which is actually handled by the worker thread for the transmission of the message. Because it is an asynchronous operation, the function returns immediately and does not wait for the transmission to complete, at which point the protocol driver (possibly another protocol driver) can invoke the API again, initiating another message transfer request, and the result is that when the worker thread is awakened, the Spi_ Master may have several pending spi_message structures hanging below it, and the worker threads will process the message requests one at a time according to the FIFO principle, and after each message transfer, the corresponding SPI_ The complete callback function of the message structure is invoked to notify the Protocol driver of preparing the next frame of data. This is the queue of spi_message. When a worker thread wakes up, the relationship between Spi_master, Spi_message, and Spi_transfer can be described in the following illustration:

queues and the initialization of worker threads

Through the Linux SPI Bus and device-driven architecture of the third: SPI controller driver This article, the SPI controller driver in the initialization, the common interface layer provides the api:spi_register_master, to complete the controller registration and initialization work, It is in this API that the initialization of the fields and worker threads associated with the queue is done. Let me just post the call sequence diagram for this API:


Figure 2 The call sequence diagram Spi_register_master

If Spi_master sets the transfer callback function number segment to indicate that the controller driver is not prepared to use the queued framework provided by the common interface layer, the initialization of the queue is not performed, otherwise the Spi_master_initialize_queue function is invoked:

/* If we ' re using a queued driver, start the queue/
        if (master->transfer)
                dev_info (Dev, "Master is unqueued, This is deprecated\n ");
        else {
                status = Spi_master_initialize_queue (master);
                if (status) {
                        Device_del (&master->dev);
                        goto done;
                }
        
We certainly do not want to implement a set of queueing frameworks ourselves, so if you are implementing a new SPI controller driver, remember not to implement and assign a transfer callback field to the Spi_master structure in your hit controller drive. Enter the Spi_master_initialize_queue function to see:

static int spi_master_initialize_queue (struct spi_master *master)
{
        ...
        Master->queued = true;
        Master->transfer = Spi_queued_transfer;
        if (!master->transfer_one_message)
                master->transfer_one_message = spi_transfer_one_message;

        /* Initialize and start queue *
        /ret = Spi_init_queue (master);
        ......
        ret = Spi_start_queue (master);
        ......
}
This function sets the Master->transfer callback field to the default implementation function: Spi_queued_transfer, if the controller driver does not implement the Transfer_one_message callback, use the default Spi_transfer_ The One_message function is assigned a value. The Spi_init_queue and Spi_start_queue functions are then invoked separately to initialize the queue and start the worker thread. The main function of the Spi_init_queue function is to establish a kernel worker thread:
static int spi_init_queue (struct spi_master *master)
{
        ...

        Init_list_head (&master->queue);
        ......
        Init_kthread_worker (&master->kworker);
        Master->kworker_task = Kthread_run (KTHREAD_WORKER_FN,
                                           &master->kworker, "%s",
                                           Dev_name (& Master->dev));
        ......
        Init_kthread_work (&master->pump_messages, spi_pump_messages);

        ......

        return 0;
}
The working function of the kernel worker thread is: Spi_pump_messages, which is the key implementation function for the entire queue, and we'll discuss that function in the next section. Spi_start_queue is simple enough to wake up the worker thread only:

static int spi_start_queue (struct spi_master *master)
{
        ...

        Master->running = true;
        master->cur_msg = NULL;
        ......
        Queue_kthread_work (&master->kworker, &master->pump_messages);

        return 0;
}
Since then, the queue-related work has been completed, the system waits for the message request to be initiated, and then handles the message transfer work in the worker thread.

the working mechanism and process of the queue

When the protocol driver initiates a message request through Spi_async, the queue and worker threads are activated, triggering the operation of some columns, and finally completing the message transfer operation. Let's take a look at the call sequence diagram for the Spi_async function:


Figure 3 Spi_async call sequence diagram

Spi_async will invoke the controller-driven transfer callback, as discussed in the previous section, and the transfer callback has been set to the default implementation function: Spi_queued_transfer, the function simply adds the spi_message structure to the SPI _master in the queue list, and then wakes the worker thread. The worker thread's work function is spi_pump_messages, it first removes the spi_message from the queue, and then invokes the controller-driven Prepare_transfer_hardware callback to allow the controller to drive the necessary hardware resources. The controller-driven Transfer_one_message callback function is then invoked to complete the transfer of the message, and the controller-driven Transfer_one_message callback function must be invoked after the transfer is completed Spi_finalize_ Current_message function, which notifies the common interface layer to continue processing the next message in the queue, and the Spi_finalize_current_message function also invokes the complete callback function of the To notify the protocol driver to prepare the next frame of data.

With regard to the controller-driven Transfer_one_message callback function, our controller driver can not implement this function, the common interface layer has prepared a standard implementation function for us: spi_transfer_one_message, so, Our controller driver as long as the implementation of Transfer_one callback to complete the actual transmission work can be, and do not care when the need to pressure oh call spi_finalize_current_message and other details. The Transfer_one_message code is also posted here:

static int spi_transfer_one_message (struct spi_master *master, struct spi_message *msg

        ) {... spi_set_cs (MSG->SPI, true); List_for_each_entry (Xfer, &msg->transfers, transfer_list) {... reinit_completio

                N (&master->xfer_completion);
                ret = Master->transfer_one (master, Msg->spi, Xfer);

                ... if (Ret > 0) wait_for_completion (&master->xfer_completion); ... if (xfer->cs_change) {if List_is_last (&xfer->tra Nsfer_list, &msg->transfers)) {Keep_cs =
                        True
                                else {cur_cs =!cur_cs;
                        Spi_set_cs (Msg->spi, Cur_cs); }
                }

                Msg->actual_length + + xfer->len;

        } out:if (ret!= 0 | |!keep_cs) SPI_SET_CS (Msg->spi, false);

        ... spi_finalize_current_message (master);
return ret; }
The logic is clear, and this is no longer explained here. Because most of the time the reader uses a kernel version that is different from the version I use when I write, it is often asked that some functions or structures are different, so here's a way to declare the kernel version I'm using: 3.13.0-RC6.


Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.