Skip to content

Commit a5f2439

Browse files
doki-nordiclstnl
authored andcommitted
ipc_service: icmsg: Add "unbound" functionality
In some cases, CPUs that may need to reset or temporary stop communication. This commit adds "unbound" functionality that provides a callback to IPC service user when connection was interrupted for some reason, e.g. expected or unexpected CPU reset, closing the endpoint. The "unbound" callback is optional to implement by endpoints. This commit implements it in the ICMsg backend. Signed-off-by: Dominik Kilian <[email protected]>
1 parent 6908622 commit a5f2439

File tree

13 files changed

+634
-233
lines changed

13 files changed

+634
-233
lines changed

doc/services/ipc/ipc_service/backends/ipc_service_icmsg.rst

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,8 +83,7 @@ connected through the IPC instance:
8383
memory.
8484
#. It then sends a signal to the other domain or CPU, informing that the data
8585
has been written. Sending the signal to the other domain or CPU is repeated
86-
with timeout specified by
87-
:kconfig:option:`CONFIG_IPC_SERVICE_ICMSG_BOND_NOTIFY_REPEAT_TO_MS` option.
86+
with timeout.
8887
#. When the signal from the other domain or CPU is received, the magic number
8988
is read from ``rx-region``. If it is correct, the bonding process is finished
9089
and the backend informs the application by calling

dts/bindings/ipc/zephyr,ipc-icmsg.yaml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,21 @@ properties:
2121
required: true
2222
type: phandle
2323

24+
unbound:
25+
type: string
26+
enum:
27+
- disable
28+
- enable
29+
- detect
30+
default: disable
31+
description: |
32+
Select unbound() callback mode. The callback can be enabled or disabled with the
33+
"enable" and "disable" option respectively. This functionality requires session
34+
number handshake procedure on both sides, so you cannot set "enable" on one side
35+
and "disable" on the other. The "detect" mode detects if the remote side
36+
supports handshake procedure and adjusts its behavior accordingly. The
37+
"detect" mode is possible only if "dcache-alignment" is at least 8 bytes.
38+
2439
dcache-alignment:
2540
type: int
2641
description: |

include/zephyr/ipc/icmsg.h

Lines changed: 48 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,14 +27,58 @@ extern "C" {
2727
*/
2828

2929
enum icmsg_state {
30+
/** Instance is not initialized yet. In this state: sending will fail, opening allowed.
31+
*/
3032
ICMSG_STATE_OFF,
31-
ICMSG_STATE_BUSY,
32-
ICMSG_STATE_READY,
33+
34+
/** Instance is initializing without session handshake. In this state: sending will fail,
35+
* opening will fail.
36+
*/
37+
ICMSG_STATE_INITIALIZING_SID_DISABLED,
38+
39+
/** Instance is initializing with session handshake. It is waiting for remote to acknowledge
40+
* local session id. In this state: sending will fail, opening is allowed (local session id
41+
* will change, so the remote may get unbound() callback).
42+
*/
43+
ICMSG_STATE_INITIALIZING_SID_ENABLED,
44+
45+
/** Instance is initializing with detection of session handshake support on remote side.
46+
* It is waiting for remote to acknowledge local session id or to send magic bytes.
47+
* In this state: sending will fail, opening is allowed (local session id
48+
* will change, so the remote may get unbound() callback if it supports it).
49+
*/
50+
ICMSG_STATE_INITIALIZING_SID_DETECT,
51+
52+
/** Instance was closed on remote side. The unbound() callback was send on local side.
53+
* In this state: sending will be silently discarded (there may be outdated sends),
54+
* opening is allowed.
55+
*/
56+
ICMSG_STATE_DISCONNECTED,
57+
58+
/* Connected states must be at the end. */
59+
60+
/** Instance is connected without session handshake support. In this state: sending will be
61+
* successful, opening will fail.
62+
*/
63+
ICMSG_STATE_CONNECTED_SID_DISABLED,
64+
65+
/** Instance is connected with session handshake support. In this state: sending will be
66+
* successful, opening is allowed (session will change and remote will get unbound()
67+
* callback).
68+
*/
69+
ICMSG_STATE_CONNECTED_SID_ENABLED,
70+
};
71+
72+
enum icmsg_unbound_mode {
73+
ICMSG_UNBOUND_MODE_DISABLE = ICMSG_STATE_INITIALIZING_SID_DISABLED,
74+
ICMSG_UNBOUND_MODE_ENABLE = ICMSG_STATE_INITIALIZING_SID_ENABLED,
75+
ICMSG_UNBOUND_MODE_DETECT = ICMSG_STATE_INITIALIZING_SID_DETECT,
3376
};
3477

3578
struct icmsg_config_t {
3679
struct mbox_dt_spec mbox_tx;
3780
struct mbox_dt_spec mbox_rx;
81+
enum icmsg_unbound_mode unbound_mode;
3882
};
3983

4084
struct icmsg_data_t {
@@ -52,9 +96,10 @@ struct icmsg_data_t {
5296
/* General */
5397
const struct icmsg_config_t *cfg;
5498
#ifdef CONFIG_MULTITHREADING
55-
struct k_work_delayable notify_work;
5699
struct k_work mbox_work;
57100
#endif
101+
uint16_t remote_sid;
102+
uint16_t local_sid;
58103
atomic_t state;
59104
};
60105

include/zephyr/ipc/ipc_service.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,21 @@ struct ipc_service_cb {
151151
*/
152152
void (*bound)(void *priv);
153153

154+
/** @brief The endpoint unbound by the remote.
155+
*
156+
* This callback is called when the endpoint binding is removed. It may happen on
157+
* different reasons, e.g. when the remote deregistered the endpoint, connection was
158+
* lost, or remote CPU got reset.
159+
*
160+
* You may want to do some cleanup, resetting, e.t.c. and after that if you want to bound
161+
* again, you can register the endpoint. When the remote becomes available again and it
162+
* also registers the endpoint, the binding will be reestablished and the `bound()`
163+
* callback will be called.
164+
*
165+
* @param[in] priv Private user data.
166+
*/
167+
void (*unbound)(void *priv);
168+
154169
/** @brief New packet arrived.
155170
*
156171
* This callback is called when new data is received.

include/zephyr/ipc/pbuf.h

Lines changed: 69 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -47,20 +47,23 @@ extern "C" {
4747
* The structure contains configuration data.
4848
*/
4949
struct pbuf_cfg {
50-
volatile uint32_t *rd_idx_loc; /* Address of the variable holding
51-
* index value of the first valid byte
52-
* in data[].
53-
*/
54-
volatile uint32_t *wr_idx_loc; /* Address of the variable holding
55-
* index value of the first free byte
56-
* in data[].
57-
*/
58-
uint32_t dcache_alignment; /* CPU data cache line size in bytes.
59-
* Used for validation - TODO: To be
60-
* replaced by flags.
61-
*/
62-
uint32_t len; /* Length of data[] in bytes. */
63-
uint8_t *data_loc; /* Location of the data[]. */
50+
volatile uint32_t *rd_idx_loc; /* Address of the variable holding
51+
* index value of the first valid byte
52+
* in data[].
53+
*/
54+
volatile uint32_t *handshake_loc;/* Address of the variable holding
55+
* handshake information.
56+
*/
57+
volatile uint32_t *wr_idx_loc; /* Address of the variable holding
58+
* index value of the first free byte
59+
* in data[].
60+
*/
61+
uint32_t dcache_alignment; /* CPU data cache line size in bytes.
62+
* Used for validation - TODO: To be
63+
* replaced by flags.
64+
*/
65+
uint32_t len; /* Length of data[] in bytes. */
66+
uint8_t *data_loc; /* Location of the data[]. */
6467
};
6568

6669
/**
@@ -111,16 +114,21 @@ struct pbuf {
111114
* @param mem_addr Memory address for pbuf.
112115
* @param size Size of the memory.
113116
* @param dcache_align Data cache alignment.
117+
* @param use_handshake Add handshake word inside shared memory that can be access with
118+
* @ref pbuf_handshake_read and @ref pbuf_handshake_write.
114119
*/
115-
#define PBUF_CFG_INIT(mem_addr, size, dcache_align) \
120+
#define PBUF_CFG_INIT(mem_addr, size, dcache_align, use_handshake) \
116121
{ \
117122
.rd_idx_loc = (uint32_t *)(mem_addr), \
118-
.wr_idx_loc = (uint32_t *)((uint8_t *)(mem_addr) + \
119-
MAX(dcache_align, _PBUF_IDX_SIZE)), \
123+
.handshake_loc = use_handshake ? (uint32_t *)((uint8_t *)(mem_addr) + \
124+
_PBUF_IDX_SIZE) : NULL, \
125+
.wr_idx_loc = (uint32_t *)((uint8_t *)(mem_addr) + MAX(dcache_align, \
126+
(use_handshake ? 2 : 1) * _PBUF_IDX_SIZE)), \
120127
.data_loc = (uint8_t *)((uint8_t *)(mem_addr) + \
121-
MAX(dcache_align, _PBUF_IDX_SIZE) + _PBUF_IDX_SIZE), \
122-
.len = (uint32_t)((uint32_t)(size) - MAX(dcache_align, _PBUF_IDX_SIZE) - \
123-
_PBUF_IDX_SIZE), \
128+
MAX(dcache_align, (use_handshake ? 2 : 1) * \
129+
_PBUF_IDX_SIZE) + _PBUF_IDX_SIZE), \
130+
.len = (uint32_t)((uint32_t)(size) - MAX(dcache_align, \
131+
(use_handshake ? 2 : 1) * _PBUF_IDX_SIZE) - _PBUF_IDX_SIZE), \
124132
.dcache_alignment = (dcache_align), \
125133
}
126134

@@ -140,9 +148,11 @@ struct pbuf {
140148
* @param name Name of the pbuf.
141149
* @param mem_addr Memory address for pbuf.
142150
* @param size Size of the memory.
143-
* @param dcache_align Data cache line size.
151+
* @param dcache_align Data cache line size.
152+
* @param use_handshake Add handshake word inside shared memory that can be access with
153+
* @ref pbuf_handshake_read and @ref pbuf_handshake_write.
144154
*/
145-
#define PBUF_DEFINE(name, mem_addr, size, dcache_align) \
155+
#define PBUF_DEFINE(name, mem_addr, size, dcache_align, use_handshake, compatibility) \
146156
BUILD_ASSERT(dcache_align >= 0, \
147157
"Cache line size must be non negative."); \
148158
BUILD_ASSERT((size) > 0 && IS_PTR_ALIGNED_BYTES(size, _PBUF_IDX_SIZE), \
@@ -151,8 +161,10 @@ struct pbuf {
151161
"Misaligned memory."); \
152162
BUILD_ASSERT(size >= (MAX(dcache_align, _PBUF_IDX_SIZE) + _PBUF_IDX_SIZE + \
153163
_PBUF_MIN_DATA_LEN), "Insufficient size."); \
164+
BUILD_ASSERT(!(compatibility) || (dcache_align) >= 8, \
165+
"Data cache alignment must be at least 8 if compatibility is enabled.");\
154166
static PBUF_MAYBE_CONST struct pbuf_cfg cfg_##name = \
155-
PBUF_CFG_INIT(mem_addr, size, dcache_align); \
167+
PBUF_CFG_INIT(mem_addr, size, dcache_align, use_handshake); \
156168
static struct pbuf name = { \
157169
.cfg = &cfg_##name, \
158170
}
@@ -223,6 +235,40 @@ int pbuf_write(struct pbuf *pb, const char *buf, uint16_t len);
223235
*/
224236
int pbuf_read(struct pbuf *pb, char *buf, uint16_t len);
225237

238+
/**
239+
* @brief Read handshake word from pbuf.
240+
*
241+
* The pb must be defined with "PBUF_DEFINE" with "use_handshake" set.
242+
*
243+
* @param pb A buffer from which data will be read.
244+
* @retval uint32_t The handshake word value.
245+
*/
246+
uint32_t pbuf_handshake_read(struct pbuf *pb);
247+
248+
/**
249+
* @brief Write handshake word to pbuf.
250+
*
251+
* The pb must be defined with "PBUF_DEFINE" with "use_handshake" set.
252+
*
253+
* @param pb A buffer to which data will be written.
254+
* @param value A handshake value.
255+
*/
256+
void pbuf_handshake_write(struct pbuf *pb, uint32_t value);
257+
258+
/**
259+
* @brief Get first buffer from pbuf.
260+
*
261+
* This function retrieves buffer located at the beginning of queue.
262+
* It will be continuous block since it is the first buffer.
263+
*
264+
* @param pb A buffer from which data will be read.
265+
* @param[out] buf A pointer to output pointer to the date of the first buffer.
266+
* @param[out] len A pointer to output length the first buffer.
267+
* @retval 0 on success.
268+
* -EINVAL when there is no buffer at the beginning of queue.
269+
*/
270+
int pbuf_get_initial_buf(struct pbuf *pb, volatile char **buf, uint16_t *len);
271+
226272
/**
227273
* @}
228274
*/

samples/subsys/ipc/ipc_service/icmsg/src/main.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,11 @@ static void ep_bound(void *priv)
4040
LOG_INF("Ep bounded");
4141
}
4242

43+
static void ep_unbound(void *priv)
44+
{
45+
LOG_INF("Ep unbounded");
46+
}
47+
4348
static void ep_recv(const void *data, size_t len, void *priv)
4449
{
4550
#if defined(CONFIG_ASSERT)
@@ -68,6 +73,11 @@ static void ep_recv(const void *data, size_t len, void *priv)
6873
}
6974
}
7075

76+
static void ep_error(const char *message, void *priv)
77+
{
78+
LOG_ERR("ICMsg error: %s", message);
79+
}
80+
7181
static int send_for_time(struct ipc_ept *ep, const int64_t sending_time_ms)
7282
{
7383
struct data_packet msg = {.data[0] = 'a'};
@@ -123,7 +133,9 @@ static int send_for_time(struct ipc_ept *ep, const int64_t sending_time_ms)
123133
static struct ipc_ept_cfg ep_cfg = {
124134
.cb = {
125135
.bound = ep_bound,
136+
.unbound = ep_unbound,
126137
.received = ep_recv,
138+
.error = ep_error,
127139
},
128140
};
129141

subsys/ipc/ipc_service/backends/ipc_icbmsg.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1402,11 +1402,11 @@ const static struct ipc_service_backend backend_ops = {
14021402
PBUF_DEFINE(tx_icbmsg_pb_##i, \
14031403
GET_MEM_ADDR_INST(i, tx), \
14041404
GET_ICMSG_SIZE_INST(i, tx, rx), \
1405-
GET_CACHE_ALIGNMENT(i)); \
1405+
GET_CACHE_ALIGNMENT(i), 0, 0); \
14061406
PBUF_DEFINE(rx_icbmsg_pb_##i, \
14071407
GET_MEM_ADDR_INST(i, rx), \
14081408
GET_ICMSG_SIZE_INST(i, rx, tx), \
1409-
GET_CACHE_ALIGNMENT(i)); \
1409+
GET_CACHE_ALIGNMENT(i), 0, 0); \
14101410
static struct backend_data backend_data_##i = { \
14111411
.control_data = { \
14121412
.tx_pb = &tx_icbmsg_pb_##i, \
@@ -1418,6 +1418,7 @@ const static struct ipc_service_backend backend_ops = {
14181418
.control_config = { \
14191419
.mbox_tx = MBOX_DT_SPEC_INST_GET(i, tx), \
14201420
.mbox_rx = MBOX_DT_SPEC_INST_GET(i, rx), \
1421+
.unbound_mode = ICMSG_UNBOUND_MODE_DISABLE, \
14211422
}, \
14221423
.tx = { \
14231424
.blocks_ptr = (uint8_t *)GET_BLOCKS_ADDR_INST(i, tx, rx), \

subsys/ipc/ipc_service/backends/ipc_icmsg.c

Lines changed: 46 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -54,33 +54,52 @@ static int backend_init(const struct device *instance)
5454
return 0;
5555
}
5656

57-
#define DEFINE_BACKEND_DEVICE(i) \
58-
static const struct icmsg_config_t backend_config_##i = { \
59-
.mbox_tx = MBOX_DT_SPEC_INST_GET(i, tx), \
60-
.mbox_rx = MBOX_DT_SPEC_INST_GET(i, rx), \
61-
}; \
62-
\
63-
PBUF_DEFINE(tx_pb_##i, \
64-
DT_REG_ADDR(DT_INST_PHANDLE(i, tx_region)), \
65-
DT_REG_SIZE(DT_INST_PHANDLE(i, tx_region)), \
66-
DT_INST_PROP_OR(i, dcache_alignment, 0)); \
67-
PBUF_DEFINE(rx_pb_##i, \
68-
DT_REG_ADDR(DT_INST_PHANDLE(i, rx_region)), \
69-
DT_REG_SIZE(DT_INST_PHANDLE(i, rx_region)), \
70-
DT_INST_PROP_OR(i, dcache_alignment, 0)); \
71-
\
72-
static struct icmsg_data_t backend_data_##i = { \
73-
.tx_pb = &tx_pb_##i, \
74-
.rx_pb = &rx_pb_##i, \
75-
}; \
76-
\
77-
DEVICE_DT_INST_DEFINE(i, \
78-
&backend_init, \
79-
NULL, \
80-
&backend_data_##i, \
81-
&backend_config_##i, \
82-
POST_KERNEL, \
83-
CONFIG_IPC_SERVICE_REG_BACKEND_PRIORITY, \
57+
#define UNBOUND_MODE(i) CONCAT(ICMSG_UNBOUND_MODE_, DT_INST_STRING_UPPER_TOKEN(i, unbound))
58+
59+
#define DEFINE_BACKEND_DEVICE(i) \
60+
static const struct icmsg_config_t backend_config_##i = { \
61+
.mbox_tx = MBOX_DT_SPEC_INST_GET(i, tx), \
62+
.mbox_rx = MBOX_DT_SPEC_INST_GET(i, rx), \
63+
.unbound_mode = UNBOUND_MODE(i), \
64+
}; \
65+
\
66+
PBUF_DEFINE(tx_pb_##i, \
67+
DT_REG_ADDR(DT_INST_PHANDLE(i, tx_region)), \
68+
DT_REG_SIZE(DT_INST_PHANDLE(i, tx_region)), \
69+
DT_INST_PROP_OR(i, dcache_alignment, 0), \
70+
UNBOUND_MODE(i) != ICMSG_UNBOUND_MODE_DISABLE, \
71+
UNBOUND_MODE(i) == ICMSG_UNBOUND_MODE_DETECT); \
72+
PBUF_DEFINE(rx_pb_##i, \
73+
DT_REG_ADDR(DT_INST_PHANDLE(i, rx_region)), \
74+
DT_REG_SIZE(DT_INST_PHANDLE(i, rx_region)), \
75+
DT_INST_PROP_OR(i, dcache_alignment, 0), \
76+
UNBOUND_MODE(i) != ICMSG_UNBOUND_MODE_DISABLE, \
77+
UNBOUND_MODE(i) == ICMSG_UNBOUND_MODE_DETECT); \
78+
\
79+
BUILD_ASSERT(UNBOUND_MODE(i) != ICMSG_UNBOUND_MODE_DISABLE || \
80+
IS_ENABLED(CONFIG_IPC_SERVICE_ICMSG_UNBOUND_DISABLED_ALLOWED), \
81+
"Unbound mode \"disabled\" is was forbidden in Kconfig."); \
82+
\
83+
BUILD_ASSERT(UNBOUND_MODE(i) != ICMSG_UNBOUND_MODE_ENABLE || \
84+
IS_ENABLED(CONFIG_IPC_SERVICE_ICMSG_UNBOUND_ENABLED_ALLOWED), \
85+
"Unbound mode \"enabled\" is was forbidden in Kconfig."); \
86+
\
87+
BUILD_ASSERT(UNBOUND_MODE(i) != ICMSG_UNBOUND_MODE_DETECT || \
88+
IS_ENABLED(CONFIG_IPC_SERVICE_ICMSG_UNBOUND_DETECT_ALLOWED), \
89+
"Unbound mode \"detect\" is was forbidden in Kconfig."); \
90+
\
91+
static struct icmsg_data_t backend_data_##i = { \
92+
.tx_pb = &tx_pb_##i, \
93+
.rx_pb = &rx_pb_##i, \
94+
}; \
95+
\
96+
DEVICE_DT_INST_DEFINE(i, \
97+
&backend_init, \
98+
NULL, \
99+
&backend_data_##i, \
100+
&backend_config_##i, \
101+
POST_KERNEL, \
102+
CONFIG_IPC_SERVICE_REG_BACKEND_PRIORITY, \
84103
&backend_ops);
85104

86105
DT_INST_FOREACH_STATUS_OKAY(DEFINE_BACKEND_DEVICE)

0 commit comments

Comments
 (0)