diff --git a/nrf_rpc/Kconfig b/nrf_rpc/Kconfig index 37cebbe3d7..ccd91e73f7 100644 --- a/nrf_rpc/Kconfig +++ b/nrf_rpc/Kconfig @@ -40,13 +40,11 @@ config NRF_RPC_CMD_CTX_POOL_SIZE threads in both local and remote pool. config NRF_RPC_GROUP_INIT_WAIT_TIME - int "Group initialization timeout" + int "Group initialization timeout in milliseconds" default 1000 - range 10 5000 help - The time period to wait for the remote cores to send group init responses with - destination group ids. - The time is given in miliseconds. + The number of milliseconds to wait for the remote cores to send group + init packets with destination group IDs. Set to -1 to wait forever. config NRF_RPC_THREAD_POOL_SIZE int "Number of threads in local thread pool" diff --git a/nrf_rpc/include/nrf_rpc.h b/nrf_rpc/include/nrf_rpc.h index af4810ae1d..7693858583 100644 --- a/nrf_rpc/include/nrf_rpc.h +++ b/nrf_rpc/include/nrf_rpc.h @@ -32,8 +32,18 @@ extern "C" { /** @brief Special value to indicate that ID is unknown or irrelevant. */ #define NRF_RPC_ID_UNKNOWN 0xFF +/** @brief Flag indicating that the group does not block on initialization. */ +#define NRF_RPC_FLAGS_WAIT_ON_INIT BIT(0) + +/** @brief Flag indicating that the peer must initiate group binding. */ +#define NRF_RPC_FLAGS_INITIATOR BIT(1) + +/** @brief Helper macro for conditional flag initialization. */ +#define NRF_RPC_FLAG_COND(_cond, _flag) ((_cond) ? (_flag) : 0UL) + /* Forward declaration. */ struct nrf_rpc_err_report; +struct nrf_rpc_group; /** @brief Type of packet. * @@ -96,6 +106,14 @@ typedef void (*nrf_rpc_ack_handler_t)(uint8_t id, void *handler_data); */ typedef void (*nrf_rpc_err_handler_t)(const struct nrf_rpc_err_report *report); +/** @brief Callback called when the command group is bound. + * + * @see NRF_RPC_GROUP_DEFINE + * + * @param group Pointer to the bound group. + */ +typedef void (*nrf_rpc_group_bound_handler_t)(const struct nrf_rpc_group *group); + /* Structure used internally to define registered command or event decoder. */ struct _nrf_rpc_decoder { uint8_t id; @@ -127,6 +145,8 @@ struct nrf_rpc_group { void *ack_handler_data; const char *strid; nrf_rpc_err_handler_t err_handler; + nrf_rpc_group_bound_handler_t bound_handler; + const uint32_t flags; }; /** @brief Error report. @@ -151,6 +171,58 @@ struct nrf_rpc_err_report { enum nrf_rpc_packet_type packet_type; }; +/** @brief Internal macro for parametrizing nrf_rpc groups. + * + * @param _name Symbol name of the group. + * @param _strid String containing a unique identifier of the group. Naming + * conventions are the same as with C symbol name. Groups on local + * and remote must have the same unique identifier. + * @param _transport Group transport. It is used by group to communicate with + * a remote processor. + * @param _ack_handler Handler of type @ref nrf_rpc_ack_handler_t, called when + * ACK was received after event completion. Can be NULL if + * the group does not want to receive ACK notifications. + * @param _ack_data Opaque pointer for the `_ack_handler`. + * @param _err_handler Handler of type @ref nrf_rpc_err_handler_t, called when + * an error occurred in context of this group. Can be NULL if + * the group does not want to receive error notifications. + * @param _bound_handler Handler of type @ref nrf_rpc_group_bound_handler_t, called + * when the group was successfuly bound. The callback is called + * each time the remote peer binds to the group. This can be used + * to detect a remote peer reset and can be used by the application + * to reset the local state. + * @param _wait_on_init The group does not block until it is bound. + * @param _initiator The group is the initiator. + */ +#define NRF_RPC_GROUP_DEFINE_INTERNAL__(_name, _strid, _transport, _ack_handler, \ + _ack_data, _err_handler, _bound_handler, \ + _wait_on_init, _initiator) \ + NRF_RPC_AUTO_ARR(NRF_RPC_CONCAT(_name, _cmd_array), \ + "cmd_" NRF_RPC_STRINGIFY(_name)); \ + NRF_RPC_AUTO_ARR(NRF_RPC_CONCAT(_name, _evt_array), \ + "evt_" NRF_RPC_STRINGIFY(_name)); \ + \ + static struct nrf_rpc_group_data NRF_RPC_CONCAT(_name, _group_data) = { \ + .src_group_id = NRF_RPC_ID_UNKNOWN, \ + .dst_group_id = NRF_RPC_ID_UNKNOWN, \ + .transport_initialized = false, \ + }; \ + \ + NRF_RPC_AUTO_ARR_ITEM(const struct nrf_rpc_group, _name, "grp", \ + _strid) = { \ + .cmd_array = &NRF_RPC_CONCAT(_name, _cmd_array), \ + .evt_array = &NRF_RPC_CONCAT(_name, _evt_array), \ + .data = &NRF_RPC_CONCAT(_name, _group_data), \ + .ack_handler = _ack_handler, \ + .ack_handler_data = _ack_data, \ + .strid = _strid, \ + .transport = _transport, \ + .err_handler = _err_handler, \ + .bound_handler = _bound_handler, \ + .flags = NRF_RPC_FLAG_COND(_wait_on_init, NRF_RPC_FLAGS_WAIT_ON_INIT) \ + | NRF_RPC_FLAG_COND(_initiator, NRF_RPC_FLAGS_INITIATOR), \ + } + /** @brief Define a group of commands and events. * * @param _name Symbol name of the group. @@ -168,28 +240,45 @@ struct nrf_rpc_err_report { */ #define NRF_RPC_GROUP_DEFINE(_name, _strid, _transport, _ack_handler, _ack_data, \ _err_handler) \ - NRF_RPC_AUTO_ARR(NRF_RPC_CONCAT(_name, _cmd_array), \ - "cmd_" NRF_RPC_STRINGIFY(_name)); \ - NRF_RPC_AUTO_ARR(NRF_RPC_CONCAT(_name, _evt_array), \ - "evt_" NRF_RPC_STRINGIFY(_name)); \ - \ - static struct nrf_rpc_group_data NRF_RPC_CONCAT(_name, _group_data) = { \ - .src_group_id = NRF_RPC_ID_UNKNOWN, \ - .dst_group_id = NRF_RPC_ID_UNKNOWN, \ - .transport_initialized = false, \ - }; \ - \ - NRF_RPC_AUTO_ARR_ITEM(const struct nrf_rpc_group, _name, "grp", \ - _strid) = { \ - .cmd_array = &NRF_RPC_CONCAT(_name, _cmd_array), \ - .evt_array = &NRF_RPC_CONCAT(_name, _evt_array), \ - .data = &NRF_RPC_CONCAT(_name, _group_data), \ - .ack_handler = _ack_handler, \ - .ack_handler_data = _ack_data, \ - .strid = _strid, \ - .transport = _transport, \ - .err_handler = _err_handler, \ - } + NRF_RPC_GROUP_DEFINE_INTERNAL__(_name, _strid, _transport, _ack_handler, \ + _ack_data, _err_handler, NULL, true, \ + true) \ + +/** @brief Define a non-blocking group of commands and events. + * + * The NOWAIT group does not block the @ref nrf_rpc_init until binding completion. + * When the NOWAIT group is bound, the @ref nrf_rpc_group_bound_handler_t will + * be called. The NOWAIT group can have two roles: initiator and follower. + * The initiator initiates the endpoint binding. The follower waits for the initiator + * to bind the group. Both peers can be initiators, but there must always be at least + * one on either side of the IPC channel. + * + * @param _name Symbol name of the group. + * @param _strid String containing a unique identifier of the group. Naming + * conventions are the same as with C symbol name. Groups on local + * and remote must have the same unique identifier. + * @param _transport Group transport. It is used by group to communicate with + * a remote processor. + * @param _ack_handler Handler of type @ref nrf_rpc_ack_handler_t, called when + * ACK was received after event completion. Can be NULL if + * the group does not want to receive ACK notifications. + * @param _ack_data Opaque pointer for the `_ack_handler`. + * @param _err_handler Handler of type @ref nrf_rpc_err_handler_t, called when + * an error occurred in context of this group. Can be NULL if + * the group does not want to receive error notifications. + * @param _bound_handler Handler of type @ref nrf_rpc_group_bound_handler_t, called + * when the group was successfuly bound. The callback is called + * each time the remote peer binds to the group. This can be used + * to detect a remote peer reset and can be used by the application + * to reset the local state. + * @param _initiator The group is the initiator. + */ +#define NRF_RPC_GROUP_DEFINE_NOWAIT(_name, _strid, _transport, _ack_handler, \ + _ack_data, _err_handler, _bound_handler, \ + _initiator) \ + NRF_RPC_GROUP_DEFINE_INTERNAL__(_name, _strid, _transport, _ack_handler, \ + _ack_data, _err_handler, _bound_handler, \ + false, _initiator) \ /** @brief Extern declaration of a group. * diff --git a/nrf_rpc/nrf_rpc.c b/nrf_rpc/nrf_rpc.c index c092403fc9..b7dae57793 100644 --- a/nrf_rpc/nrf_rpc.c +++ b/nrf_rpc/nrf_rpc.c @@ -92,6 +92,7 @@ static struct nrf_rpc_os_event groups_init_event; /* Number of groups */ static uint8_t group_count; +static uint8_t waiting_group_count; static uint8_t initialized_group_count; @@ -297,7 +298,7 @@ static int group_init_send(const struct nrf_rpc_group *group) hdr.type = NRF_RPC_PACKET_TYPE_INIT; hdr.id = 0; hdr.src_group_id = group->data->src_group_id; - hdr.dst_group_id = NRF_RPC_ID_UNKNOWN; + hdr.dst_group_id = group->data->dst_group_id; len = strlen(group->strid) + NRF_RPC_PROTOCOL_VERSION_FIELD_SIZE; @@ -344,7 +345,7 @@ static inline bool packet_validate(const uint8_t *packet) static int transport_init(nrf_rpc_tr_receive_handler_t receive_cb) { - int err; + int err = 0; void *iter; const struct nrf_rpc_group *group; @@ -369,17 +370,25 @@ static int transport_init(nrf_rpc_tr_receive_handler_t receive_cb) } group->data->transport_initialized = true; - err = group_init_send(group); - if (err) { - NRF_RPC_ERR("Failed to send group init packet for group id: %d strid: %s", - data->src_group_id, group->strid); - continue; + + if (group->flags & NRF_RPC_FLAGS_INITIATOR) { + err = group_init_send(group); + if (err) { + NRF_RPC_ERR("Failed to send group init packet for group id: %d strid: %s err: %d", + data->src_group_id, group->strid, err); + continue; + } } } - err = nrf_rpc_os_event_wait(&groups_init_event, CONFIG_NRF_RPC_GROUP_INIT_WAIT_TIME); - if (err) { - NRF_RPC_ERR("Not all groups are ready to use."); + /* Group initialization errors are not propagated to the caller. */ + err = 0; + + if (waiting_group_count > 0) { + err = nrf_rpc_os_event_wait(&groups_init_event, CONFIG_NRF_RPC_GROUP_INIT_WAIT_TIME); + if (err) { + NRF_RPC_ERR("Not all groups are ready to use."); + } } return err; @@ -518,8 +527,7 @@ static bool protocol_version_check(const struct init_packet_data *init_data) return false; } -static const struct nrf_rpc_group *remote_group_init(uint8_t src_group_id, const char *strid, - size_t strid_len) +static const struct nrf_rpc_group *group_from_strid(const char *strid, size_t strid_len) { void *iter; const struct nrf_rpc_group *group; @@ -528,16 +536,10 @@ static const struct nrf_rpc_group *remote_group_init(uint8_t src_group_id, const const struct nrf_rpc_group)) { if ((strid_len == strlen(group->strid)) && (memcmp(strid, group->strid, strid_len) == 0)) { - group->data->dst_group_id = src_group_id; - NRF_RPC_DBG("Found corresponding local group. Remote id: %d, Local id: %d", - src_group_id, group->data->dst_group_id); - return group; } } - NRF_RPC_ERR("Remote group does not match local group"); - return NULL; } @@ -578,6 +580,9 @@ static int init_packet_handle(struct header *hdr, const struct nrf_rpc_group **g { int err; struct init_packet_data init_data = {0}; + struct nrf_rpc_group_data *group_data; + bool first_init; + bool wait_on_init; *group = NULL; @@ -597,19 +602,48 @@ static int init_packet_handle(struct header *hdr, const struct nrf_rpc_group **g } /* Check for the corresponding local group and initialize it. */ - *group = remote_group_init(hdr->src_group_id, init_data.strid, init_data.strid_len); + *group = group_from_strid(init_data.strid, init_data.strid_len); if (*group == NULL) { + NRF_RPC_ERR("Remote group does not match local group"); NRF_RPC_ASSERT(false); return -NRF_EFAULT; } - initialized_group_count++; - if (initialized_group_count == group_count) { - /* All group are initialized. */ - nrf_rpc_os_event_set(&groups_init_event); + group_data = (*group)->data; + first_init = group_data->dst_group_id == NRF_RPC_ID_UNKNOWN; + group_data->dst_group_id = hdr->src_group_id; + wait_on_init = (*group)->flags & NRF_RPC_FLAGS_WAIT_ON_INIT; + nrf_rpc_group_bound_handler_t bound_handler = (*group)->bound_handler; + + NRF_RPC_DBG("Found corresponding local group. Remote id: %d, Local id: %d", + hdr->src_group_id, group_data->src_group_id); + + if (bound_handler != NULL) { + bound_handler(*group); } - return 0; + if (first_init && wait_on_init) { + ++initialized_group_count; + + if (initialized_group_count == waiting_group_count) { + /* All groups are initialized. */ + nrf_rpc_os_event_set(&groups_init_event); + } + } + + if (hdr->dst_group_id == NRF_RPC_ID_UNKNOWN) { + /* + * If remote processor does not know our group id, send an init packet back, + * since it might have missed our original init packet. + */ + err = group_init_send(*group); + if (err) { + NRF_RPC_ERR("Failed to send group init packet for group id: %d strid: %s", + group_data->src_group_id, (**group).strid); + } + } + + return err; } /* Callback from transport layer that handles incoming. */ @@ -966,6 +1000,7 @@ int nrf_rpc_init(nrf_rpc_err_handler_t err_handler) void *iter; const struct nrf_rpc_group *group; uint8_t group_id = 0; + uint8_t wait_count = 0; NRF_RPC_DBG("Initializing nRF RPC module"); @@ -986,9 +1021,13 @@ int nrf_rpc_init(nrf_rpc_err_handler_t err_handler) NRF_RPC_DBG("Group '%s' has id %d", group->strid, group_id); data->src_group_id = group_id; group_id++; + if (group->flags & NRF_RPC_FLAGS_WAIT_ON_INIT) { + wait_count++; + } } group_count = group_id; + waiting_group_count = wait_count; memset(&cmd_ctx_pool, 0, sizeof(cmd_ctx_pool));