From d2bb5973ae493f718b71d2d62f632aadb6a56fbb Mon Sep 17 00:00:00 2001 From: Jordan Yates Date: Fri, 5 Jul 2024 20:15:13 +1000 Subject: [PATCH] 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 --- include/zephyr/zbus/zbus.h | 117 ++++++++++++++++++++++++++----------- subsys/zbus/Kconfig | 3 + subsys/zbus/zbus.c | 43 ++++++++++++++ 3 files changed, 128 insertions(+), 35 deletions(-) diff --git a/include/zephyr/zbus/zbus.h b/include/zephyr/zbus/zbus.h index e5657c0a5493..478268d85b4c 100644 --- a/include/zephyr/zbus/zbus.h +++ b/include/zephyr/zbus/zbus.h @@ -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. * diff --git a/subsys/zbus/Kconfig b/subsys/zbus/Kconfig index 8f742b8bd99c..9cd518c6215b 100644 --- a/subsys/zbus/Kconfig +++ b/subsys/zbus/Kconfig @@ -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" diff --git a/subsys/zbus/zbus.c b/subsys/zbus/zbus.c index 78f8ce99fd4e..e4defffbc57b 100644 --- a/subsys/zbus/zbus.c +++ b/subsys/zbus/zbus.c @@ -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)