zbus: optional unique channel numeric identifiers

Add the option for a unique numeric identifiers to be attached to a
zbus channel. This identifier can then be used to lookup the channel
structure at runtime. This is useful in two situations (that I can
immediately think of).

Firstly for external interaction, i.e a text shell or remote procedure
calls. The current state of a channel can be queried by an ID that never
changes, as opposed to the external entity needing to know the exact
memory address of the channel for a given application binary.

Secondly for integrating with dynamically loaded extensions (llext).
These extensions can hook into the existing data streams without each
individual channel needing to be exported and visible to the loader.

Signed-off-by: Jordan Yates <jordan@embeint.com>
This commit is contained in:
Jordan Yates 2024-07-05 20:15:13 +10:00 committed by Benjamin Cabé
commit d2bb5973ae
3 changed files with 128 additions and 35 deletions

View file

@ -81,6 +81,10 @@ struct zbus_channel {
#if defined(CONFIG_ZBUS_CHANNEL_NAME) || defined(__DOXYGEN__)
/** Channel name. */
const char *name;
#endif
#if defined(CONFIG_ZBUS_CHANNEL_ID) || defined(__DOXYGEN__)
/** Unique numeric channel identifier. */
uint32_t id;
#endif
/** Message reference. Represents the message's reference that points to the actual
* shared memory region.
@ -263,6 +267,34 @@ struct zbus_channel_observation {
#define _ZBUS_RUNTIME_OBSERVERS_DECL(_name)
#endif /* CONFIG_ZBUS_RUNTIME_OBSERVERS */
#define _ZBUS_MESSAGE_NAME(_name) _CONCAT(_zbus_message_, _name)
/* clang-format off */
#define _ZBUS_CHAN_DEFINE(_name, _id, _type, _validator, _user_data) \
static struct zbus_channel_data _CONCAT(_zbus_chan_data_, _name) = { \
.observers_start_idx = -1, \
.observers_end_idx = -1, \
.sem = Z_SEM_INITIALIZER(_CONCAT(_zbus_chan_data_, _name).sem, 1, 1), \
IF_ENABLED(CONFIG_ZBUS_PRIORITY_BOOST, \
(.highest_observer_priority = ZBUS_MIN_THREAD_PRIORITY,)) \
IF_ENABLED(CONFIG_ZBUS_RUNTIME_OBSERVERS, \
(.observers = SYS_SLIST_STATIC_INIT( \
&_CONCAT(_zbus_chan_data_, _name).observers),)) \
}; \
static K_MUTEX_DEFINE(_CONCAT(_zbus_mutex_, _name)); \
_ZBUS_CPP_EXTERN const STRUCT_SECTION_ITERABLE(zbus_channel, _name) = { \
ZBUS_CHANNEL_NAME_INIT(_name) /* Maybe removed */ \
IF_ENABLED(CONFIG_ZBUS_CHANNEL_ID, (.id = _id,)) \
.message = &_ZBUS_MESSAGE_NAME(_name), \
.message_size = sizeof(_type), \
.user_data = _user_data, \
.validator = _validator, \
.data = &_CONCAT(_zbus_chan_data_, _name), \
IF_ENABLED(ZBUS_MSG_SUBSCRIBER_NET_BUF_POOL_ISOLATION, \
(.msg_subscriber_pool = &_zbus_msg_subscribers_pool,)) \
}
/* clang-format on */
/** @endcond */
/* clang-format off */
@ -328,7 +360,11 @@ struct zbus_channel_observation {
*/
#define ZBUS_OBSERVERS(...) __VA_ARGS__
/* clang-format off */
/**
* @def ZBUS_CHAN_ID_INVALID
* This macro indicates the channel does not have a unique ID.
*/
#define ZBUS_CHAN_ID_INVALID UINT32_MAX
/**
* @brief Zbus channel definition.
@ -345,37 +381,37 @@ struct zbus_channel_observation {
* first the highest priority.
* @param _init_val The message initialization.
*/
#define ZBUS_CHAN_DEFINE(_name, _type, _validator, _user_data, _observers, _init_val) \
static _type _CONCAT(_zbus_message_, _name) = _init_val; \
static struct zbus_channel_data _CONCAT(_zbus_chan_data_, _name) = { \
.observers_start_idx = -1, \
.observers_end_idx = -1, \
.sem = Z_SEM_INITIALIZER(_CONCAT(_zbus_chan_data_, _name).sem, 1, 1), \
IF_ENABLED(CONFIG_ZBUS_PRIORITY_BOOST, ( \
.highest_observer_priority = ZBUS_MIN_THREAD_PRIORITY, \
)) \
IF_ENABLED(CONFIG_ZBUS_RUNTIME_OBSERVERS, ( \
.observers = SYS_SLIST_STATIC_INIT( \
&_CONCAT(_zbus_chan_data_, _name).observers), \
)) \
}; \
static K_MUTEX_DEFINE(_CONCAT(_zbus_mutex_, _name)); \
_ZBUS_CPP_EXTERN const STRUCT_SECTION_ITERABLE(zbus_channel, _name) = { \
ZBUS_CHANNEL_NAME_INIT(_name) /* Maybe removed */ \
.message = &_CONCAT(_zbus_message_, _name), \
.message_size = sizeof(_type), \
.user_data = _user_data, \
.validator = _validator, \
.data = &_CONCAT(_zbus_chan_data_, _name), \
IF_ENABLED(ZBUS_MSG_SUBSCRIBER_NET_BUF_POOL_ISOLATION, ( \
.msg_subscriber_pool = &_zbus_msg_subscribers_pool, \
)) \
}; \
/* Extern declaration of observers */ \
ZBUS_OBS_DECLARE(_observers); \
/* Create all channel observations from observers list */ \
#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); \
/* Extern declaration of observers */ \
ZBUS_OBS_DECLARE(_observers); \
/* Create all channel observations from observers list */ \
FOR_EACH_FIXED_ARG_NONEMPTY_TERM(_ZBUS_CHAN_OBSERVATION, (;), _name, _observers)
/**
* @brief Zbus channel definition with numeric identifier.
*
* This macro defines a channel.
*
* @param _name The channel's name.
* @param _id The channel's unique numeric identifier.
* @param _type The Message type. It must be a struct or union.
* @param _validator The validator function.
* @param _user_data A pointer to the user data.
*
* @see struct zbus_channel
* @param _observers The observers list. The sequence indicates the priority of the observer. The
* first the highest priority.
* @param _init_val The message initialization.
*/
#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); \
/* Extern declaration of observers */ \
ZBUS_OBS_DECLARE(_observers); \
/* Create all channel observations from observers list */ \
FOR_EACH_FIXED_ARG_NONEMPTY_TERM(_ZBUS_CHAN_OBSERVATION, (;), _name, _observers)
/* clang-format on */
/**
* @brief Initialize a message.
@ -386,10 +422,7 @@ struct zbus_channel_observation {
* @param[in] _val Variadic with the initial values. ``ZBUS_INIT(0)`` means ``{0}``, as
* ZBUS_INIT(.a=10, .b=30) means ``{.a=10, .b=30}``.
*/
#define ZBUS_MSG_INIT(_val, ...) \
{ \
_val, ##__VA_ARGS__ \
}
#define ZBUS_MSG_INIT(_val, ...) {_val, ##__VA_ARGS__}
/* clang-format off */
@ -638,6 +671,20 @@ static inline const char *zbus_chan_name(const struct zbus_channel *chan)
#endif
#if defined(CONFIG_ZBUS_CHANNEL_ID) || defined(__DOXYGEN__)
/**
* @brief Retrieve a zbus channel from its numeric identifier
*
* @param channel_id Unique channel ID from @ref ZBUS_CHAN_DEFINE_WITH_ID
*
* @retval NULL If channel with ID @a channel_id does not exist.
* @retval chan Channel pointer with ID @a channel_id otherwise.
*/
const struct zbus_channel *zbus_chan_from_id(uint32_t channel_id);
#endif
/**
* @brief Get the reference for a channel message directly.
*

View file

@ -16,6 +16,9 @@ config ZBUS_CHANNELS_SYS_INIT_PRIORITY
config ZBUS_CHANNEL_NAME
bool "Channel name field"
config ZBUS_CHANNEL_ID
bool "Channel identifier field"
config ZBUS_OBSERVER_NAME
bool "Observer name field"

View file

@ -75,10 +75,53 @@ int _zbus_init(void)
++(curr->data->observers_end_idx);
}
#if defined(CONFIG_ZBUS_CHANNEL_ID)
STRUCT_SECTION_FOREACH(zbus_channel, chan) {
/* Check for duplicate channel IDs */
if (chan->id == ZBUS_CHAN_ID_INVALID) {
continue;
}
/* Iterate over all previous channels */
STRUCT_SECTION_FOREACH(zbus_channel, chan_prev) {
if (chan_prev == chan) {
break;
}
if (chan->id == chan_prev->id) {
#if defined(CONFIG_ZBUS_CHANNEL_NAME)
LOG_WRN("Channels %s and %s have matching IDs (%d)", chan->name,
chan_prev->name, chan->id);
#else
LOG_WRN("Channels %p and %p have matching IDs (%d)", chan,
chan_prev, chan->id);
#endif /* CONFIG_ZBUS_CHANNEL_NAME */
}
}
}
#endif /* CONFIG_ZBUS_CHANNEL_ID */
return 0;
}
SYS_INIT(_zbus_init, APPLICATION, CONFIG_ZBUS_CHANNELS_SYS_INIT_PRIORITY);
#if defined(CONFIG_ZBUS_CHANNEL_ID)
const struct zbus_channel *zbus_chan_from_id(uint32_t channel_id)
{
if (channel_id == ZBUS_CHAN_ID_INVALID) {
return NULL;
}
STRUCT_SECTION_FOREACH(zbus_channel, chan) {
if (chan->id == channel_id) {
/* Found matching channel */
return chan;
}
}
/* No matching channel exists */
return NULL;
}
#endif /* CONFIG_ZBUS_CHANNEL_ID */
static inline int _zbus_notify_observer(const struct zbus_channel *chan,
const struct zbus_observer *obs, k_timepoint_t end_time,
struct net_buf *buf)