| .. _microkernel_mailboxes: |
| |
| Mailboxes |
| ######### |
| |
| Definition |
| ********** |
| |
| A mailbox is defined in include :file:`/microkernel/mailbox.h`. |
| Mailboxes are a flexible way to pass data and for tasks to exchange messages. |
| |
| Function |
| ******** |
| |
| Each transfer within a mailbox can vary in size. The size of a data |
| transfer is only limited by the available memory on the platform. |
| Transmitted data is not buffered in the mailbox itself. Instead, the |
| buffer is either allocated from a memory pool block, or in block of |
| memory defined by the user. |
| |
| Mailboxes can work synchronously and asynchronously. Asynchronous |
| mailboxes require the sender to allocate a buffer from a memory pool |
| block, while synchronous mailboxes will copy the sender data to the |
| receiver buffer. |
| |
| The transfer contains one word of information that identifies either the |
| sender, or the receiver, or both. The sender task specifies the task it |
| wants to send to. The receiver task specifies the task it wants to |
| receive from. Then the mailbox checks the identity of the sender and |
| receiver tasks before passing the data. |
| |
| Usage |
| ***** |
| |
| Defining a Mailbox |
| ================== |
| |
| The following parameters must be defined: |
| |
| *name* |
| This specifies a unique name for the mailbox. |
| |
| Public Mailbox |
| -------------- |
| |
| Define the mailbox in the application's MDEF using the following syntax: |
| |
| .. code-block:: console |
| |
| MAILBOX name |
| |
| For example, the file :file:`projName.mdef` defines a mailbox as follows: |
| |
| .. code-block:: console |
| |
| % MAILBOX NAME |
| % ========================== |
| MAILBOX REQUEST_BOX |
| |
| A public mailbox can be referenced by name from any source file that |
| includes the file :file:`zephyr.h`. |
| |
| |
| Private Mailbox |
| --------------- |
| |
| Define the mailbox in a source file using the following syntax: |
| |
| .. code-block:: c |
| |
| DEFINE_MAILBOX(name); |
| |
| For example, the following code defines a private mailbox named ``PRIV_MBX``. |
| |
| .. code-block:: c |
| |
| DEFINE_MAILBOX(PRIV_MBX); |
| |
| The mailbox ``PRIV_MBX`` can be used in the same style as those |
| defined in the MDEF. |
| |
| To utilize this mailbox from a different source file use the following syntax: |
| |
| .. code-block:: c |
| |
| extern const kmbox_t PRIV_MBX; |
| |
| |
| Example: Sending Variable-Sized Mailbox Messages |
| ================================================ |
| |
| This code uses a mailbox to synchronously pass variable-sized requests |
| from a producing task to any consuming task that wants it. The message |
| "info" field is used to exchange information about the maximum size buffer |
| that each task can handle. |
| |
| .. code-block:: c |
| |
| void producer_task(void) |
| { |
| char buffer[100]; |
| int buffer_bytes_used; |
| |
| struct k_msg send_msg; |
| k_priority_t send_priority = task_priority_get(); |
| |
| while (1) { |
| |
| /* generate data to send */ |
| ... |
| buffer_bytes_used = ... ; |
| memcpy(buffer, source, buffer_bytes_used); |
| |
| /* prepare to send message */ |
| send_msg.info = buffer_bytes_used; |
| send_msg.size = buffer_bytes_used; |
| send_msg.tx_data = buffer; |
| send_msg.rx_task = ANYTASK; |
| |
| /* send message and wait until a consumer receives it */ |
| task_mbox_put_wait(REQUEST_BOX, send_priority, &send_msg); |
| |
| /* info, size, and rx_task fields have been updated */ |
| |
| /* verify that message data was fully received */ |
| if (send_msg.size < buffer_bytes_used) { |
| printf("some message data dropped during transfer!"); |
| printf("receiver only had room for %d bytes", send_msg.info); |
| } |
| } |
| } |
| |
| Example: Receiving Variable-Sized Mailbox Messages |
| ================================================== |
| |
| This code uses a mailbox to process variable-sized requests from any |
| producing task. The message "info" field is used to exchange information |
| about the maximum size buffer that each task can handle. |
| |
| .. code-block:: c |
| |
| void consumer_task(void) |
| { |
| struct k_msg recv_msg; |
| char buffer[100]; |
| |
| int i; |
| int total; |
| |
| while (1) { |
| /* prepare to receive message */ |
| recv_msg.info = 100; |
| recv_msg.size = 100; |
| recv_msg.rx_data = buffer; |
| recv_msg.rx_task = ANYTASK; |
| |
| /* get a data item, waiting as long as needed */ |
| task_mbox_get_wait(REQUEST_BOX, &recv_msg); |
| |
| /* info, size, and tx_task fields have been updated */ |
| |
| /* verify that message data was fully received */ |
| if (recv_msg.info != recv_msg.size) { |
| printf("some message data dropped during transfer!"); |
| printf("sender tried to send %d bytes", recv_msg.info); |
| } |
| |
| /* compute sum of all message bytes (from 0 to 100 of them) */ |
| total = 0; |
| for (i = 0; i < recv_msg.size; i++) { |
| total += buffer[i]; |
| } |
| } |
| } |
| |
| Example: Sending an Empty Mailbox Message |
| ========================================= |
| |
| This code uses a mailbox to synchronously pass 4 byte random values |
| to any consuming task that wants one. The message "info" field is |
| large enough to carry the information being exchanged, so the data buffer |
| portion of the message isn't used. |
| |
| .. code-block:: c |
| |
| void producer_task(void) |
| { |
| struct k_msg send_msg; |
| k_priority_t send_priority = task_priority_get(); |
| |
| while (1) { |
| |
| /* generate random value to send */ |
| uint32_t random_value = sys_rand32_get(); |
| |
| /* prepare to send empty message */ |
| send_msg.info = random_value; |
| send_msg.size = 0; |
| send_msg.tx_data = NULL; |
| send_msg.rx_task = ANYTASK; |
| |
| /* send message and wait until a consumer receives it */ |
| task_mbox_put_wait(REQUEST_BOX, send_priority, &send_msg); |
| |
| /* no need to examine the receiver's "info" value */ |
| } |
| } |
| |
| Example: Receiving a Mailbox Message in 2 Stages |
| ================================================ |
| |
| This code uses a mailbox to receive data from a producing task only if |
| it meets certain criteria, thereby eliminating unneeded data copying. |
| The message "info" field supplied by the sender is used to classify the message. |
| |
| .. code-block:: c |
| |
| void consumer_task(void) |
| { |
| struct k_msg recv_msg; |
| char buffer[10000]; |
| |
| while (1) { |
| /* prepare to receive message */ |
| recv_msg.size = 10000; |
| recv_msg.rx_data = NULL; |
| recv_msg.rx_task = ANYTASK; |
| |
| /* get message, but not its data */ |
| task_mbox_get_wait(REQUEST_BOX, &recv_msg); |
| |
| /* get message data for only some certain messages */ |
| if (is_message_type_ok(recv_msg.info)) { |
| /* retrieve message data and discard message */ |
| recv_msg.rx_data = buffer; |
| task_mbox_data_get(&recv_msg); |
| |
| /* process data in "buffer" */ |
| ... |
| } else { |
| /* ignore message data and discard message */ |
| recv_msg.size = 0; |
| task_mbox_data_get(&recv_msg); |
| } |
| } |
| } |
| |
| Example: Sending an Asynchronous Mailbox Message |
| ================================================ |
| |
| This code uses a mailbox to send asynchronous messages using memory blocks |
| obtained from TXPOOL, thereby eliminating unneeded data copying when exchanging |
| large messages. The optional semaphore capability is used to hold off |
| the sending of a new message until the previous message has been consumed, |
| so that a backlog of messages doesn't build up if the consuming task is unable |
| to keep up. |
| |
| .. code-block:: c |
| |
| void producer_task(void) |
| { |
| struct k_msg send_msg; |
| kpriority_t send_priority = task_priority_get(); |
| |
| volatile char *hw_buffer; |
| |
| /* indicate that all previous messages have been processed */ |
| task_sem_give(MY_SEMA); |
| |
| while (1) { |
| /* allocate memory block that will hold message data */ |
| task_mem_pool_alloc_wait(&send_msg.tx_block, TXPOOL, 4096); |
| |
| /* keep saving hardware-generated data in the memory block */ |
| /* until the previous message has been received by the consumer */ |
| do { |
| memcpy(send_msg.tx_block.pointer_to_data, hw_buffer, 4096); |
| } while (task_sem_take(MY_SEMA) != RC_OK); |
| |
| /* finish preparing to send message */ |
| send_msg.size = 4096; |
| send_msg.rx_task = ANYTASK; |
| |
| /* send message containing most current data and loop around */ |
| task_mbox_block_put(REQUEST_BOX, send_priority, &send_msg, MY_SEMA); |
| } |
| } |
| |
| Example: Receiving an Asynchronous Mailbox Message |
| ================================================== |
| |
| This code uses a mailbox to receive messages sent asynchronously using a |
| memory block, thereby eliminating unneeded data copying when processing |
| a large message. |
| |
| .. code-block:: c |
| |
| void consumer_task(void) |
| { |
| struct k_msg recv_msg; |
| struct k_block recv_block; |
| |
| int total; |
| char *data_ptr; |
| int i; |
| |
| while (1) { |
| /* prepare to receive message */ |
| recv_msg.size = 10000; |
| recv_msg.rx_data = NULL; |
| recv_msg.rx_task = ANYTASK; |
| |
| /* get message, but not its data */ |
| task_mbox_get_wait(REQUEST_BOX, &recv_msg); |
| |
| /* get message data as a memory block and discard message */ |
| task_mbox_data_block_get_wait(&recv_msg, &recv_block, RXPOOL); |
| |
| /* compute sum of all message bytes in memory block */ |
| total = 0; |
| data_ptr = (char *)(recv_block.pointer_to_data); |
| for (i = 0; i < recv_msg.size; i++) { |
| total += data_ptr++; |
| } |
| |
| /* release memory block containing data */ |
| task_mem_pool_free(&recv_block); |
| } |
| } |
| |
| .. note:: |
| An incoming message that was sent synchronously is also processed correctly |
| by this algorithm, since the mailbox automatically creates a memory block |
| containing the message data using RXPOOL. However, the performance benefit |
| of using the asynchronous approach is lost. |
| |
| |
| APIs |
| **** |
| |
| The following APIs for synchronous mailbox operations are provided |
| by microkernel.h. |
| |
| +-----------------------------------------+------------------------------------+ |
| | Call | Description | |
| +=========================================+====================================+ |
| | :c:func:`task_mbox_put()` | Puts message in a mailbox, or | |
| | | fails if a receiver isn't waiting. | |
| +-----------------------------------------+------------------------------------+ |
| | :c:func:`task_mbox_put_wait()` | Puts message in a mailbox and | |
| | | waits until it is received. | |
| +-----------------------------------------+------------------------------------+ |
| | :c:func:`task_mbox_put_wait_timeout()` | Puts message in a mailbox and | |
| | | waits for a specified time period | |
| | | for it to be received. | |
| +-----------------------------------------+------------------------------------+ |
| | :c:func:`task_mbox_get()` | Gets message from a mailbox, or | |
| | | fails if no message is available. | |
| +-----------------------------------------+------------------------------------+ |
| | :c:func:`task_mbox_get_wait()` | Gets message from a mailbox, or | |
| | | waits until one is available. | |
| +-----------------------------------------+------------------------------------+ |
| | :c:func:`task_mbox_get_wait_timeout()` | Gets message from a mailbox, or | |
| | | waits for a specified time period | |
| | | for one to become available. | |
| +-----------------------------------------+------------------------------------+ |
| | :c:func:`task_mbox_data_get()` | Finishes receiving message that | |
| | | was received without its data. | |
| +-----------------------------------------+------------------------------------+ |
| |
| The following APIs for asynchronous mailbox operations using memory pool blocks |
| are provided by microkernel.h. |
| |
| +---------------------------------------------------+-----------------------------------+ |
| | Call | Description | |
| +===================================================+===================================+ |
| | :c:func:`task_mbox_block_put()` | Puts message in a mailbox, even | |
| | | if a receiver isn't waiting. | |
| +---------------------------------------------------+-----------------------------------+ |
| | :c:func:`task_mbox_data_block_get()` | Finishes receiving message that | |
| | | was received without its data, or | |
| | | fails if no block is available. | |
| +---------------------------------------------------+-----------------------------------+ |
| | :c:func:`task_mbox_data_block_get_wait()` | Finishes receiving message that | |
| | | was received without its data, or | |
| | | waits until a block is available. | |
| +---------------------------------------------------+-----------------------------------+ |
| | :c:func:`task_mbox_data_block_get_wait_timeout()` | Finishes receiving message that | |
| | | was received without its data, or | |
| | | waits for a specified time period | |
| | | for a block to become available. | |
| +---------------------------------------------------+-----------------------------------+ |