Linux SPI bus and device driver architecture Four: queuing of SPI data transfer

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 originator of the data transfer must wait for the end of the transmission, the period can not do other things, in code to explain that the function is called after the transfer, until the data transfer is complete, the function will return. And the asynchronous way is just the opposite, the initiator of data transmission does not need to wait for the end of the transmission, the data transfer can also do other things, in code to explain that the function is called after the transfer function will immediately return without waiting for the data transfer to complete, we just set a callback function, after the transfer is complete, The callback function is called to notify the initiator that the data transfer is complete. Synchronization is simple and easy to use, and is ideal for handling a single transmission of small amounts of data. However, the asynchronous approach is more appropriate for large data volumes and many times of transmission.

For the SPI controller, there are two conditions that must be considered in order to support the asynchronous approach:

    1. For the initiator of the same data transfer, since the Async method does not have to wait for the data transfer to be completed, the initiator can immediately launch a message when it returns, and the previous message has not been processed.
    2. For another different initiator, it is also possible to initiate a message transfer request at the same time.

/*****************************************************************************************************/
Statement: The content of this blog by Http://blog.csdn.net/droidphone Original, reproduced please indicate the source, thank you!
/*****************************************************************************************************/

Queuing is precisely in order to solve the above problem, so-called queueing, is to wait for the transmission of the message into a waiting queue, initiating a transfer operation, in fact, the corresponding message in order to put in a waiting queue, The system continuously detects if there is a message waiting to be transmitted in the queue, and if there is a constant dispatch of the data transfer kernel thread, remove the message from the queue and process it until the queue becomes empty. The SPI Universal interface layer enables us to implement the basic framework of queueing.

Spi_transfer of the queue

Recalling the introduction of the common interface layer, a spi_message is an atomic request for the protocol driver, and Spi_message is composed of multiple spi_transfer structures, which are organized together by a list, Let's take a look at these two data structures related fields for spi_transfer linked lists:

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;        ......};

As can be seen, a spi_message structure has a linked 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 using the following API:

Static inline Voidspi_message_add_tail (struct spi_transfer *t, struct spi_message *m) {        List_add_tail (&t-> Transfer_list, &m->transfers);}

The universal interface layer invokes the controller-driven Transfer_one_message callback function in a worker thread in a single message to complete the processing and transfer of the Spi_transfer list, and we'll leave it behind for the worker thread to discuss.

Spi_message of the queue

One or more protocol drivers can request multiple Spi_message requests at the same time to the controller driver, and these spi_message are also in the form of a linked list below the queue field that represents the spi_master structure of the controller:

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 *message);

The Spi_async function is an API that initiates an asynchronous transfer that hangs the spi_message structure under the Spi_master queue field and then initiates a kernel worker that is specifically prepared for the SPI Transport, which actually handles the transmission of the message. Because it is an asynchronous operation, the function returns immediately without waiting for the transfer 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_ There may be several pending spi_message structures under Master, and the worker thread will process each of these message requests on a first-in, one-out basis, after each message transfer is complete, corresponding to the Spi_ The complete callback function of the message structure is called to notify the Protocol driver to prepare the next frame of data. This is the spi_message of the queue. When a worker thread wakes up, the relationship between Spi_master, Spi_message, and spi_transfer can be used to describe:

Queues and initialization of worker threads

Through the Linux SPI Bus and device driver architecture Three: SPI controller driver This article, the SPI controller driver at initialization, will call the general interface layer provided by the Api:spi_register_master, to complete the controller registration and initialization work, The fields that are related to the queue and the initialization of the worker thread are done in that API. I'll first post the call sequence diagram for the API:

Figure 2 Call sequence diagram for Spi_register_master

If Spi_master sets the transfer callback function number field, indicating that the controller driver is not ready to use the queueing framework provided by the universal interface layer, the initialization of the queue will not occur, otherwise the Spi_master_initialize_queue function will be called:

/* 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, so if you are implementing a new SPI controller driver, remember, do not implement and assign values to the transfer callback field of the Spi_master structure in the controller driver you are playing! 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 called separately to initialize the queue and start the worker thread. The main function of the Spi_init_queue function is to create 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 is: spi_pump_messages, the function is the entire queued key implementation function, which we will discuss in the next section. Spi_start_queue is simple, just waking up the worker thread:

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 related work of the queue has been completed, the system waits for a message request to be initiated, and then handles the delivery of the message in the worker thread.

The working mechanism and process of queueing

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

Figure 3 Spi_async call sequence diagram

The Spi_async invokes 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 the queue list, and then wakes up the worker thread. The working function of a worker is spi_pump_messages, which first removes the spi_message from the queue and then invokes the controller-driven Prepare_transfer_hardware callback to let the controller drive the necessary hardware resources. The controller-driven Transfer_one_message callback function is then called to complete the transfer of the message, and the controller-driven Transfer_one_message callback function must be called after the transfer is complete spi_finalize_ The Current_message function notifies the universal interface layer to continue processing the next message in the queue, and the Spi_finalize_current_message function also invokes the complete callback function for that message. 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 general interface layer has prepared a standard implementation function for us: spi_transfer_one_message, so, Our controller driver simply implements the Transfer_one callback to complete the actual transfer work, without worrying about when it needs to be pressurized, OH call spi_finalize_current_message and so on. Here by the way also posted Transfer_one_message code:

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_completion (                &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->transfer_li                        St, &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 is using a different kernel version than the one I used to write, it is often asked if some functions or structures are different, so here's a way of declaring the kernel version I'm using: 3.13.0-RC6.

Linux SPI bus and device driver architecture Four: queuing of SPI data transfer

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.