diff --git a/boards/nxp/frdm_mcxw71/doc/index.rst b/boards/nxp/frdm_mcxw71/doc/index.rst index 0169c52eff72..ad0802ecbb00 100644 --- a/boards/nxp/frdm_mcxw71/doc/index.rst +++ b/boards/nxp/frdm_mcxw71/doc/index.rst @@ -70,6 +70,10 @@ The ``frdm_mcxw71`` board target in Zephyr currently supports the following feat +-----------+------------+-------------------------------------+ | LPADC | on-chip | adc | +-----------+------------+-------------------------------------+ +| ELE | on-chip | entropy | ++-----------+------------+-------------------------------------+ +| PHY | on-chip | ieee802154 | ++-----------+------------+-------------------------------------+ Fetch Binary Blobs ****************** @@ -130,8 +134,26 @@ Connect a USB cable from your PC to J10, and use the serial terminal of your cho - Parity: None - Stop bits: 1 -Flashing -======== +Application Building +==================== + +Openthread applications +----------------------- + +.. zephyr-app-commands:: + :zephyr-app: samples/net/sockets/echo_server + :board: frdm_mcxw71 + :goals: build + :west-args: -- -DEXTRA_CONF_FILE=overlay-ot.conf + +.. zephyr-app-commands:: + :zephyr-app: samples/net/sockets/echo_client + :board: frdm_mcxw71 + :goals: build + :west-args: -- -DEXTRA_CONF_FILE=overlay-ot.conf + +Application Flashing +==================== Here is an example for the :zephyr:code-sample:`hello_world` application. @@ -166,15 +188,17 @@ should see the following message in the terminal: *** Booting Zephyr OS build v3.7.0-xxx-xxxx *** Hello World! frdm_mcxw71/mcxw716c -Bluetooth -========= +NBU Flashing +============ BLE functionality requires to fetch binary blobs, so make sure to follow the ``Fetch Binary Blobs`` section first. Two images must be written to the board: one for the host (CM33) and one for the NBU (CM3). -- To flash the application (CM33) refer to the ``Flashing`` section above. -- To flash the NBU, follow the instructions below: + +- To flash the application (CM33) refer to the ``Application Flashing`` section above. + +- To flash the ``NBU Flashing``, follow the instructions below: * Install ``blhost`` from NXP's website. This is the tool that will allow you to flash the NBU. * Enter ISP mode. To boot the MCU in ISP mode, follow these steps: @@ -184,17 +208,39 @@ Two images must be written to the board: one for the host (CM33) and one for the - Reconnect any external power supply, if needed. * Use the following command to flash NBU file: -.. code-block:: console +.. tabs:: + + .. group-tab:: BLE NBU - Windows + + .. code-block:: console + :caption: Flash BLE only NBU on Windows + + blhost.exe -p COMxx -- receive-sb-file mcxw71_nbu_ble.sb3 + + .. group-tab:: BLE NBU - Linux + + .. code-block:: console + :caption: Flash BLE only NBU on Linux + + ./blhost -p /dev/ttyxx -- receive-sb-file mcxw71_nbu_ble.sb3 + + .. group-tab:: DYN NBU - Windows + + .. code-block:: console + :caption: Flash Dynamic NBU (BLE + 15.4) on Windows + + blhost.exe -p COMxx -- receive-sb-file mcxw71_nbu_ble_15_4_dyn.sb3 + + .. group-tab:: DYN NBU - Linux - # On Windows - blhost.exe -p COMxx -- receive-sb-file mcxw71_nbu_ble.sb3 + .. code-block:: console + :caption: Flash Dynamic NBU (BLE + 15.4) on Linux - # On Linux - ./blhost -p /dev/ttyxx -- receive-sb-file mcxw71_nbu_ble.sb3 + ./blhost -p /dev/ttyxx -- receive-sb-file mcxw71_nbu_ble_15_4_dyn.sb3 Please consider changing ``COMxx`` on Windows or ``ttyxx`` on Linux to the serial port used by your board. -The NBU file can be found in : ``/modules/hal/nxp/zephyr/blobs/mcxw71/mcxw71_nbu_ble.sb3`` +The NBU files can be found in : ``/modules/hal/nxp/zephyr/blobs/mcxw71/`` folder. For more details: diff --git a/drivers/entropy/CMakeLists.txt b/drivers/entropy/CMakeLists.txt index 6decc76781b1..6f27e39c4c6a 100644 --- a/drivers/entropy/CMakeLists.txt +++ b/drivers/entropy/CMakeLists.txt @@ -11,6 +11,7 @@ zephyr_library_sources_ifdef(CONFIG_ENTROPY_MCUX_RNG entropy_mcux_rng. zephyr_library_sources_ifdef(CONFIG_ENTROPY_MCUX_RNGA entropy_mcux_rnga.c) zephyr_library_sources_ifdef(CONFIG_ENTROPY_MCUX_TRNG entropy_mcux_trng.c) zephyr_library_sources_ifdef(CONFIG_ENTROPY_MCUX_CAAM entropy_mcux_caam.c) +zephyr_library_sources_ifdef(CONFIG_ENTROPY_NXP_ELE_TRNG entropy_nxp_ele.c) zephyr_library_sources_ifdef(CONFIG_ENTROPY_NRF5_RNG entropy_nrf5.c) zephyr_library_sources_ifdef(CONFIG_ENTROPY_NRF_CRACEN_CTR_DRBG entropy_nrf_cracen.c) zephyr_library_sources_ifdef(CONFIG_ENTROPY_SAM_RNG entropy_sam.c) diff --git a/drivers/entropy/Kconfig b/drivers/entropy/Kconfig index e170d3381a1b..62242ecaccf2 100644 --- a/drivers/entropy/Kconfig +++ b/drivers/entropy/Kconfig @@ -23,6 +23,7 @@ config ENTROPY_INIT_PRIORITY source "drivers/entropy/Kconfig.b91" source "drivers/entropy/Kconfig.cc13xx_cc26xx" source "drivers/entropy/Kconfig.mcux" +source "drivers/entropy/Kconfig.nxp" source "drivers/entropy/Kconfig.stm32" source "drivers/entropy/Kconfig.esp32" source "drivers/entropy/Kconfig.nrf5" diff --git a/drivers/entropy/Kconfig.nxp b/drivers/entropy/Kconfig.nxp new file mode 100644 index 000000000000..895ff59dd76f --- /dev/null +++ b/drivers/entropy/Kconfig.nxp @@ -0,0 +1,12 @@ +# NXP ELE entropy configuration options + +# Copyright 2025 NXP +# SPDX-License-Identifier: Apache-2.0 + +config ENTROPY_NXP_ELE_TRNG + bool "NXP ELE TRNG driver" + default y + depends on DT_HAS_NXP_ELE_TRNG_ENABLED + select ENTROPY_HAS_DRIVER + help + This option enables the ELE true random number generator (TRNG) diff --git a/drivers/entropy/entropy_nxp_ele.c b/drivers/entropy/entropy_nxp_ele.c new file mode 100644 index 000000000000..8d8e037de7f0 --- /dev/null +++ b/drivers/entropy/entropy_nxp_ele.c @@ -0,0 +1,71 @@ +/* entropy_nxp_ele.c - NXP ELE entropy driver */ + +/* + * Copyright 2025 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT nxp_ele_trng + +#include +#include +#include +#include +#include +#include + +#include "sss_crypto.h" + +struct entropy_ele_data_str { + struct k_sem sem_lock; +}; + +static struct entropy_ele_data_str entropy_ele_data; + +static int entropy_ele_get_entropy(const struct device *dev, uint8_t *buf, uint16_t len) +{ + sss_sscp_rng_t ctx; + int result = -EIO; + + __ASSERT_NO_MSG(buf != NULL); + __ASSERT_NO_MSG(&entropy_ele_data == dev->data); + + k_sem_take(&entropy_ele_data.sem_lock, K_FOREVER); + + if (CRYPTO_InitHardware() != kStatus_Success) { + goto exit; + } + + if (sss_sscp_rng_context_init(&g_sssSession, &ctx, 0u) != kStatus_SSS_Success) { + goto exit; + } + + if (sss_sscp_rng_get_random(&ctx, buf, len) != kStatus_SSS_Success) { + goto exit; + } + + if (sss_sscp_rng_free(&ctx) != kStatus_SSS_Success) { + goto exit; + } + + result = 0; + +exit: + k_sem_give(&entropy_ele_data.sem_lock); + return result; +} + +static int entropy_ele_init(const struct device *dev) +{ + __ASSERT_NO_MSG(&entropy_ele_data == dev->data); + + k_sem_init(&entropy_ele_data.sem_lock, 1, 1); + + return 0; +} + +static DEVICE_API(entropy, entropy_ele_api_funcs) = {.get_entropy = entropy_ele_get_entropy}; + +DEVICE_DT_INST_DEFINE(0, entropy_ele_init, NULL, &entropy_ele_data, NULL, PRE_KERNEL_1, + CONFIG_ENTROPY_INIT_PRIORITY, &entropy_ele_api_funcs); diff --git a/drivers/ieee802154/CMakeLists.txt b/drivers/ieee802154/CMakeLists.txt index 3fdb65790afe..f731bdb9f55b 100644 --- a/drivers/ieee802154/CMakeLists.txt +++ b/drivers/ieee802154/CMakeLists.txt @@ -15,6 +15,7 @@ zephyr_library_sources_ifdef(CONFIG_IEEE802154_CC2520 ieee802154_cc2520.c) zephyr_library_sources_ifdef(CONFIG_IEEE802154_DW1000 ieee802154_dw1000.c) zephyr_library_sources_ifdef(CONFIG_IEEE802154_KW41Z ieee802154_kw41z.c) zephyr_library_sources_ifdef(CONFIG_IEEE802154_MCR20A ieee802154_mcr20a.c) +zephyr_library_sources_ifdef(CONFIG_IEEE802154_MCXW ieee802154_mcxw.c ieee802154_mcxw_utils.c) zephyr_library_sources_ifdef(CONFIG_IEEE802154_NRF5 ieee802154_nrf5.c) zephyr_library_sources_ifdef(CONFIG_IEEE802154_RF2XX ieee802154_rf2xx.c) zephyr_library_sources_ifdef(CONFIG_IEEE802154_RF2XX ieee802154_rf2xx_iface.c) diff --git a/drivers/ieee802154/Kconfig b/drivers/ieee802154/Kconfig index 447b32ccbe06..a3f89c72665b 100644 --- a/drivers/ieee802154/Kconfig +++ b/drivers/ieee802154/Kconfig @@ -76,6 +76,8 @@ source "drivers/ieee802154/Kconfig.kw41z" source "drivers/ieee802154/Kconfig.mcr20a" +source "drivers/ieee802154/Kconfig.mcxw" + source "drivers/ieee802154/Kconfig.nrf5" source "drivers/ieee802154/Kconfig.cc1200" diff --git a/drivers/ieee802154/Kconfig.mcxw b/drivers/ieee802154/Kconfig.mcxw new file mode 100644 index 000000000000..5829a8f9736f --- /dev/null +++ b/drivers/ieee802154/Kconfig.mcxw @@ -0,0 +1,28 @@ +# NXP MCXW 802.15.4 configuration options + +# Copyright 2025 NXP +# SPDX-License-Identifier: Apache-2.0 + +menuconfig IEEE802154_MCXW + bool "NXP MCXW series IEEE 802.15.4 Driver" + default y + depends on DT_HAS_NXP_MCXW_IEEE802154_ENABLED + depends on COUNTER + +if IEEE802154_MCXW + +config IEEE802154_MCXW_INIT_PRIO + int "Initial priority for the IEEE802154 driver" + default 80 + +config IEEE802154_MCXW_RX_STACK_SIZE + int "Driver's internal RX thread stack size" + default 800 + + +config IEEE802154_MCXW_CSL_ACCURACY + int "Csl accuracy for delayed operations" + default 100 + + +endif diff --git a/drivers/ieee802154/ieee802154_mcxw.c b/drivers/ieee802154/ieee802154_mcxw.c new file mode 100644 index 000000000000..247342e66a13 --- /dev/null +++ b/drivers/ieee802154/ieee802154_mcxw.c @@ -0,0 +1,1360 @@ +/* ieee802154_mcxw.c - NXP MCXW 802.15.4 driver */ + +/* + * Copyright 2025 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT nxp_mcxw_ieee802154 + +#define LOG_MODULE_NAME ieee802154_mcxw + +#include +LOG_MODULE_REGISTER(LOG_MODULE_NAME); + +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(CONFIG_NET_L2_OPENTHREAD) +#include +#include +#endif + +#include +#include + +#include + +#include +#include +#include + +#include "EmbeddedTypes.h" +#include "Phy.h" + +#include "fwk_platform_ot.h" + +#include "ieee802154_mcxw.h" +#include "ieee802154_mcxw_utils.h" + +void PLATFORM_RemoteActiveReq(void); +void PLATFORM_RemoteActiveRel(void); + +#if CONFIG_IEEE802154_CSL_ENDPOINT + +#define CMP_OVHD (4 * IEEE802154_SYMBOL_TIME_US) /* 2 LPTRM (32 kHz) ticks */ + +static bool_t csl_rx = FALSE; + +static void set_csl_sample_time(void); +static void start_csl_receiver(void); +static void stop_csl_receiver(void); +static uint16_t rf_compute_csl_phase(uint32_t aTimeUs); + +#else /* CONFIG_IEEE802154_CSL_ENDPOINT */ +#define start_csl_receiver() +#define stop_csl_receiver() +#endif /* CONFIG_IEEE802154_CSL_ENDPOINT */ + +static volatile uint32_t sun_rx_mode = RX_ON_IDLE_START; + +/* Private functions */ +static void rf_abort(void); +static void rf_set_channel(uint8_t channel); +static void rf_set_tx_power(int8_t tx_power); +static uint64_t rf_adjust_tstamp_from_phy(uint64_t ts); + +#if CONFIG_IEEE802154_CSL_ENDPOINT || CONFIG_NET_PKT_TXTIME +static uint32_t rf_adjust_tstamp_from_app(uint32_t time); +#endif /* CONFIG_IEEE802154_CSL_ENDPOINT || CONFIG_NET_PKT_TXTIME */ + +static void rf_rx_on_idle(uint32_t newValue); + +static uint8_t ot_phy_ctx = (uint8_t)(-1); + +static struct mcxw_context mcxw_ctx; + +/** + * Stub function used for controlling low power mode + */ +WEAK void app_allow_device_to_slepp(void) +{ +} + +/** + * Stub function used for controlling low power mode + */ +WEAK void app_disallow_device_to_slepp(void) +{ +} + +void mcxw_get_eui64(uint8_t *eui64) +{ + __ASSERT_NO_MSG(eui64); + + /* PLATFORM_GetIeee802_15_4Addr(); */ + sys_rand_get(eui64, sizeof(mcxw_ctx.mac)); + + eui64[0] = (eui64[0] & ~0x01) | 0x02; +} + +static int mcxw_set_pan_id(const struct device *dev, uint16_t aPanId) +{ + struct mcxw_context *mcxw_radio = dev->data; + + macToPlmeMessage_t msg; + + msg.msgType = gPlmeSetReq_c; + msg.msgData.setReq.PibAttribute = gPhyPibPanId_c; + msg.msgData.setReq.PibAttributeValue = (uint64_t)aPanId; + + (void)MAC_PLME_SapHandler(&msg, ot_phy_ctx); + + mcxw_radio->pan_id = aPanId; + + return 0; +} + +static int mcxw_set_extended_address(const struct device *dev, const uint8_t *ieee_addr) +{ + struct mcxw_context *mcxw_radio = dev->data; + + macToPlmeMessage_t msg; + + msg.msgType = gPlmeSetReq_c; + msg.msgData.setReq.PibAttribute = gPhyPibLongAddress_c; + msg.msgData.setReq.PibAttributeValue = *(uint64_t *)ieee_addr; + + memcpy(mcxw_radio->mac, ieee_addr, 8); + + (void)MAC_PLME_SapHandler(&msg, ot_phy_ctx); + + return 0; +} + +static int mcxw_set_short_address(const struct device *dev, uint16_t aShortAddress) +{ + ARG_UNUSED(dev); + + macToPlmeMessage_t msg; + + msg.msgType = gPlmeSetReq_c; + msg.msgData.setReq.PibAttribute = gPhyPibShortAddress_c; + msg.msgData.setReq.PibAttributeValue = (uint64_t)aShortAddress; + + (void)MAC_PLME_SapHandler(&msg, ot_phy_ctx); + + return 0; +} + +static int mcxw_filter(const struct device *dev, bool set, enum ieee802154_filter_type type, + const struct ieee802154_filter *filter) +{ + LOG_DBG("Applying filter %u", type); + + if (!set) { + return -ENOTSUP; + } + + if (type == IEEE802154_FILTER_TYPE_IEEE_ADDR) { + return mcxw_set_extended_address(dev, filter->ieee_addr); + } else if (type == IEEE802154_FILTER_TYPE_SHORT_ADDR) { + return mcxw_set_short_address(dev, filter->short_addr); + } else if (type == IEEE802154_FILTER_TYPE_PAN_ID) { + return mcxw_set_pan_id(dev, filter->pan_id); + } + + return -ENOTSUP; +} + +void mcxw_radio_receive(void) +{ + macToPlmeMessage_t msg; + phyStatus_t phy_status; + + app_disallow_device_to_slepp(); + + __ASSERT(mcxw_ctx.state != RADIO_STATE_DISABLED, "Radio RX invalid state"); + + mcxw_ctx.state = RADIO_STATE_RECEIVE; + + rf_abort(); + rf_set_channel(mcxw_ctx.channel); + + if (sun_rx_mode) { + start_csl_receiver(); + + /* restart Rx on idle only if it was enabled */ + msg.msgType = gPlmeSetReq_c; + msg.msgData.setReq.PibAttribute = gPhyPibRxOnWhenIdle; + msg.msgData.setReq.PibAttributeValue = (uint64_t)1; + + phy_status = MAC_PLME_SapHandler(&msg, ot_phy_ctx); + __ASSERT_NO_MSG(phy_status == gPhySuccess_c); + } +} + +static uint8_t mcxw_get_acc(const struct device *dev) +{ + ARG_UNUSED(dev); + + return CONFIG_IEEE802154_MCXW_CSL_ACCURACY; +} + +static int mcxw_start(const struct device *dev) +{ + struct mcxw_context *mcxw_radio = dev->data; + + __ASSERT(mcxw_radio->state == RADIO_STATE_DISABLED, "%s", __func__); + + mcxw_radio->state = RADIO_STATE_SLEEP; + + rf_rx_on_idle(RX_ON_IDLE_START); + + mcxw_radio_receive(); + + return 0; +} + +static int mcxw_stop(const struct device *dev) +{ + struct mcxw_context *mcxw_radio = dev->data; + + __ASSERT(mcxw_radio->state != RADIO_STATE_DISABLED, "%s", __func__); + + stop_csl_receiver(); + + mcxw_radio->state = RADIO_STATE_DISABLED; + + return 0; +} + +void mcxw_radio_sleep(void) +{ + __ASSERT_NO_MSG(((mcxw_ctx.state != RADIO_STATE_TRANSMIT) && + (mcxw_ctx.state != RADIO_STATE_DISABLED))); + + rf_abort(); + + stop_csl_receiver(); + + app_allow_device_to_slepp(); + + mcxw_ctx.state = RADIO_STATE_SLEEP; +} + +static void mcxw_enable_src_match(bool enable) +{ + macToPlmeMessage_t msg; + + msg.msgType = gPlmeSetSAMState_c; + msg.msgData.SAMState = enable; + + (void)MAC_PLME_SapHandler(&msg, ot_phy_ctx); +} + +static int mcxw_src_match_entry(bool extended, uint8_t *address) +{ + macToPlmeMessage_t msg; + + msg.msgType = gPlmeAddToSapTable_c; + msg.msgData.deviceAddr.panId = mcxw_ctx.pan_id; + + if (extended) { + msg.msgData.deviceAddr.mode = 3; + memcpy(msg.msgData.deviceAddr.addr, address, 8); + } else { + msg.msgData.deviceAddr.mode = 2; + memcpy(msg.msgData.deviceAddr.addr, address, 2); + } + + if (gPhySuccess_c != MAC_PLME_SapHandler(&msg, ot_phy_ctx)) { + /* the status is not returned from PHY over RPMSG */ + return -ENOMEM; + } + + return 0; +} + +static int mcxw_src_clear_entry(bool extended, uint8_t *address) +{ + macToPlmeMessage_t msg; + + msg.msgType = gPlmeRemoveFromSAMTable_c; + msg.msgData.deviceAddr.panId = mcxw_ctx.pan_id; + + if (extended) { + msg.msgData.deviceAddr.mode = 3; + memcpy(msg.msgData.deviceAddr.addr, address, 8); + } else { + msg.msgData.deviceAddr.mode = 2; + memcpy(msg.msgData.deviceAddr.addr, address, 2); + } + + if (gPhySuccess_c != MAC_PLME_SapHandler(&msg, ot_phy_ctx)) { + /* the status is not returned from PHY over RPMSG */ + return -ENOENT; + } + + return 0; +} + +static int handle_ack(struct mcxw_context *mcxw_radio) +{ + uint8_t len; + struct net_pkt *pkt; + int err = 0; + + len = mcxw_radio->rx_ack_frame.length; + pkt = net_pkt_rx_alloc_with_buffer(mcxw_radio->iface, len, AF_UNSPEC, 0, K_NO_WAIT); + if (!pkt) { + LOG_ERR("No free packet available."); + err = -ENOMEM; + goto exit; + } + + if (net_pkt_write(pkt, mcxw_radio->rx_ack_frame.psdu, len) < 0) { + LOG_ERR("Failed to write to a packet."); + err = -ENOMEM; + goto free_ack; + } + + /* Use some fake values for LQI and RSSI. */ + net_pkt_set_ieee802154_lqi(pkt, 80); + net_pkt_set_ieee802154_rssi_dbm(pkt, -40); + + net_pkt_set_timestamp_ns(pkt, mcxw_radio->rx_ack_frame.timestamp); + + net_pkt_cursor_init(pkt); + + if (ieee802154_handle_ack(mcxw_radio->iface, pkt) != NET_OK) { + LOG_ERR("ACK packet not handled - releasing."); + } + +free_ack: + net_pkt_unref(pkt); + +exit: + mcxw_radio->rx_ack_frame.length = 0; + return err; +} + +static int mcxw_tx(const struct device *dev, enum ieee802154_tx_mode mode, struct net_pkt *pkt, + struct net_buf *frag) +{ + struct mcxw_context *mcxw_radio = dev->data; + /* tx_data buffer has reserved memory for both macToPdDataMessage_t and actual data frame + * after + */ + macToPdDataMessage_t *msg = (macToPdDataMessage_t *)mcxw_radio->tx_data; + phyStatus_t phy_status; + + uint8_t payload_len = frag->len; + uint8_t *payload = frag->data; + + app_disallow_device_to_slepp(); + + __ASSERT(mcxw_radio->state != RADIO_STATE_DISABLED, "%s: radio disabled", __func__); + + if (payload_len > IEEE802154_MTU) { + LOG_ERR("Payload too large: %d", payload_len); + return -EMSGSIZE; + } + + mcxw_radio->tx_frame.length = payload_len + IEEE802154_FCS_LENGTH; + memcpy(mcxw_radio->tx_frame.psdu, payload, payload_len); + + mcxw_radio->tx_frame.sec_processed = net_pkt_ieee802154_frame_secured(pkt); + mcxw_radio->tx_frame.hdr_updated = net_pkt_ieee802154_mac_hdr_rdy(pkt); + + rf_set_channel(mcxw_radio->channel); + + msg->msgType = gPdDataReq_c; + msg->msgData.dataReq.slottedTx = gPhyUnslottedMode_c; + msg->msgData.dataReq.psduLength = mcxw_radio->tx_frame.length; + msg->msgData.dataReq.CCABeforeTx = gPhyNoCCABeforeTx_c; + msg->msgData.dataReq.startTime = gPhySeqStartAsap_c; + + /* tx_frame.psdu will point to tx_frame.tx_data data buffer after macToPdDataMessage_t + * structure + */ + msg->msgData.dataReq.pPsdu = mcxw_radio->tx_frame.psdu; + + if (ieee802154_is_ar_flag_set(frag)) { + msg->msgData.dataReq.ackRequired = gPhyRxAckRqd_c; + /* The 3 bytes are 1 byte frame length and 2 bytes FCS */ + msg->msgData.dataReq.txDuration = + IEEE802154_CCA_LEN_SYM + IEEE802154_PHY_SHR_LEN_SYM + + (3 + mcxw_radio->tx_frame.length) * RADIO_SYMBOLS_PER_OCTET + + IEEE802154_TURNAROUND_LEN_SYM; + + if (is_frame_version_2015(frag->data, frag->len)) { + /* Because enhanced ack can be of variable length we need to set the timeout + * value to account for the FCF and addressing fields only, and stop the + * timeout timer after they are received and validated as a valid ACK + */ + msg->msgData.dataReq.txDuration += IEEE802154_ENH_ACK_WAIT_SYM; + } else { + msg->msgData.dataReq.txDuration += IEEE802154_IMM_ACK_WAIT_SYM; + } + } else { + msg->msgData.dataReq.ackRequired = gPhyNoAckRqd_c; + msg->msgData.dataReq.txDuration = 0xFFFFFFFFU; + } + + switch (mode) { + case IEEE802154_TX_MODE_DIRECT: + msg->msgData.dataReq.CCABeforeTx = gPhyNoCCABeforeTx_c; + break; + case IEEE802154_TX_MODE_CCA: + msg->msgData.dataReq.CCABeforeTx = gPhyCCAMode1_c; + break; + +#if defined(CONFIG_NET_PKT_TXTIME) + case IEEE802154_TX_MODE_TXTIME: + case IEEE802154_TX_MODE_TXTIME_CCA: + mcxw_radio->tx_frame.tx_delay = net_pkt_timestamp_ns(pkt); + msg->msgData.dataReq.startTime = + rf_adjust_tstamp_from_app(mcxw_radio->tx_frame.tx_delay); + msg->msgData.dataReq.startTime /= IEEE802154_SYMBOL_TIME_US; + break; +#endif + default: + break; + } + + msg->msgData.dataReq.flags = 0; + +#if OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2 + if (is_keyid_mode_1(mcxw_radio->tx_frame.psdu, mcxw_radio->tx_frame.length)) { + if (!net_pkt_ieee802154_frame_secured(pkt)) { + msg->msgData.dataReq.flags |= gPhyEncFrame; + + if (!net_pkt_ieee802154_mac_hdr_rdy(pkt)) { + msg->msgData.dataReq.flags |= gPhyUpdHDr; + +#if CONFIG_IEEE802154_CSL_ENDPOINT + /* Previously aFrame->mInfo.mTxInfo.mCslPresent was used to + * determine if the radio code should update the IE header. This + * field is no longer set by the OT stack. Until the issue is fixed + * in OT stack check if CSL period is > 0 and always update CSL IE + * in that case. + */ + if (mcxw_radio->csl_period) { + uint32_t hdr_time_us; + + start_csl_receiver(); + + /* Add TX_ENCRYPT_DELAY_SYM symbols delay to allow + * encryption to finish + */ + msg->msgData.dataReq.startTime = + PhyTime_ReadClock() + TX_ENCRYPT_DELAY_SYM; + + hdr_time_us = mcxw_get_time(NULL) + + (TX_ENCRYPT_DELAY_SYM + + IEEE802154_PHY_SHR_LEN_SYM) * + IEEE802154_SYMBOL_TIME_US; + set_csl_ie(mcxw_radio->tx_frame.psdu, + mcxw_radio->tx_frame.length, + mcxw_radio->csl_period, + rf_compute_csl_phase(hdr_time_us)); + } +#endif /* CONFIG_IEEE802154_CSL_ENDPOINT */ + } + } + } + +#endif + + k_sem_reset(&mcxw_radio->tx_wait); + + phy_status = MAC_PD_SapHandler(msg, ot_phy_ctx); + if (phy_status == gPhySuccess_c) { + mcxw_radio->tx_status = 0; + mcxw_radio->state = RADIO_STATE_TRANSMIT; + } else { + return -EIO; + } + + k_sem_take(&mcxw_radio->tx_wait, K_FOREVER); + + /* PWR_AllowDeviceToSleep(); */ + + mcxw_radio_receive(); + + switch (mcxw_radio->tx_status) { + case 0: + if (mcxw_radio->rx_ack_frame.length) { + return handle_ack(mcxw_radio); + } + return 0; + + default: + return -(mcxw_radio->tx_status); + } +} + +void mcxw_rx_thread(void *arg1, void *arg2, void *arg3) +{ + struct mcxw_context *mcxw_radio = (struct mcxw_context *)arg1; + struct net_pkt *pkt; + struct mcxw_rx_frame rx_frame; + + ARG_UNUSED(arg2); + ARG_UNUSED(arg3); + + while (true) { + pkt = NULL; + + LOG_DBG("Waiting for frame"); + + if (k_msgq_get(&mcxw_radio->rx_msgq, &rx_frame, K_FOREVER) < 0) { + LOG_ERR("Failed to get RX data from message queue"); + continue; + } + + pkt = net_pkt_rx_alloc_with_buffer(mcxw_radio->iface, rx_frame.length, AF_UNSPEC, 0, + K_FOREVER); + + if (net_pkt_write(pkt, rx_frame.psdu, rx_frame.length)) { + goto drop; + } + + net_pkt_set_ieee802154_lqi(pkt, rx_frame.lqi); + net_pkt_set_ieee802154_rssi_dbm(pkt, rx_frame.rssi); + net_pkt_set_ieee802154_ack_fpb(pkt, rx_frame.ack_fpb); + +#if defined(CONFIG_NET_PKT_TIMESTAMP) + net_pkt_set_timestamp_ns(pkt, rx_frame.timestamp); +#endif + +#if defined(CONFIG_NET_L2_OPENTHREAD) + net_pkt_set_ieee802154_ack_seb(pkt, rx_frame.ack_seb); +#endif + if (net_recv_data(mcxw_radio->iface, pkt) < 0) { + LOG_ERR("Packet dropped by NET stack"); + goto drop; + } + + k_free(rx_frame.phy_buffer); + rx_frame.phy_buffer = NULL; + + /* restart rx on idle if enough space in message queue */ + if (k_msgq_num_free_get(&mcxw_radio->rx_msgq) >= 2) { + rf_rx_on_idle(RX_ON_IDLE_START); + } + + continue; + +drop: + /* PWR_AllowDeviceToSleep(); */ + net_pkt_unref(pkt); + } +} + +int8_t mcxw_get_rssi(void) +{ + macToPlmeMessage_t msg; + + msg.msgType = gPlmeGetReq_c; + msg.msgData.getReq.PibAttribute = gPhyGetRSSILevel_c; + msg.msgData.getReq.PibAttributeValue = 127; /* RSSI is invalid*/ + + (void)MAC_PLME_SapHandler(&msg, ot_phy_ctx); + + return (int8_t)msg.msgData.getReq.PibAttributeValue; +} + +void mcxw_set_promiscuous(bool aEnable) +{ + macToPlmeMessage_t msg; + + msg.msgType = gPlmeSetReq_c; + msg.msgData.setReq.PibAttribute = gPhyPibPromiscuousMode_c; + msg.msgData.setReq.PibAttributeValue = (uint64_t)aEnable; + + (void)MAC_PLME_SapHandler(&msg, ot_phy_ctx); +} + +void mcxw_set_pan_coord(bool aEnable) +{ + macToPlmeMessage_t msg; + + msg.msgType = gPlmeSetReq_c; + msg.msgData.setReq.PibAttribute = gPhyPibPanCoordinator_c; + msg.msgData.setReq.PibAttributeValue = (uint64_t)aEnable; + + (void)MAC_PLME_SapHandler(&msg, ot_phy_ctx); +} + +static int mcxw_energy_scan(const struct device *dev, uint16_t duration, + energy_scan_done_cb_t done_cb) +{ + + int status = 0; + phyStatus_t phy_status; + macToPlmeMessage_t msg; + + app_disallow_device_to_slepp(); + + struct mcxw_context *mcxw_radio = dev->data; + + __ASSERT_NO_MSG(((mcxw_radio->state != RADIO_STATE_TRANSMIT) && + (mcxw_radio->state != RADIO_STATE_DISABLED))); + + rf_abort(); + + rf_set_channel(mcxw_radio->channel); + + mcxw_radio->energy_scan_done = done_cb; + + msg.msgType = gPlmeEdReq_c; + msg.msgData.edReq.startTime = gPhySeqStartAsap_c; + msg.msgData.edReq.measureDurationSym = duration * 1000; + + phy_status = MAC_PLME_SapHandler(&msg, ot_phy_ctx); + if (phy_status != gPhySuccess_c) { + mcxw_radio->energy_scan_done = NULL; + status = -EIO; + } + + return status; +} + +static int mcxw_set_txpower(const struct device *dev, int16_t dbm) +{ + struct mcxw_context *mcxw_radio = dev->data; + + LOG_DBG("%d", dbm); + + if (dbm != mcxw_radio->tx_pwr_lvl) { + /* Set Power level for TX */ + rf_set_tx_power(dbm); + mcxw_radio->tx_pwr_lvl = dbm; + } + + return 0; +} + +static void mcxw_configure_enh_ack_probing(const struct ieee802154_config *config) +{ + uint32_t ie_param = 0; + macToPlmeMessage_t msg; + + uint8_t *header_ie_buf = (uint8_t *)(config->ack_ie.header_ie); + + ie_param = (header_ie_buf[6] == 0x03 ? IeData_Lqi_c : 0) | + (header_ie_buf[7] == 0x02 ? IeData_LinkMargin_c : 0) | + (header_ie_buf[8] == 0x01 ? IeData_Rssi_c : 0); + + msg.msgType = gPlmeConfigureAckIeData_c; + msg.msgData.AckIeData.param = (ie_param > 0 ? IeData_MSB_VALID_DATA : 0); + msg.msgData.AckIeData.param |= ie_param; + msg.msgData.AckIeData.shortAddr = config->ack_ie.short_addr; + memcpy(msg.msgData.AckIeData.extAddr, config->ack_ie.ext_addr, 8); + memcpy(msg.msgData.AckIeData.data, config->ack_ie.header_ie, + config->ack_ie.header_ie->length); + + (void)MAC_PLME_SapHandler(&msg, ot_phy_ctx); +} + +static void mcxw_set_mac_key(struct ieee802154_key *mac_keys) +{ + macToPlmeMessage_t msg; + + __ASSERT_NO_MSG(mac_keys); + __ASSERT_NO_MSG(mac_keys[0].key_id && mac_keys[0].key_value); + __ASSERT_NO_MSG(mac_keys[1].key_id && mac_keys[1].key_value); + __ASSERT_NO_MSG(mac_keys[2].key_id && mac_keys[2].key_value); + + msg.msgType = gPlmeSetMacKey_c; + msg.msgData.MacKeyData.keyId = *(mac_keys[1].key_id); + + memcpy(msg.msgData.MacKeyData.prevKey, mac_keys[0].key_value, 16); + memcpy(msg.msgData.MacKeyData.currKey, mac_keys[1].key_value, 16); + memcpy(msg.msgData.MacKeyData.nextKey, mac_keys[2].key_value, 16); + + (void)MAC_PLME_SapHandler(&msg, ot_phy_ctx); +} + +void mcxw_set_mac_frame_counter(uint32_t frame_counter) +{ + macToPlmeMessage_t msg; + + msg.msgType = gPlmeSetMacFrameCounter_c; + msg.msgData.MacFrameCounter = frame_counter; + + (void)MAC_PLME_SapHandler(&msg, ot_phy_ctx); +} + +void mcxw_set_mac_frame_counter_if_larger(uint32_t frame_counter) +{ + macToPlmeMessage_t msg; + + msg.msgType = gPlmeSetMacFrameCounterIfLarger_c; + msg.msgData.MacFrameCounter = frame_counter; + + (void)MAC_PLME_SapHandler(&msg, ot_phy_ctx); +} + +#if CONFIG_IEEE802154_CSL_ENDPOINT + +static void mcxw_receive_at(uint8_t channel, uint32_t start, uint32_t duration) +{ + macToPlmeMessage_t msg; + + __ASSERT_NO_MSG(mcxw_ctx.state == RADIO_STATE_SLEEP); + mcxw_ctx.state = RADIO_STATE_RECEIVE; + + /* checks internally if the channel needs to be changed */ + rf_set_channel(mcxw_ctx.channel); + + start = rf_adjust_tstamp_from_app(start); + + msg.msgType = gPlmeSetTRxStateReq_c; + msg.msgData.setTRxStateReq.slottedMode = gPhyUnslottedMode_c; + msg.msgData.setTRxStateReq.state = gPhySetRxOn_c; + msg.msgData.setTRxStateReq.rxDuration = duration / IEEE802154_SYMBOL_TIME_US; + msg.msgData.setTRxStateReq.startTime = start / IEEE802154_SYMBOL_TIME_US; + + (void)MAC_PLME_SapHandler(&msg, ot_phy_ctx); +} + +static void mcxw_enable_csl(uint16_t period) +{ + mcxw_ctx.csl_period = period; + + macToPlmeMessage_t msg; + + msg.msgType = gPlmeCslEnable_c; + msg.msgData.cslPeriod = period; + + (void)MAC_PLME_SapHandler(&msg, ot_phy_ctx); +} + +static void set_csl_sample_time(void) +{ + if (!mcxw_ctx.csl_period) { + return; + } + + macToPlmeMessage_t msg; + uint32_t csl_period = mcxw_ctx.csl_period * 10 * IEEE802154_SYMBOL_TIME_US; + uint32_t dt = mcxw_ctx.csl_sample_time - (uint32_t)mcxw_get_time(NULL); + + /* next channel sample should be in the future */ + while ((dt <= CMP_OVHD) || (dt > (CMP_OVHD + 2 * csl_period))) { + mcxw_ctx.csl_sample_time += csl_period; + dt = mcxw_ctx.csl_sample_time - (uint32_t)mcxw_get_time(NULL); + } + + /* The CSL sample time is in microseconds and PHY function expects also microseconds */ + msg.msgType = gPlmeCslSetSampleTime_c; + msg.msgData.cslSampleTime = rf_adjust_tstamp_from_app(mcxw_ctx.csl_sample_time); + + (void)MAC_PLME_SapHandler(&msg, ot_phy_ctx); +} + +static void start_csl_receiver(void) +{ + if (!mcxw_ctx.csl_period) { + return; + } + + /* NBU has to be awake during CSL receiver trx so that conversion from + * PHY timebase (NBU) to TMR timebase (host) is valid + */ + if (!csl_rx) { + PLATFORM_RemoteActiveReq(); + + csl_rx = TRUE; + } + + /* sample time is converted to PHY time */ + set_csl_sample_time(); +} + +static void stop_csl_receiver(void) +{ + if (csl_rx) { + PLATFORM_RemoteActiveRel(); + + csl_rx = FALSE; + } +} + +/* + * Compute the CSL Phase for the time_us - i.e. the time from the time_us to + * mcxw_ctx.csl_sample_time. The assumption is that mcxw_ctx.csl_sample_time > time_us. Since the + * time is kept with a limited timer in reality it means that sometimes mcxw_ctx.csl_sample_time < + * time_us, when the timer overflows. Therefore the formula should be: + * + * if (time_us <= mcxw_ctx.csl_sample_time) + * csl_phase_us = mcxw_ctx.csl_sample_time - time_us; + * else + * csl_phase_us = MAX_TIMER_VALUE - time_us + mcxw_ctx.csl_sample_time; + * + * For simplicity the formula below has been used. + */ +static uint16_t rf_compute_csl_phase(uint32_t time_us) +{ + /* convert CSL Period in microseconds - it was given in 10 symbols */ + uint32_t csl_period_us = mcxw_ctx.csl_period * 10 * IEEE802154_SYMBOL_TIME_US; + uint32_t csl_phase_us = + (csl_period_us - (time_us % csl_period_us) + + (mcxw_ctx.csl_sample_time % csl_period_us)) % csl_period_us; + + return (uint16_t)(csl_phase_us / (10 * IEEE802154_SYMBOL_TIME_US) + 1); +} +#endif /* CONFIG_IEEE802154_CSL_ENDPOINT */ + +/*************************************************************************************************/ +static void rf_abort(void) +{ + macToPlmeMessage_t msg; + + sun_rx_mode = RX_ON_IDLE_START; + msg.msgType = gPlmeSetReq_c; + msg.msgData.setReq.PibAttribute = gPhyPibRxOnWhenIdle; + msg.msgData.setReq.PibAttributeValue = (uint64_t)0; + + (void)MAC_PLME_SapHandler(&msg, ot_phy_ctx); + + msg.msgType = gPlmeSetTRxStateReq_c; + msg.msgData.setTRxStateReq.state = gPhyForceTRxOff_c; + + (void)MAC_PLME_SapHandler(&msg, ot_phy_ctx); +} + +static void rf_set_channel(uint8_t channel) +{ + macToPlmeMessage_t msg; + + msg.msgType = gPlmeSetReq_c; + msg.msgData.setReq.PibAttribute = gPhyPibCurrentChannel_c; + msg.msgData.setReq.PibAttributeValue = (uint64_t)channel; + + (void)MAC_PLME_SapHandler(&msg, ot_phy_ctx); +} + +static int mcxw_cca(const struct device *dev) +{ + macToPlmeMessage_t msg; + phyStatus_t phy_status; + + struct mcxw_context *mcxw_radio = dev->data; + + msg.msgType = gPlmeCcaReq_c; + msg.msgData.ccaReq.ccaType = gPhyCCAMode1_c; + msg.msgData.ccaReq.contCcaMode = gPhyContCcaDisabled; + + phy_status = MAC_PLME_SapHandler(&msg, ot_phy_ctx); + + __ASSERT_NO_MSG(phy_status == gPhySuccess_c); + + k_sem_take(&mcxw_radio->cca_wait, K_FOREVER); + + return (mcxw_radio->tx_status == OT_ERROR_CHANNEL_ACCESS_FAILURE) ? -EBUSY : 0; +} + +static int mcxw_set_channel(const struct device *dev, uint16_t channel) +{ + struct mcxw_context *mcxw_radio = dev->data; + + LOG_DBG("%u", channel); + + if (channel != mcxw_radio->channel) { + + if (channel < 11 || channel > 26) { + return channel < 11 ? -ENOTSUP : -EINVAL; + } + + mcxw_radio->channel = channel; + } + + return 0; +} + +static net_time_t mcxw_get_time(const struct device *dev) +{ + static uint64_t sw_timestamp; + static uint64_t hw_timestamp; + + ARG_UNUSED(dev); + + /* Get new 32bit HW timestamp */ + uint32_t ticks; + uint64_t hw_timestamp_new; + uint64_t wrapped_val = 0; + uint64_t increment; + unsigned int key; + + key = irq_lock(); + + if (counter_get_value(mcxw_ctx.counter, &ticks)) { + irq_unlock(key); + return -1; + } + + hw_timestamp_new = counter_ticks_to_us(mcxw_ctx.counter, ticks); + + /* Check if the timestamp has wrapped around */ + if (hw_timestamp > hw_timestamp_new) { + wrapped_val = + COUNT_TO_USEC(((uint64_t)1 << 32), counter_get_frequency(mcxw_ctx.counter)); + } + + increment = (hw_timestamp_new + wrapped_val) - hw_timestamp; + sw_timestamp += increment; + + /* Store new HW timestamp for next iteration */ + hw_timestamp = hw_timestamp_new; + + irq_unlock(key); + + return (net_time_t)sw_timestamp; +} + +static void rf_set_tx_power(int8_t tx_power) +{ + macToPlmeMessage_t msg; + + msg.msgType = gPlmeSetReq_c; + msg.msgData.setReq.PibAttribute = gPhyPibTransmitPower_c; + msg.msgData.setReq.PibAttributeValue = (uint64_t)tx_power; + + MAC_PLME_SapHandler(&msg, ot_phy_ctx); +} + +/* Used to convert from phy clock timestamp (in symbols) to platform time (in us) + * the reception timestamp which must use a true 64bit timestamp source + */ +static uint64_t rf_adjust_tstamp_from_phy(uint64_t ts) +{ + uint64_t now = PhyTime_ReadClock(); + uint64_t delta; + + delta = (now >= ts) ? (now - ts) : ((PHY_TMR_MAX_VALUE + now) - ts); + delta *= IEEE802154_SYMBOL_TIME_US; + + return mcxw_get_time(NULL) - delta; +} + +#if CONFIG_IEEE802154_CSL_ENDPOINT || CONFIG_NET_PKT_TXTIME +static uint32_t rf_adjust_tstamp_from_app(uint32_t time) +{ + /* The phy timestamp is in symbols so we need to convert it to microseconds */ + uint64_t ts = PhyTime_ReadClock() * IEEE802154_SYMBOL_TIME_US; + uint32_t delta = time - (uint32_t)mcxw_get_time(NULL); + + return (uint32_t)(ts + delta); +} +#endif /* CONFIG_IEEE802154_CSL_ENDPOINT || CONFIG_NET_PKT_TXTIME */ + +/* Phy Data Service Access Point handler + * Called by Phy to notify when Tx has been done or Rx data is available + */ +phyStatus_t pd_mac_sap_handler(void *msg, instanceId_t instance) +{ + pdDataToMacMessage_t *data_msg = (pdDataToMacMessage_t *)msg; + + __ASSERT_NO_MSG(msg != NULL); + + /* PWR_DisallowDeviceToSleep(); */ + + switch (data_msg->msgType) { + case gPdDataCnf_c: + /* TX is done */ +#if OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2 + if (is_keyid_mode_1(mcxw_ctx.tx_frame.psdu, mcxw_ctx.tx_frame.length) && + !mcxw_ctx.tx_frame.sec_processed && !mcxw_ctx.tx_frame.hdr_updated) { + set_frame_counter(mcxw_ctx.tx_frame.psdu, mcxw_ctx.tx_frame.length, + data_msg->fc); + mcxw_ctx.tx_frame.hdr_updated = true; + } +#endif + + mcxw_ctx.tx_frame.length = 0; + mcxw_ctx.tx_status = 0; + mcxw_ctx.state = RADIO_STATE_RECEIVE; + + mcxw_ctx.rx_ack_frame.channel = mcxw_ctx.channel; + mcxw_ctx.rx_ack_frame.length = data_msg->msgData.dataCnf.ackLength; + mcxw_ctx.rx_ack_frame.timestamp = data_msg->msgData.dataCnf.timeStamp; + memcpy(mcxw_ctx.rx_ack_frame.psdu, data_msg->msgData.dataCnf.ackData, + mcxw_ctx.rx_ack_frame.length); + + k_sem_give(&mcxw_ctx.tx_wait); + + k_free(msg); + break; + + case gPdDataInd_c: + /* RX is done */ + struct mcxw_rx_frame rx_frame; + + /* retrieve frame information and data */ + rx_frame.lqi = data_msg->msgData.dataInd.ppduLinkQuality; + rx_frame.rssi = data_msg->msgData.dataInd.ppduRssi; + rx_frame.timestamp = rf_adjust_tstamp_from_phy(data_msg->msgData.dataInd.timeStamp); + rx_frame.ack_fpb = data_msg->msgData.dataInd.rxAckFp; + rx_frame.length = data_msg->msgData.dataInd.psduLength; + rx_frame.psdu = data_msg->msgData.dataInd.pPsdu; + rx_frame.ack_seb = data_msg->msgData.dataInd.ackedWithSecEnhAck; + + rx_frame.phy_buffer = (void *)msg; + + /* stop rx on idle if message queue is almost full */ + if (k_msgq_num_free_get(&mcxw_ctx.rx_msgq) == 1) { + rf_rx_on_idle(RX_ON_IDLE_STOP); + } + + /* add the rx message in queue */ + if (k_msgq_put(&mcxw_ctx.rx_msgq, &rx_frame, K_NO_WAIT) < 0) { + LOG_ERR("Failed to push RX data to message queue"); + } + break; + + default: + /* PWR_AllowDeviceToSleep(); */ + break; + } + + stop_csl_receiver(); + + return gPhySuccess_c; +} + +/* Phy Layer Management Entities Service Access Point handler + * Called by Phy to notify PLME event + */ +phyStatus_t plme_mac_sap_handler(void *msg, instanceId_t instance) +{ + plmeToMacMessage_t *plme_msg = (plmeToMacMessage_t *)msg; + + __ASSERT_NO_MSG(msg != NULL); + + /* PWR_DisallowDeviceToSleep(); */ + + switch (plme_msg->msgType) { + case gPlmeCcaCnf_c: + if (plme_msg->msgData.ccaCnf.status == gPhyChannelBusy_c) { + /* Channel is busy */ + mcxw_ctx.tx_status = EBUSY; + } else { + mcxw_ctx.tx_status = 0; + } + mcxw_ctx.state = RADIO_STATE_RECEIVE; + + k_sem_give(&mcxw_ctx.cca_wait); + break; + case gPlmeEdCnf_c: + /* Scan done */ + if (mcxw_ctx.energy_scan_done != NULL) { + energy_scan_done_cb_t callback = mcxw_ctx.energy_scan_done; + + mcxw_ctx.max_ed = plme_msg->msgData.edCnf.maxEnergyLeveldB; + + mcxw_ctx.energy_scan_done = NULL; + callback(net_if_get_device(mcxw_ctx.iface), mcxw_ctx.max_ed); + } + break; + case gPlmeTimeoutInd_c: + if (RADIO_STATE_TRANSMIT == mcxw_ctx.state) { + /* Ack timeout */ +#if OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2 + if (is_keyid_mode_1(mcxw_ctx.tx_frame.psdu, mcxw_ctx.tx_frame.length) && + !mcxw_ctx.tx_frame.sec_processed && !mcxw_ctx.tx_frame.hdr_updated) { + set_frame_counter(mcxw_ctx.tx_frame.psdu, mcxw_ctx.tx_frame.length, + plme_msg->fc); + mcxw_ctx.tx_frame.hdr_updated = true; + } +#endif + + mcxw_ctx.state = RADIO_STATE_RECEIVE; + /* No ack */ + mcxw_ctx.tx_status = ENOMSG; + + k_sem_give(&mcxw_ctx.tx_wait); + } else if (RADIO_STATE_RECEIVE == mcxw_ctx.state) { + /* CSL Receive AT state has ended with timeout and we are returning to SLEEP + * state + */ + mcxw_ctx.state = RADIO_STATE_SLEEP; + /* PWR_AllowDeviceToSleep(); */ + } + break; + case gPlmeAbortInd_c: + /* TX Packet was loaded into TX Packet RAM but the TX/TR seq did not ended ok */ +#if OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2 + if (is_keyid_mode_1(mcxw_ctx.tx_frame.psdu, mcxw_ctx.tx_frame.length) && + !mcxw_ctx.tx_frame.sec_processed && !mcxw_ctx.tx_frame.hdr_updated) { + set_frame_counter(mcxw_ctx.tx_frame.psdu, mcxw_ctx.tx_frame.length, + plme_msg->fc); + mcxw_ctx.tx_frame.hdr_updated = true; + } +#endif + + mcxw_ctx.state = RADIO_STATE_RECEIVE; + mcxw_ctx.tx_status = EIO; + + k_sem_give(&mcxw_ctx.tx_wait); + break; + default: + /* PWR_AllowDeviceToSleep(); */ + break; + } + /* The message has been allocated by the Phy, we have to free it */ + k_free(msg); + + stop_csl_receiver(); + + return gPhySuccess_c; +} + +static int mcxw_configure(const struct device *dev, enum ieee802154_config_type type, + const struct ieee802154_config *config) +{ + ARG_UNUSED(dev); + + switch (type) { + + case IEEE802154_CONFIG_AUTO_ACK_FPB: + if (config->auto_ack_fpb.mode == IEEE802154_FPB_ADDR_MATCH_THREAD) { + mcxw_enable_src_match(config->auto_ack_fpb.enabled); + } + /* TODO IEEE802154_FPB_ADDR_MATCH_ZIGBEE */ + break; + + case IEEE802154_CONFIG_ACK_FPB: + if (config->ack_fpb.enabled) { + return mcxw_src_match_entry(config->ack_fpb.extended, config->ack_fpb.addr); + } else { + return mcxw_src_clear_entry(config->ack_fpb.extended, config->ack_fpb.addr); + } + + /* TODO otPlatRadioClearSrcMatchShortEntries */ + /* TODO otPlatRadioClearSrcMatchExtEntries */ + break; + + case IEEE802154_CONFIG_PAN_COORDINATOR: + mcxw_set_pan_coord(config->pan_coordinator); + break; + + case IEEE802154_CONFIG_PROMISCUOUS: + mcxw_set_promiscuous(config->promiscuous); + break; + + case IEEE802154_CONFIG_MAC_KEYS: + mcxw_set_mac_key(config->mac_keys); + break; + + case IEEE802154_CONFIG_FRAME_COUNTER: + mcxw_set_mac_frame_counter(config->frame_counter); + break; + + case IEEE802154_CONFIG_FRAME_COUNTER_IF_LARGER: + mcxw_set_mac_frame_counter_if_larger(config->frame_counter); + break; + + case IEEE802154_CONFIG_ENH_ACK_HEADER_IE: + mcxw_configure_enh_ack_probing(config); + break; + +#if defined(CONFIG_IEEE802154_CSL_ENDPOINT) + case IEEE802154_CONFIG_EXPECTED_RX_TIME: + mcxw_ctx.csl_sample_time = config->expected_rx_time; + break; + + case IEEE802154_CONFIG_RX_SLOT: + mcxw_receive_at(config->rx_slot.channel, config->rx_slot.start / NSEC_PER_USEC, + config->rx_slot.duration / NSEC_PER_USEC); + break; + + case IEEE802154_CONFIG_CSL_PERIOD: + mcxw_enable_csl(config->csl_period); + break; +#endif /* CONFIG_IEEE802154_CSL_ENDPOINT */ + + case IEEE802154_CONFIG_RX_ON_WHEN_IDLE: + if (config->rx_on_when_idle) { + rf_rx_on_idle(RX_ON_IDLE_START); + } else { + rf_rx_on_idle(RX_ON_IDLE_STOP); + } + break; + + case IEEE802154_CONFIG_EVENT_HANDLER: + break; + + case IEEE802154_OPENTHREAD_CONFIG_MAX_EXTRA_CCA_ATTEMPTS: + break; + + default: + return -EINVAL; + } + + return 0; +} + +IEEE802154_DEFINE_PHY_SUPPORTED_CHANNELS(drv_attr, 11, 26); + +static int mcxw_attr_get(const struct device *dev, enum ieee802154_attr attr, + struct ieee802154_attr_value *value) +{ + ARG_UNUSED(dev); + + if (ieee802154_attr_get_channel_page_and_range( + attr, IEEE802154_ATTR_PHY_CHANNEL_PAGE_ZERO_OQPSK_2450_BPSK_868_915, + &drv_attr.phy_supported_channels, value) == 0) { + return 0; + } + + return -EIO; +} + +static enum ieee802154_hw_caps mcxw_get_capabilities(const struct device *dev) +{ + enum ieee802154_hw_caps caps; + + caps = IEEE802154_HW_FCS | IEEE802154_HW_PROMISC | IEEE802154_HW_FILTER | + IEEE802154_HW_TX_RX_ACK | IEEE802154_HW_RX_TX_ACK | IEEE802154_HW_ENERGY_SCAN | + IEEE802154_HW_TXTIME | IEEE802154_HW_RXTIME | IEEE802154_HW_SLEEP_TO_TX | + IEEE802154_RX_ON_WHEN_IDLE | IEEE802154_HW_TX_SEC | + IEEE802154_OPENTHREAD_HW_MULTIPLE_CCA | IEEE802154_HW_SELECTIVE_TXCHANNEL | + IEEE802154_OPENTHREAD_HW_CST; + return caps; +} + +static int mcxw_init(const struct device *dev) +{ + struct mcxw_context *mcxw_radio = dev->data; + + macToPlmeMessage_t msg; + + if (PLATFORM_InitOT() < 0) { + return -EIO; + } + + Phy_Init(); + + ot_phy_ctx = PHY_get_ctx(); + + /* Register Phy Data Service Access Point and Phy Layer Management Entities Service Access + * Point handlers + */ + Phy_RegisterSapHandlers((PD_MAC_SapHandler_t)pd_mac_sap_handler, + (PLME_MAC_SapHandler_t)plme_mac_sap_handler, ot_phy_ctx); + + msg.msgType = gPlmeEnableEncryption_c; + (void)MAC_PLME_SapHandler(&msg, ot_phy_ctx); + + mcxw_radio->state = RADIO_STATE_DISABLED; + mcxw_radio->energy_scan_done = NULL; + + mcxw_radio->channel = DEFAULT_CHANNEL; + rf_set_channel(mcxw_radio->channel); + + mcxw_radio->tx_frame.length = 0; + /* Make the psdu point to the space after macToPdDataMessage_t in the data buffer */ + mcxw_radio->tx_frame.psdu = mcxw_radio->tx_data + sizeof(macToPdDataMessage_t); + + /* Get and start LPTRM counter */ + mcxw_radio->counter = DEVICE_DT_GET(DT_NODELABEL(lptmr0)); + if (counter_start(mcxw_radio->counter)) { + return -EIO; + } + + /* Init TX semaphore */ + k_sem_init(&mcxw_radio->tx_wait, 0, 1); + /* Init CCA semaphore */ + k_sem_init(&mcxw_radio->cca_wait, 0, 1); + + /* Init RX message queue */ + k_msgq_init(&mcxw_radio->rx_msgq, mcxw_radio->rx_msgq_buffer, sizeof(mcxw_rx_frame), + NMAX_RXRING_BUFFERS); + + memset(&(mcxw_radio->rx_ack_frame), 0, sizeof(mcxw_radio->rx_ack_frame)); + mcxw_radio->rx_ack_frame.psdu = mcxw_radio->rx_ack_data; + + k_thread_create(&mcxw_radio->rx_thread, mcxw_radio->rx_stack, + CONFIG_IEEE802154_MCXW_RX_STACK_SIZE, mcxw_rx_thread, mcxw_radio, NULL, + NULL, K_PRIO_COOP(2), 0, K_NO_WAIT); + + k_thread_name_set(&mcxw_radio->rx_thread, "mcxw_rx"); + + return 0; +} + +static void mcxw_iface_init(struct net_if *iface) +{ + const struct device *dev = net_if_get_device(iface); + struct mcxw_context *mcxw_radio = dev->data; + + mcxw_get_eui64(mcxw_radio->mac); + + net_if_set_link_addr(iface, mcxw_radio->mac, sizeof(mcxw_radio->mac), NET_LINK_IEEE802154); + mcxw_radio->iface = iface; + ieee802154_init(iface); +} + +static void rf_rx_on_idle(uint32_t new_val) +{ + macToPlmeMessage_t msg; + phyStatus_t phy_status; + + new_val %= 2; + if (sun_rx_mode != new_val) { + sun_rx_mode = new_val; + msg.msgType = gPlmeSetReq_c; + msg.msgData.setReq.PibAttribute = gPhyPibRxOnWhenIdle; + msg.msgData.setReq.PibAttributeValue = (uint64_t)sun_rx_mode; + + phy_status = MAC_PLME_SapHandler(&msg, ot_phy_ctx); + + __ASSERT_NO_MSG(phy_status == gPhySuccess_c); + } +} + +static const struct ieee802154_radio_api mcxw71_radio_api = { + .iface_api.init = mcxw_iface_init, + + .get_capabilities = mcxw_get_capabilities, + .cca = mcxw_cca, + .set_channel = mcxw_set_channel, + .filter = mcxw_filter, + .set_txpower = mcxw_set_txpower, + .start = mcxw_start, + .stop = mcxw_stop, + .configure = mcxw_configure, + .tx = mcxw_tx, + .ed_scan = mcxw_energy_scan, + .get_time = mcxw_get_time, + .get_sch_acc = mcxw_get_acc, + .attr_get = mcxw_attr_get, +}; + +#if defined(CONFIG_NET_L2_IEEE802154) +#define L2 IEEE802154_L2 +#define L2_CTX_TYPE NET_L2_GET_CTX_TYPE(IEEE802154_L2) +#define MTU IEEE802154_MTU +#elif defined(CONFIG_NET_L2_OPENTHREAD) +#define L2 OPENTHREAD_L2 +#define L2_CTX_TYPE NET_L2_GET_CTX_TYPE(OPENTHREAD_L2) +#define MTU 1280 +#elif defined(CONFIG_NET_L2_CUSTOM_IEEE802154) +#define L2 CUSTOM_IEEE802154_L2 +#define L2_CTX_TYPE NET_L2_GET_CTX_TYPE(CUSTOM_IEEE802154_L2) +#define MTU CONFIG_NET_L2_CUSTOM_IEEE802154_MTU +#endif + +NET_DEVICE_DT_INST_DEFINE(0, mcxw_init, NULL, &mcxw_ctx, NULL, CONFIG_IEEE802154_MCXW_INIT_PRIO, + &mcxw71_radio_api, L2, L2_CTX_TYPE, MTU); diff --git a/drivers/ieee802154/ieee802154_mcxw.h b/drivers/ieee802154/ieee802154_mcxw.h new file mode 100644 index 000000000000..eb3fb284dc6e --- /dev/null +++ b/drivers/ieee802154/ieee802154_mcxw.h @@ -0,0 +1,125 @@ +/* ieee802154_mcxw.h - NXP MCXW 802.15.4 driver */ + +/* + * Copyright 2025 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_DRIVERS_IEEE802154_IEEE802154_MCXW_H_ +#define ZEPHYR_DRIVERS_IEEE802154_IEEE802154_MCXW_H_ + +#include + +#include "PhyInterface.h" + +#define TX_ENCRYPT_DELAY_SYM 200 + +#define DEFAULT_CHANNEL (11) +#define DEFAULT_CCA_MODE (gPhyCCAMode1_c) +#define IEEE802154_ACK_REQUEST (1 << 5) +#define IEEE802154_MIN_LENGTH (5) +#define IEEE802154_FRM_CTL_LO_OFFSET (0) +#define IEEE802154_DSN_OFFSET (2) +#define IEEE802154_FRM_TYPE_MASK (0x7) +#define IEEE802154_FRM_TYPE_ACK (0x2) +#define IEEE802154_SYMBOL_TIME_US (16) +#define IEEE802154_TURNAROUND_LEN_SYM (12) +#define IEEE802154_CCA_LEN_SYM (8) +#define IEEE802154_PHY_SHR_LEN_SYM (10) +#define IEEE802154_IMM_ACK_WAIT_SYM (54) +#define IEEE802154_ENH_ACK_WAIT_SYM (90) + +#define NMAX_RXRING_BUFFERS (8) +#define RX_ON_IDLE_START (1) +#define RX_ON_IDLE_STOP (0) + +#define PHY_TMR_MAX_VALUE (0x00FFFFFF) + +/* The Uncertainty of the scheduling CSL of transmission by the parent, in ±10 us units. */ +#define CSL_UNCERT 32 + +#define RADIO_SYMBOLS_PER_OCTET (2) + +typedef enum mcxw_radio_state { + RADIO_STATE_DISABLED = 0, + RADIO_STATE_SLEEP = 1, + RADIO_STATE_RECEIVE = 2, + RADIO_STATE_TRANSMIT = 3, + RADIO_STATE_INVALID = 255, +} mcxw_radio_state; + +typedef struct mcxw_rx_frame { + uint8_t *psdu; + uint8_t length; + int8_t rssi; + uint8_t lqi; + uint32_t timestamp; + bool ack_fpb; + bool ack_seb; + uint64_t time; + void *phy_buffer; + uint8_t channel; +} mcxw_rx_frame; + +typedef struct mcxw_tx_frame { + uint8_t *psdu; + uint8_t length; + uint32_t tx_delay; + uint32_t tx_delay_base; + bool sec_processed; + bool hdr_updated; +} mcxw_tx_frame; + +struct mcxw_context { + /* Pointer to the network interface. */ + struct net_if *iface; + /* Pointer to the LPTMR counter device structure*/ + const struct device *counter; + /* 802.15.4 HW address. */ + uint8_t mac[8]; + /* RX thread stack. */ + K_KERNEL_STACK_MEMBER(rx_stack, CONFIG_IEEE802154_MCXW_RX_STACK_SIZE); + /* RX thread control block. */ + struct k_thread rx_thread; + /* RX message queue */ + struct k_msgq rx_msgq; + /* RX message queue buffer */ + char rx_msgq_buffer[NMAX_RXRING_BUFFERS * sizeof(mcxw_rx_frame)]; + /* TX synchronization semaphore */ + struct k_sem tx_wait; + /* TX synchronization semaphore */ + struct k_sem cca_wait; + /* Radio state */ + mcxw_radio_state state; + /* Pan ID */ + uint16_t pan_id; + /* Channel */ + uint8_t channel; + /* Maximum energy detected during ED scan */ + int8_t max_ed; + /* TX power level */ + int8_t tx_pwr_lvl; + /* Enery detect */ + energy_scan_done_cb_t energy_scan_done; + /* TX Status */ + int tx_status; + /* TX frame */ + mcxw_tx_frame tx_frame; + /* TX data */ + uint8_t tx_data[sizeof(macToPdDataMessage_t) + IEEE802154_MAX_PHY_PACKET_SIZE]; + /* RX mode */ + uint32_t rx_mode; + /* RX ACK buffers */ + mcxw_rx_frame rx_ack_frame; + /* RX ACK data */ + uint8_t rx_ack_data[IEEE802154_MAX_PHY_PACKET_SIZE]; + /* CSL period */ + uint32_t csl_period; + /* CSL sample time in microseconds */ + uint32_t csl_sample_time; + /* PHY context */ + uint8_t ot_phy_ctx; +}; + +#endif /* ZEPHYR_DRIVERS_IEEE802154_IEEE802154_MCXW_H_ */ diff --git a/drivers/ieee802154/ieee802154_mcxw_utils.c b/drivers/ieee802154/ieee802154_mcxw_utils.c new file mode 100644 index 000000000000..0367f8a26b64 --- /dev/null +++ b/drivers/ieee802154/ieee802154_mcxw_utils.c @@ -0,0 +1,356 @@ +/* ieee802154_mcxw_utils.c - NXP MCXW 802.15.4 driver utils*/ + +/* + * Copyright 2025 NXP + * + * SPDX-License-Identifier: Apache-2.0 + * + */ + +#include +#include +#include +#include + +#include + +#include "ieee802154_mcxw_utils.h" + +/* TODO IEEE 802.15.4 MAC Multipurpose frame format */ +/* TODO add function checks */ + +enum offset_fcf_fields { + OffsetFrameType = 0x00, + OffsetSecurityEnabled = 0x03, + OffsetFramePending = 0x04, + OffsetAR = 0x05, + OffsetPanIdCompression = 0x06, + OffsetSeqNumberSuppression = 0x08, + OffsetIEPresent = 0x09, + OffsetDstAddrMode = 0x0A, + OffsetFrameVersion = 0x0C, + OffsetSrcAddrMode = 0x0E, +}; + +enum mask_fcf_fields { + MaskFrameType = (0x7 << OffsetFrameType), + MaskSecurityEnabled = (0x01 << OffsetSecurityEnabled), + MaskFramePending = (0x01 << OffsetFramePending), + MaskAR = (0x01 << OffsetAR), + MaskPanIdCompression = (0x01 << OffsetPanIdCompression), + MaskSeqNumberSuppression = (0x01 << OffsetSeqNumberSuppression), + MaskIEPresent = (0x01 << OffsetIEPresent), + MaskDstAddrMode = (0x03 << OffsetDstAddrMode), + MaskFrameVersion = (0x03 << OffsetFrameVersion), + MaskSrcAddrMode = (0x03 << OffsetSrcAddrMode), +}; + +enum modes_dst_addr { + ModeDstAddrNone = 0x00, + ModeDstAddrShort = (0x02 << OffsetDstAddrMode), + ModeDstAddrExt = (0x03 << OffsetDstAddrMode), +}; + +enum version_frame { + VersionIeee2003 = 0x00, + VersionIeee2006 = 0x01, + VersionIeee2015 = 0x02, +}; + +enum modes_src_addr { + ModeSrcAddrNone = 0x00, + ModeSrcAddrShort = (0x02 << OffsetSrcAddrMode), + ModeSrcAddrExt = (0x03 << OffsetSrcAddrMode), +}; + +enum offset_scf_fields { + OffsetSecurityLevel = 0x00, + OffsetKeyIdMode = 0x03, + OffsetFrameCntSuppression = 0x05, + OffsetASNinNonce = 0x06, +}; + +enum mask_scf_fields { + MaskSecurityLevel = (0x07 << OffsetSecurityLevel), + MaskKeyIdMode = (0x03 << OffsetKeyIdMode), + MaskFrameCntSuppression = (0x1 << OffsetFrameCntSuppression), + MaskASNinNonce = (0x01 << OffsetASNinNonce), +}; + +static uint16_t get_frame_control_field(uint8_t *pdu, uint16_t length) +{ + if ((pdu == NULL) || (length < 3)) { + return 0x00; + } + + return (uint16_t)(pdu[0] | (pdu[1] << 8)); +} + +static bool is_security_enabled(uint16_t fcf) +{ + if (fcf) { + return (bool)(fcf & MaskSecurityEnabled); + } + + return false; +} + +static bool is_ie_present(uint16_t fcf) +{ + if (fcf) { + return (bool)(fcf & MaskIEPresent); + } + + return false; +} + +static uint8_t get_frame_version(uint16_t fcf) +{ + if (fcf) { + return (uint8_t)((fcf & MaskFrameVersion) >> OffsetFrameVersion); + } + + return 0xFF; +} + +static bool is_frame_version_2015_fcf(uint16_t fcf) +{ + if (fcf) { + return get_frame_version(fcf) == VersionIeee2015; + } + + return false; +} + +bool is_frame_version_2015(uint8_t *pdu, uint16_t length) +{ + uint16_t fcf = get_frame_control_field(pdu, length); + + if (fcf) { + return get_frame_version(fcf) == VersionIeee2015; + } + + return false; +} + +static bool is_sequence_number_suppression(uint16_t fcf) +{ + if (fcf) { + return (bool)(fcf & MaskSeqNumberSuppression); + } + + return false; +} + +static bool is_dst_panid_present(uint16_t fcf) +{ + bool present; + + if (!fcf) { + return false; + } + + if (is_frame_version_2015_fcf(fcf)) { + switch (fcf & (MaskDstAddrMode | MaskSrcAddrMode | MaskPanIdCompression)) { + case (ModeDstAddrNone | ModeSrcAddrNone): + case (ModeDstAddrShort | ModeSrcAddrNone | MaskPanIdCompression): + case (ModeDstAddrExt | ModeSrcAddrNone | MaskPanIdCompression): + case (ModeDstAddrNone | ModeSrcAddrShort): + case (ModeDstAddrNone | ModeSrcAddrExt): + case (ModeDstAddrNone | ModeSrcAddrShort | MaskPanIdCompression): + case (ModeDstAddrNone | ModeSrcAddrExt | MaskPanIdCompression): + case (ModeDstAddrExt | ModeSrcAddrExt | MaskPanIdCompression): + present = false; + break; + default: + present = true; + } + } else { + present = (bool)(fcf & MaskDstAddrMode); + } + + return present; +} + +static bool is_src_panid_present(uint16_t fcf) +{ + bool present; + + if (!fcf) { + return false; + } + + if (is_frame_version_2015_fcf(fcf)) { + switch (fcf & (MaskDstAddrMode | MaskSrcAddrMode | MaskPanIdCompression)) { + case (ModeDstAddrNone | ModeSrcAddrShort): + case (ModeDstAddrNone | ModeSrcAddrExt): + case (ModeDstAddrShort | ModeSrcAddrShort): + case (ModeDstAddrShort | ModeSrcAddrExt): + case (ModeDstAddrExt | ModeSrcAddrShort): + present = true; + break; + default: + present = false; + } + + } else { + present = ((fcf & MaskSrcAddrMode) != 0) && ((fcf & MaskPanIdCompression) == 0); + } + + return present; +} + +static uint8_t calculate_addr_field_size(uint16_t fcf) +{ + uint8_t size = 2; + + if (!fcf) { + return 0; + } + + if (!is_sequence_number_suppression(fcf)) { + size += 1; + } + + if (is_dst_panid_present(fcf)) { + size += 2; + } + + /* destination addressing mode */ + switch (fcf & MaskDstAddrMode) { + case ModeDstAddrShort: + size += 2; + break; + case ModeDstAddrExt: + size += 8; + break; + default: + break; + } + + if (is_src_panid_present(fcf)) { + size += 2; + } + + /* source addressing mode */ + switch (fcf & MaskSrcAddrMode) { + case ModeSrcAddrShort: + size += 2; + break; + case ModeSrcAddrExt: + size += 8; + break; + default: + break; + } + + return size; +} + +static uint8_t get_keyid_mode(uint8_t *pdu, uint16_t length) +{ + uint16_t fcf = get_frame_control_field(pdu, length); + uint8_t ash_start; + + if (is_security_enabled(fcf)) { + ash_start = calculate_addr_field_size(fcf); + return (uint8_t)((pdu[ash_start] & MaskKeyIdMode) >> OffsetKeyIdMode); + } + + return 0xFF; +} + +bool is_keyid_mode_1(uint8_t *pdu, uint16_t length) +{ + uint8_t key_mode = get_keyid_mode(pdu, length); + + if (key_mode == 0x01) { + return true; + } + + return false; +} + +void set_frame_counter(uint8_t *pdu, uint16_t length, uint32_t fc) +{ + uint16_t fcf = get_frame_control_field(pdu, length); + + if (is_security_enabled(fcf)) { + uint8_t ash_start = calculate_addr_field_size(fcf); + uint8_t scf = pdu[ash_start]; + + /* check that Frame Counter Suppression is not set */ + if (!(scf & MaskFrameCntSuppression)) { + sys_put_le32(fc, &pdu[ash_start + 1]); + } + } +} + +static uint8_t get_asn_size(uint8_t *pdu, uint16_t length) +{ + uint16_t fcf = get_frame_control_field(pdu, length); + + if (is_security_enabled(fcf)) { + uint8_t ash_start = calculate_addr_field_size(fcf); + uint8_t scf = pdu[ash_start]; + uint8_t size = 1; + + /* Frame Counter Suppression is not set */ + if (!(scf & MaskFrameCntSuppression)) { + size += 4; + } + + uint8_t key_mode = get_keyid_mode(pdu, length); + + switch (key_mode) { + case 0x01: + size += 1; + break; + case 0x02: + size += 5; + case 0x03: + size += 9; + default: + break; + } + + return size; + } + return 0; +} + +static uint8_t *get_csl_ie_content_start(uint8_t *pdu, uint16_t length) +{ + uint16_t fcf = get_frame_control_field(pdu, length); + + if (is_ie_present(fcf)) { + uint8_t ie_start_idx = calculate_addr_field_size(fcf) + get_asn_size(pdu, length); + uint8_t *cur_ie = &pdu[ie_start_idx]; + + uint8_t ie_header = (uint16_t)(cur_ie[0] | (cur_ie[1] << 8)); + uint8_t ie_length = ie_header & 0x7F; + uint8_t ie_el_id = ie_header & 0x7F80; + + while ((ie_el_id != 0x7e) && (ie_el_id != 0x7f)) { + if (ie_el_id == 0x1a) { + return (cur_ie + 2); + } + cur_ie += (2 + ie_length); + ie_header = (uint16_t)(cur_ie[0] | (cur_ie[1] << 8)); + ie_length = ie_header & 0x7F; + ie_el_id = ie_header & 0x7F80; + } + } + + return NULL; +} + +void set_csl_ie(uint8_t *pdu, uint16_t length, uint16_t period, uint16_t phase) +{ + uint8_t *csl_ie_content = get_csl_ie_content_start(pdu, length); + + if (csl_ie_content) { + sys_put_le16(phase, csl_ie_content); + sys_put_le16(period, csl_ie_content + 2); + } +} diff --git a/drivers/ieee802154/ieee802154_mcxw_utils.h b/drivers/ieee802154/ieee802154_mcxw_utils.h new file mode 100644 index 000000000000..2b37538eeeb0 --- /dev/null +++ b/drivers/ieee802154/ieee802154_mcxw_utils.h @@ -0,0 +1,16 @@ +/* ieee802154_mcxw_utils.h - NXP MCXW 802.15.4 driver utils*/ + +/* + * Copyright 2025 NXP + * + * SPDX-License-Identifier: Apache-2.0 + * + */ + +bool is_frame_version_2015(uint8_t *pdu, uint16_t length); + +bool is_keyid_mode_1(uint8_t *pdu, uint16_t length); + +void set_frame_counter(uint8_t *pdu, uint16_t length, uint32_t fc); + +void set_csl_ie(uint8_t *pdu, uint16_t length, uint16_t period, uint16_t phase); diff --git a/dts/arm/nxp/nxp_mcxw7x_common.dtsi b/dts/arm/nxp/nxp_mcxw7x_common.dtsi index dba26683e83c..7e0a42c40572 100644 --- a/dts/arm/nxp/nxp_mcxw7x_common.dtsi +++ b/dts/arm/nxp/nxp_mcxw7x_common.dtsi @@ -19,6 +19,8 @@ chosen { zephyr,bt-hci = &hci; + zephyr,ieee802154 = &ieee802154; + zephyr,entropy = &trng; zephyr,nbu = &nbu; }; @@ -286,6 +288,14 @@ compatible = "nxp,hci-ble"; }; + ieee802154: ieee802154 { + compatible = "nxp,mcxw-ieee802154"; + }; + + trng: trng { + compatible = "nxp,ele-trng"; + }; + flexcan0: can@3b000 { compatible = "nxp,flexcan"; reg = <0x3b000 0x3080>; diff --git a/dts/bindings/ieee802154/nxp,mcxw-ieee802154.yaml b/dts/bindings/ieee802154/nxp,mcxw-ieee802154.yaml new file mode 100644 index 000000000000..cb76bf63ac8c --- /dev/null +++ b/dts/bindings/ieee802154/nxp,mcxw-ieee802154.yaml @@ -0,0 +1,8 @@ +# Copyright (c) 2025, NXP +# SPDX-License-Identifier: Apache-2.0 + +description: NXP MCXW71 IEEE 802.15.4 node + +compatible: "nxp,mcxw-ieee802154" + +include: base.yaml diff --git a/dts/bindings/rng/nxp,ele-trng.yaml b/dts/bindings/rng/nxp,ele-trng.yaml new file mode 100644 index 000000000000..c501461da760 --- /dev/null +++ b/dts/bindings/rng/nxp,ele-trng.yaml @@ -0,0 +1,8 @@ +# Copyright (c) 2025, NXP +# SPDX-License-Identifier: Apache-2.0 + +description: NXP ELE (EdgeLock secure enclave) TRNG (True Random Number Generator) + +compatible: "nxp,ele-trng" + +include: base.yaml diff --git a/samples/net/sockets/echo_client/boards/frdm_mcxw71.conf b/samples/net/sockets/echo_client/boards/frdm_mcxw71.conf new file mode 100644 index 000000000000..36b66408f867 --- /dev/null +++ b/samples/net/sockets/echo_client/boards/frdm_mcxw71.conf @@ -0,0 +1,4 @@ +CONFIG_COUNTER=y + +CONFIG_OPENTHREAD_MAX_IP_ADDR_PER_CHILD=4 +CONFIG_OPENTHREAD_MAX_CHILDREN=30 diff --git a/samples/net/sockets/echo_client/boards/frdm_mcxw71.overlay b/samples/net/sockets/echo_client/boards/frdm_mcxw71.overlay new file mode 100644 index 000000000000..71da6a25089f --- /dev/null +++ b/samples/net/sockets/echo_client/boards/frdm_mcxw71.overlay @@ -0,0 +1,20 @@ +/* + * Copyright 2023-2025 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +&stcm { + ranges = <0x0 0x30000000 DT_SIZE_K(112)>; + + stcm1: system_memory@1a000 { + compatible = "zephyr,memory-region","mmio-sram"; + reg = <0x1a000 DT_SIZE_K(4)>; + zephyr,memory-region = "RetainedMem"; + }; +}; + +&stcm0 { + /* With only the first 64KB having ECC */ + reg = <0x0 DT_SIZE_K(108)>; +}; diff --git a/samples/net/sockets/echo_client/sample.yaml b/samples/net/sockets/echo_client/sample.yaml index ccde0349ed4d..6ded48ae319f 100644 --- a/samples/net/sockets/echo_client/sample.yaml +++ b/samples/net/sockets/echo_client/sample.yaml @@ -70,33 +70,21 @@ tests: sample.net.sockets.echo_client.nrf_802154: extra_args: EXTRA_CONF_FILE="overlay-802154.conf" platform_allow: nrf52840dk/nrf52840 - sample.net.sockets.echo_client.nrf_openthread: + sample.net.sockets.echo_client.openthread: extra_args: EXTRA_CONF_FILE="overlay-ot.conf" slow: true tags: - net - openthread - platform_allow: nrf52840dk/nrf52840 + platform_allow: + - frdm_kw41z + - frdm_mcxw71 + - nrf52840dk/nrf52840 + - tlsr9518adk80d filter: CONFIG_FULL_LIBC_SUPPORTED and not CONFIG_NATIVE_LIBC sample.net.sockets.echo_client.b91_802154: extra_args: EXTRA_CONF_FILE="overlay-802154.conf" platform_allow: tlsr9518adk80d - sample.net.sockets.echo_client.b91_openthread: - extra_args: EXTRA_CONF_FILE="overlay-ot.conf" - slow: true - tags: - - net - - openthread - platform_allow: tlsr9518adk80d - filter: CONFIG_FULL_LIBC_SUPPORTED and not CONFIG_NATIVE_LIBC - sample.net.sockets.echo_client.kw41z_openthread: - extra_args: EXTRA_CONF_FILE="overlay-ot.conf" - slow: true - tags: - - net - - openthread - platform_allow: frdm_kw41z - filter: CONFIG_FULL_LIBC_SUPPORTED and not CONFIG_NATIVE_LIBC sample.net.sockets.echo_client.rw612_openthread_rcp_host: build_only: true extra_args: diff --git a/samples/net/sockets/echo_server/boards/frdm_mcxw71.conf b/samples/net/sockets/echo_server/boards/frdm_mcxw71.conf new file mode 100644 index 000000000000..36b66408f867 --- /dev/null +++ b/samples/net/sockets/echo_server/boards/frdm_mcxw71.conf @@ -0,0 +1,4 @@ +CONFIG_COUNTER=y + +CONFIG_OPENTHREAD_MAX_IP_ADDR_PER_CHILD=4 +CONFIG_OPENTHREAD_MAX_CHILDREN=30 diff --git a/samples/net/sockets/echo_server/boards/frdm_mcxw71.overlay b/samples/net/sockets/echo_server/boards/frdm_mcxw71.overlay new file mode 100644 index 000000000000..71da6a25089f --- /dev/null +++ b/samples/net/sockets/echo_server/boards/frdm_mcxw71.overlay @@ -0,0 +1,20 @@ +/* + * Copyright 2023-2025 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +&stcm { + ranges = <0x0 0x30000000 DT_SIZE_K(112)>; + + stcm1: system_memory@1a000 { + compatible = "zephyr,memory-region","mmio-sram"; + reg = <0x1a000 DT_SIZE_K(4)>; + zephyr,memory-region = "RetainedMem"; + }; +}; + +&stcm0 { + /* With only the first 64KB having ECC */ + reg = <0x0 DT_SIZE_K(108)>; +}; diff --git a/samples/net/sockets/echo_server/sample.yaml b/samples/net/sockets/echo_server/sample.yaml index deaa6247aa49..9595e2444439 100644 --- a/samples/net/sockets/echo_server/sample.yaml +++ b/samples/net/sockets/echo_server/sample.yaml @@ -88,29 +88,17 @@ tests: - native_sim/native/64 - native_posix - native_posix/native/64 - sample.net.sockets.echo_server.nrf_openthread: + sample.net.sockets.echo_server.openthread: extra_args: EXTRA_CONF_FILE="overlay-ot.conf" slow: true tags: - net - openthread - platform_allow: nrf52840dk/nrf52840 - filter: CONFIG_FULL_LIBC_SUPPORTED and not CONFIG_NATIVE_LIBC - sample.net.sockets.echo_server.b91_openthread: - extra_args: EXTRA_CONF_FILE="overlay-ot.conf" - slow: true - tags: - - net - - openthread - platform_allow: tlsr9518adk80d - filter: CONFIG_FULL_LIBC_SUPPORTED and not CONFIG_NATIVE_LIBC - sample.net.sockets.echo_server.kw41z_openthread: - extra_args: EXTRA_CONF_FILE="overlay-ot.conf" - slow: true - tags: - - net - - openthread - platform_allow: frdm_kw41z + platform_allow: + - frdm_kw41z + - frdm_mcxw71 + - nrf52840dk/nrf52840 + - tlsr9518adk80d filter: CONFIG_FULL_LIBC_SUPPORTED and not CONFIG_NATIVE_LIBC sample.net.sockets.echo_server.rw612_openthread_rcp_host: build_only: true diff --git a/soc/nxp/mcx/mcxw/CMakeLists.txt b/soc/nxp/mcx/mcxw/CMakeLists.txt index 5109d3175ab3..aad110de351b 100644 --- a/soc/nxp/mcx/mcxw/CMakeLists.txt +++ b/soc/nxp/mcx/mcxw/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright 2023-2024 NXP +# Copyright 2023-2025 NXP # # SPDX-License-Identifier: Apache-2.0 @@ -14,4 +14,6 @@ zephyr_include_directories(.) set(SOC_LINKER_SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/linker.ld CACHE INTERNAL "") -zephyr_linker_sources_ifdef(CONFIG_BT RAM_SECTIONS sections.ld) +if (CONFIG_BT OR CONFIG_IEEE802154) + zephyr_linker_sources(RAM_SECTIONS sections.ld) +endif() diff --git a/soc/nxp/mcx/mcxw/Kconfig.defconfig b/soc/nxp/mcx/mcxw/Kconfig.defconfig index 5f7ad6800e62..d04ed76905a8 100644 --- a/soc/nxp/mcx/mcxw/Kconfig.defconfig +++ b/soc/nxp/mcx/mcxw/Kconfig.defconfig @@ -1,4 +1,4 @@ -# Copyright 2023-2024 NXP +# Copyright 2023-2025 NXP # SPDX-License-Identifier: Apache-2.0 if SOC_SERIES_MCXW @@ -45,4 +45,14 @@ config HCI_NXP_RX_THREAD default y endif # BT + +if NET_L2_IEEE802154 || NET_L2_OPENTHREAD + +# Include intercore messaging component +config NXP_RF_IMU + default y + +endif # NET_L2_IEEE802154 || NET_L2_OPENTHREAD + + endif # SOC_SERIES_MCXW diff --git a/west.yml b/west.yml index f446f270996f..d6f104e3e320 100644 --- a/west.yml +++ b/west.yml @@ -203,7 +203,7 @@ manifest: groups: - hal - name: hal_nxp - revision: 5e1979c7dd94dc84a09f5deb1b59ab83c94d5e83 + revision: 605560a680318ec417c1ee38d05ac092c383dc1d path: modules/hal/nxp groups: - hal