/* * Copyright (C) 2020 ML!PA Consulting GmbH * * This file is subject to the terms and conditions of the GNU Lesser * General Public License v2.1. See the file LICENSE in the top level * directory for more details. */ /** * @ingroup core_msg * * @experimental * * @{ * * @file * @brief Messaging Bus API for inter process message broadcast. * * Threads can subscribe to messages on one or multiple buses. * If a message is posted on one bus, all threads that are * subscribed to that message type will receive the message. * * On one bus only 32 different message types are possible. * Do not use the `type` and `sender_pid` fields of a message * directly if it may have been received over a message bus. * Instead, use the @ref msg_bus_get_type and * @ref msg_bus_get_sender_pid functions. * * If you want to check if a message was sent directly (not * over a bus, you can use the @ref msg_is_from_bus function. * * @note Make sure to unsubscribe from all previously subscribed * buses before terminating a thread. * * @author Benjamin Valentin */ #ifndef MSG_BUS_H #define MSG_BUS_H #include #include #include "list.h" #include "msg.h" #ifdef __cplusplus extern "C" { #endif /** * @brief A message bus is just a list of subscribers. */ typedef struct { list_node_t subs; /**< List of subscribers to the bus */ uint16_t id; /**< Message Bus ID */ } msg_bus_t; /** * @brief Message bus subscriber entry. * Should not be modified by the user. */ typedef struct { list_node_t next; /**< next subscriber */ uint32_t event_mask; /**< Bitmask of event classes */ kernel_pid_t pid; /**< Subscriber PID */ } msg_bus_entry_t; /** * @brief Flag set on `sender_pid` of `msg_t` that indicates that * the message was sent over a bus. */ #define MSB_BUS_PID_FLAG (1U << ((8 * sizeof(kernel_pid_t)) - 1)) /** * @brief Initialize a message bus. * * Must be called by the owner of a ``msg_bus_t`` struct. * * Message buses are considered to be long-running and must be * created before any threads can attach to them. * * There can be a maximum number of 2047 buses in total. */ void msg_bus_init(msg_bus_t *bus); /** * @brief Get the message type of a message bus message. * * The `type` field of the`msg_t` also encodes the message bus ID, * so use this function to get the real 5 bit message type. * * If the message was not sent over a bus, this will return the * original message ID. * * @param[in] msg A message that was received over a bus * * @return The message type */ static inline uint16_t msg_bus_get_type(const msg_t *msg) { if (msg->sender_pid & MSB_BUS_PID_FLAG) { return msg->type & 0x1F; } else { return msg->type; } } /** * @brief Get the sender PID of a message bus message. * * The `sender_pid` field of the`msg_t` has a flag bit set * to indicate the message was sent over a bus, thus it should * not be used directly. * * @param[in] msg A message that was received over a bus * * @return The sender pid */ static inline kernel_pid_t msg_bus_get_sender_pid(const msg_t *msg) { return msg->sender_pid & ~MSB_BUS_PID_FLAG; } /** * @brief Check if a message originates from a bus * * If a thread is attached to multiple buses, this function can be used * to determine if a message originated from a certain bus. * * @param[in] bus The bus to check for, may be NULL * @param[in] msg The received message * * @return True if the messages @p m was sent over @p bus * If @p bus is NULL, this function returns true * if the message was sent over *any* bus. * False if the messages @p m was a direct message * or from a different bus. */ static inline bool msg_is_from_bus(const msg_bus_t *bus, const msg_t *msg) { if (bus != NULL && (bus->id != (msg->type >> 5))) { return false; } return msg->sender_pid & MSB_BUS_PID_FLAG; } /** * @brief Attach a thread to a message bus. * * This attaches a message bus subscriber entry to a message bus. * Subscribe to events on the bus using @ref msg_bus_detach. * The thread will then receive events with a matching type that are * posted on the bus. * * Events can be received with @ref msg_receive. * **The contents of the received message must not be modified.** * * @param[in] bus The message bus to attach to * @param[in] entry Message bus subscriber entry */ void msg_bus_attach(msg_bus_t *bus, msg_bus_entry_t *entry); /** * @brief Remove a thread from a message bus. * * This removes the calling thread from the message bus. * * @note Call this function before the thread terminates. * * @param[in] bus The message bus from which to detach * @param[in] entry Message bus subscriber entry */ void msg_bus_detach(msg_bus_t *bus, msg_bus_entry_t *entry); /** * @brief Get the message bus entry for the current thread. * * Traverse the message bus to find the subscriber entry for the * current thread. * * @param[in] bus The message bus to search * * @return The subscriber entry for the current thread. * NULL if the thread is not attached to @p bus. */ msg_bus_entry_t *msg_bus_get_entry(msg_bus_t *bus); /** * @brief Subscribe to an event on the message bus. * * @pre The @p entry has been attached to a bus with @ref msg_bus_attach. * * @param[in] entry The message bus entry * @param[in] type The event type to subscribe to (range: 0…31) */ static inline void msg_bus_subscribe(msg_bus_entry_t *entry, uint8_t type) { assert(type < 32); entry->event_mask |= (1UL << type); } /** * @brief Unsubscribe from an event on the message bus. * * @pre The @p entry has been attached to a bus with @ref msg_bus_attach. * * @param[in] entry The message bus entry * @param[in] type The event type to unsubscribe (range: 0…31) */ static inline void msg_bus_unsubscribe(msg_bus_entry_t *entry, uint8_t type) { assert(type < 32); entry->event_mask &= ~(1UL << type); } /** * @brief Post a pre-assembled message to a bus. * * This function sends a message to all threads listening on the bus which are * listening for messages with the message type of @p m. * * It behaves identical to @ref msg_bus_post, but sends a pre-defined message. * * @note The message is expected to have the event ID encoded in the lower * 5 bits and the bus ID encoded in the upper 11 bits of the message type. * * @param[in] m The message to post the bus * @param[in] bus The message bus to post the message on * * @return The number of threads the message was sent to. */ int msg_send_bus(msg_t *m, msg_bus_t *bus); /** * @brief Post a message to a bus. * * This function sends a message to all threads listening on the bus which are * listening for messages of @p type. * * It is safe to call this function from interrupt context. * * @param[in] bus The message bus to post this on * @param[in] type The event type (range: 0…31) * @param[in] arg Optional event parameter * * @return The number of threads the event was posted to. */ static inline int msg_bus_post(msg_bus_t *bus, uint8_t type, const void *arg) { assert(type < 32); msg_t m = { .type = type | ((bus->id) << 5), .content.ptr = (void *)arg, }; return msg_send_bus(&m, bus); } #ifdef __cplusplus } #endif #endif /* MSG_BUS_H */ /** @} */