Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added doc/services/zbus/images/zbus_proxy_agent.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
164 changes: 163 additions & 1 deletion doc/services/zbus/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -878,6 +878,136 @@ illustrates the runtime registration usage.
the channel observer it was first associated with through :c:func:`zbus_chan_rm_obs`.


Multi-domain Communication
***************************

ZBus supports multi-domain communication, enabling message passing between different execution
domains such as CPU cores or separate devices. This is achieved through proxy agents that
forward messages between domains.

.. figure:: images/zbus_proxy_agent.png
:alt: ZBus publish processing detail
:width: 75%

..
Temporary image illustrating zbus multi-domain communication with proxy agents.

Concepts
========

Multi-domain zbus introduces several key concepts:

* **Master channels**: Channels where messages are published locally within a domain
* **Shadow channels**: Read-only channels that mirror master channels from other domains
* **Proxy agents**: Background services that synchronize channel data between domains
* **Transport backends**: Communication mechanisms (IPC, UART) used by proxy agents

The :c:macro:`ZBUS_MULTIDOMAIN_CHAN_DEFINE` macro enables conditional compilation to create
master channels in one domain and corresponding shadow channels in other domains.

Transport Backends
==================

ZBus multi-domain communication relies on transport backends to forward messages between different
execution domains.

IPC Backend
-----------

The IPC backend facilitates communication between CPU cores within the same system using
Inter-Process Communication mechanisms.

See the :zephyr:code-sample:`zbus-ipc-forwarder` sample for a complete implementation.

UART Backend
------------

The UART backend enables communication between physically separate devices over serial connections.
This backend extends zbus messaging across device boundaries, allowing distributed systems to
maintain a unified message bus architecture.

See the :zephyr:code-sample:`zbus-uart-forwarder` sample for a complete implementation.

Usage
=====

Multi-domain zbus requires defining shared channels and setting up proxy agents. Here's a typical setup:

**common.h** - Shared channel definitions:

.. code-block:: c

#include <zephyr/zbus/zbus.h>
#include <zephyr/zbus/multidomain/zbus_multidomain.h>

struct request_data {
// ...
};

struct response_data {
// ...
};

ZBUS_MULTIDOMAIN_CHAN_DEFINE(request_channel, struct request_data,
NULL, NULL, ZBUS_OBSERVERS_EMPTY,
ZBUS_MSG_INIT(0),
IS_ENABLED(DOMAIN_A), /* Master on domain A */
1 /* Include on both domains */);

ZBUS_MULTIDOMAIN_CHAN_DEFINE(response_channel, struct response_data,
NULL, NULL, ZBUS_OBSERVERS_EMPTY,
ZBUS_MSG_INIT(0),
IS_ENABLED(DOMAIN_B), /* Master on domain B */
1 /* Include on both domains */);

**Domain A** - Requester setup:

.. code-block:: c

#define DOMAIN_A 1
#include "common.h"

/* Set up proxy agent and add channels for forwarding */
#define IPC_INSTANCE DT_NODELABEL(ipc_node)
ZBUS_PROXY_AGENT_DEFINE(proxy_agent, ZBUS_MULTIDOMAIN_TYPE_IPC, IPC_INSTANCE);
ZBUS_PROXY_ADD_CHANNEL(proxy_agent, request_channel);

void response_listener_cb(const struct zbus_channel *chan) {
const struct response_data *resp = zbus_chan_const_msg(chan);
LOG_INF("Received response: ...");
}
ZBUS_LISTENER_DEFINE(response_listener, response_listener_cb);
ZBUS_CHAN_ADD_OBS(response_channel, response_listener, 0);

int main(void)
{
struct request_data req = { /* ... */ };
zbus_chan_pub(&request_channel, &req, K_MSEC(100));
return 0;
}

**Domain B** - Responder setup:

.. code-block:: c

#define DOMAIN_B 1
#include "common.h"

/* Set up proxy agent and add channels for forwarding */
#define IPC_INSTANCE DT_NODELABEL(ipc_node)
ZBUS_PROXY_AGENT_DEFINE(proxy_agent, ZBUS_MULTIDOMAIN_TYPE_IPC, IPC_INSTANCE);
ZBUS_PROXY_ADD_CHANNEL(proxy_agent, response_channel);

/* Observe requests and publish responses */
void request_listener_cb(const struct zbus_channel *chan) {
const struct request_data *req = zbus_chan_const_msg(chan);
struct response_data resp = { /* ... */ };
zbus_chan_pub(&response_channel, &resp, K_MSEC(100));
LOG_INF("Processed request ...");
}
ZBUS_LISTENER_DEFINE(request_listener, request_listener_cb);
ZBUS_CHAN_ADD_OBS(request_channel, request_listener, 0);

Samples
*******

Expand All @@ -901,7 +1031,8 @@ available:
observer registration feature;
* :zephyr:code-sample:`zbus-confirmed-channel` implements a way of implement confirmed channel only
with subscribers;
* :zephyr:code-sample:`zbus-benchmark` implements a benchmark with different combinations of inputs.
* :zephyr:code-sample:`zbus-ipc-forwarder` demonstrates multi-core communication using IPC forwarders;
* :zephyr:code-sample:`zbus-uart-forwarder` demonstrates inter-device communication using UART forwarders.

Suggested Uses
**************
Expand All @@ -913,6 +1044,13 @@ subscribers (if you need a thread) or listeners (if you need to be lean and fast
the listener, another asynchronous message processing mechanism (like :ref:`message queues
<message_queues_v2>`) may be necessary to retain the pending message until it gets processed.

For multi-domain scenarios, use zbus to enable communication across execution boundaries:

* **Multi-core systems**: Use IPC backend to coordinate between application and network processors,
or distribute workloads across multiple CPU cores.
* **Distributed devices**: Use UART backend to create distributed applications where multiple
connected devices participate in the same logical message bus over serial connections.

.. note::
ZBus can be used to transfer streams from the producer to the consumer. However, this can
increase zbus' communication latency. So maybe consider a Pipe a good alternative for this
Expand Down Expand Up @@ -954,6 +1092,30 @@ Related configuration options:
observers to statically allocate.
* :kconfig:option:`CONFIG_ZBUS_RUNTIME_OBSERVERS_NODE_ALLOC_NONE` use user-provided runtime
observers nodes;
* :kconfig:option:`CONFIG_ZBUS_MULTIDOMAIN` enable multi-domain communication support.

Multi-domain Configuration Options
==================================

* :kconfig:option:`CONFIG_ZBUS_MULTIDOMAIN_IPC` enable IPC backend for multi-domain communication;
* :kconfig:option:`CONFIG_ZBUS_MULTIDOMAIN_UART` enable UART backend for multi-domain communication;
* :kconfig:option:`CONFIG_ZBUS_MULTIDOMAIN_LOG_LEVEL` set the log level for multi-domain operations;
* :kconfig:option:`CONFIG_ZBUS_MULTIDOMAIN_MESSAGE_SIZE` maximum message size for multi-domain
channels;
* :kconfig:option:`CONFIG_ZBUS_MULTIDOMAIN_CHANNEL_NAME_SIZE` maximum size of channel names in
multi-domain communication;
* :kconfig:option:`CONFIG_ZBUS_MULTIDOMAIN_PROXY_STACK_SIZE` stack size for proxy agent threads;
* :kconfig:option:`CONFIG_ZBUS_MULTIDOMAIN_PROXY_PRIORITY` priority of proxy agent threads;
* :kconfig:option:`CONFIG_ZBUS_MULTIDOMAIN_SENT_MSG_POOL_SIZE` number of sent messages to track for
acknowledgments;
* :kconfig:option:`CONFIG_ZBUS_MULTIDOMAIN_MAX_TRANSMIT_ATTEMPTS` maximum retry attempts for message
transmission;
* :kconfig:option:`CONFIG_ZBUS_MULTIDOMAIN_SENT_MSG_ACK_TIMEOUT` initial acknowledgment timeout for
sent messages;
* :kconfig:option:`CONFIG_ZBUS_MULTIDOMAIN_SENT_MSG_ACK_TIMEOUT_MAX` maximum acknowledgment timeout
for exponential backoff;
* :kconfig:option:`CONFIG_ZBUS_MULTIDOMAIN_UART_BUF_COUNT` number of UART buffers for multi-domain
communication;

API Reference
*************
Expand Down
189 changes: 189 additions & 0 deletions include/zephyr/zbus/multidomain/zbus_multidomain.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
/*
* Copyright (c) 2025 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/

#ifndef ZEPHYR_INCLUDE_ZBUS_MULTIDOMAIN_H_
#define ZEPHYR_INCLUDE_ZBUS_MULTIDOMAIN_H_

#include <zephyr/kernel.h>
#include <zephyr/sys/crc.h>
#include <string.h>
#include <zephyr/net_buf.h>
#include <zephyr/sys/slist.h>
#include <zephyr/zbus/zbus.h>
#include <zephyr/zbus/multidomain/zbus_multidomain_types.h>

#if defined(CONFIG_ZBUS_MULTIDOMAIN_UART)
#include <zephyr/zbus/multidomain/zbus_multidomain_uart.h>
#endif
#if defined(CONFIG_ZBUS_MULTIDOMAIN_IPC)
#include <zephyr/zbus/multidomain/zbus_multidomain_ipc.h>
#endif

#ifdef __cplusplus
extern "C" {
#endif

/**
* @brief Zbus Multi-domain API
* @defgroup zbus_multidomain_apis Zbus Multi-domain APIs
* @ingroup zbus_apis
* @since 3.3.0
* @version 1.0.0
* @ingroup os_services
* @{
*/

/**
* @brief Structure for tracking sent messages awaiting acknowledgment.
*
* This structure is used internally by the proxy agent to keep track of messages
* that have been sent but not yet acknowledged. It contains a copy of the message,
* the number of transmit attempts, and a delayed work item for timeout handling.
*/
struct zbus_proxy_agent_tracked_msg {
/** Copy of the sent message */
struct zbus_proxy_agent_msg msg;

/** Pointer to the proxy agent configuration */
struct zbus_proxy_agent_config *config;

/** Number of transmit attempts made for this message */
uint8_t transmit_attempts;

/** Work item for handling acknowledgment timeout */
struct k_work_delayable work;
};

/**
* @brief Configuration structure for the proxy agent.
*
* This structure holds the configuration for a proxy agent, including its name,
* type, backend specific API, and backend specific configuration.
*/
struct zbus_proxy_agent_config {
/* The name of the proxy agent */
const char *name;

/* The type of the proxy agent */
enum zbus_multidomain_type type;

/* Pointer to the backend specific API */
const struct zbus_proxy_agent_api *api;

/* Pointer to the backend specific configuration */
void *backend_config;

/* Pool for tracking sent messages awaiting acknowledgment */
struct net_buf_pool *sent_msg_pool;

/* List of sent messages awaiting acknowledgment */
sys_slist_t sent_msg_list;
};

/**
* @brief Set up a proxy agent using the provided configuration.
*
* Starts the proxy agent thread and initializes the necessary resources.
*
* @note This macro sets up net_buf_pool for tracking sent messages, defines
* a zbus subscriber, and creates a thread for the proxy agent.
*
* @note the ZBUS_MULTIDOMAIN_SENT_MSG_POOL_SIZE configuration option
* must be set to a value greater than or equal to the maximum number of
* unacknowledged messages that can be in flight at any given time.
*
* @note The configuration options ZBUS_MULTIDOMAIN_PROXY_STACK_SIZE and
* ZBUS_MULTIDOMAIN_PROXY_PRIORITY define the stack size and priority of the
* proxy agent thread, respectively.
*
* @param _name The name of the proxy agent.
* @param _type The type of the proxy agent (enum zbus_multidomain_type)
* @param _nodeid The device node ID for the proxy agent.
*/
#define ZBUS_PROXY_AGENT_DEFINE(_name, _type, _nodeid) \
NET_BUF_POOL_DEFINE(_name##_sent_msg_pool, CONFIG_ZBUS_MULTIDOMAIN_SENT_MSG_POOL_SIZE, \
sizeof(struct zbus_proxy_agent_tracked_msg), sizeof(uint32_t), NULL); \
_ZBUS_GENERATE_BACKEND_CONFIG(_name, _type, _nodeid); \
struct zbus_proxy_agent_config _name##_config = { \
.name = #_name, \
.type = _type, \
.api = _ZBUS_GET_API(_type), \
.backend_config = _ZBUS_GET_CONFIG(_name, _type), \
.sent_msg_pool = &_name##_sent_msg_pool, \
}; \
ZBUS_MSG_SUBSCRIBER_DEFINE(_name##_subscriber); \
K_THREAD_DEFINE(_name##_thread_id, CONFIG_ZBUS_MULTIDOMAIN_PROXY_STACK_SIZE, \
zbus_proxy_agent_thread, &_name##_config, &_name##_subscriber, NULL, \
CONFIG_ZBUS_MULTIDOMAIN_PROXY_PRIORITY, 0, 0);

/**
* @brief Add a channel to the proxy agent.
*
* @param _name The name of the proxy agent.
* @param _chan The channel to be added.
*/
#define ZBUS_PROXY_ADD_CHANNEL(_name, _chan) ZBUS_CHAN_ADD_OBS(_chan, _name##_subscriber, 0);

/**
* @brief Thread function for the proxy agent.
*
* This function runs in a separate thread and continuously listens for messages
* on the zbus observer. It processes incoming messages and forwards them
* to the appropriate backend for sending.
*
* @param config Pointer to the configuration structure for the proxy agent.
* @param subscriber Pointer to the zbus observer that the proxy agent listens to.
* @return negative error code on failure.
*/
int zbus_proxy_agent_thread(struct zbus_proxy_agent_config *config,
const struct zbus_observer *subscriber);

/** @cond INTERNAL_HIDDEN */

/**
* @brief Macros to generate backend specific configurations for the proxy agent.
*
* This macro generates the backend specific configurations based on the type of
* the proxy agent.
*
* @param _name The name of the proxy agent.
* @param _type The type of the proxy agent (enum zbus_multidomain_type).
* @param _nodeid The device node ID for the proxy agent.
*
* @note This macro finds the matching backend configuration macro from the
* backend specific header files. Requires the backend specific header files to
* define the macros in the format `_ZBUS_GENERATE_BACKEND_CONFIG_<type>(_name, _nodeid)`.
*/
#define _ZBUS_GENERATE_BACKEND_CONFIG(_name, _type, _nodeid) \
_ZBUS_GENERATE_BACKEND_CONFIG_##_type(_name, _nodeid)

/**
* @brief Generic macros to get the API and configuration for the specified type of proxy agent.
*
* These macros are used to retrieve the API and configuration for the specified type of
* proxy agent. The type is specified as an argument to the macro.
*
* @param _type The type of the proxy agent (enum zbus_multidomain_type).
* @param _name The name of the proxy agent.
*
* @note These macros are used to retrieve the API and configuration for the specified type of
* proxy agent. Requires the backend specific header files to define the macros in the format
* `_ZBUS_GET_API_<type>()` and `_ZBUS_GET_CONFIG_<name, type>()`.
*/
#define _ZBUS_GET_API(_type) _ZBUS_GET_API_##_type()
#define _ZBUS_GET_CONFIG(_name, _type) _ZBUS_GET_CONFIG_##_type(_name)

/** @endcond */

/**
* @}
*/

#ifdef __cplusplus
}
#endif

#endif /* ZEPHYR_INCLUDE_ZBUS_MULTIDOMAIN_H_ */
Loading