Skip to content

Commit

Permalink
zbus: optional unique channel numeric identifiers
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
JordanYates authored and kartben committed Dec 18, 2024
1 parent 40744c3 commit d2bb597
Show file tree
Hide file tree
Showing 3 changed files with 128 additions and 35 deletions.
117 changes: 82 additions & 35 deletions include/zephyr/zbus/zbus.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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 */
Expand Down Expand Up @@ -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.
Expand All @@ -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.
Expand All @@ -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 */

Expand Down Expand Up @@ -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.
*
Expand Down
3 changes: 3 additions & 0 deletions subsys/zbus/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down
43 changes: 43 additions & 0 deletions subsys/zbus/zbus.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down

0 comments on commit d2bb597

Please sign in to comment.