Skip to content

Commit 5610a02

Browse files
zbus: add multidomain support infrastructure
Add core multidomain support to ZBus, enabling communication between separate domains. This includes: - New shadow channel concept with is_shadow_channel flag - ZBUS_SHADOW_CHAN_DEFINE macros for defining shadow channels - ZBUS_MULTIDOMAIN_CHAN_DEFINE for conditional channel definitions - ZBUS_CHANNEL_IS_SHADOW macro for checking shadow status - ZBUS_CHANNEL_IS_MASTER macro for checking master status - zbus_chan_pub_shadow() for internal shadow channel publishing - Protection against publishing to shadow channels from application code - Configuration options for multidomain support Shadow channels represent channels defined in other domains, allowing observation without direct publishing. The multidomain macros enable shared channel definitions that create master channels in one domain and shadow channels in others. Signed-off-by: Trond F. Christiansen <[email protected]>
1 parent 54d638b commit 5610a02

File tree

3 files changed

+276
-3
lines changed

3 files changed

+276
-3
lines changed

include/zephyr/zbus/zbus.h

Lines changed: 206 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,16 @@ struct zbus_channel {
109109

110110
/** Mutable channel data struct. */
111111
struct zbus_channel_data *data;
112+
113+
#if defined(CONFIG_ZBUS_MULTIDOMAIN) || defined(__DOXYGEN__)
114+
115+
/** Indicates if the channel is a shadow channel.
116+
* A shadow channel is a channel that should not be used directly, but rather
117+
* shadows another channel, usually one that is defined in another domain.
118+
*/
119+
bool is_shadow_channel;
120+
121+
#endif /* CONFIG_ZBUS_MULTIDOMAIN */
112122
};
113123

114124
/**
@@ -275,7 +285,7 @@ struct zbus_channel_observation {
275285
#define _ZBUS_MESSAGE_NAME(_name) _CONCAT(_zbus_message_, _name)
276286

277287
/* clang-format off */
278-
#define _ZBUS_CHAN_DEFINE(_name, _id, _type, _validator, _user_data) \
288+
#define _ZBUS_CHAN_DEFINE(_name, _id, _type, _validator, _user_data, _is_shadow) \
279289
static struct zbus_channel_data _CONCAT(_zbus_chan_data_, _name) = { \
280290
.observers_start_idx = -1, \
281291
.observers_end_idx = -1, \
@@ -295,6 +305,7 @@ struct zbus_channel_observation {
295305
.user_data = _user_data, \
296306
.validator = _validator, \
297307
.data = &_CONCAT(_zbus_chan_data_, _name), \
308+
IF_ENABLED(CONFIG_ZBUS_MULTIDOMAIN, (.is_shadow_channel = _is_shadow,)) \
298309
IF_ENABLED(ZBUS_MSG_SUBSCRIBER_NET_BUF_POOL_ISOLATION, \
299310
(.msg_subscriber_pool = &_zbus_msg_subscribers_pool,)) \
300311
}
@@ -388,12 +399,46 @@ struct zbus_channel_observation {
388399
*/
389400
#define ZBUS_CHAN_DEFINE(_name, _type, _validator, _user_data, _observers, _init_val) \
390401
static _type _ZBUS_MESSAGE_NAME(_name) = _init_val; \
391-
_ZBUS_CHAN_DEFINE(_name, ZBUS_CHAN_ID_INVALID, _type, _validator, _user_data); \
402+
_ZBUS_CHAN_DEFINE(_name, ZBUS_CHAN_ID_INVALID, _type, _validator, _user_data, false); \
403+
/* Extern declaration of observers */ \
404+
ZBUS_OBS_DECLARE(_observers); \
405+
/* Create all channel observations from observers list */ \
406+
FOR_EACH_FIXED_ARG_NONEMPTY_TERM(_ZBUS_CHAN_OBSERVATION, (;), _name, _observers)
407+
408+
#if defined(CONFIG_ZBUS_MULTIDOMAIN) || defined(__DOXYGEN__)
409+
410+
/**
411+
* @brief Zbus shadow channel definition.
412+
*
413+
* This macro defines a shadow channel.
414+
* Similar to ZBUS_CHAN_DEFINE, but defines the channel with the
415+
* is_shadow_channel flag set to true, blocking the channel from
416+
* being published to normally.
417+
*
418+
* @param _name The channel's name.
419+
* @param _type The Message type. It must be a struct or union.
420+
* @param _validator The validator function.
421+
* @param _user_data A pointer to the user data.
422+
*
423+
* @see struct zbus_channel
424+
* @param _observers The observers list. The sequence indicates the priority of the observer. The
425+
* first the highest priority.
426+
* @param _init_val The message initialization.
427+
*
428+
* @note This macro is used to define shadow channels in a multi-domain setup.
429+
* Shadow channels are used to represent channels that are defined in another domain, allowing
430+
* the current domain to observe them without directly publishing to them.
431+
*/
432+
#define ZBUS_SHADOW_CHAN_DEFINE(_name, _type, _validator, _user_data, _observers, _init_val) \
433+
static _type _ZBUS_MESSAGE_NAME(_name) = _init_val; \
434+
_ZBUS_CHAN_DEFINE(_name, ZBUS_CHAN_ID_INVALID, _type, _validator, _user_data, true); \
392435
/* Extern declaration of observers */ \
393436
ZBUS_OBS_DECLARE(_observers); \
394437
/* Create all channel observations from observers list */ \
395438
FOR_EACH_FIXED_ARG_NONEMPTY_TERM(_ZBUS_CHAN_OBSERVATION, (;), _name, _observers)
396439

440+
#endif /* CONFIG_ZBUS_MULTIDOMAIN */
441+
397442
/**
398443
* @brief Zbus channel definition with numeric identifier.
399444
*
@@ -412,12 +457,133 @@ struct zbus_channel_observation {
412457
*/
413458
#define ZBUS_CHAN_DEFINE_WITH_ID(_name, _id, _type, _validator, _user_data, _observers, _init_val) \
414459
static _type _ZBUS_MESSAGE_NAME(_name) = _init_val; \
415-
_ZBUS_CHAN_DEFINE(_name, _id, _type, _validator, _user_data); \
460+
_ZBUS_CHAN_DEFINE(_name, _id, _type, _validator, _user_data, false); \
416461
/* Extern declaration of observers */ \
417462
ZBUS_OBS_DECLARE(_observers); \
418463
/* Create all channel observations from observers list */ \
419464
FOR_EACH_FIXED_ARG_NONEMPTY_TERM(_ZBUS_CHAN_OBSERVATION, (;), _name, _observers)
420465

466+
#if defined(CONFIG_ZBUS_MULTIDOMAIN) || defined(__DOXYGEN__)
467+
468+
/**
469+
* @brief Zbus shadow channel definition.
470+
*
471+
* This macro defines a shadow channel.
472+
* Similar to ZBUS_CHAN_DEFINE, but defines the channel with the
473+
* is_shadow_channel flag set to true, blocking the channel from
474+
* being published to normally.
475+
*
476+
* @param _name The channel's name.
477+
* @param _id The channel's unique numeric identifier.
478+
* @param _type The Message type. It must be a struct or union.
479+
* @param _validator The validator function.
480+
* @param _user_data A pointer to the user data.
481+
*
482+
* @see struct zbus_channel
483+
* @param _observers The observers list. The sequence indicates the priority of the observer. The
484+
* first the highest priority.
485+
* @param _init_val The message initialization.
486+
*
487+
* @note This macro is used to define shadow channels in a multi-domain setup.
488+
* Shadow channels are used to represent channels that are defined in another domain, allowing
489+
* the current domain to observe them without directly publishing to them.
490+
*/
491+
#define ZBUS_SHADOW_CHAN_DEFINE_WITH_ID(_name, _id, _type, _validator, _user_data, _observers, \
492+
_init_val) \
493+
static _type _ZBUS_MESSAGE_NAME(_name) = _init_val; \
494+
_ZBUS_CHAN_DEFINE(_name, _id, _type, _validator, _user_data, true); \
495+
/* Extern declaration of observers */ \
496+
ZBUS_OBS_DECLARE(_observers); \
497+
/* Create all channel observations from observers list */ \
498+
FOR_EACH_FIXED_ARG_NONEMPTY_TERM(_ZBUS_CHAN_OBSERVATION, (;), _name, _observers)
499+
500+
/**
501+
* @brief Macro to check if a channel is a shadow channel.
502+
*
503+
* @param _chan The channel to check.
504+
* @return true if the channel is a shadow channel, false otherwise.
505+
*/
506+
#define ZBUS_CHANNEL_IS_SHADOW(_chan) ((_chan)->is_shadow_channel)
507+
508+
/**
509+
* @brief Macro to check if a channel is a master channel.
510+
*
511+
* @param _chan The channel to check.
512+
* @return true if the channel is a master channel, false if it is a shadow channel.
513+
*/
514+
#define ZBUS_CHANNEL_IS_MASTER(_chan) (!(_chan)->is_shadow_channel)
515+
516+
/**
517+
* @brief Zbus multi-domain channel definition.
518+
*
519+
* This macro defines a channel that can be either a master or a shadow channel based on the
520+
* is_master and is_included flags. If is_master is true, it defines a normal channel, otherwise
521+
* it defines a shadow channel. If is_included is false, the channel will not be defined at all.
522+
* Intended usage is in a shared header in multi-domain setups where the channel is defined in
523+
* one domain and shadowed in others, using device specific defines to control the inclusion
524+
* and master status.
525+
*
526+
* @param _name The channel's name.
527+
* @param _type The Message type. It must be a struct or union.
528+
* @param _validator The validator function.
529+
* @param _user_data A pointer to the user data.
530+
* @param _observers The observers list. The sequence indicates the priority of the observer. The
531+
* first the highest priority.
532+
* @param _init_val The message initialization.
533+
* @param _is_master Indicates if this is the master channel (true) or a shadow channel (false).
534+
* @param _is_included Indicates if the channel should be included in this device (true) or not
535+
* (false).
536+
*
537+
* @note This macro is used to define channels in a multi-domain setup where the channel can be
538+
* either a master channel or a shadow channel, depending on the device configuration.
539+
*/
540+
#define ZBUS_MULTIDOMAIN_CHAN_DEFINE(_name, _type, _validator, _user_data, _observers, _init_val, \
541+
_is_master, _is_included) \
542+
COND_CODE_1(_is_included, \
543+
(COND_CODE_1(_is_master, \
544+
(ZBUS_CHAN_DEFINE(_name, _type, _validator, _user_data, _observers, \
545+
_init_val)), \
546+
(ZBUS_SHADOW_CHAN_DEFINE(_name, _type, _validator, _user_data, _observers, \
547+
_init_val)))), \
548+
(/* Channel not included on this device - no definition*/))
549+
550+
/**
551+
* @brief Zbus multi-domain channel definition with ID.
552+
*
553+
* This macro defines a channel that can be either a master or a shadow channel based on the
554+
* is_master and is_included flags. If is_master is true, it defines a normal channel with a unique
555+
* ID, otherwise it defines a shadow channel with the same ID. If is_included is false, the channel
556+
* will not be defined at all. Intended usage is in a shared header in multi-domain setups where the
557+
* channel is defined in one domain and shadowed in others, using device specific defines to control
558+
* the inclusion and master status.
559+
*
560+
* @param _name The channel's name.
561+
* @param _id The channel's unique numeric identifier.
562+
* @param _type The Message type. It must be a struct or union.
563+
* @param _validator The validator function.
564+
* @param _user_data A pointer to the user data.
565+
* @param _observers The observers list. The sequence indicates the priority of the observer. The
566+
* first the highest priority.
567+
* @param _init_val The message initialization.
568+
* @param _is_master Indicates if this is the master channel (true) or a shadow channel (false).
569+
* @param _is_included Indicates if the channel should be included in this device (true) or not
570+
* (false).
571+
*
572+
* @note This macro is used to define channels in a multi-domain setup where the channel can be
573+
* either a master channel or a shadow channel, depending on the device configuration.
574+
*/
575+
#define ZBUS_MULTIDOMAIN_CHAN_DEFINE_WITH_ID(_name, _id, _type, _validator, _user_data, \
576+
_observers, _init_val, _is_master, _is_included) \
577+
COND_CODE_1(_is_included, \
578+
(COND_CODE_1(_is_master, \
579+
(ZBUS_CHAN_DEFINE_WITH_ID(_name, _id, _type, _validator, _user_data, \
580+
_observers, _init_val)), \
581+
(ZBUS_SHADOW_CHAN_DEFINE_WITH_ID(_name, _id, _type, _validator, _user_data,\
582+
_observers, _init_val)))), \
583+
(/* Channel not included on this device - no definition*/))
584+
585+
#endif /* CONFIG_ZBUS_MULTIDOMAIN */
586+
421587
/**
422588
* @brief Initialize a message.
423589
*
@@ -575,9 +741,46 @@ struct zbus_channel_observation {
575741
* @retval -EFAULT A parameter is incorrect, the notification could not be sent to one or more
576742
* observer, or the function context is invalid (inside an ISR). The function only returns this
577743
* value when the @kconfig{CONFIG_ZBUS_ASSERT_MOCK} is enabled.
744+
* @retval -EPERM Attempt to publish a shadow channel. Shadow channels can only be published to
745+
* via the zbus_chan_pub_shadow function. The function only returns this value when the
746+
* @kconfig{CONFIG_ZBUS_MULTIDOMAIN} is enabled.
578747
*/
579748
int zbus_chan_pub(const struct zbus_channel *chan, const void *msg, k_timeout_t timeout);
580749

750+
#if defined(CONFIG_ZBUS_MULTIDOMAIN)
751+
752+
/** @cond INTERNAL_HIDDEN */
753+
754+
/**
755+
* @brief Publish to a shadow channel
756+
*
757+
* This routine publishes a message to a shadow channel.
758+
*
759+
* @param chan The channel's reference.
760+
* @param msg Reference to the message where the publish function copies the channel's
761+
* message data from.
762+
* @param timeout Waiting period to publish the channel,
763+
* or one of the special values K_NO_WAIT and K_FOREVER.
764+
*
765+
* @retval 0 Channel published.
766+
* @retval -ENOMSG The message is invalid based on the validator function or some of the
767+
* observers could not receive the notification.
768+
* @retval -EBUSY The channel is busy.
769+
* @retval -EAGAIN Waiting period timed out.
770+
* @retval -EFAULT A parameter is incorrect, the notification could not be sent to one or more
771+
* observer, or the function context is invalid (inside an ISR). The function only returns this
772+
* value when the @kconfig{CONFIG_ZBUS_ASSERT_MOCK} is enabled.
773+
*
774+
* @note This function is used to publish messages to shadow channels in a multi-domain setup,
775+
* and should not be used by application logic directly. It is intended for internal use to
776+
* handle shadow channels.
777+
*/
778+
int zbus_chan_pub_shadow(const struct zbus_channel *chan, const void *msg, k_timeout_t timeout);
779+
780+
/** @endcond */
781+
782+
#endif /* CONFIG_ZBUS_MULTIDOMAIN */
783+
581784
/**
582785
* @brief Read a channel
583786
*

subsys/zbus/Kconfig

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,13 @@ config ZBUS_ASSERT_MOCK
107107
enabled, _ZBUS_ASSERT returns -EFAULT instead of assert. It makes it more straightforward to test invalid
108108
parameters.
109109

110+
config ZBUS_MULTIDOMAIN
111+
bool "ZBus multidomain support"
112+
select ZBUS_MSG_SUBSCRIBER
113+
select ZBUS_CHANNEL_NAME
114+
help
115+
Enables support for ZBus multidomain. This feature allows the ZBus to work with multiple domains,
116+
enabling communication between them.
110117

111118
config HEAP_MEM_POOL_ADD_SIZE_ZBUS
112119
int "ZBus requested heap pool size."

subsys/zbus/zbus.c

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -403,6 +403,18 @@ int zbus_chan_pub(const struct zbus_channel *chan, const void *msg, k_timeout_t
403403
_ZBUS_ASSERT(k_is_in_isr() ? K_TIMEOUT_EQ(timeout, K_NO_WAIT) : true,
404404
"inside an ISR, the timeout must be K_NO_WAIT");
405405

406+
#if defined(CONFIG_ZBUS_MULTIDOMAIN)
407+
408+
/* Normal publish to a shadow channel is not allowed from application logic. */
409+
if (chan->is_shadow_channel) {
410+
LOG_ERR("Channel is defined as shadow. Cannot publish to shadow channel %s from "
411+
"application logic, only from ZBUS proxy agent",
412+
_ZBUS_CHAN_NAME(chan));
413+
return -EPERM;
414+
}
415+
416+
#endif /* CONFIG_ZBUS_MULTIDOMAIN */
417+
406418
if (k_is_in_isr()) {
407419
timeout = K_NO_WAIT;
408420
}
@@ -434,6 +446,57 @@ int zbus_chan_pub(const struct zbus_channel *chan, const void *msg, k_timeout_t
434446
return err;
435447
}
436448

449+
#if defined(CONFIG_ZBUS_MULTIDOMAIN)
450+
451+
int zbus_chan_pub_shadow(const struct zbus_channel *chan, const void *msg, k_timeout_t timeout)
452+
{
453+
int err;
454+
455+
_ZBUS_ASSERT(chan != NULL, "chan is required");
456+
_ZBUS_ASSERT(msg != NULL, "msg is required");
457+
_ZBUS_ASSERT(k_is_in_isr() ? K_TIMEOUT_EQ(timeout, K_NO_WAIT) : true,
458+
"inside an ISR, the timeout must be K_NO_WAIT");
459+
460+
if (!chan->is_shadow_channel) {
461+
/* Shadow publish to a non-shadow channel is not allowed */
462+
LOG_ERR("Channel is not defined as shadow. Cannot publish to non-shadow channel %s",
463+
_ZBUS_CHAN_NAME(chan));
464+
return -EPERM;
465+
}
466+
467+
if (k_is_in_isr()) {
468+
timeout = K_NO_WAIT;
469+
}
470+
471+
k_timepoint_t end_time = sys_timepoint_calc(timeout);
472+
473+
if (chan->validator != NULL && !chan->validator(msg, chan->message_size)) {
474+
return -ENOMSG;
475+
}
476+
477+
int context_priority = ZBUS_MIN_THREAD_PRIORITY;
478+
479+
err = chan_lock(chan, timeout, &context_priority);
480+
if (err) {
481+
return err;
482+
}
483+
484+
#if defined(CONFIG_ZBUS_CHANNEL_PUBLISH_STATS)
485+
chan->data->publish_timestamp = k_uptime_ticks();
486+
chan->data->publish_count += 1;
487+
#endif
488+
489+
memcpy(chan->message, msg, chan->message_size);
490+
491+
err = _zbus_vded_exec(chan, end_time);
492+
493+
chan_unlock(chan, context_priority);
494+
495+
return err;
496+
}
497+
498+
#endif /* CONFIG_ZBUS_MULTIDOMAIN */
499+
437500
int zbus_chan_read(const struct zbus_channel *chan, void *msg, k_timeout_t timeout)
438501
{
439502
_ZBUS_ASSERT(chan != NULL, "chan is required");

0 commit comments

Comments
 (0)