Skip to content

Instantly share code, notes, and snippets.

@royratcliffe
Last active April 10, 2023 08:26
Show Gist options
  • Save royratcliffe/8dcac1f78ce929afbe34eca4f544718c to your computer and use it in GitHub Desktop.
Save royratcliffe/8dcac1f78ce929afbe34eca4f544718c to your computer and use it in GitHub Desktop.
Mailbox on FreeRTOS

FreeRTOS Mailbox

Copyright (c) 2023, Roy Ratcliffe, Northumberland, United Kingdom

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

/*!
* \file containerof.h
*/
#pragma once
#include <stddef.h>
/*!
* \brief Casts member pointer to containing structure pointer.
* \param ptr Pointer to member.
* \param type Type of container structure.
* \param member Name of container's member.
*
* Utilises GCC's \c offsetof built-in function to displace the member pointer
* backwards by the difference between the container's address and the member's
* address. Subtracting a size from a void pointer decrements the pointer by one
* byte for each displacement unit.
*/
#define CONTAINER_OF(ptr, type, member) \
({ \
void *void_ptr = (void *)(ptr); \
((type *)(void_ptr - offsetof(type, member))); \
})
/*
* mailbox.c
*/
/*
* \file
*/
#include "mailbox_prv.h"
#include <memory.h>
#include <stdio.h>
#include "containerof.h"
MailboxHandle_t xMailboxCreate(size_t xBufferSizeBytes) {
MailboxHandle_t xMailbox = pvPortMalloc(sizeof(*xMailbox));
configASSERT(xMailbox);
(void)memset(xMailbox, 0, sizeof(*xMailbox));
xMailbox->xMessageBuffer = xMessageBufferCreate(xBufferSizeBytes);
configASSERT(xMailbox->xMessageBuffer);
vListInitialise(&xMailbox->xLinking);
vListInitialiseItem(&xMailbox->xLinked);
listSET_LIST_ITEM_OWNER(&xMailbox->xLinked, NULL);
listSET_LIST_ITEM_VALUE(&xMailbox->xLinked, portMAX_DELAY);
return xMailbox;
}
MailboxHandle_t xMailboxNew() { return xMailboxCreate(mailboxBUFFER_SIZE_BYTES); }
void vMailboxDelete(MailboxHandle_t xMailbox) {
if (xMailbox == NULL) return;
vMailboxUnlink(xMailbox);
vMailboxUnlinkAll(xMailbox);
vMessageBufferDelete(xMailbox->xMessageBuffer);
vPortFree(xMailbox);
}
BaseType_t xMailboxSpawnWith(MailboxHandle_t xMailbox, TaskFunction_t xTaskCode, const char *pcName,
const configSTACK_DEPTH_TYPE usStackDepth, UBaseType_t uxPriority) {
char name[configMAX_TASK_NAME_LEN];
(void)snprintf(name, sizeof(name), "%s@%p", pcName ? pcName : "mailbox", xMailbox);
return xTaskCreate(xTaskCode, name, usStackDepth, xMailbox, uxPriority, &xMailbox->xTask);
}
BaseType_t xMailboxSpawn(MailboxHandle_t xMailbox, TaskFunction_t xTaskCode, const char *pcName) {
return xMailboxSpawnWith(xMailbox, xTaskCode, pcName, mailboxSTACK_DEPTH, mailboxPRIORITY);
}
BaseType_t xMailboxLink(MailboxHandle_t xLinking, MailboxHandle_t xLinked) {
if (xLinking == xLinked) return pdFAIL;
/*
* Switch from one linking list to another, or from some list to none,
* entirely atomically.
*/
taskENTER_CRITICAL();
{
if (xLinked) {
if (listLIST_ITEM_CONTAINER(&xLinked->xLinked) != NULL) (void)uxListRemove(&xLinked->xLinked);
if (xLinking) vListInsert(&xLinking->xLinking, &xLinked->xLinked);
}
}
taskEXIT_CRITICAL();
return pdPASS;
}
void vMailboxUnlink(MailboxHandle_t xMailbox) {
taskENTER_CRITICAL();
{
if (listLIST_ITEM_CONTAINER(&xMailbox->xLinked) != NULL) (void)uxListRemove(&xMailbox->xLinked);
}
taskEXIT_CRITICAL();
}
void vMailboxUnlinkAll(MailboxHandle_t xMailbox) {
BaseType_t prvYield(MailboxHandle_t xLinked) {
vMailboxUnlink(xLinked);
return pdFAIL;
}
(void)xMailboxYieldLinked(xMailbox, prvYield);
}
BaseType_t xMailboxSetLinkOwner(MailboxHandle_t xMailbox, void *pvOwner) {
if (xMailbox == NULL && (xMailbox = xMailboxSelf()) == NULL) return pdFAIL;
listSET_LIST_ITEM_OWNER(&xMailbox->xLinked, pvOwner);
return pdPASS;
}
void *pvMailboxGetLinkOwner(MailboxHandle_t xMailbox) {
if (xMailbox == NULL && (xMailbox = xMailboxSelf()) == NULL) return NULL;
return listGET_LIST_ITEM_OWNER(&xMailbox->xLinked);
}
BaseType_t xMailboxSetLinkValue(MailboxHandle_t xMailbox, TickType_t xValue) {
if (xMailbox == NULL && (xMailbox = xMailboxSelf()) == NULL) return pdFAIL;
listSET_LIST_ITEM_VALUE(&xMailbox->xLinked, xValue);
return pdPASS;
}
TickType_t xMailboxGetLinkValue(MailboxHandle_t xMailbox) {
if (xMailbox == NULL && (xMailbox = xMailboxSelf()) == NULL) return portMAX_DELAY;
return listGET_LIST_ITEM_VALUE(&xMailbox->xLinked);
}
MailboxHandle_t xMailboxYieldLinked(MailboxHandle_t xMailbox, BaseType_t (*pxYield)(MailboxHandle_t xMailbox)) {
if (xMailbox == NULL && (xMailbox = xMailboxSelf()) == NULL) return NULL;
for (const ListItem_t *pxHeadEntry = listGET_HEAD_ENTRY(&xMailbox->xLinking),
*pxEndMarker = listGET_END_MARKER(&xMailbox->xLinking), *pxNextEntry;
pxHeadEntry != pxEndMarker; pxHeadEntry = pxNextEntry) {
MailboxHandle_t xLinked = CONTAINER_OF(pxHeadEntry, struct Mailbox, xLinked);
/*
* Allow for removal of the link during iteration. Step to the next link of
* the mailbox before yielding.
*/
pxNextEntry = listGET_NEXT(pxHeadEntry);
if (pxYield(xLinked) == pdPASS) return xLinked;
}
return NULL;
}
MailboxHandle_t xMailboxLinking(MailboxHandle_t xMailbox) {
if (xMailbox == NULL && (xMailbox = xMailboxSelf()) == NULL) return NULL;
List_t *pxLinking = listLIST_ITEM_CONTAINER(&xMailbox->xLinked);
return pxLinking != NULL ? CONTAINER_OF(pxLinking, struct Mailbox, xLinking) : NULL;
}
size_t xMailboxSend(MailboxHandle_t xMailbox, const void *pvTxData, size_t xDataLengthBytes, TickType_t xTicksToWait) {
if (xMailbox == NULL && (xMailbox = xMailboxSelf()) == NULL) return 0UL;
return xMessageBufferSend(xMailbox, pvTxData, xDataLengthBytes, xTicksToWait);
}
BaseType_t xMailboxSent(MailboxHandle_t xMailbox) {
if (xMailbox == NULL && (xMailbox = xMailboxSelf()) == NULL) return pdFAIL;
#ifdef mailboxSENT_NOTIFIED
if (xMailbox->xTask == NULL) return pdFAIL;
return xTaskNotify(xMailbox->xTask, mailboxSENT_NOTIFIED, eSetBits);
#else
return pdFAIL;
#endif
}
size_t vMailboxReceive(MailboxHandle_t xMailbox, void *pvRxData, size_t xBufferLengthBytes, TickType_t xTicksToWait) {
if (xMailbox == NULL && (xMailbox = xMailboxSelf()) == NULL) return 0UL;
return xMessageBufferReceive(xMailbox, pvRxData, xBufferLengthBytes, xTicksToWait);
}
void vMailboxSetUp(void *pvParameters) {
#ifdef mailboxTASK_TLS_INDEX
vTaskSetThreadLocalStoragePointer(NULL, mailboxTASK_TLS_INDEX, pvParameters);
#endif
}
void vMailboxTearDown() {
#ifdef mailboxTASK_TLS_INDEX
vTaskSetThreadLocalStoragePointer(NULL, mailboxTASK_TLS_INDEX, NULL);
#endif
}
MailboxHandle_t xMailboxSelf() {
#ifdef mailboxTASK_TLS_INDEX
return pvTaskGetThreadLocalStoragePointer(NULL, mailboxTASK_TLS_INDEX);
#else
return NULL;
#endif
}
/*
* mailbox.h
*/
#pragma once
/*!
* \file
*
* Echo mailbox task exemplar:
* \code
* static portTASK_FUNCTION(prvEchoMailboxTask, pvParameters) {
* vMailboxSetUp(pvParameters);
* MsgBindingHandle_t xMsgBinding = xMsgBindingNew();
* MsgUnifierHandle_t xMsgUnifier = xMsgUnifierNew();
* for (;;) {
* uint32_t ulNotified;
* xTaskNotifyWait(0UL, ULONG_MAX, &ulNotified, portMAX_DELAY);
* if (ulNotified & mailboxSENT_NOTIFIED)
* // Pattern-match the mailbox messages one by one. Do not wait for
* // additional messages, hence no ticks to wait.
* while (xMailboxReceiveMsg(NULL, xMsgUnifier, 0UL)) {
* if (xMsgUnify(xMsgUnifier) != eMsgUnifySuccess)
* continue;
*
* // Match an optional mailbox sender handle but not by sending to itself.
* // Perform a basic sanity check. The sender must not be the receiver.
* MailboxHandle_t xMailbox;
* if (xMsgUnifyMailbox(xMsgUnifier, &xMailbox)) {
* if (xMailbox == xMailboxSelf())
* continue;
* if (xMsgUnify(xMsgUnifier) != eMsgUnifySuccess)
* continue;
* } else
* xMailbox = NULL;
*
* // Handle ping by sending back pong.
* if (xMsgUnifyStrCmp(xMsgUnifier, "ping")) {
* if (xMailbox) {
* vMsgBindingClear(xMsgBinding);
* xMsgBindStr(xMsgBinding, "pong");
* xMailboxSendMsg(xMailbox, xMsgBinding, portMAX_DELAY);
* xMailboxSent(xMailbox);
* continue;
* }
* }
* }
* }
* vMsgUnifierDestroy(xMsgUnifier);
* vMsgBindingDestroy(xMsgBinding);
* vMailboxTearDown();
* vTaskDelete(NULL);
* }
* \endcode
*/
#include "FreeRTOS.h"
/*
* The mailbox buffer size amounts to a small number of bytes by default.
* 256 bytes suffices when using packed messaging.
*/
#define mailboxBUFFER_SIZE_BYTES 256U
#define mailboxSTACK_DEPTH 1024U
#define mailboxPRIORITY 24U
#if configUSE_TASK_NOTIFICATIONS == 1
#ifndef mailboxSENT_NOTIFIED
#define mailboxSENT_NOTIFIED (1UL << ('M' - 'A'))
#endif
#endif
#if configNUM_THREAD_LOCAL_STORAGE_POINTERS > 0
#ifndef mailboxTASK_TLS_INDEX
#define mailboxTASK_TLS_INDEX 0U
#endif
#endif
/*!
* \brief Handle of mailbox.
*
* The mailbox sends and receives messages via a message handle. Senders can
* pass messages to tasks if a sender knows the mailbox.
*/
typedef struct Mailbox *MailboxHandle_t;
/*!
* Asserts against memory allocation failures.
*/
MailboxHandle_t xMailboxCreate(size_t xBufferSizeBytes);
MailboxHandle_t xMailboxNew();
void vMailboxDelete(MailboxHandle_t xMailbox);
/*!
* \brief Spawns the mailbox task.
*
* Notice that the mailbox is prime. Create the mailbox, spawn the mailbox. The
* task appears to service the mailbox. The mailbox remains the message target
* not the task.
*/
BaseType_t xMailboxSpawnWith(MailboxHandle_t xMailbox, TaskFunction_t xTaskCode, const char *pcName,
const configSTACK_DEPTH_TYPE usStackDepth, UBaseType_t uxPriority);
BaseType_t xMailboxSpawn(MailboxHandle_t xMailbox, TaskFunction_t xTaskCode, const char *pcName);
/*!
* \brief Links or unlinks two mailboxes.
* \param[in] xLinking Handle of linking mailbox or \c NULL to unlink \c xLinked.
* \param[in] xLinked Newly linked mailbox.
*
* Establishes link from one mailbox to another, a one-to-many association
* between mailboxes.
*/
BaseType_t xMailboxLink(MailboxHandle_t xLinking, MailboxHandle_t xLinked);
void vMailboxUnlink(MailboxHandle_t xMailbox);
/*!
* \brief Unlinks all mailboxes linked by the given mailbox.
*
* Makes a safe assumption about mutating a list while iterating the same list.
*/
void vMailboxUnlinkAll(MailboxHandle_t xMailbox);
BaseType_t xMailboxSetLinkOwner(MailboxHandle_t xMailbox, void *pvOwner);
void *pvMailboxGetLinkOwner(MailboxHandle_t xMailbox);
/*!
* \brief Sets the link value of the mailbox.
*
* The link value determines the ordering of the mailbox within its link
* association, and therefore the iteration order when yielding links. Defaults
* to \c portMAX_DELAY which configures the mailbox for natural ordering where
* the mailboxes appear in their link association in the order that they insert.
*/
BaseType_t xMailboxSetLinkValue(MailboxHandle_t xMailbox, TickType_t xValue);
TickType_t xMailboxGetLinkValue(MailboxHandle_t xMailbox);
MailboxHandle_t xMailboxYieldLinked(MailboxHandle_t xMailbox, BaseType_t (*pxYield)(MailboxHandle_t xMailbox));
/*!
* \brief Mailbox linking the given mailbox.
* \returns \c NULL if not linked, else the linking mailbox.
*
* Follows the mailbox link from the many to the one side of the association.
*/
MailboxHandle_t xMailboxLinking(MailboxHandle_t xMailbox);
/*!
* \brief Sends a message.
*
* Always pass messages by value, not by reference unless with great
* caution.
*
* The message includes the sending mailbox.
*/
size_t xMailboxSend(MailboxHandle_t xMailbox, const void *pvTxData, size_t xDataLengthBytes, TickType_t xTicksToWait);
/*!
* \brief Notifies mailbox message sent.
* \returns \c pdPASS if the mailbox has spawned, i.e. its associated task
* exists.
*
* Notify "sent" after sending one or more mailbox messages in order to prompt
* the mailbox task. Mailbox receivers may periodically receive without specific
* notification. The sent notification adds extra responsiveness and also makes
* it easier for receivers to mesh with interrupt-level notifications.
*
* Use as follows.
* \code
* // Send a packed message. Wait indefinitely for the message to enter the
* // mailbox's message buffer. Notify the receiver once the message has been
* // recorded.
* xMailboxSendMsg(xMailbox, xMsgPack, portMAX_DELAY);
* xMailboxSent(xMailbox);
* \endcode
*/
BaseType_t xMailboxSent(MailboxHandle_t xMailbox);
size_t vMailboxReceive(MailboxHandle_t xMailbox, void *pvRxData, size_t xBufferLengthBytes, TickType_t xTicksToWait);
void vMailboxSetUp(void *pvParameters);
void vMailboxTearDown(void);
MailboxHandle_t xMailboxSelf();
/*
* mailbox_msg.c
*/
#include "mailbox_msg.h"
#include "mailbox_prv.h"
#include <memory.h>
BaseType_t xMailboxSendMsg(MailboxHandle_t xMailbox, MsgBindingHandle_t xMsgBinding, TickType_t xTicksToWait) {
if (xMailbox == NULL && (xMailbox = xMailboxSelf()) == NULL) return pdFAIL;
return xMsgBindingSend(xMsgBinding, xMailbox->xMessageBuffer, xTicksToWait) != 0UL;
}
BaseType_t xMailboxReceiveMsg(MailboxHandle_t xMailbox, MsgUnifierHandle_t xMsgUnifier, TickType_t xTicksToWait) {
if (xMailbox == NULL && (xMailbox = xMailboxSelf()) == NULL) return pdFAIL;
/*
* Throw away the size of the message received. The size does not matter. The
* unifier knows the size. Answer pass or fail.
*/
return xMsgUnifierReceive(xMsgUnifier, xMailbox->xMessageBuffer, xTicksToWait) != 0UL;
}
BaseType_t xMailboxRelayMsg(MailboxHandle_t xMailbox, MsgUnifierHandle_t xMsgUnifier, TickType_t xTicksToWait) {
if (xMailbox == NULL && (xMailbox = xMailboxSelf()) == NULL) return pdFAIL;
return xMsgUnifierRelay(xMsgUnifier, xMailbox->xMessageBuffer, xTicksToWait) != 0UL;
}
BaseType_t xMsgBindMailbox(MsgBindingHandle_t xMsgPack, MailboxHandle_t xMailbox) {
if (xMailbox == NULL && (xMailbox = xMailboxSelf()) == NULL) return pdFAIL;
return xMsgBindExtWithBody(xMsgPack, &xMailbox, sizeof(xMailbox), msgEXT_TYPE_MAILBOX);
}
BaseType_t xMsgUnifyMailbox(MsgUnifierHandle_t xMsgUnpack, MailboxHandle_t *pxMailbox) {
const void *pvExt;
size_t xExtLengthBytes;
if (!xMsgUnifyExtType(xMsgUnpack, &pvExt, &xExtLengthBytes, msgEXT_TYPE_MAILBOX) ||
xExtLengthBytes != sizeof(*pxMailbox))
return pdFAIL;
if (pxMailbox) (void)memcpy(pxMailbox, pvExt, sizeof(*pxMailbox));
return pdPASS;
}
/*
* Is this a good idea? Failure means either of two things: either no unpack
* next success, or mailbox extension type failure. The interface has no way to
* discern the difference.
*/
BaseType_t xMsgUnifierNextMailbox(MsgUnifierHandle_t xMsgUnifier, MailboxHandle_t *pxMailbox) {
return xMsgUnify(xMsgUnifier) == eMsgUnifySuccess && xMsgUnifyMailbox(xMsgUnifier, pxMailbox);
}
/*
* mailbox_msg.h
*/
#pragma once
#include "mailbox.h"
#include "msg_binding.h"
#include "msg_unifier.h"
/*!
* \brief Message extension type for a mailbox handle.
*
* Extension type is a signed 8-bit integer of arbitrary but unique value.
*/
#ifndef msgEXT_TYPE_MAILBOX
#define msgEXT_TYPE_MAILBOX 'M'
#endif
/*!
* \brief Sends a message binding to a mailbox.
*
* \param[in] xMailbox Handle of mailbox receiver. Can be \c NULL and defaults
* to self. Mailboxes can send messages to themselves but take care not to
* generate infinite loops.
*
* You can send the \e same message binding to multiple mailboxes. Sending does
* \e not change the binding.
*
* Send a message to a mailbox as follows.
* \code
* // Ping the echo mailbox. Bind a task handle followed by a string.
* // Send the message then notify.
* MsgBindingHandle_t xMsgBinding = xMsgBindingNew();
* xMsgBindMailbox(xMsgBinding, NULL);
* xMsgBindStr(xMsgBinding, "ping");
* xMailboxSendMsg(xMailbox, xMsgBinding, portMAX_DELAY);
* vMsgBindingDestroy(xMsgBinding);
* xMailboxSent(xMailbox);
* \endcode
*/
BaseType_t xMailboxSendMsg(MailboxHandle_t xMailbox, MsgBindingHandle_t xMsgBinding, TickType_t xTicksToWait);
/*!
* \brief Receives a message from a mailbox.
* \param[in] xMailbox Mailbox to receive from, else \c NULL for the current
* task's mailbox.
* \param[out] xMsgUnifier Unifier for mailbox message.
* \returns \c pdPASS if one message received else \c pdFAIL.
*/
BaseType_t xMailboxReceiveMsg(MailboxHandle_t xMailbox, MsgUnifierHandle_t xMsgUnifier, TickType_t xTicksToWait);
/*!
* \brief Relays a previously-received message.
* \param[in] xMsgUnifier Carries the received message. May or may not have
* already been unified, either partially or completely.
*/
BaseType_t xMailboxRelayMsg(MailboxHandle_t xMailbox, MsgUnifierHandle_t xMsgUnifier, TickType_t xTicksToWait);
BaseType_t xMsgBindMailbox(MsgBindingHandle_t xMsgBinding, MailboxHandle_t xMailbox);
BaseType_t xMsgUnifyMailbox(MsgUnifierHandle_t xMsgUnifier, MailboxHandle_t *pxMailbox);
/*!
* \brief Unifies the next mailbox handle.
* \param[in] xMsgUnifier Message unifier handle.
* \param[out] pxMailbox Pointer to unpacked mailbox handle.
* \returns \c pdPASS if successfully unpacked a valid mailbox handle. Fails
* with \c pdFAIL if the next unpack attempt does not succeed.
*
* Always unpacks the next object.
*/
BaseType_t xMsgUnifierNextMailbox(MsgUnifierHandle_t xMsgUnpack, MailboxHandle_t *pxMailbox);
/*
* mailbox_prv.h
*/
#pragma once
#include "mailbox.h"
#include "FreeRTOS.h"
#include "message_buffer.h"
#include "task.h"
struct Mailbox {
MessageBufferHandle_t xMessageBuffer;
TaskHandle_t xTask;
List_t xLinking;
ListItem_t xLinked;
};
/*
* msg_binding.c
*/
#include "msg_binding.h"
/*!
* \file
*
* This implementation utilises standard C Message Pack using standard-library
* memory-allocated string buffers. It links with the full MsgPack library
* though only invokes a much smaller subset.
*/
#include <msgpack.h>
#include "task.h"
/*!
* \addtogroup MsgBinding
* \{
*/
struct MsgBinding {
msgpack_sbuffer xBuffer;
msgpack_packer xPacker;
};
MsgBindingHandle_t xMsgBindingNew() {
MsgBindingHandle_t xMsgBinding = pvPortMalloc(sizeof(*xMsgBinding));
configASSERT(xMsgBinding);
msgpack_sbuffer_init(&xMsgBinding->xBuffer);
msgpack_packer_init(&xMsgBinding->xPacker, &xMsgBinding->xBuffer, msgpack_sbuffer_write);
return xMsgBinding;
}
void vMsgBindingDestroy(MsgBindingHandle_t xMsgBinding) {
msgpack_sbuffer_destroy(&xMsgBinding->xBuffer);
vPortFree(xMsgBinding);
}
void vMsgBindingClear(MsgBindingHandle_t xMsgBinding) { msgpack_sbuffer_clear(&xMsgBinding->xBuffer); }
size_t xMsgBindingBuffer(MsgBindingHandle_t xMsgBinding, const void **ppvBuffer) {
if (ppvBuffer) *ppvBuffer = xMsgBinding->xBuffer.data;
return xMsgBinding->xBuffer.size;
}
size_t xMsgBindingSend(MsgBindingHandle_t xMsgBinding, MessageBufferHandle_t xMessageBuffer, TickType_t xTicksToWait) {
return xMessageBufferSend(xMessageBuffer, xMsgBinding->xBuffer.data, xMsgBinding->xBuffer.size, xTicksToWait);
}
BaseType_t xMsgBindBool(MsgBindingHandle_t xMsgBinding, BaseType_t xBool) {
int (*pxPackFunction)(msgpack_packer *) = xBool ? msgpack_pack_true : msgpack_pack_false;
/*
* A zero return code indicates success. Translate success to pdPASS.
*/
return pxPackFunction(&xMsgBinding->xPacker) == 0;
}
BaseType_t xMsgBindInt(MsgBindingHandle_t xMsgBinding, int lInt) {
return msgpack_pack_int(&xMsgBinding->xPacker, lInt) == 0;
}
BaseType_t xMsgBindUInt(MsgBindingHandle_t xMsgBinding, unsigned int uInt) {
return msgpack_pack_unsigned_int(&xMsgBinding->xPacker, uInt) == 0;
}
BaseType_t xMsgBindFloat(MsgBindingHandle_t xMsgBinding, float lFloat) {
return msgpack_pack_float(&xMsgBinding->xPacker, lFloat) == 0;
}
BaseType_t xMsgBindStrWithBody(MsgBindingHandle_t xMsgBinding, const char *pcStr, size_t xStrLengthBytes) {
return msgpack_pack_str_with_body(&xMsgBinding->xPacker, pcStr, xStrLengthBytes) == 0;
}
BaseType_t xMsgBindStr(MsgBindingHandle_t xMsgBinding, const char *pcStr) {
return xMsgBindStrWithBody(xMsgBinding, pcStr, strlen(pcStr));
}
BaseType_t xMsgBindExtWithBody(MsgBindingHandle_t xMsgBinding, const void *pvExt, size_t xExtLengthBytes,
int8_t cType) {
return msgpack_pack_ext_with_body(&xMsgBinding->xPacker, pvExt, xExtLengthBytes, cType) == 0;
}
/*!
* \}
*/
/*
* msg_binding.h
*/
#pragma once
/*!
* \file
*
* Do not forget to predefine \c MSGPACK_SBUFFER_INIT_SIZE for the binding's packer.
*/
#include "FreeRTOS.h"
#include "message_buffer.h"
/*!
* \defgroup MsgBinding Message Binding
* \{
*/
typedef struct MsgBinding *MsgBindingHandle_t;
/*!
* \brief Creates an new initialised message binding.
*
* Allocates the new binding using the FreeRTOS heap.
*/
MsgBindingHandle_t xMsgBindingNew();
void vMsgBindingDestroy(MsgBindingHandle_t xMsgBinding);
void vMsgBindingClear(MsgBindingHandle_t xMsgBinding);
/*!
* \brief Accesses the packed binding buffer.
* \returns Size of buffer.
*/
size_t xMsgBindingBuffer(MsgBindingHandle_t xMsgBinding, const void **ppvBuffer);
size_t xMsgBindingSend(MsgBindingHandle_t xMsgBinding, MessageBufferHandle_t xMessageBuffer, TickType_t xTicksToWait);
BaseType_t xMsgBindBool(MsgBindingHandle_t xMsgBinding, BaseType_t xBool);
BaseType_t xMsgBindInt(MsgBindingHandle_t xMsgBinding, int lInt);
BaseType_t xMsgBindUInt(MsgBindingHandle_t xMsgBinding, unsigned int uInt);
BaseType_t xMsgBindFloat(MsgBindingHandle_t xMsgBinding, float lFloat);
/*!
* The body should have UTF-8 encoding.
*/
BaseType_t xMsgBindStrWithBody(MsgBindingHandle_t xMsgBinding, const char *pcStr, size_t xStrLengthBytes);
BaseType_t xMsgBindStr(MsgBindingHandle_t xMsgBinding, const char *pcStr);
BaseType_t xMsgBindExtWithBody(MsgBindingHandle_t xMsgBinding, const void *pvExt, size_t xExtLengthBytes, int8_t cType);
/*!
* \}
*/
/*
* msg_unifier.c
*/
#include "msg_unifier.h"
#include <msgpack.h>
#include "task.h"
/*!
* \addtogroup MsgUnifier
* \{
*/
struct MsgUnifier {
msgpack_unpacker xUnpacker;
msgpack_unpacked xUnpacked;
};
MsgUnifierHandle_t xMsgUnifierCreate(size_t xInitialBufferSizeBytes) {
MsgUnifierHandle_t xMsgUnpack = pvPortMalloc(sizeof(*xMsgUnpack));
configASSERT(xMsgUnpack);
msgpack_unpacker_init(&xMsgUnpack->xUnpacker, xInitialBufferSizeBytes);
msgpack_unpacked_init(&xMsgUnpack->xUnpacked);
return xMsgUnpack;
}
MsgUnifierHandle_t xMsgUnifierNew() { return xMsgUnifierCreate(msgunifierINITIAL_BUFFER_SIZE_BYTES); }
void vMsgUnifierDestroy(MsgUnifierHandle_t xMsgUnifier) {
msgpack_unpacked_destroy(&xMsgUnifier->xUnpacked);
msgpack_unpacker_destroy(&xMsgUnifier->xUnpacker);
}
BaseType_t xMsgUnifierReserve(MsgUnifierHandle_t xMsgUnifier, size_t xBufferSizeBytes) {
return msgpack_unpacker_reserve_buffer(&xMsgUnifier->xUnpacker, xBufferSizeBytes);
}
size_t xMsgUnifierReceive(MsgUnifierHandle_t xMsgUnifier, MessageBufferHandle_t xMessageBuffer,
TickType_t xTicksToWait) {
/*
* Receiving automatically resets the packer. It does not reserve any
* additional capacity automatically beyond that currently reserved.
*/
msgpack_unpacker_reset(&xMsgUnifier->xUnpacker);
size_t xReceivedBytes =
xMessageBufferReceive(xMessageBuffer, msgpack_unpacker_buffer(&xMsgUnifier->xUnpacker),
msgpack_unpacker_buffer_capacity(&xMsgUnifier->xUnpacker), xTicksToWait);
msgpack_unpacker_buffer_consumed(&xMsgUnifier->xUnpacker, xReceivedBytes);
return xReceivedBytes;
}
/*
* The implementation breaks unpacker encapsulation. The interface provides no
* access to the used part of the buffer. Used therefore implies gone forever.
* Resurrect the bits by accessing the unpacker members directly.
*/
size_t xMsgUnifierRelay(MsgUnifierHandle_t xMsgUnifier, MessageBufferHandle_t xMessageBuffer, TickType_t xTicksToWait) {
return xMessageBufferSend(xMessageBuffer, xMsgUnifier->xUnpacker.buffer, xMsgUnifier->xUnpacker.used, xTicksToWait);
}
BaseType_t xMsgUnify(MsgUnifierHandle_t xMsgUnifier) {
return msgpack_unpacker_next(&xMsgUnifier->xUnpacker, &xMsgUnifier->xUnpacked);
}
BaseType_t xMsgUnifyType(MsgUnifierHandle_t xMsgUnifier) {
switch (xMsgUnifier->xUnpacked.data.type) {
case MSGPACK_OBJECT_NIL:
return eMsgUnifyTypeNil;
case MSGPACK_OBJECT_BOOLEAN:
return eMsgUnifyTypeBool;
case MSGPACK_OBJECT_POSITIVE_INTEGER:
case MSGPACK_OBJECT_NEGATIVE_INTEGER:
return eMsgUnifyTypeInt;
case MSGPACK_OBJECT_FLOAT32:
case MSGPACK_OBJECT_FLOAT64:
return eMsgUnifyTypeFloat;
case MSGPACK_OBJECT_STR:
return eMsgUnifyTypeStr;
default:
return eMsgUnifyTypeOther;
}
}
BaseType_t xMsgUnifyBool(MsgUnifierHandle_t xMsgUnifier, BaseType_t *pxValue) {
if (xMsgUnifier->xUnpacked.data.type != MSGPACK_OBJECT_BOOLEAN) return pdFAIL;
if (pxValue) *pxValue = xMsgUnifier->xUnpacked.data.via.boolean != false;
return pdPASS;
}
BaseType_t xMsgUnifyUInt32(MsgUnifierHandle_t xMsgUnifier, uint32_t *pulValue) {
if (xMsgUnifier->xUnpacked.data.type != MSGPACK_OBJECT_POSITIVE_INTEGER) return pdFAIL;
if (pulValue) *pulValue = xMsgUnifier->xUnpacked.data.via.u64;
return pdPASS;
}
BaseType_t xMsgUnifyInt32(MsgUnifierHandle_t xMsgUnifier, int32_t *plValue) {
if (xMsgUnifier->xUnpacked.data.type != MSGPACK_OBJECT_NEGATIVE_INTEGER) return pdFAIL;
/*
* Does the following assignment reverse the sign extension?
*/
if (plValue) *plValue = xMsgUnifier->xUnpacked.data.via.i64;
return pdPASS;
}
BaseType_t xMsgUnifyFloat32(MsgUnifierHandle_t xMsgUnifier, float *plValue) {
if (xMsgUnifier->xUnpacked.data.type != MSGPACK_OBJECT_FLOAT32) return pdFAIL;
if (plValue) *plValue = xMsgUnifier->xUnpacked.data.via.f64;
return pdPASS;
}
BaseType_t xMsgUnifyStr(MsgUnifierHandle_t xMsgUnifier, const char **ppcStr, size_t *pxStrLengthBytes) {
if (xMsgUnifier->xUnpacked.data.type != MSGPACK_OBJECT_STR) return pdFAIL;
if (ppcStr) *ppcStr = xMsgUnifier->xUnpacked.data.via.str.ptr;
if (pxStrLengthBytes) *pxStrLengthBytes = xMsgUnifier->xUnpacked.data.via.str.size;
return pdPASS;
}
BaseType_t xMsgUnifyStrDup(MsgUnifierHandle_t xMsgUnifier, char **ppcStrDup) {
const char *pcStr;
size_t xStrLengthBytes;
if (!xMsgUnifyStr(xMsgUnifier, &pcStr, &xStrLengthBytes)) return pdFAIL;
if (ppcStrDup) {
char *pcStrDup = malloc(xStrLengthBytes + 1U);
if (pcStrDup == NULL) return errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY;
(void)memcpy(pcStrDup, pcStr, xStrLengthBytes);
pcStrDup[xStrLengthBytes] = '\0';
*ppcStrDup = pcStrDup;
}
return pdPASS;
}
BaseType_t xMsgUnifyStrCmp(MsgUnifierHandle_t xMsgUnifier, const char *pcStrCmp) {
const char *pcStr;
size_t xStrLengthBytes;
if (!xMsgUnifyStr(xMsgUnifier, &pcStr, &xStrLengthBytes)) return pdFAIL;
return strlen(pcStrCmp) == xStrLengthBytes && memcmp(pcStrCmp, pcStr, xStrLengthBytes) == 0;
}
BaseType_t xMsgUnifyExtType(MsgUnifierHandle_t xMsgUnifier, const void **pvExt, size_t *pxExtLengthBytes,
int8_t cType) {
if (xMsgUnifier->xUnpacked.data.type != MSGPACK_OBJECT_EXT || xMsgUnifier->xUnpacked.data.via.ext.type != cType)
return pdFAIL;
if (pvExt) *pvExt = xMsgUnifier->xUnpacked.data.via.ext.ptr;
if (pxExtLengthBytes) *pxExtLengthBytes = xMsgUnifier->xUnpacked.data.via.ext.size;
return pdPASS;
}
/*!
* \}
*/
/*
* msg_unifier.h
*/
#pragma once
/*!
* \file
*/
#include "FreeRTOS.h"
#include "message_buffer.h"
/*!
* \defgroup MsgUnifier Message Unifier
* \{
*/
#ifndef msgunifierINITIAL_BUFFER_SIZE_BYTES
#define msgunifierINITIAL_BUFFER_SIZE_BYTES 16U
#endif
enum msgUnify { eMsgUnifyMemError = -2, eMsgUnifyParseError, eMsgUnifyContinue, eMsgUnifyExtraBytes, eMsgUnifySuccess };
typedef struct MsgUnifier *MsgUnifierHandle_t;
enum msgUnpackType {
eMsgUnifyTypeNil,
eMsgUnifyTypeBool,
eMsgUnifyTypeInt,
eMsgUnifyTypeFloat,
eMsgUnifyTypeStr,
eMsgUnifyTypeOther
};
MsgUnifierHandle_t xMsgUnifierCreate(size_t xInitialBufferSizeBytes);
MsgUnifierHandle_t xMsgUnifierNew();
void vMsgUnifierDestroy(MsgUnifierHandle_t xMsgUnifier);
/*!
* \brief Reserves additional buffer capacity for the unifier.
*
* Attempts to expand the unification buffer if the current amount of free space
* is less than the given number of buffer bytes.
*/
BaseType_t xMsgUnifierReserve(MsgUnifierHandle_t xMsgUnifier, size_t xBufferSizeBytes);
/*!
* \returns Answers zero if the unpacker fails to reserve sufficient memory to
* hold the incoming message.
*
* Receives zero or one message from the given message buffer.
*
* Unpacks directly to the unpacking buffer. Begins by reserving the necessary
* unpack buffer space.
*/
size_t xMsgUnifierReceive(MsgUnifierHandle_t xMsgUnifier, MessageBufferHandle_t xMessageBuffer,
TickType_t xTicksToWait);
/*!
* \brief Relays a previously-received message.
*
* Relays the used buffer.
*/
size_t xMsgUnifierRelay(MsgUnifierHandle_t xMsgUnifier, MessageBufferHandle_t xMessageBuffer, TickType_t xTicksToWait);
/*!
* \brief Unifies with the \e next message object.
*/
BaseType_t xMsgUnify(MsgUnifierHandle_t xMsgUnifier);
/*!
* \brief Type of last unify.
* \returns eMsgUnifierType for generic as-yet-unmapped type, else some more
* specific type if currently known.
*/
BaseType_t xMsgUnifyType(MsgUnifierHandle_t xMsgUnifier);
/*!
* Converts standard \c bool to \c BaseType_t.
*/
BaseType_t xMsgUnifyBool(MsgUnifierHandle_t xMsgUnifier, BaseType_t *pxValue);
BaseType_t xMsgUnifyUInt32(MsgUnifierHandle_t xMsgUnifier, uint32_t *pulValue);
/*!
* \brief Unpacks a signed 32-bit integer.
*
* Note that "negative" integer really means \e signed integer.
*/
BaseType_t xMsgUnifyInt32(MsgUnifierHandle_t xMsgUnifier, int32_t *plValue);
BaseType_t xMsgUnifyFloat32(MsgUnifierHandle_t xMsgUnifier, float *plValue);
/*!
* \returns Succeeds only if the last unpack was a string object.
*/
BaseType_t xMsgUnifyStr(MsgUnifierHandle_t xMsgUnifier, const char **ppcStr, size_t *pxStrLengthBytes);
/*!
* \param[out] ppcStr Pointer to string pointer. The type is omits \c const to
* allow for freeing of the duplicate copy.
*
* \returns \c pdPASS if unification \e and allocation succeeds. Fails either if
* cannot unify returning \c pdFAIL \e or allocation fails returning \c
* errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY.
*
* Unifies with a string by allocating and copying the string to a
* null-terminated dynamic buffer. Free the allocated buffer after usage.
*/
BaseType_t xMsgUnifyStrDup(MsgUnifierHandle_t xMsgUnifier, char **ppcStrDup);
/*!
* \brief Unifies with precisely-matching null-terminated string.
*
* Performance is good: comparison proceeds by type first, followed by length
* and finally by body character-by-character with early-out optimisation.
*/
BaseType_t xMsgUnifyStrCmp(MsgUnifierHandle_t xMsgUnifier, const char *pcStrCmp);
/*!
* \returns Fails when the type mismatches.
*/
BaseType_t xMsgUnifyExtType(MsgUnifierHandle_t xMsgUnifier, const void **pvExt, size_t *pxExtLengthBytes, int8_t cType);
/*!
* \}
*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment