mirror of
https://github.com/apache/nuttx-apps.git
synced 2026-10-02 03:46:26 +00:00
Port of the Zephyr RTOS zbus (many-to-many message bus with typed channels and decoupled observers), built entirely on native NuttX primitives and preserving the original declarative API (ZBUS_CHAN_DEFINE, ZBUS_LISTENER_DEFINE, ZBUS_SUBSCRIBER_DEFINE, ...). Features: listeners (synchronous callbacks), subscribers (queue of channel references), message subscribers (ordered message copies), async listeners (callback on a dedicated task), runtime observers, per-observation notification masks, observer enable/disable, message validators, channel user data, publish statistics, lookup by name/numeric id and channel/observer iteration. Mapping to NuttX primitives: - Channel/observer registration: link-time iterable sections (include/nuttx/iterable_sections.h); the observers of a channel are named after their position in the definition, so the linker sorts the notification order, and the declarative macros are built on nuttx/macro.h (CONCATENATE, FOREACH_ARG and the FOREACH_IDX_ARG added in a companion nuttx commit) rather than on a private macro engine. Notification masks live in .bss with their initial value preserved in ROM and applied on lazy init. - Channel lock: sem_t (enable CONFIG_PRIORITY_INHERITANCE instead of the Zephyr priority-boost/HLP); timeouts are computed with the clock_timespec_* helpers from nuttx/clock.h. - Subscriber queues: kernel message queues (file_mq_*) opened lazily via pthread_once, usable from any task; mq payload copying replaces the Zephyr net_buf machinery entirely. - Async listeners: one task per listener (task_create, priority and stack size configurable) blocking on the listener queue; a task rather than a pthread so it outlives the first API caller. - Timeouts: milliseconds with CLOCK_MONOTONIC deadlines (ZBUS_NO_WAIT/ZBUS_FOREVER). Includes a runnable example (examples/zbus, CONFIG_EXAMPLES_ZBUS) and a cmocka test suite (testing/zbus, CONFIG_TESTING_ZBUS) covering the full API: 17/17 tests passing on linum-stm32h753bi hardware, including multi-channel index grouping, mask semantics, runtime observer error paths, notification order (the observers of a channel run in the order they are listed, and an observation bound with ZBUS_CHAN_ADD_OBS() runs after all of them), queue overflow/timeout semantics, async listener bursts, bit-exact float/double payload delivery across every observer type (sensor-style messages with a float-math validator) and an interrupt-driven publisher (kernel timer interrupt -> signal -> sampling thread -> zbus_chan_pub, the recommended pattern for interrupt sources). Requirements: FLAT build; CONFIG_MQ_MAXMSGSIZE >= pointer size + CONFIG_ZBUS_MSG_SUBSCRIBER_MAX_MSG_SIZE for message subscribers; board linker script including <nuttx/linker/common-rom.ld> or the generic CONFIG_ITERABLE_SECTIONS_LINKER_INSERT mode. Not ported: multi-domain proxy agent (experimental upstream); publishing from interrupt handlers (userspace library: hand the data to a thread). Documentation lives in the nuttx repository (Documentation/applications/system/zbus). Assisted-by: Claude Code Signed-off-by: Jorge Guzman <jorge.gzm@gmail.com>
719 lines
25 KiB
C
719 lines
25 KiB
C
/****************************************************************************
|
|
* apps/include/system/zbus.h
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*
|
|
* Copyright (c) 2022 Rodrigo Peixoto <rodrigopex@gmail.com>
|
|
* Copyright (c) 2026 NuttX port
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
* not use this file except in compliance with the License. You may obtain
|
|
* a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
* License for the specific language governing permissions and limitations
|
|
* under the License.
|
|
*
|
|
****************************************************************************/
|
|
|
|
/* NuttX port of the Zephyr zbus message bus.
|
|
*
|
|
* Differences from the Zephyr original:
|
|
* - Timeouts are given in milliseconds (int32_t): ZBUS_NO_WAIT (0) and
|
|
* ZBUS_FOREVER (-1) replace K_NO_WAIT/K_FOREVER.
|
|
* - Subscribers and message subscribers use POSIX message queues opened
|
|
* lazily on first zbus API call (no k_msgq/k_fifo/net_buf).
|
|
* - Priority boost (HLP) is not implemented; enable NuttX native
|
|
* CONFIG_PRIORITY_INHERITANCE for equivalent protection.
|
|
* - Publishing from interrupt context is not supported.
|
|
* - Requires the board linker script to include the iterable section
|
|
* fragments <nuttx/linker/common-rom.ld> and common-ram.ld.
|
|
*/
|
|
|
|
#ifndef __APPS_INCLUDE_SYSTEM_ZBUS_H
|
|
#define __APPS_INCLUDE_SYSTEM_ZBUS_H
|
|
|
|
/****************************************************************************
|
|
* Included Files
|
|
****************************************************************************/
|
|
|
|
#include <nuttx/config.h>
|
|
#include <nuttx/fs/fs.h>
|
|
#include <nuttx/iterable_sections.h>
|
|
|
|
#include <assert.h>
|
|
#include <errno.h>
|
|
#include <semaphore.h>
|
|
#include <stdbool.h>
|
|
#include <stddef.h>
|
|
#include <stdint.h>
|
|
#include <string.h>
|
|
#include <time.h>
|
|
|
|
#ifdef CONFIG_ZBUS_RUNTIME_OBSERVERS
|
|
# include <nuttx/list.h>
|
|
#endif
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <nuttx/macro.h>
|
|
|
|
#ifdef __cplusplus
|
|
#define _ZBUS_CPP_EXTERN extern
|
|
extern "C"
|
|
{
|
|
#else
|
|
#define _ZBUS_CPP_EXTERN
|
|
#endif
|
|
|
|
/****************************************************************************
|
|
* Pre-processor Definitions
|
|
****************************************************************************/
|
|
|
|
/* Timeout special values (milliseconds) */
|
|
|
|
#define ZBUS_NO_WAIT 0
|
|
#define ZBUS_FOREVER (-1)
|
|
|
|
/* Channel without a unique numeric identifier */
|
|
|
|
#define ZBUS_CHAN_ID_INVALID UINT32_MAX
|
|
|
|
#ifdef CONFIG_ZBUS_ASSERT_MOCK
|
|
# define _ZBUS_ASSERT(cond, msg) \
|
|
do \
|
|
{ \
|
|
if (!(cond)) \
|
|
{ \
|
|
return -EFAULT; \
|
|
} \
|
|
} \
|
|
while (0)
|
|
#else
|
|
# define _ZBUS_ASSERT(cond, msg) DEBUGASSERT(cond)
|
|
#endif
|
|
|
|
/****************************************************************************
|
|
* Public Types
|
|
****************************************************************************/
|
|
|
|
struct zbus_channel;
|
|
|
|
/* Mutable data associated with every channel */
|
|
|
|
struct zbus_channel_data
|
|
{
|
|
/* Boundaries of this channel's static observations inside the sorted
|
|
* zbus_channel_observation iterable section (computed on first use).
|
|
*/
|
|
|
|
int16_t observers_start_idx;
|
|
int16_t observers_end_idx;
|
|
|
|
/* Channel access semaphore */
|
|
|
|
sem_t sem;
|
|
|
|
#ifdef CONFIG_ZBUS_RUNTIME_OBSERVERS
|
|
/* Runtime (dynamically added) observers */
|
|
|
|
struct list_node observers;
|
|
#endif
|
|
|
|
#ifdef CONFIG_ZBUS_CHANNEL_PUBLISH_STATS
|
|
struct timespec publish_timestamp;
|
|
uint32_t publish_count;
|
|
#endif
|
|
};
|
|
|
|
/* A channel: constant descriptor placed in ROM (iterable section) */
|
|
|
|
struct zbus_channel
|
|
{
|
|
#ifdef CONFIG_ZBUS_CHANNEL_NAME
|
|
const char *name;
|
|
#endif
|
|
#ifdef CONFIG_ZBUS_CHANNEL_ID
|
|
uint32_t id;
|
|
#endif
|
|
|
|
/* Shared message memory, its size, and optional user data/validator */
|
|
|
|
void *message;
|
|
size_t message_size;
|
|
void *user_data;
|
|
bool (*validator)(const void *msg, size_t msg_size);
|
|
|
|
struct zbus_channel_data *data;
|
|
};
|
|
|
|
/* Observer types */
|
|
|
|
enum zbus_observer_type
|
|
{
|
|
ZBUS_OBSERVER_LISTENER_TYPE = 0,
|
|
ZBUS_OBSERVER_SUBSCRIBER_TYPE,
|
|
ZBUS_OBSERVER_MSG_SUBSCRIBER_TYPE,
|
|
ZBUS_OBSERVER_ASYNC_LISTENER_TYPE,
|
|
};
|
|
|
|
/* Mutable data associated with every observer */
|
|
|
|
struct zbus_observer_data
|
|
{
|
|
bool enabled;
|
|
|
|
/* Notification queue (subscriber/msg subscriber/async listener), opened
|
|
* lazily with file_mq_open() so it is usable from any task, unlike
|
|
* per-task mqd_t descriptors. mq.f_inode == NULL means "not opened".
|
|
*/
|
|
|
|
struct file mq;
|
|
|
|
#ifdef CONFIG_ZBUS_ASYNC_LISTENER
|
|
/* Dedicated task running the async listener callback. A task (not a
|
|
* pthread) so it outlives the task that triggered the lazy init.
|
|
*/
|
|
|
|
pid_t pid;
|
|
#endif
|
|
};
|
|
|
|
/* An observer: constant descriptor placed in ROM (iterable section) */
|
|
|
|
struct zbus_observer
|
|
{
|
|
#ifdef CONFIG_ZBUS_OBSERVER_NAME
|
|
const char *name;
|
|
#endif
|
|
|
|
enum zbus_observer_type type;
|
|
|
|
/* Notification queue depth (subscriber types only) */
|
|
|
|
uint16_t queue_size;
|
|
|
|
struct zbus_observer_data *data;
|
|
|
|
/* Listener callback (listener type only) */
|
|
|
|
void (*callback)(const struct zbus_channel *chan);
|
|
|
|
#ifdef CONFIG_ZBUS_ASYNC_LISTENER
|
|
/* Async listener callback (async listener type only). Executed on the
|
|
* listener's dedicated task with a copy of the published message.
|
|
*/
|
|
|
|
void (*async_callback)(const struct zbus_channel *chan, const void *msg);
|
|
#endif
|
|
};
|
|
|
|
/* Link between one channel and one observer (ROM iterable section, sorted
|
|
* by name so that entries are grouped by channel and ordered by observer
|
|
* priority). The mutable notification mask lives in .bss and is pointed
|
|
* to from here; its initial value is preserved in ROM (mask_init) and
|
|
* applied by the one-time lazy initialization.
|
|
*/
|
|
|
|
struct zbus_channel_observation
|
|
{
|
|
const struct zbus_channel *chan;
|
|
const struct zbus_observer *obs;
|
|
bool *mask;
|
|
bool mask_init;
|
|
};
|
|
|
|
#ifdef CONFIG_ZBUS_RUNTIME_OBSERVERS
|
|
/* Node linking a runtime observer to a channel */
|
|
|
|
struct zbus_observer_node
|
|
{
|
|
struct list_node node;
|
|
const struct zbus_observer *obs;
|
|
};
|
|
#endif
|
|
|
|
/****************************************************************************
|
|
* Definition macros
|
|
****************************************************************************/
|
|
|
|
#ifdef CONFIG_ZBUS_CHANNEL_NAME
|
|
# define ZBUS_CHANNEL_NAME_INIT(_name) .name = #_name,
|
|
#else
|
|
# define ZBUS_CHANNEL_NAME_INIT(_name)
|
|
#endif
|
|
|
|
#ifdef CONFIG_ZBUS_CHANNEL_ID
|
|
# define _ZBUS_CHANNEL_ID_INIT(_id) .id = _id,
|
|
#else
|
|
# define _ZBUS_CHANNEL_ID_INIT(_id)
|
|
#endif
|
|
|
|
#ifdef CONFIG_ZBUS_OBSERVER_NAME
|
|
# define ZBUS_OBSERVER_NAME_INIT(_name) .name = #_name,
|
|
#else
|
|
# define ZBUS_OBSERVER_NAME_INIT(_name)
|
|
#endif
|
|
|
|
#ifdef CONFIG_ZBUS_RUNTIME_OBSERVERS
|
|
# define _ZBUS_RUNTIME_OBS_INIT(_name) \
|
|
.observers = LIST_INITIAL_VALUE(_zbus_chan_data_##_name.observers),
|
|
#else
|
|
# define _ZBUS_RUNTIME_OBS_INIT(_name)
|
|
#endif
|
|
|
|
#define _ZBUS_MESSAGE_NAME(_name) _zbus_message_##_name
|
|
|
|
/* Declare channels/observers defined in other files */
|
|
|
|
#define _ZBUS_OBS_EXTERN(_p, _name, _i) \
|
|
extern const struct zbus_observer _name;
|
|
#define _ZBUS_CHAN_EXTERN(_p, _name, _i) \
|
|
extern const struct zbus_channel _name;
|
|
|
|
#define ZBUS_OBS_DECLARE(...) \
|
|
FOREACH_ARG(_ZBUS_OBS_EXTERN, 0, __VA_ARGS__)
|
|
#define ZBUS_CHAN_DECLARE(...) \
|
|
FOREACH_ARG(_ZBUS_CHAN_EXTERN, 0, __VA_ARGS__)
|
|
|
|
/* Observer list helpers for ZBUS_CHAN_DEFINE */
|
|
|
|
#define ZBUS_OBSERVERS_EMPTY
|
|
#define ZBUS_OBSERVERS(...) __VA_ARGS__
|
|
|
|
/* Message initializer: ZBUS_MSG_INIT(.a = 1, .b = 2) -> {.a = 1, .b = 2} */
|
|
|
|
#define ZBUS_MSG_INIT(_val, ...) {_val, ##__VA_ARGS__}
|
|
|
|
/* One channel<->observer observation and its mask. The variable name
|
|
* embeds the channel and observer names, so the linker's SORT_BY_NAME()
|
|
* keeps the observations of a channel contiguous, and the index embedded
|
|
* in the name gives the notification order within the channel.
|
|
*/
|
|
|
|
/* FOREACH_ARG() hands the position as a plain literal, and the linker
|
|
* sorts the observations of a channel by name, so the index has to be
|
|
* zero padded to a fixed width for the sort to follow the declaration
|
|
* order beyond ten observers. Same table as the original zbus.
|
|
*/
|
|
#define _ZBUS_OBS_IDX_0 00
|
|
#define _ZBUS_OBS_IDX_1 01
|
|
#define _ZBUS_OBS_IDX_2 02
|
|
#define _ZBUS_OBS_IDX_3 03
|
|
#define _ZBUS_OBS_IDX_4 04
|
|
#define _ZBUS_OBS_IDX_5 05
|
|
#define _ZBUS_OBS_IDX_6 06
|
|
#define _ZBUS_OBS_IDX_7 07
|
|
#define _ZBUS_OBS_IDX_8 08
|
|
#define _ZBUS_OBS_IDX_9 09
|
|
#define _ZBUS_OBS_IDX_10 10
|
|
#define _ZBUS_OBS_IDX_11 11
|
|
#define _ZBUS_OBS_IDX_12 12
|
|
#define _ZBUS_OBS_IDX_13 13
|
|
#define _ZBUS_OBS_IDX_14 14
|
|
#define _ZBUS_OBS_IDX_15 15
|
|
#define _ZBUS_OBS_IDX_16 16
|
|
#define _ZBUS_OBS_IDX_17 17
|
|
#define _ZBUS_OBS_IDX_18 18
|
|
#define _ZBUS_OBS_IDX_19 19
|
|
#define _ZBUS_OBS_IDX_20 20
|
|
#define _ZBUS_OBS_IDX_21 21
|
|
#define _ZBUS_OBS_IDX_22 22
|
|
#define _ZBUS_OBS_IDX_23 23
|
|
#define _ZBUS_OBS_IDX_24 24
|
|
#define _ZBUS_OBS_IDX_25 25
|
|
#define _ZBUS_OBS_IDX_26 26
|
|
#define _ZBUS_OBS_IDX_27 27
|
|
#define _ZBUS_OBS_IDX_28 28
|
|
#define _ZBUS_OBS_IDX_29 29
|
|
#define _ZBUS_OBS_IDX_30 30
|
|
#define _ZBUS_OBS_IDX_31 31
|
|
|
|
#define _ZBUS_OBS_IDX(_idx) CONCATENATE(_ZBUS_OBS_IDX_, _idx)
|
|
|
|
#define _ZBUS_OBSERVATION_NAME_(_chan, _idx, _obs) \
|
|
_zbus_obn_##_chan##_##_idx##_##_obs
|
|
|
|
/* One level of indirection so that _idx is expanded before the paste,
|
|
* which is what lets the caller build it with CONCATENATE().
|
|
*/
|
|
|
|
#define _ZBUS_OBSERVATION_NAME(_chan, _idx, _obs) \
|
|
_ZBUS_OBSERVATION_NAME_(_chan, _idx, _obs)
|
|
|
|
/* The observation name reaches this macro already expanded, so the input
|
|
* section is named after the variable. It must not expand FOREACH_ARG():
|
|
* it is itself expanded from within one, and the preprocessor does not
|
|
* rescan a macro that is already being expanded.
|
|
*/
|
|
|
|
#define _ZBUS_OBSERVATION_DEFINE(_obn, _chan, _obs, _masked) \
|
|
static bool CONCATENATE(_obn, _mask) = _masked; \
|
|
const STRUCT_SECTION_ITERABLE(zbus_channel_observation, _obn) = \
|
|
{ \
|
|
.chan = &_chan, \
|
|
.obs = &_obs, \
|
|
.mask = &CONCATENATE(_obn, _mask), \
|
|
.mask_init = _masked, \
|
|
}
|
|
|
|
#define _ZBUS_CHAN_OBSERVATION(_chan, _obs, _idx) \
|
|
_ZBUS_OBSERVATION_DEFINE( \
|
|
_ZBUS_OBSERVATION_NAME(_chan, _ZBUS_OBS_IDX(_idx), _obs), \
|
|
_chan, _obs, false);
|
|
|
|
#define _ZBUS_CHAN_DEFINE(_name, _id, _type, _validator, _user_data) \
|
|
static struct zbus_channel_data _zbus_chan_data_##_name = \
|
|
{ \
|
|
.observers_start_idx = -1, \
|
|
.observers_end_idx = -1, \
|
|
.sem = SEM_INITIALIZER(1), \
|
|
_ZBUS_RUNTIME_OBS_INIT(_name) \
|
|
}; \
|
|
_ZBUS_CPP_EXTERN const STRUCT_SECTION_ITERABLE(zbus_channel, _name) = \
|
|
{ \
|
|
ZBUS_CHANNEL_NAME_INIT(_name) \
|
|
_ZBUS_CHANNEL_ID_INIT(_id) \
|
|
.message = &_ZBUS_MESSAGE_NAME(_name), \
|
|
.message_size = sizeof(_type), \
|
|
.user_data = _user_data, \
|
|
.validator = _validator, \
|
|
.data = &_zbus_chan_data_##_name, \
|
|
}
|
|
|
|
/* Define a channel.
|
|
*
|
|
* _name channel name (C identifier)
|
|
* _type message type (struct or union)
|
|
* _validator optional validator function or NULL
|
|
* _user_data optional user data pointer or NULL
|
|
* _observers ZBUS_OBSERVERS(obs1, obs2, ...) or ZBUS_OBSERVERS_EMPTY;
|
|
* the position in the list becomes the observation priority,
|
|
* so the observers are notified in the order listed
|
|
* _init_val message initial value, e.g. ZBUS_MSG_INIT(0)
|
|
*/
|
|
|
|
#define ZBUS_CHAN_DEFINE(_name, _type, _validator, _user_data, _observers, \
|
|
_init_val) \
|
|
static _type _ZBUS_MESSAGE_NAME(_name) = _init_val; \
|
|
_ZBUS_CHAN_DEFINE(_name, ZBUS_CHAN_ID_INVALID, _type, _validator, \
|
|
_user_data); \
|
|
ZBUS_OBS_DECLARE(_observers) \
|
|
FOREACH_ARG(_ZBUS_CHAN_OBSERVATION, _name, _observers)
|
|
|
|
/* Same as ZBUS_CHAN_DEFINE with a unique numeric channel identifier */
|
|
|
|
#define ZBUS_CHAN_DEFINE_WITH_ID(_name, _id, _type, _validator, _user_data, \
|
|
_observers, _init_val) \
|
|
static _type _ZBUS_MESSAGE_NAME(_name) = _init_val; \
|
|
_ZBUS_CHAN_DEFINE(_name, _id, _type, _validator, _user_data); \
|
|
ZBUS_OBS_DECLARE(_observers) \
|
|
FOREACH_ARG(_ZBUS_CHAN_OBSERVATION, _name, _observers)
|
|
|
|
/* Add a static observation to a channel defined elsewhere. _prio defines
|
|
* the notification order relative to other ADD_OBS observations of the
|
|
* same channel (use two-digit literals, e.g. 01, 02, ... so the linker
|
|
* name sort orders them correctly). ADD_OBS observations are notified
|
|
* after the ones listed in ZBUS_CHAN_DEFINE.
|
|
*/
|
|
|
|
/* Observations added out of line are notified after every observer listed
|
|
* in the channel definition, and _prio only orders them among themselves:
|
|
* the "zz" infix places them after the two digit indexes in the name
|
|
* sorted section, exactly as the original zbus does.
|
|
*/
|
|
|
|
#define ZBUS_CHAN_ADD_OBS_WITH_MASK(_chan, _obs, _masked, _prio) \
|
|
_ZBUS_OBSERVATION_DEFINE( \
|
|
_ZBUS_OBSERVATION_NAME(_chan, CONCATENATE(zz, _prio), _obs), \
|
|
_chan, _obs, _masked)
|
|
|
|
#define ZBUS_CHAN_ADD_OBS(_chan, _obs, _prio) \
|
|
ZBUS_CHAN_ADD_OBS_WITH_MASK(_chan, _obs, false, _prio)
|
|
|
|
/* Define a listener observer (synchronous callback) */
|
|
|
|
#define ZBUS_LISTENER_DEFINE_WITH_ENABLE(_name, _cb, _enable) \
|
|
static struct zbus_observer_data _zbus_obs_data_##_name = \
|
|
{ \
|
|
.enabled = _enable, \
|
|
}; \
|
|
_ZBUS_CPP_EXTERN const STRUCT_SECTION_ITERABLE(zbus_observer, _name) = \
|
|
{ \
|
|
ZBUS_OBSERVER_NAME_INIT(_name) \
|
|
.type = ZBUS_OBSERVER_LISTENER_TYPE, \
|
|
.queue_size = 0, \
|
|
.data = &_zbus_obs_data_##_name, \
|
|
.callback = (_cb), \
|
|
}
|
|
|
|
#define ZBUS_LISTENER_DEFINE(_name, _cb) \
|
|
ZBUS_LISTENER_DEFINE_WITH_ENABLE(_name, _cb, true)
|
|
|
|
/* Define a subscriber observer (receives channel references through a
|
|
* message queue of depth _queue_size; use zbus_sub_wait() to wait).
|
|
*/
|
|
|
|
#define ZBUS_SUBSCRIBER_DEFINE_WITH_ENABLE(_name, _queue_size, _enable) \
|
|
static struct zbus_observer_data _zbus_obs_data_##_name = \
|
|
{ \
|
|
.enabled = _enable, \
|
|
}; \
|
|
_ZBUS_CPP_EXTERN const STRUCT_SECTION_ITERABLE(zbus_observer, _name) = \
|
|
{ \
|
|
ZBUS_OBSERVER_NAME_INIT(_name) \
|
|
.type = ZBUS_OBSERVER_SUBSCRIBER_TYPE, \
|
|
.queue_size = _queue_size, \
|
|
.data = &_zbus_obs_data_##_name, \
|
|
.callback = NULL, \
|
|
}
|
|
|
|
#define ZBUS_SUBSCRIBER_DEFINE(_name, _queue_size) \
|
|
ZBUS_SUBSCRIBER_DEFINE_WITH_ENABLE(_name, _queue_size, true)
|
|
|
|
#ifdef CONFIG_ZBUS_MSG_SUBSCRIBER
|
|
|
|
/* Define a message subscriber observer (receives copies of the published
|
|
* messages through a message queue; use zbus_sub_wait_msg() to wait).
|
|
* Messages larger than CONFIG_ZBUS_MSG_SUBSCRIBER_MAX_MSG_SIZE cannot be
|
|
* delivered to message subscribers.
|
|
*/
|
|
|
|
#define ZBUS_MSG_SUBSCRIBER_DEFINE_WITH_ENABLE(_name, _enable) \
|
|
static struct zbus_observer_data _zbus_obs_data_##_name = \
|
|
{ \
|
|
.enabled = _enable, \
|
|
}; \
|
|
_ZBUS_CPP_EXTERN const STRUCT_SECTION_ITERABLE(zbus_observer, _name) = \
|
|
{ \
|
|
ZBUS_OBSERVER_NAME_INIT(_name) \
|
|
.type = ZBUS_OBSERVER_MSG_SUBSCRIBER_TYPE, \
|
|
.queue_size = CONFIG_ZBUS_MSG_SUBSCRIBER_QUEUE_SIZE, \
|
|
.data = &_zbus_obs_data_##_name, \
|
|
.callback = NULL, \
|
|
}
|
|
|
|
#define ZBUS_MSG_SUBSCRIBER_DEFINE(_name) \
|
|
ZBUS_MSG_SUBSCRIBER_DEFINE_WITH_ENABLE(_name, true)
|
|
|
|
#endif /* CONFIG_ZBUS_MSG_SUBSCRIBER */
|
|
|
|
#ifdef CONFIG_ZBUS_ASYNC_LISTENER
|
|
|
|
/* Define an async listener observer. The callback executes on a
|
|
* dedicated task (not in the publisher context) and receives a copy of
|
|
* the published message. Messages larger than
|
|
* CONFIG_ZBUS_MSG_SUBSCRIBER_MAX_MSG_SIZE cannot be delivered.
|
|
*/
|
|
|
|
#define ZBUS_ASYNC_LISTENER_DEFINE_WITH_ENABLE(_name, _cb, _enable) \
|
|
static struct zbus_observer_data _zbus_obs_data_##_name = \
|
|
{ \
|
|
.enabled = _enable, \
|
|
}; \
|
|
_ZBUS_CPP_EXTERN const STRUCT_SECTION_ITERABLE(zbus_observer, _name) = \
|
|
{ \
|
|
ZBUS_OBSERVER_NAME_INIT(_name) \
|
|
.type = ZBUS_OBSERVER_ASYNC_LISTENER_TYPE, \
|
|
.queue_size = CONFIG_ZBUS_MSG_SUBSCRIBER_QUEUE_SIZE, \
|
|
.data = &_zbus_obs_data_##_name, \
|
|
.callback = NULL, \
|
|
.async_callback = (_cb), \
|
|
}
|
|
|
|
#define ZBUS_ASYNC_LISTENER_DEFINE(_name, _cb) \
|
|
ZBUS_ASYNC_LISTENER_DEFINE_WITH_ENABLE(_name, _cb, true)
|
|
|
|
#endif /* CONFIG_ZBUS_ASYNC_LISTENER */
|
|
|
|
/****************************************************************************
|
|
* Public Function Prototypes
|
|
****************************************************************************/
|
|
|
|
/* Publish a message to a channel. Copies *msg into the channel and runs
|
|
* the dispatcher, notifying every observer. Returns 0 or -errno
|
|
* (-ENOMSG: validator rejected; -EBUSY/-EAGAIN: could not lock in time).
|
|
*/
|
|
|
|
int zbus_chan_pub(const struct zbus_channel *chan, const void *msg,
|
|
int32_t timeout_ms);
|
|
|
|
/* Read a channel message (copies the channel message into *msg) */
|
|
|
|
int zbus_chan_read(const struct zbus_channel *chan, void *msg,
|
|
int32_t timeout_ms);
|
|
|
|
/* Force the notification of a channel's observers without publishing */
|
|
|
|
int zbus_chan_notify(const struct zbus_channel *chan, int32_t timeout_ms);
|
|
|
|
/* Claim/finish a channel for direct access to zbus_chan_msg() */
|
|
|
|
int zbus_chan_claim(const struct zbus_channel *chan, int32_t timeout_ms);
|
|
int zbus_chan_finish(const struct zbus_channel *chan);
|
|
|
|
/* Wait for a notification (subscriber observers) */
|
|
|
|
int zbus_sub_wait(const struct zbus_observer *sub,
|
|
const struct zbus_channel **chan, int32_t timeout_ms);
|
|
|
|
#ifdef CONFIG_ZBUS_MSG_SUBSCRIBER
|
|
/* Wait for a message copy (message subscriber observers) */
|
|
|
|
int zbus_sub_wait_msg(const struct zbus_observer *sub,
|
|
const struct zbus_channel **chan, void *msg,
|
|
int32_t timeout_ms);
|
|
#endif
|
|
|
|
/* Enable/disable an observer */
|
|
|
|
int zbus_obs_set_enable(const struct zbus_observer *obs, bool enabled);
|
|
|
|
/* Mask/unmask the notifications from one channel to one observer */
|
|
|
|
int zbus_obs_set_chan_notification_mask(const struct zbus_observer *obs,
|
|
const struct zbus_channel *chan,
|
|
bool masked);
|
|
int zbus_obs_is_chan_notification_masked(const struct zbus_observer *obs,
|
|
const struct zbus_channel *chan,
|
|
bool *masked);
|
|
|
|
#ifdef CONFIG_ZBUS_RUNTIME_OBSERVERS
|
|
/* Add/remove observers at runtime */
|
|
|
|
int zbus_chan_add_obs(const struct zbus_channel *chan,
|
|
const struct zbus_observer *obs, int32_t timeout_ms);
|
|
int zbus_chan_rm_obs(const struct zbus_channel *chan,
|
|
const struct zbus_observer *obs, int32_t timeout_ms);
|
|
#endif
|
|
|
|
#ifdef CONFIG_ZBUS_CHANNEL_ID
|
|
const struct zbus_channel *zbus_chan_from_id(uint32_t channel_id);
|
|
#endif
|
|
|
|
#ifdef CONFIG_ZBUS_CHANNEL_NAME
|
|
const struct zbus_channel *zbus_chan_from_name(const char *name);
|
|
#endif
|
|
|
|
/* Iteration over all channels/observers. The iterator function returns
|
|
* false to stop the iteration.
|
|
*/
|
|
|
|
bool zbus_iterate_over_channels(
|
|
bool (*iterator_func)(const struct zbus_channel *chan));
|
|
bool zbus_iterate_over_channels_with_user_data(
|
|
bool (*iterator_func)(const struct zbus_channel *chan, void *user_data),
|
|
void *user_data);
|
|
bool zbus_iterate_over_observers(
|
|
bool (*iterator_func)(const struct zbus_observer *obs));
|
|
bool zbus_iterate_over_observers_with_user_data(
|
|
bool (*iterator_func)(const struct zbus_observer *obs, void *user_data),
|
|
void *user_data);
|
|
|
|
/****************************************************************************
|
|
* Inline Functions
|
|
****************************************************************************/
|
|
|
|
#ifdef CONFIG_ZBUS_CHANNEL_NAME
|
|
static inline const char *zbus_chan_name(const struct zbus_channel *chan)
|
|
{
|
|
DEBUGASSERT(chan != NULL);
|
|
return chan->name;
|
|
}
|
|
#endif
|
|
|
|
/* Direct access to the channel message. Only valid while the channel is
|
|
* locked (inside a listener callback or between claim/finish).
|
|
*/
|
|
|
|
static inline void *zbus_chan_msg(const struct zbus_channel *chan)
|
|
{
|
|
DEBUGASSERT(chan != NULL);
|
|
return chan->message;
|
|
}
|
|
|
|
static inline const void *zbus_chan_const_msg(
|
|
const struct zbus_channel *chan)
|
|
{
|
|
DEBUGASSERT(chan != NULL);
|
|
return chan->message;
|
|
}
|
|
|
|
static inline size_t zbus_chan_msg_size(const struct zbus_channel *chan)
|
|
{
|
|
DEBUGASSERT(chan != NULL);
|
|
return chan->message_size;
|
|
}
|
|
|
|
static inline void *zbus_chan_user_data(const struct zbus_channel *chan)
|
|
{
|
|
DEBUGASSERT(chan != NULL);
|
|
return chan->user_data;
|
|
}
|
|
|
|
static inline int zbus_obs_is_enabled(const struct zbus_observer *obs,
|
|
bool *enable)
|
|
{
|
|
_ZBUS_ASSERT(obs != NULL, "obs is required");
|
|
_ZBUS_ASSERT(enable != NULL, "enable is required");
|
|
|
|
*enable = obs->data->enabled;
|
|
return 0;
|
|
}
|
|
|
|
#ifdef CONFIG_ZBUS_OBSERVER_NAME
|
|
static inline const char *zbus_obs_name(const struct zbus_observer *obs)
|
|
{
|
|
DEBUGASSERT(obs != NULL);
|
|
return obs->name;
|
|
}
|
|
#endif
|
|
|
|
#ifdef CONFIG_ZBUS_CHANNEL_PUBLISH_STATS
|
|
|
|
/* Update the publish statistics (claim/finish workflow only; the channel
|
|
* must be locked).
|
|
*/
|
|
|
|
static inline void zbus_chan_pub_stats_update(
|
|
const struct zbus_channel *chan)
|
|
{
|
|
DEBUGASSERT(chan != NULL);
|
|
|
|
clock_gettime(CLOCK_MONOTONIC, &chan->data->publish_timestamp);
|
|
chan->data->publish_count += 1;
|
|
}
|
|
|
|
static inline struct timespec zbus_chan_pub_stats_last_time(
|
|
const struct zbus_channel *chan)
|
|
{
|
|
DEBUGASSERT(chan != NULL);
|
|
return chan->data->publish_timestamp;
|
|
}
|
|
|
|
static inline uint32_t zbus_chan_pub_stats_count(
|
|
const struct zbus_channel *chan)
|
|
{
|
|
DEBUGASSERT(chan != NULL);
|
|
return chan->data->publish_count;
|
|
}
|
|
|
|
#else
|
|
|
|
static inline void zbus_chan_pub_stats_update(
|
|
const struct zbus_channel *chan)
|
|
{
|
|
(void)chan;
|
|
}
|
|
|
|
#endif /* CONFIG_ZBUS_CHANNEL_PUBLISH_STATS */
|
|
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif
|
|
|
|
#endif /* __APPS_INCLUDE_SYSTEM_ZBUS_H */
|