diff --git a/include/net/ot_rpc.h b/include/net/ot_rpc.h new file mode 100644 index 000000000000..d9a80496de99 --- /dev/null +++ b/include/net/ot_rpc.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2025 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +#ifndef OT_RPC_H_ +#define OT_RPC_H_ + +#include + +/** + * @file + * @defgroup ot_rpc OpenThread over RPC API additions + * @{ + * @brief API additions for OpenThread over RPC. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** @brief Set factory assigned IEEE EUI64 on the remote device. + * + * The function issues an nRF RPC command that configures the factory-assigned IEEE EUI64, + * which can then be retrieved using the @c otLinkGetFactoryAssignedIeeeEui64 API. + * + * @param eui64 IEEE EUI64 identifier. + * + * @retval OT_ERROR_NONE IEEE EUI64 saved successfully. + * @retval OT_ERROR_NOT_CAPABLE Setting IEEE EUI64 unsupported by the remote device. + * @retval OT_ERROR_FAILED Failed to save IEEE EUI64. + */ +otError ot_rpc_set_factory_assigned_ieee_eui64(const otExtAddress * eui64); + +#ifdef __cplusplus +} +#endif + +/** + * @} + */ + +#endif /* OT_RPC_H_ */ diff --git a/samples/nrf_rpc/protocols_serialization/client/src/ot_shell.c b/samples/nrf_rpc/protocols_serialization/client/src/ot_shell.c index 38e419bc463c..1ad122883fe6 100644 --- a/samples/nrf_rpc/protocols_serialization/client/src/ot_shell.c +++ b/samples/nrf_rpc/protocols_serialization/client/src/ot_shell.c @@ -5,6 +5,7 @@ */ #include "ot_shell.h" +#include #include #include @@ -1978,6 +1979,33 @@ static int cmd_dns_client_browse(const struct shell *sh, size_t argc, char *argv return 0; } +static otError ot_cli_command_eui64(const struct shell *sh, size_t argc, char *argv[]) +{ + otExtAddress ext_addr; + + if (argc == 1) { + /* Read current EUI64 */ + otLinkGetFactoryAssignedIeeeEui64(NULL, &ext_addr); + shell_print(sh, "%02x%02x%02x%02x%02x%02x%02x%02x", ext_addr.m8[0], ext_addr.m8[1], + ext_addr.m8[2], ext_addr.m8[3], ext_addr.m8[4], ext_addr.m8[5], + ext_addr.m8[6], ext_addr.m8[7]); + return OT_ERROR_NONE; + } + + /* Set new EUI64 */ + if (hex2bin(argv[1], strlen(argv[1]), ext_addr.m8, sizeof(ext_addr.m8)) != + sizeof(ext_addr.m8)) { + return OT_ERROR_INVALID_ARGS; + } + + return ot_rpc_set_factory_assigned_ieee_eui64(&ext_addr); +} + +static int cmd_eui64(const struct shell *sh, size_t argc, char *argv[]) +{ + return ot_cli_command_invoke(ot_cli_command_eui64, sh, argc, argv); +} + SHELL_STATIC_SUBCMD_SET_CREATE( ot_cmds, SHELL_CMD_ARG(ifconfig, NULL, "Interface management", cmd_ifconfig, 1, 1), SHELL_CMD_ARG(ipmaddr, NULL, "IPv6 multicast configuration", cmd_ipmaddr, 1, 2), @@ -1987,6 +2015,7 @@ SHELL_STATIC_SUBCMD_SET_CREATE( SHELL_CMD_ARG(thread, NULL, "Role management", cmd_thread, 2, 0), SHELL_CMD_ARG(discover, NULL, "Thread discovery scan", cmd_discover, 1, 4), SHELL_CMD_ARG(radio, NULL, "Radio configuration", cmd_radio, 1, 1), + SHELL_CMD_ARG(eui64, NULL, "EUI64 configuration", cmd_eui64, 1, 1), SHELL_CMD_ARG(test_message, NULL, "Test message API", cmd_test_message, 1, 0), SHELL_CMD_ARG(test_udp_init, NULL, "Test udp init API", cmd_test_udp_init, 1, 0), SHELL_CMD_ARG(test_udp_send, NULL, "Test udp send API", cmd_test_udp_send, 1, 0), @@ -2067,8 +2096,9 @@ SHELL_STATIC_SUBCMD_SET_CREATE( "Network diag, args: " " ", cmd_test_net_diag, 4, 255), - SHELL_CMD_ARG(test_mesh_diag, NULL, "Mesh diag topology discovery, args: " - "[ip6] [children]", + SHELL_CMD_ARG(test_mesh_diag, NULL, + "Mesh diag topology discovery, args: " + "[ip6] [children]", cmd_test_mesh_diag, 2, 2), SHELL_SUBCMD_SET_END); diff --git a/samples/nrf_rpc/protocols_serialization/server/snippets/openthread/boards/nrf54l15dk_nrf54l15_cpuapp.conf b/samples/nrf_rpc/protocols_serialization/server/snippets/openthread/boards/nrf54l15dk_nrf54l15_cpuapp.conf new file mode 100644 index 000000000000..c7221375e6b2 --- /dev/null +++ b/samples/nrf_rpc/protocols_serialization/server/snippets/openthread/boards/nrf54l15dk_nrf54l15_cpuapp.conf @@ -0,0 +1,7 @@ +# +# Copyright (c) 2024 Nordic Semiconductor +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +# + +CONFIG_IEEE802154_NRF5_UICR_EUI64_ENABLE=y diff --git a/samples/nrf_rpc/protocols_serialization/server/snippets/openthread/snippet.yml b/samples/nrf_rpc/protocols_serialization/server/snippets/openthread/snippet.yml index 87b39b61a7bc..88ee89c71682 100644 --- a/samples/nrf_rpc/protocols_serialization/server/snippets/openthread/snippet.yml +++ b/samples/nrf_rpc/protocols_serialization/server/snippets/openthread/snippet.yml @@ -12,6 +12,7 @@ boards: nrf54l15dk/nrf54l15/cpuapp: append: EXTRA_DTC_OVERLAY_FILE: boards/nrf54l15dk_nrf54l15_cpuapp.overlay + EXTRA_CONF_FILE: boards/nrf54l15dk_nrf54l15_cpuapp.conf nrf52840dk/nrf52840: append: EXTRA_DTC_OVERLAY_FILE: boards/nrf52840dk_nrf52840.overlay diff --git a/subsys/net/openthread/rpc/client/ot_rpc_link.c b/subsys/net/openthread/rpc/client/ot_rpc_link.c index 052eb213134e..d8700ec00b2f 100644 --- a/subsys/net/openthread/rpc/client/ot_rpc_link.c +++ b/subsys/net/openthread/rpc/client/ot_rpc_link.c @@ -97,11 +97,24 @@ void otLinkGetFactoryAssignedIeeeEui64(otInstance *aInstance, otExtAddress *aEui ARG_UNUSED(aInstance); - if (aEui64 == NULL) { - return; - } + __ASSERT_NO_MSG(aEui64 != NULL); NRF_RPC_CBOR_ALLOC(&ot_group, ctx, 0); nrf_rpc_cbor_cmd_no_err(&ot_group, OT_RPC_CMD_LINK_GET_FACTORY_ASSIGNED_EUI64, &ctx, ot_rpc_decode_eui64, aEui64->m8); } + +otError ot_rpc_set_factory_assigned_ieee_eui64(const otExtAddress *eui64) +{ + struct nrf_rpc_cbor_ctx ctx; + otError error; + + __ASSERT_NO_MSG(eui64 != NULL); + + NRF_RPC_CBOR_ALLOC(&ot_group, ctx, 1 + OT_EXT_ADDRESS_SIZE); + nrf_rpc_encode_buffer(&ctx, &eui64->m8, OT_EXT_ADDRESS_SIZE); + nrf_rpc_cbor_cmd_no_err(&ot_group, OT_RPC_CMD_LINK_SET_FACTORY_ASSIGNED_EUI64, &ctx, + ot_rpc_decode_error, &error); + + return error; +} diff --git a/subsys/net/openthread/rpc/common/ot_rpc_ids.h b/subsys/net/openthread/rpc/common/ot_rpc_ids.h index 193414e4ea53..cf1b3c4a5a1f 100644 --- a/subsys/net/openthread/rpc/common/ot_rpc_ids.h +++ b/subsys/net/openthread/rpc/common/ot_rpc_ids.h @@ -170,6 +170,9 @@ enum ot_rpc_cmd_server { OT_RPC_CMD_DNS_SERVICE_RESP_GET_SERVICE_INFO, OT_RPC_CMD_DNS_SERVICE_RESP_GET_HOST_ADDRESS, OT_RPC_CMD_LINK_RAW_GET_RADIO_TIME, + + /* OpenThread over RPC API additions */ + OT_RPC_CMD_LINK_SET_FACTORY_ASSIGNED_EUI64, }; #endif /* OT_RPC_IDS_H_ */ diff --git a/subsys/net/openthread/rpc/server/ot_rpc_link.c b/subsys/net/openthread/rpc/server/ot_rpc_link.c index a642fce9efeb..11966f229b1f 100644 --- a/subsys/net/openthread/rpc/server/ot_rpc_link.c +++ b/subsys/net/openthread/rpc/server/ot_rpc_link.c @@ -12,10 +12,18 @@ #include +#include #include +#include #include +#ifdef CONFIG_IEEE802154_NRF5_UICR_EUI64_ENABLE +#ifdef CONFIG_NRFX_RRAMC +#include +#endif +#endif + static void ot_rpc_cmd_set_poll_period(const struct nrf_rpc_group *group, struct nrf_rpc_cbor_ctx *ctx, void *handler_data) { @@ -123,6 +131,55 @@ static void ot_rpc_cmd_get_factory_assigned_eui64(const struct nrf_rpc_group *gr nrf_rpc_cbor_rsp_no_err(group, &rsp_ctx); } +static void ot_rpc_cmd_set_factory_assigned_eui64(const struct nrf_rpc_group *group, + struct nrf_rpc_cbor_ctx *ctx, void *handler_data) +{ + otError error = OT_ERROR_NOT_CAPABLE; + otExtAddress ext_addr; + + nrf_rpc_decode_buffer(ctx, ext_addr.m8, OT_EXT_ADDRESS_SIZE); + + if (!nrf_rpc_decoding_done_and_check(group, ctx)) { + ot_rpc_report_cmd_decoding_error(OT_RPC_CMD_LINK_SET_FACTORY_ASSIGNED_EUI64); + return; + } + +#ifdef CONFIG_IEEE802154_NRF5_UICR_EUI64_ENABLE +#ifdef CONFIG_NRFX_RRAMC + if (nrfx_rramc_otp_word_write(CONFIG_IEEE802154_NRF5_UICR_EUI64_REG + 1, + sys_get_le32(ext_addr.m8)) && + nrfx_rramc_otp_word_write(CONFIG_IEEE802154_NRF5_UICR_EUI64_REG, + sys_get_le32(ext_addr.m8 + 4))) { + error = OT_ERROR_NONE; + } else { + error = OT_ERROR_FAILED; + } +#endif +#endif + + /* + * The IEEE EUI64 is only copied from UICR to the network interface once, during + * the network interface initialization, so the network interface must also be + * updated after writing the new identifier to UICR. + */ + if (IS_ENABLED(CONFIG_NET_L2_OPENTHREAD) && error == OT_ERROR_NONE) { + struct net_if *iface = net_if_get_first_by_type(&NET_L2_GET_NAME(OPENTHREAD)); + struct net_linkaddr *addr; + + __ASSERT_NO_MSG(iface != NULL); + + net_if_lock(iface); + addr = net_if_get_link_addr(iface); + + __ASSERT_NO_MSG(addr != NULL && addr->len == OT_EXT_ADDRESS_SIZE); + + memcpy(addr->addr, ext_addr.m8, OT_EXT_ADDRESS_SIZE); + net_if_unlock(iface); + } + + nrf_rpc_rsp_send_uint(group, error); +} + NRF_RPC_CBOR_CMD_DECODER(ot_group, ot_rpc_cmd_set_poll_period, OT_RPC_CMD_LINK_SET_POLL_PERIOD, ot_rpc_cmd_set_poll_period, NULL); @@ -143,3 +200,7 @@ NRF_RPC_CBOR_CMD_DECODER(ot_group, ot_rpc_cmd_set_link_enabled, OT_RPC_CMD_LINK_ NRF_RPC_CBOR_CMD_DECODER(ot_group, ot_rpc_cmd_get_factory_assigned_eui64, OT_RPC_CMD_LINK_GET_FACTORY_ASSIGNED_EUI64, ot_rpc_cmd_get_factory_assigned_eui64, NULL); + +NRF_RPC_CBOR_CMD_DECODER(ot_group, ot_rpc_cmd_set_factory_assigned_eui64, + OT_RPC_CMD_LINK_SET_FACTORY_ASSIGNED_EUI64, + ot_rpc_cmd_set_factory_assigned_eui64, NULL);