diff --git a/MAINTAINERS.yml b/MAINTAINERS.yml index 08d6a1d5eee40..43ff3532068c5 100644 --- a/MAINTAINERS.yml +++ b/MAINTAINERS.yml @@ -4359,6 +4359,7 @@ STM32 Platforms: files-exclude: - boards/st/*wb*/ - drivers/bluetooth/hci/*stm32*.c + - drivers/ieee802154/*stm32*.c - soc/st/stm32/stm32wb*/ labels: - "platform: STM32" @@ -4372,7 +4373,6 @@ STM32 Wireless Platforms: collaborators: - asm5878 - HoZHel - - benothmn-st - mathieuchopstm files: - boards/shields/x_nucleo_bnrg2a1/ @@ -4381,12 +4381,13 @@ STM32 Wireless Platforms: - boards/st/*wb*/ - drivers/bluetooth/hci/*stm32*.c - drivers/bluetooth/hci/hci_spi_st.c + - drivers/ieee802154/*stm32*.c - soc/st/stm32/stm32wb*/ labels: - "platform: STM32" description: >- - STM32WB SOCs, dts files and related drivers. STM32WB development boards - and ST bluetooth shields. + STM32WB and STM32WBA SOCs, dts files and related drivers. + STM32WB and STM32WBA development boards and ST bluetooth shields. Samples: status: maintained diff --git a/boards/st/nucleo_wba65ri/doc/nucleo_wba65ri.rst b/boards/st/nucleo_wba65ri/doc/nucleo_wba65ri.rst index d06053983ba99..6a24e7a250b8b 100644 --- a/boards/st/nucleo_wba65ri/doc/nucleo_wba65ri.rst +++ b/boards/st/nucleo_wba65ri/doc/nucleo_wba65ri.rst @@ -152,6 +152,19 @@ Supported Features .. zephyr:board-supported-hw:: +Bluetooth and IEEE 802.15.4 support +----------------------------------- + +BLE and IEEE 802.15.4 support are enabled on nucleo_wba65ri. To build a zephyr sample using this board +you first need to install Bluetooth and/or IEEE 802.15.4 Controller libraries available in Zephyr as +binary blobs. + +To fetch Binary Blobs: + +.. code-block:: console + + west blobs fetch hal_stm32 + Connections and IOs =================== diff --git a/boards/st/nucleo_wba65ri/nucleo_wba65ri.dts b/boards/st/nucleo_wba65ri/nucleo_wba65ri.dts index 885aa269f7017..0d0742d7e2864 100644 --- a/boards/st/nucleo_wba65ri/nucleo_wba65ri.dts +++ b/boards/st/nucleo_wba65ri/nucleo_wba65ri.dts @@ -158,6 +158,10 @@ stm32_lp_tick_source: &lptim1 { status = "okay"; }; +&ieee802154 { + status = "okay"; +}; + &flash0 { partitions { compatible = "fixed-partitions"; diff --git a/drivers/ieee802154/CMakeLists.txt b/drivers/ieee802154/CMakeLists.txt index f328ad96b2ac1..2026dae3c4d01 100644 --- a/drivers/ieee802154/CMakeLists.txt +++ b/drivers/ieee802154/CMakeLists.txt @@ -20,4 +20,5 @@ zephyr_library_sources_ifdef(CONFIG_IEEE802154_MCXW ieee802154_mcxw.c ieee8021 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) +zephyr_library_sources_ifdef(CONFIG_IEEE802154_STM32WBA ieee802154_stm32wba.c) zephyr_library_sources_ifdef(CONFIG_IEEE802154_TELINK_B91 ieee802154_b91.c) diff --git a/drivers/ieee802154/Kconfig b/drivers/ieee802154/Kconfig index c982d9562dea7..1f13d654eaff2 100644 --- a/drivers/ieee802154/Kconfig +++ b/drivers/ieee802154/Kconfig @@ -91,6 +91,8 @@ source "drivers/ieee802154/Kconfig.dw1000" source "drivers/ieee802154/Kconfig.esp32" +source "drivers/ieee802154/Kconfig.stm32wba" + source "drivers/ieee802154/Kconfig.uart_pipe" config IEEE802154_CSL_ENDPOINT diff --git a/drivers/ieee802154/Kconfig.stm32wba b/drivers/ieee802154/Kconfig.stm32wba new file mode 100644 index 0000000000000..cb1b8e0280d67 --- /dev/null +++ b/drivers/ieee802154/Kconfig.stm32wba @@ -0,0 +1,69 @@ +# STMicroelectronics STM32WBAxx 802.15.4 configuration options + +# Copyright (c) 2025 STMicroelectronics + +menuconfig IEEE802154_STM32WBA + bool "STM32WBA series IEEE 802.15.4 Driver" + default y + depends on DT_HAS_ST_STM32WBA_IEEE802154_ENABLED + +if IEEE802154_STM32WBA + +config IEEE802154_STM32WBA_RX_BUFFERS + int "STM32WBA 802.15.4 receive buffers" + default 16 + range 16 128 + help + Number of buffers in 802.15.4 driver serialization host's receive queue. + Increase this number if you are expected to be in a noisy environment, handling many + end-devices and you have sufficient RAM to allocate memory to buffers. + If this value is increased, IEEE802154_STM32WBA_RX_STACK_SIZE value might require + to be updated as well. + +config IEEE802154_STM32WBA_RX_STACK_SIZE + int "Driver RX thread stack size" + default 800 + help + This option sets the driver's stack size for its internal RX thread. + The default value should be sufficient, but in case it proves to be + too low, adjust as needed. + +config IEEE802154_STM32WBA_CSMA_CA_ENABLED + bool "CSMA-CA configuration" + default y + help + This option enables the CSMA-CA feature. + +config IEEE802154_STM32WBA_INIT_PRIO + int "STM32WBA IEEE 802.15.4 initialization priority" + default 80 + help + Set the initialization priority number. Do not mess with it unless + you know what you are doing. + +config IEEE802154_STM32WBA_DELAY_TRX_ACC + int "Clock accuracy for delayed operations" + default 20 + range 0 255 + help + Accuracy of the clock used for scheduling radio delayed operations (delayed transmission + or delayed reception), in ppm. + +config IEEE802154_STM32WBA_LOG_RX_FAILURES + bool "Frame reception failures logging" + help + There are few cases where the frame reception failure can happen because of + internal causes. These cases are reported forward by general code error. + + This option enables logging the reason of frame reception failure. + It can be helpful for the network traffic analysis but it generates also + a lot of log records in a stressed environment. + +config NET_L2_CUSTOM_IEEE802154_STM32WBA + bool "Use ST MAC as L2" + default y + depends on NET_L2_CUSTOM_IEEE802154 + help + Custom STM32WBA series IEEE 802.15.4 L2 Implementation. + +endif # IEEE802154_STM32WBA diff --git a/drivers/ieee802154/ieee802154_stm32wba.c b/drivers/ieee802154/ieee802154_stm32wba.c new file mode 100644 index 0000000000000..a796fcc5c2278 --- /dev/null +++ b/drivers/ieee802154/ieee802154_stm32wba.c @@ -0,0 +1,1070 @@ +/* ieee802154_stm32wba.c - STM32WBxx 802.15.4 driver */ + +/* + * Copyright (c) 2025 STMicroelectronics + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(CONFIG_NET_L2_OPENTHREAD) +#include +#include +#endif + +#ifdef CONFIG_PM_DEVICE +#include +#include +#include +#include "app_conf.h" +#include "linklayer_plat.h" +#include +#include +#endif + +#include +#include "ieee802154_stm32wba.h" +#include + +#define DT_DRV_COMPAT st_stm32wba_ieee802154 + +#define LOG_MODULE_NAME ieee802154_stm32wba + +#define LOG_LEVEL _LOG_LEVEL_RESOLVE(CONFIG_IEEE802154_DRIVER_LOG_LEVEL) + +LOG_MODULE_REGISTER(LOG_MODULE_NAME, LOG_LEVEL); + +extern uint32_t llhwc_cmn_is_dp_slp_enabled(void); + +static struct stm32wba_802154_data_t stm32wba_802154_data; + +/* driver-allocated attribute memory - constant across all driver instances */ +IEEE802154_DEFINE_PHY_SUPPORTED_CHANNELS(drv_attr, 11, 26); + +#define NSEC_PER_TEN_SYMBOLS (10 * IEEE802154_PHY_OQPSK_780_TO_2450MHZ_SYMBOL_PERIOD_NS) + +#define MAX_CSMA_BACKOFF 4 +#define MAX_FRAME_RETRY 3 +#define CCA_THRESHOLD (-70) + +static void stm32wba_802154_receive_done(uint8_t *p_buffer, + stm32wba_802154_ral_receive_done_metadata_t *p_metadata); +void stm32wba_802154_tx_ack_started(bool ack_fpb, bool ack_seb); +static void stm32wba_802154_transmit_done( + uint8_t *p_frame, + stm32wba_802154_ral_tx_error_t error, + const stm32wba_802154_ral_transmit_done_metadata_t *p_metadata); +static void stm32wba_802154_cca_done(uint8_t error); +static void stm32wba_802154_energy_scan_done(int8_t rssi_result); + +static const struct device *stm32wba_802154_get_device(void) +{ + LOG_DBG("Getting device instance"); + + return net_if_get_device(stm32wba_802154_data.iface); +} + +static void stm32wba_802154_get_eui64(uint8_t *mac) +{ + stm32wba_802154_ral_eui64_get(mac); + + LOG_DBG("Device EUI64: %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x", + mac[0], mac[1], mac[2], mac[3], mac[4], mac[5], mac[6], mac[7]); +} + +static void stm32wba_802154_rx_thread(void *arg1, void *arg2, void *arg3) +{ + struct stm32wba_802154_data_t *stm32wba_radio = (struct stm32wba_802154_data_t *)arg1; + struct net_pkt *pkt; + struct stm32wba_802154_rx_frame *rx_frame; + uint8_t pkt_len; + + ARG_UNUSED(arg2); + ARG_UNUSED(arg3); + + LOG_DBG("RX thread started"); + + while (1) { + pkt = NULL; + + rx_frame = k_fifo_get(&stm32wba_radio->rx_fifo, K_FOREVER); + + __ASSERT_NO_MSG(rx_frame->psdu != NULL); + + /* Depending on the net L2 layer, the FCS may be included in length or not */ + if (IS_ENABLED(CONFIG_IEEE802154_L2_PKT_INCL_FCS)) { + pkt_len = rx_frame->length; + } else { + pkt_len = rx_frame->length - IEEE802154_FCS_LENGTH; + } + +#if defined(CONFIG_NET_BUF_DATA_SIZE) + __ASSERT_NO_MSG(pkt_len <= CONFIG_NET_BUF_DATA_SIZE); +#endif + + LOG_DBG("Frame received - sequence nb: %u, length: %u", rx_frame->psdu[2], + pkt_len); + + /* Block the RX thread until net_pkt is available, so that we + * don't drop already ACKed frame in case of temporary net_pkt + * scarcity. The STM32WBA 802154 radio driver will accumulate any + * incoming frames until it runs out of internal buffers (and + * thus stops acknowledging consecutive frames). + */ + pkt = net_pkt_rx_alloc_with_buffer(stm32wba_radio->iface, pkt_len, + AF_UNSPEC, 0, K_FOREVER); + + if (net_pkt_write(pkt, rx_frame->psdu, pkt_len) != 0) { + LOG_ERR("Failed to write packet data"); + net_pkt_unref(pkt); + } else { + 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_L2_OPENTHREAD) + net_pkt_set_ieee802154_ack_seb(pkt, rx_frame->ack_seb); +#endif + + if (net_recv_data(stm32wba_radio->iface, pkt) < 0) { + LOG_ERR("Packet dropped by NET stack"); + net_pkt_unref(pkt); + } else { + if (LOG_LEVEL >= LOG_LEVEL_DBG) { + log_stack_usage(&stm32wba_radio->rx_thread); + } + } + } + rx_frame->psdu = NULL; + } +} + +static void stm32wba_802154_receive_failed(stm32wba_802154_ral_rx_error_t error) +{ + const struct device *dev = stm32wba_802154_get_device(); + enum ieee802154_rx_fail_reason reason; + + if (error == STM32WBA_802154_RAL_RX_ERROR_NO_BUFFERS) { + reason = IEEE802154_RX_FAIL_NOT_RECEIVED; + } else { + reason = IEEE802154_RX_FAIL_OTHER; + } + + if (IS_ENABLED(CONFIG_IEEE802154_STM32WBA_LOG_RX_FAILURES)) { + LOG_INF("Receive failed, error = %d", error); + } + + stm32wba_802154_data.last_frame_ack_fpb = false; + stm32wba_802154_data.last_frame_ack_seb = false; + + if (stm32wba_802154_data.event_handler != NULL) { + stm32wba_802154_data.event_handler(dev, IEEE802154_EVENT_RX_FAILED, &reason); + } +} + +#if defined(CONFIG_NET_L2_CUSTOM_IEEE802154) +static int stm32wba_802154_configure_extended(enum ieee802154_stm32wba_config_type type, + const struct ieee802154_stm32wba_config *config) +{ + stm32wba_802154_ral_error_t ret; + + switch (type) { + case IEEE802154_STM32WBA_CONFIG_CCA_THRESHOLD: + LOG_DBG("Setting CCA_THRESHOLD: %d", config->cca_thr); + ret = stm32wba_802154_ral_set_cca_energy_detect_threshold(config->cca_thr); + if (ret != STM32WBA_802154_RAL_ERROR_NONE) { + return -EIO; + } + break; + + case IEEE802154_STM32WBA_CONFIG_CONTINUOUS_RECEPTION: + LOG_DBG("Setting CONTINUOUS_RECEPTION: %u", config->en_cont_rec); + stm32wba_802154_ral_set_continuous_reception(config->en_cont_rec); + break; + + case IEEE802154_STM32WBA_CONFIG_MAX_FRAME_RETRIES: + LOG_DBG("Setting MAX_FRAME_RETRIES: %u", config->max_frm_retries); + stm32wba_802154_ral_set_max_frame_retries(config->max_frm_retries); + break; + + case IEEE802154_STM32WBA_CONFIG_MAX_CSMA_FRAME_RETRIES: + LOG_DBG("Setting MAX_CSMA_FRAME_RETRIES: %u", config->max_csma_frm_retries); + stm32wba_802154_ral_set_max_csma_frame_retries(config->max_csma_frm_retries); + break; + + case IEEE802154_STM32WBA_CONFIG_MIN_CSMA_BE: + LOG_DBG("Setting MIN_CSMA_BE: %u", config->min_csma_be); + stm32wba_802154_ral_set_min_csma_be(config->min_csma_be); + break; + + case IEEE802154_STM32WBA_CONFIG_MAX_CSMA_BE: + LOG_DBG("Setting MAX_CSMA_BE: %u", config->max_csma_be); + stm32wba_802154_ral_set_max_csma_be(config->max_csma_be); + break; + + case IEEE802154_STM32WBA_CONFIG_MAX_CSMA_BACKOFF: + LOG_DBG("Setting MAX_CSMA_BACKOFF: %u", config->max_csma_backoff); + stm32wba_802154_ral_set_max_csma_backoff(config->max_csma_backoff); + break; + + case IEEE802154_STM32WBA_CONFIG_IMPLICIT_BROADCAST: + LOG_DBG("Setting IMPLICIT_BROADCAST: %u", config->impl_brdcast); + stm32wba_802154_ral_set_implicitbroadcast(config->impl_brdcast); + break; + + case IEEE802154_STM32WBA_CONFIG_ANTENNA_DIV: + LOG_DBG("Setting ANTENNA_DIV: %u", config->ant_div); + ret = stm32wba_802154_ral_set_ant_div_enable(config->ant_div); + if (ret != STM32WBA_802154_RAL_ERROR_NONE) { + return -EIO; + } + break; + + case IEEE802154_STM32WBA_CONFIG_RADIO_RESET: + LOG_DBG("Setting RADIO_RESET"); + ret = stm32wba_802154_ral_radio_reset(); + if (ret != STM32WBA_802154_RAL_ERROR_NONE) { + return -EIO; + } + break; + + default: + LOG_ERR("Unsupported configuration type: %d", type); + return -EINVAL; + } + + return 0; +} + +static int stm32wba_802154_attr_get_extended(enum ieee802154_stm32wba_attr attr, + struct ieee802154_stm32wba_attr_value *value) +{ + stm32wba_802154_ral_error_t ret; + + switch ((uint32_t)attr) { + case IEEE802154_STM32WBA_ATTR_CCA_THRESHOLD: + static int8_t l_cca_thr; + + LOG_DBG("Getting CCA_THRESHOLD attribute"); + ret = stm32wba_802154_ral_get_cca_energy_detect_threshold(&l_cca_thr); + if (ret != STM32WBA_802154_RAL_ERROR_NONE) { + return -ENOENT; + } + value->cca_thr = &l_cca_thr; + break; + + case IEEE802154_STM32WBA_ATTR_IEEE_EUI64: + uint8_t l_eui64[sizeof(value->eui64)]; + + LOG_DBG("Getting IEEE_EUI64 attribute"); + stm32wba_802154_get_eui64(l_eui64); + memcpy(value->eui64, l_eui64, sizeof(l_eui64)); + break; + + case IEEE802154_STM32WBA_ATTR_TX_POWER: + static uint8_t l_tx_power; + + LOG_DBG("Getting TX_POWER attribute"); + ret = stm32wba_802154_ral_tx_power_get(&l_tx_power); + if (ret != STM32WBA_802154_RAL_ERROR_NONE) { + return -ENOENT; + } + value->tx_power = &l_tx_power; + break; + + case IEEE802154_STM32WBA_ATTR_RAND_NUM: + static uint8_t l_rand_num; + + LOG_DBG("Getting RAND_NUM attribute"); + ret = stm32wba_802154_ral_mac_gen_rnd_num(&l_rand_num, 1, true); + if (ret != STM32WBA_802154_RAL_ERROR_NONE) { + return -ENOENT; + } + value->rand_num = &l_rand_num; + break; + + default: + LOG_ERR("Unsupported attribute: %u", attr); + return -ENOENT; + } + + return 0; +} +#endif + + +/* Radio device API */ + +static enum ieee802154_hw_caps stm32wba_802154_get_capabilities(const struct device *dev) +{ + ARG_UNUSED(dev); + + return IEEE802154_HW_ENERGY_SCAN | + IEEE802154_HW_FCS | + IEEE802154_HW_FILTER | + IEEE802154_HW_PROMISC | +#ifdef CONFIG_IEEE802154_STM32WBA_CSMA_CA_ENABLED + IEEE802154_HW_CSMA | +#endif + IEEE802154_HW_TX_RX_ACK | + IEEE802154_HW_RETRANSMISSION | + IEEE802154_HW_RX_TX_ACK | +#if (STM32WBA_802154_CSL_TRANSMITTER_ENABLE == 1) + IEEE802154_HW_TXTIME | +#endif +#if (STM32WBA_802154_CSL_RECEIVER_ENABLE == 1) + IEEE802154_HW_RXTIME | +#endif + IEEE802154_HW_SLEEP_TO_TX | + IEEE802154_RX_ON_WHEN_IDLE; +} + +static int stm32wba_802154_cca(const struct device *dev) +{ + struct stm32wba_802154_data_t *stm32wba_radio = dev->data; + + if (stm32wba_802154_ral_cca() != STM32WBA_802154_RAL_ERROR_NONE) { + LOG_DBG("CCA failed"); + return -EBUSY; + } + + /* The STM32WBA driver guarantees that a callback will be called once + * the CCA function is done, thus unlocking the semaphore. + */ + k_sem_take(&stm32wba_radio->cca_wait, K_FOREVER); + + LOG_DBG("Channel free? %d", stm32wba_radio->channel_free); + + return stm32wba_radio->channel_free ? 0 : -EBUSY; +} + +static int stm32wba_802154_set_channel(const struct device *dev, uint16_t channel) +{ + ARG_UNUSED(dev); + + if (channel < drv_attr.phy_channel_range.from_channel || + channel > drv_attr.phy_channel_range.to_channel) { + LOG_ERR("Invalid channel: %u (valid range: %u to %u)", channel, + drv_attr.phy_channel_range.from_channel, + drv_attr.phy_channel_range.to_channel); + return channel < drv_attr.phy_channel_range.from_channel ? -ENOTSUP : -EINVAL; + } + + LOG_DBG("Setting channel %u", channel); + stm32wba_802154_ral_set_channel(channel); + + return 0; +} + +static int stm32wba_802154_energy_scan_start(const struct device *dev, + uint16_t duration, + energy_scan_done_cb_t done_cb) +{ + int err = 0; + stm32wba_802154_ral_error_t ret; + + ARG_UNUSED(dev); + + LOG_DBG("Starting energy scan with duration: %u ms", duration); + + if (stm32wba_802154_data.energy_scan_done_cb == NULL) { + stm32wba_802154_data.energy_scan_done_cb = done_cb; + ret = stm32wba_802154_ral_energy_detection(duration); + if (ret != STM32WBA_802154_RAL_ERROR_NONE) { + LOG_ERR("Energy detection failed, device is busy"); + stm32wba_802154_data.energy_scan_done_cb = NULL; + err = -EBUSY; + } + } else { + LOG_ERR("Energy scan already in progress"); + err = -EALREADY; + } + + return err; +} + +static int stm32wba_802154_filter(const struct device *dev, bool set, + enum ieee802154_filter_type type, + const struct ieee802154_filter *filter) +{ + int err = 0; + + if (!set) { + LOG_ERR("Filter unset, operation is not supported"); + return -ENOTSUP; + } + + switch (type) { + case IEEE802154_FILTER_TYPE_IEEE_ADDR: + LOG_DBG("Setting extended address filter to " + "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x", + filter->ieee_addr[0], filter->ieee_addr[1], filter->ieee_addr[2], + filter->ieee_addr[3], filter->ieee_addr[4], filter->ieee_addr[5], + filter->ieee_addr[6], filter->ieee_addr[7]); + stm32wba_802154_ral_extended_address_set(filter->ieee_addr); + break; + + case IEEE802154_FILTER_TYPE_SHORT_ADDR: + LOG_DBG("Setting short address filter to 0x%04x", filter->short_addr); + stm32wba_802154_ral_short_address_set(filter->short_addr); + break; + + case IEEE802154_FILTER_TYPE_PAN_ID: + LOG_DBG("Setting PAN ID filter to 0x%04x", filter->pan_id); + stm32wba_802154_ral_pan_id_set(filter->pan_id); + break; + + default: + LOG_ERR("Unsupported filter type: %u", type); + err = -ENOTSUP; + break; + } + + return err; +} + +static int stm32wba_802154_set_txpower(const struct device *dev, int16_t dbm) +{ + ARG_UNUSED(dev); + + if (!IN_RANGE(dbm, STM32WBA_PWR_MIN, STM32WBA_PWR_MAX)) { + LOG_ERR("Invalid TX power: %d dBm (valid range: %d to %d dBm)", + dbm, STM32WBA_PWR_MIN, STM32WBA_PWR_MAX); + return -EINVAL; + } + + stm32wba_802154_ral_tx_power_set(dbm); + stm32wba_802154_data.txpwr = dbm; + + LOG_DBG("Setting TX power to %d dBm", dbm); + + return 0; +} + +static int handle_ack(struct stm32wba_802154_data_t *stm32wba_radio) +{ + uint8_t ack_len; + struct net_pkt *ack_pkt; + int err = 0; + + if (IS_ENABLED(CONFIG_IEEE802154_L2_PKT_INCL_FCS)) { + ack_len = stm32wba_radio->ack_frame.length; + } else { + ack_len = stm32wba_radio->ack_frame.length - IEEE802154_FCS_LENGTH; + } + + ack_pkt = net_pkt_rx_alloc_with_buffer(stm32wba_radio->iface, ack_len, + AF_UNSPEC, 0, K_NO_WAIT); + if (ack_pkt == NULL) { + LOG_ERR("No free packet available."); + return -ENOMEM; + } + + if (net_pkt_write(ack_pkt, stm32wba_radio->ack_frame.psdu, ack_len) < 0) { + LOG_ERR("Failed to write to a packet."); + err = -ENOMEM; + goto free_net_ack; + } + + net_pkt_set_ieee802154_lqi(ack_pkt, stm32wba_radio->ack_frame.lqi); + net_pkt_set_ieee802154_rssi_dbm(ack_pkt, stm32wba_radio->ack_frame.rssi); + + net_pkt_cursor_init(ack_pkt); + + if (ieee802154_handle_ack(stm32wba_radio->iface, ack_pkt) != NET_OK) { + LOG_WRN("ACK packet not handled - releasing."); + } else { + LOG_DBG("ACK packet received - sequence nb: %u", + stm32wba_radio->ack_frame.psdu[2]); + } + +free_net_ack: + net_pkt_unref(ack_pkt); + + return err; +} + +static void stm32wba_802154_tx_started(const struct device *dev, + struct net_pkt *pkt, + struct net_buf *frag) +{ + ARG_UNUSED(pkt); + + if (stm32wba_802154_data.event_handler != NULL) { + stm32wba_802154_data.event_handler(dev, IEEE802154_EVENT_TX_STARTED, frag); + } +} + +static stm32wba_802154_ral_error_t stm32wba_802154_transmit(struct net_pkt *pkt, + uint8_t *payload, uint8_t payload_len, bool cca) +{ + LOG_DBG("TX frame - sequence nb: %u, length: %u", payload[2], payload_len); + + stm32wba_802154_ral_transmit_metadata_t metadata = { + .is_secured = net_pkt_ieee802154_frame_secured(pkt), + .dynamic_data_is_set = net_pkt_ieee802154_mac_hdr_rdy(pkt), + .cca = cca, + .tx_power = stm32wba_802154_data.txpwr, + .tx_channel = stm32wba_802154_ral_channel_get() + }; + + return stm32wba_802154_ral_transmit(payload, payload_len, &metadata); +} + +static int stm32wba_802154_tx(const struct device *dev, + enum ieee802154_tx_mode mode, + struct net_pkt *pkt, + struct net_buf *frag) +{ + uint8_t payload_len = frag->len + IEEE802154_FCS_LENGTH; + uint8_t *payload = frag->data; + stm32wba_802154_ral_error_t err; + + if (payload_len > IEEE802154_MTU + IEEE802154_FCS_LENGTH) { + LOG_ERR("Payload too large: %d", payload_len); + return -EMSGSIZE; + } + + memcpy(stm32wba_802154_data.tx_psdu, payload, payload_len); + + /* Reset semaphore in case ACK was received after timeout */ + k_sem_reset(&stm32wba_802154_data.tx_wait); + + switch (mode) { + case IEEE802154_TX_MODE_DIRECT: + case IEEE802154_TX_MODE_CCA: + err = stm32wba_802154_transmit(pkt, + stm32wba_802154_data.tx_psdu, + payload_len, + false); + break; +#ifdef CONFIG_IEEE802154_STM32WBA_CSMA_CA_ENABLED + case IEEE802154_TX_MODE_CSMA_CA: + err = stm32wba_802154_transmit(pkt, + stm32wba_802154_data.tx_psdu, + payload_len, + true); + break; +#endif + default: + LOG_ERR("TX mode %d not supported", mode); + return -ENOTSUP; + } + + if (err != STM32WBA_802154_RAL_ERROR_NONE) { + LOG_ERR("Cannot send frame"); + return -EIO; + } + + stm32wba_802154_tx_started(dev, pkt, frag); + + /* Wait for the callback from the radio driver. */ + k_sem_take(&stm32wba_802154_data.tx_wait, K_FOREVER); + + LOG_DBG("Transmit done, result: %d", stm32wba_802154_data.tx_result); + + net_pkt_set_ieee802154_frame_secured(pkt, stm32wba_802154_data.tx_frame_is_secured); + net_pkt_set_ieee802154_mac_hdr_rdy(pkt, stm32wba_802154_data.tx_frame_mac_hdr_rdy); + + switch (stm32wba_802154_data.tx_result) { + case STM32WBA_802154_RAL_TX_ERROR_NONE: + if (stm32wba_802154_data.ack_frame.psdu == NULL) { + /* No ACK was requested. */ + return 0; + } + /* Handle ACK packet. */ + return handle_ack(&stm32wba_802154_data); + case STM32WBA_802154_RAL_TX_ERROR_NO_MEM: + return -ENOBUFS; + case STM32WBA_802154_RAL_TX_ERROR_BUSY_CHANNEL: + return -EBUSY; + case STM32WBA_802154_RAL_TX_ERROR_NO_ACK: + return -ENOMSG; + case STM32WBA_802154_RAL_TX_ERROR_ABORTED: + default: + return -EIO; + } +} + +static net_time_t stm32wba_802154_get_time(const struct device *dev) +{ + ARG_UNUSED(dev); + + return (net_time_t)stm32wba_802154_ral_time_get() * NSEC_PER_USEC; +} + +static uint8_t stm32wba_802154_get_acc(const struct device *dev) +{ + ARG_UNUSED(dev); + + return CONFIG_IEEE802154_STM32WBA_DELAY_TRX_ACC; +} + +static int stm32wba_802154_start(const struct device *dev) +{ + ARG_UNUSED(dev); + + stm32wba_802154_ral_tx_power_set(stm32wba_802154_data.txpwr); + + if (stm32wba_802154_ral_receive() != STM32WBA_802154_RAL_ERROR_NONE) { + LOG_ERR("Failed to enter receive state"); + return -EIO; + } + + LOG_DBG("802.15.4 radio started on channel: %u", + stm32wba_802154_ral_channel_get()); + + return 0; +} + +static int stm32wba_802154_stop(const struct device *dev) +{ + ARG_UNUSED(dev); + + if (stm32wba_802154_ral_sleep() != STM32WBA_802154_RAL_ERROR_NONE) { + LOG_ERR("Error while stopping radio"); + return -EIO; + } + + LOG_DBG("802.15.4 radio stopped"); + + return 0; +} + +static int stm32wba_802154_driver_init(const struct device *dev) +{ + ARG_UNUSED(dev); + + k_fifo_init(&stm32wba_802154_data.rx_fifo); + k_sem_init(&stm32wba_802154_data.tx_wait, 0, 1); + k_sem_init(&stm32wba_802154_data.cca_wait, 0, 1); + + stm32wba_802154_ral_init(); + stm32wba_802154_ral_promiscuous_set(false); + + stm32wba_802154_data.rx_on_when_idle = true; + + k_thread_create(&stm32wba_802154_data.rx_thread, stm32wba_802154_data.rx_stack, + CONFIG_IEEE802154_STM32WBA_RX_STACK_SIZE, + stm32wba_802154_rx_thread, &stm32wba_802154_data, NULL, NULL, + K_PRIO_COOP(2), 0, K_NO_WAIT); + + k_thread_name_set(&stm32wba_802154_data.rx_thread, "stm32wba_rx"); + + LOG_DBG("STM32WBA 802.15.4 radio initialized"); + + return 0; +} + +static void stm32wba_802154_iface_init(struct net_if *iface) +{ + static struct stm32wba_802154_ral_cbk_dispatch_tbl ll_cbk_dispatch_tbl = { + .stm32wba_802154_ral_cbk_ed_scan_done = stm32wba_802154_energy_scan_done, + .stm32wba_802154_ral_cbk_tx_done = stm32wba_802154_transmit_done, + .stm32wba_802154_ral_cbk_rx_done = stm32wba_802154_receive_done, + .stm32wba_802154_ral_cbk_cca_done = stm32wba_802154_cca_done, + .stm32wba_802154_ral_cbk_tx_ack_started = stm32wba_802154_tx_ack_started, + }; + + link_layer_register_isr(); + +#if !defined(CONFIG_NET_L2_CUSTOM_IEEE802154_STM32WBA) + ll_sys_thread_init(); + /* Intentionally discard the return values */ + (void)stm32wba_802154_ral_set_max_csma_backoff(MAX_CSMA_BACKOFF); + (void)stm32wba_802154_ral_set_max_frame_retries(MAX_FRAME_RETRY); + (void)stm32wba_802154_ral_set_cca_energy_detect_threshold(CCA_THRESHOLD); +#endif + stm32wba_802154_ral_call_back_funcs_init(&ll_cbk_dispatch_tbl); + + stm32wba_802154_get_eui64(stm32wba_802154_data.mac); + net_if_set_link_addr(iface, stm32wba_802154_data.mac, + sizeof(stm32wba_802154_data.mac), NET_LINK_IEEE802154); + + stm32wba_802154_data.iface = iface; + + ieee802154_init(iface); + +#if defined(CONFIG_NET_L2_CUSTOM_IEEE802154_STM32WBA) + ll_sys_mac_cntrl_init(); +#endif +} + +static int stm32wba_802154_set_ack_fpb(const struct ieee802154_config *config) +{ + int err = 0; + stm32wba_802154_ral_error_t ret; + + if (config->ack_fpb.extended) { + ret = stm32wba_802154_ral_pending_bit_for_ext_addr_set( + (const uint8_t *)config->ack_fpb.addr); + if (ret != STM32WBA_802154_RAL_ERROR_NONE) { + LOG_ERR("Failed to set ACK_FPB for extended address: " + "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x", + config->ack_fpb.addr[0], config->ack_fpb.addr[1], + config->ack_fpb.addr[2], config->ack_fpb.addr[3], + config->ack_fpb.addr[4], config->ack_fpb.addr[5], + config->ack_fpb.addr[6], config->ack_fpb.addr[7]); + err = -ENOMEM; + } else { + LOG_DBG("Set ACK_FPB for extended address: " + "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x", + config->ack_fpb.addr[0], config->ack_fpb.addr[1], + config->ack_fpb.addr[2], config->ack_fpb.addr[3], + config->ack_fpb.addr[4], config->ack_fpb.addr[5], + config->ack_fpb.addr[6], config->ack_fpb.addr[7]); + } + } else { + uint16_t short_addr = (uint16_t)config->ack_fpb.addr[0] | + (uint16_t)(config->ack_fpb.addr[1] << 8); + + ret = stm32wba_802154_ral_pending_bit_for_short_addr_set(short_addr); + if (ret != STM32WBA_802154_RAL_ERROR_NONE) { + LOG_ERR("Failed to set ACK_FPB for short address: 0x%02X%02X", + config->ack_fpb.addr[1], + config->ack_fpb.addr[0]); + err = -ENOMEM; + } else { + LOG_DBG("Set ACK_FPB for short address: 0x%02X%02X", + config->ack_fpb.addr[1], + config->ack_fpb.addr[0]); + } + } + return err; +} + +static int stm32wba_802154_clear_ack_fpb(const struct ieee802154_config *config) +{ + int err = 0; + stm32wba_802154_ral_error_t ret; + + if (config->ack_fpb.extended) { + ret = stm32wba_802154_ral_pending_bit_for_ext_addr_clear( + (const uint8_t *)config->ack_fpb.addr); + if (ret != STM32WBA_802154_RAL_ERROR_NONE) { + LOG_ERR("Failed to clear ACK_FPB for extended address: " + "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x", + config->ack_fpb.addr[0], config->ack_fpb.addr[1], + config->ack_fpb.addr[2], config->ack_fpb.addr[3], + config->ack_fpb.addr[4], config->ack_fpb.addr[5], + config->ack_fpb.addr[6], config->ack_fpb.addr[7]); + err = -ENOENT; + } else { + LOG_DBG("Clear ACK_FPB for extended address: " + "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x", + config->ack_fpb.addr[0], config->ack_fpb.addr[1], + config->ack_fpb.addr[2], config->ack_fpb.addr[3], + config->ack_fpb.addr[4], config->ack_fpb.addr[5], + config->ack_fpb.addr[6], config->ack_fpb.addr[7]); + } + } else { + uint16_t short_addr = (uint16_t)config->ack_fpb.addr[0] | + (uint16_t)(config->ack_fpb.addr[1] << 8); + + ret = stm32wba_802154_ral_pending_bit_for_short_addr_clear(short_addr); + if (ret != STM32WBA_802154_RAL_ERROR_NONE) { + LOG_ERR("Failed to clear ACK_FPB for short address: 0x%02X%02X", + config->ack_fpb.addr[1], + config->ack_fpb.addr[0]); + err = -ENOENT; + } else { + LOG_DBG("Clear ACK_FPB for short address: 0x%02X%02X", + config->ack_fpb.addr[1], + config->ack_fpb.addr[0]); + } + } + return err; +} + +static int stm32wba_802154_clear_all_ack_fpb(const struct ieee802154_config *config) +{ + if (config->ack_fpb.extended) { + stm32wba_802154_ral_pending_bit_for_ext_addr_reset(); + LOG_DBG("Clear ACK_FPB for all extended addresses"); + } else { + stm32wba_802154_ral_pending_bit_for_short_addr_reset(); + LOG_DBG("Clear ACK_FPB for all short addresses"); + } + return 0; +} + +static int stm32wba_802154_configure_ack_fpb(const struct ieee802154_config *config) +{ + int ret; + + if (config->ack_fpb.enabled) { + /* Set ACK pending bit for an addr (short or extended) */ + ret = stm32wba_802154_set_ack_fpb(config); + } else if (config->ack_fpb.addr != NULL) { + /* Clear ACK pending bit for an addr (short or extended) */ + ret = stm32wba_802154_clear_ack_fpb(config); + } else { + /* Clear all ACK pending bit for addr (short or extended) */ + ret = stm32wba_802154_clear_all_ack_fpb(config); + } + + return ret; +} + +static int stm32wba_802154_configure(const struct device *dev, + enum ieee802154_config_type type, + const struct ieee802154_config *config) +{ + int ret = 0; + + ARG_UNUSED(dev); + + switch (type) { + case IEEE802154_CONFIG_AUTO_ACK_FPB: + LOG_DBG("Setting AUTO_ACK_FPB: enabled = %d", config->auto_ack_fpb.enabled); + stm32wba_802154_ral_auto_pending_bit_set(config->auto_ack_fpb.enabled); + break; + + case IEEE802154_CONFIG_ACK_FPB: + ret = stm32wba_802154_configure_ack_fpb(config); + break; + + case IEEE802154_CONFIG_PAN_COORDINATOR: + LOG_DBG("Setting PAN_COORDINATOR: %d", config->pan_coordinator); + stm32wba_802154_ral_pan_coord_set(config->pan_coordinator); + break; + + case IEEE802154_CONFIG_PROMISCUOUS: + LOG_DBG("Setting PROMISCUOUS mode: %d", config->promiscuous); + stm32wba_802154_ral_promiscuous_set(config->promiscuous); + break; + + case IEEE802154_CONFIG_EVENT_HANDLER: + LOG_DBG("Setting EVENT_HANDLER"); + stm32wba_802154_data.event_handler = config->event_handler; + break; + + default: +#if defined(CONFIG_NET_L2_CUSTOM_IEEE802154) + ret = stm32wba_802154_configure_extended( + (enum ieee802154_stm32wba_config_type)type, + (const struct ieee802154_stm32wba_config *)config); +#else + LOG_ERR("Unsupported configuration type: %d", type); + ret = -EINVAL; +#endif + } + + return ret; +} + +static int stm32wba_802154_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) { + LOG_DBG("Attribute successfully retrieved for channel page and range"); + return 0; + } + +#if defined(CONFIG_NET_L2_CUSTOM_IEEE802154) + return stm32wba_802154_attr_get_extended((enum ieee802154_stm32wba_attr)attr, + (struct ieee802154_stm32wba_attr_value *)value); +#else + LOG_ERR("Unsupported attribute: %u", attr); + return -ENOENT; +#endif +} + +/* WBA radio driver callbacks */ + +static void stm32wba_802154_receive_done(uint8_t *p_buffer, + stm32wba_802154_ral_receive_done_metadata_t *p_metadata) +{ + if (p_buffer == NULL) { + stm32wba_802154_receive_failed(p_metadata->error); + return; + } + + for (uint32_t i = 0; i < ARRAY_SIZE(stm32wba_802154_data.rx_frames); i++) { + if (stm32wba_802154_data.rx_frames[i].psdu != NULL) { + continue; + } + + stm32wba_802154_data.rx_frames[i].psdu = p_buffer; + stm32wba_802154_data.rx_frames[i].length = p_metadata->length; + stm32wba_802154_data.rx_frames[i].rssi = p_metadata->power; + stm32wba_802154_data.rx_frames[i].lqi = p_metadata->lqi; + stm32wba_802154_data.rx_frames[i].time = p_metadata->time; + stm32wba_802154_data.rx_frames[i].ack_fpb = + stm32wba_802154_data.last_frame_ack_fpb; + stm32wba_802154_data.rx_frames[i].ack_seb = + stm32wba_802154_data.last_frame_ack_seb; + stm32wba_802154_data.last_frame_ack_fpb = false; + stm32wba_802154_data.last_frame_ack_seb = false; + + k_fifo_put(&stm32wba_802154_data.rx_fifo, &stm32wba_802154_data.rx_frames[i]); + + return; + } + + LOG_ERR("Not enough RX frames allocated for 802.15.4 driver"); +} + +void stm32wba_802154_tx_ack_started(bool ack_fpb, bool ack_seb) +{ + stm32wba_802154_data.last_frame_ack_fpb = ack_fpb; + stm32wba_802154_data.last_frame_ack_seb = ack_seb; +} + +static void stm32wba_802154_transmit_done( + uint8_t *p_frame, + stm32wba_802154_ral_tx_error_t error, + const stm32wba_802154_ral_transmit_done_metadata_t *p_metadata) +{ + ARG_UNUSED(p_frame); + + stm32wba_802154_data.tx_result = error; + stm32wba_802154_data.tx_frame_is_secured = p_metadata->is_secured; + stm32wba_802154_data.tx_frame_mac_hdr_rdy = p_metadata->dynamic_data_is_set; + stm32wba_802154_data.ack_frame.length = p_metadata->length; + + if (stm32wba_802154_data.ack_frame.length != 0) { + stm32wba_802154_data.ack_frame.psdu = p_metadata->p_ack; + stm32wba_802154_data.ack_frame.rssi = p_metadata->power; + stm32wba_802154_data.ack_frame.lqi = p_metadata->lqi; + } else { + stm32wba_802154_data.ack_frame.psdu = NULL; + stm32wba_802154_data.ack_frame.rssi = 0; + stm32wba_802154_data.ack_frame.lqi = 0; + } + + k_sem_give(&stm32wba_802154_data.tx_wait); +} + +static void stm32wba_802154_cca_done(uint8_t error) +{ + if (error == STM32WBA_802154_RAL_RX_ERROR_NONE) { + stm32wba_802154_data.channel_free = true; + } + + k_sem_give(&stm32wba_802154_data.cca_wait); +} + +static void stm32wba_802154_energy_scan_done(int8_t rssi_result) +{ + if (stm32wba_802154_data.energy_scan_done_cb != NULL) { + energy_scan_done_cb_t callback = stm32wba_802154_data.energy_scan_done_cb; + + stm32wba_802154_data.energy_scan_done_cb = NULL; + callback(stm32wba_802154_get_device(), rssi_result); + } +} + +#ifdef CONFIG_PM_DEVICE +static int radio_pm_action(const struct device *dev, enum pm_device_action action) +{ + switch (action) { + case PM_DEVICE_ACTION_RESUME: + LL_AHB5_GRP1_EnableClock(LL_AHB5_GRP1_PERIPH_RADIO); +#if defined(CONFIG_PM_S2RAM) + if (LL_PWR_IsActiveFlag_SB() == 1U) { + /* Put the radio in active state */ + LL_AHB5_GRP1_EnableClock(LL_AHB5_GRP1_PERIPH_RADIO); + link_layer_register_isr(); + } + LINKLAYER_PLAT_NotifyWFIExit(); + ll_sys_dp_slp_exit(); +#endif + break; + case PM_DEVICE_ACTION_SUSPEND: +#if defined(CONFIG_PM_S2RAM) + uint64_t next_radio_evt; + enum pm_state state = pm_state_next_get(_current_cpu->id)->state; + + if (state == PM_STATE_SUSPEND_TO_RAM) { + next_radio_evt = os_timer_get_earliest_time(); + if (llhwc_cmn_is_dp_slp_enabled() == 0) { + if (next_radio_evt > CFG_LPM_STDBY_WAKEUP_TIME) { + /* No event in a "near" futur */ + next_radio_evt -= CFG_LPM_STDBY_WAKEUP_TIME; + ll_sys_dp_slp_enter(next_radio_evt); + } + } + } +#endif + LINKLAYER_PLAT_NotifyWFIEnter(); + break; + default: + return -ENOTSUP; + } + + return 0; +} + +PM_DEVICE_DT_INST_DEFINE(0, radio_pm_action); +#endif /* CONFIG_PM_DEVICE */ + +static const struct ieee802154_radio_api stm32wba_802154_radio_api = { + .iface_api.init = stm32wba_802154_iface_init, + .get_capabilities = stm32wba_802154_get_capabilities, + .cca = stm32wba_802154_cca, + .set_channel = stm32wba_802154_set_channel, + .filter = stm32wba_802154_filter, + .set_txpower = stm32wba_802154_set_txpower, + .start = stm32wba_802154_start, + .stop = stm32wba_802154_stop, + .tx = stm32wba_802154_tx, + .ed_scan = stm32wba_802154_energy_scan_start, + .get_time = stm32wba_802154_get_time, + .get_sch_acc = stm32wba_802154_get_acc, + .configure = stm32wba_802154_configure, + .attr_get = stm32wba_802154_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 + +#if defined(CONFIG_NET_L2_PHY_IEEE802154) +NET_DEVICE_DT_INST_DEFINE(0, + stm32wba_802154_driver_init, + PM_DEVICE_DT_INST_GET(0), + &stm32wba_802154_data, + NULL, + CONFIG_IEEE802154_STM32WBA_INIT_PRIO, + &stm32wba_802154_radio_api, + L2, + L2_CTX_TYPE, + MTU); +#else +DEVICE_DT_INST_DEFINE(0, + stm32wba_802154_driver_init, + PM_DEVICE_DT_INST_GET(0), + &stm32wba_802154_data, + NULL, + POST_KERNEL, + CONFIG_IEEE802154_STM32WBA_INIT_PRIO, + &stm32wba_802154_radio_api); +#endif diff --git a/drivers/ieee802154/ieee802154_stm32wba.h b/drivers/ieee802154/ieee802154_stm32wba.h new file mode 100644 index 0000000000000..923e7b3f8ea36 --- /dev/null +++ b/drivers/ieee802154/ieee802154_stm32wba.h @@ -0,0 +1,254 @@ +/* ieee802154_stm32wba.h - STM32WBAxx 802.15.4 driver */ + +/* + * Copyright (c) 2025 STMicroelectronics + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef ZEPHYR_DRIVERS_IEEE802154_IEEE802154_STM32WBA_H_ +#define ZEPHYR_DRIVERS_IEEE802154_IEEE802154_STM32WBA_H_ +#include +#include + +#define STM32WBA_PHR_LENGTH 1 + +#define STM32WBA_PWR_MIN (-20) +#define STM32WBA_PWR_MAX 10 + + +#if defined(CONFIG_NET_L2_CUSTOM_IEEE802154) +/** + * STM32WBA specific configuration types of ieee802154 driver. + * This type extends @ref ieee802154_config_type. + */ +enum ieee802154_stm32wba_config_type { + /** + * Allows to configure the CCA energy detection threshold value + */ + IEEE802154_STM32WBA_CONFIG_CCA_THRESHOLD = IEEE802154_CONFIG_PRIV_START, + + /** + * Configure (enable/disable) the continuous reception mode + */ + IEEE802154_STM32WBA_CONFIG_CONTINUOUS_RECEPTION, + + /** + * Set the maximum frame retries on a transmission failure + */ + IEEE802154_STM32WBA_CONFIG_MAX_FRAME_RETRIES, + + /** + * Set the maximum CSMA retries on a transmission failure + */ + IEEE802154_STM32WBA_CONFIG_MAX_CSMA_FRAME_RETRIES, + + /** + * Set the minimum CSMA backoff exponent value + */ + IEEE802154_STM32WBA_CONFIG_MIN_CSMA_BE, + + /** + * Set the maximum CSMA backoff exponent value + */ + IEEE802154_STM32WBA_CONFIG_MAX_CSMA_BE, + + /** + * Set the maximum CSMA backoff attempts counter + */ + IEEE802154_STM32WBA_CONFIG_MAX_CSMA_BACKOFF, + + /** + * Configure (enable/disable) the MAC implicit broadcast PIB + */ + IEEE802154_STM32WBA_CONFIG_IMPLICIT_BROADCAST, + + /** + * Configure (enable/disable) the antenna diversity + */ + IEEE802154_STM32WBA_CONFIG_ANTENNA_DIV, + + /** + * Reset the radio + */ + IEEE802154_STM32WBA_CONFIG_RADIO_RESET, +}; + +/** STM32WBA specific configuration data of ieee802154 driver. */ +struct ieee802154_stm32wba_config { + union { + /** Common configuration */ + struct ieee802154_config common; + + /** @brief Attribute value for @ref IEEE802154_STM32WBA_CONFIG_CCA_THRESHOLD */ + int8_t cca_thr; + + /** @brief Attribute value for + * @ref IEEE802154_STM32WBA_CONFIG_CONTINUOUS_RECEPTION + */ + bool en_cont_rec; + + /** @brief Attribute value for @ref IEEE802154_STM32WBA_CONFIG_MAX_FRAME_RETRIES */ + uint8_t max_frm_retries; + + /** @brief Attribute value for + * @ref IEEE802154_STM32WBA_CONFIG_MAX_CSMA_FRAME_RETRIES + */ + uint8_t max_csma_frm_retries; + + /** @brief Attribute value for @ref IEEE802154_STM32WBA_CONFIG_MIN_CSMA_BE */ + uint8_t min_csma_be; + + /** @brief Attribute value for @ref IEEE802154_STM32WBA_CONFIG_MAX_CSMA_BE */ + uint8_t max_csma_be; + + /** @brief Attribute value for @ref IEEE802154_STM32WBA_CONFIG_MAX_CSMA_BACKOFF */ + uint8_t max_csma_backoff; + + /** @brief Attribute value for + * @ref IEEE802154_STM32WBA_CONFIG_IMPLICIT_BROADCAST + */ + bool impl_brdcast; + + /** @brief Attribute value for @ref IEEE802154_STM32WBA_CONFIG_ANTENNA_DIV */ + uint8_t ant_div; + + /** @brief Attribute value for @ref IEEE802154_STM32WBA_CONFIG_RADIO_RESET */ + bool radio_reset; + }; +}; + +/** + * STM32WBA specific attribute types of ieee802154 driver. + * This type extends @ref ieee802154_attr. + */ +enum ieee802154_stm32wba_attr { + /** + * Get the CCA energy detection threshold value + */ + IEEE802154_STM32WBA_ATTR_CCA_THRESHOLD = IEEE802154_CONFIG_PRIV_START, + + /** + * Get the IEEE EUI64 of the device + */ + IEEE802154_STM32WBA_ATTR_IEEE_EUI64, + + /** + * Get the transmit power value + */ + IEEE802154_STM32WBA_ATTR_TX_POWER, + + /** + * Get a random number + */ + IEEE802154_STM32WBA_ATTR_RAND_NUM, +}; + +/** + * STM32WBA specific attribute value data of ieee802154 driver. + * This type extends @ref ieee802154_attr_value + */ +struct ieee802154_stm32wba_attr_value { + union { + /** Common attribute value */ + struct ieee802154_attr_value common; + + /** @brief Attribute value for @ref IEEE802154_STM32WBA_ATTR_CCA_THRESHOLD */ + int8_t *cca_thr; + + /** @brief Attribute value for @ref IEEE802154_STM32WBA_ATTR_IEEE_EUI64 */ + uint8_t eui64[8]; + + /** @brief Attribute value for @ref IEEE802154_STM32WBA_ATTR_TX_POWER */ + int8_t *tx_power; + + /** @brief Attribute value for @ref IEEE802154_STM32WBA_ATTR_RAND_NUM */ + uint8_t *rand_num; + }; +}; +#endif /* CONFIG_NET_L2_CUSTOM_IEEE802154 */ + +struct stm32wba_802154_rx_frame { + uint8_t *psdu; /* Pointer to a received frame. */ + uint8_t length; /* Received frame's length */ + uint64_t time; /* RX timestamp. */ + uint8_t lqi; /* Last received frame LQI value. */ + int8_t rssi; /* Last received frame RSSI value. */ + bool ack_fpb; /* FPB value in ACK sent for the received frame. */ + bool ack_seb; /* SEB value in ACK sent for the received frame. */ +}; + +struct stm32wba_802154_data_t { + /* Pointer to the network interface. */ + struct net_if *iface; + + /* 802.15.4 HW address. */ + uint8_t mac[8]; + + /* RX thread stack. */ + K_KERNEL_STACK_MEMBER(rx_stack, CONFIG_IEEE802154_STM32WBA_RX_STACK_SIZE); + + /* RX thread control block. */ + struct k_thread rx_thread; + + /* RX fifo queue. */ + struct k_fifo rx_fifo; + + /* Buffers for passing received frame pointers and data to the + * RX thread via rx_fifo object. + */ + struct stm32wba_802154_rx_frame rx_frames[CONFIG_IEEE802154_STM32WBA_RX_BUFFERS]; + + /* Frame pending bit value in ACK sent for the last received frame. */ + bool last_frame_ack_fpb; + + /* Security Enabled bit value in ACK sent for the last received frame. */ + bool last_frame_ack_seb; + + /* CCA complete semaphore. Unlocked when CCA is complete. */ + struct k_sem cca_wait; + + /* CCA result. Holds information whether channel is free or not. */ + bool channel_free; + + /* TX synchronization semaphore. Unlocked when frame has been + * sent or send procedure failed. + */ + struct k_sem tx_wait; + + /* TX buffer. First byte is PHR (length), remaining bytes are + * MPDU data. + */ + uint8_t tx_psdu[STM32WBA_PHR_LENGTH + IEEE802154_MAX_PHY_PACKET_SIZE]; + + /* TX result, updated in radio transmit callbacks. */ + uint8_t tx_result; + + /* A buffer for the received ACK frame. psdu pointer be NULL if no + * ACK was requested/received. + */ + struct stm32wba_802154_rx_frame ack_frame; + + /* Callback handler of the currently ongoing energy scan. + * It shall be NULL if energy scan is not in progress. + */ + energy_scan_done_cb_t energy_scan_done_cb; + + /* Callback handler to notify of any important radio events. + * Can be NULL if event notification is not needed. + */ + ieee802154_event_cb_t event_handler; + + /* Indicates if currently processed TX frame is secured. */ + bool tx_frame_is_secured; + + /* Indicates if currently processed TX frame has dynamic data updated. */ + bool tx_frame_mac_hdr_rdy; + + /* The TX power in dBm. */ + int8_t txpwr; + + /* Indicates if RxOnWhenIdle mode is enabled. */ + bool rx_on_when_idle; +}; + +#endif /* ZEPHYR_DRIVERS_IEEE802154_IEEE802154_STM32WBA_H_ */ diff --git a/dts/arm/st/wba/stm32wba.dtsi b/dts/arm/st/wba/stm32wba.dtsi index 94927a69be20d..eec89fad92574 100644 --- a/dts/arm/st/wba/stm32wba.dtsi +++ b/dts/arm/st/wba/stm32wba.dtsi @@ -23,6 +23,7 @@ zephyr,flash-controller = &flash; st,lptim-stdby-timer = &rtc; zephyr,bt-hci = &bt_hci_wba; + zephyr,ieee802154 = &ieee802154; }; cpus { @@ -566,6 +567,11 @@ status = "okay"; }; + ieee802154: ieee802154 { + compatible = "st,stm32wba-ieee802154"; + status = "disabled"; + }; + swj_port: swj_port { compatible = "swj-connector"; pinctrl-0 = <&debug_jtms_swdio_pa13 &debug_jtck_swclk_pa14 diff --git a/dts/bindings/ieee802154/st,stm32wba-ieee802154.yaml b/dts/bindings/ieee802154/st,stm32wba-ieee802154.yaml new file mode 100644 index 0000000000000..39c6e388535db --- /dev/null +++ b/dts/bindings/ieee802154/st,stm32wba-ieee802154.yaml @@ -0,0 +1,8 @@ +# Copyright (c) 2025 STMicroelectronics +# SPDX-License-Identifier: Apache-2.0 + +description: ST STM32WBA IEEE 802.15.4 + +compatible: "st,stm32wba-ieee802154" + +include: base.yaml diff --git a/soc/st/stm32/stm32wbax/CMakeLists.txt b/soc/st/stm32/stm32wbax/CMakeLists.txt index cb31d8dcc78bd..0bd36f97e555e 100644 --- a/soc/st/stm32/stm32wbax/CMakeLists.txt +++ b/soc/st/stm32/stm32wbax/CMakeLists.txt @@ -10,12 +10,10 @@ zephyr_sources_ifdef(CONFIG_PM ) zephyr_sources_ifdef(CONFIG_POWEROFF poweroff.c) - -if(CONFIG_BT_STM32WBA) +if(CONFIG_BT_STM32WBA OR CONFIG_IEEE802154_STM32WBA) zephyr_include_directories(hci_if) - zephyr_sources(hci_if/linklayer_plat_adapt.c) - zephyr_sources(hci_if/bleplat.c) + zephyr_sources(hci_if/sys_wireless_plat.c) zephyr_sources(hci_if/host_stack_if.c) zephyr_sources(hci_if/ll_sys_if_adapt.c) zephyr_sources(hci_if/stm32_timer.c) diff --git a/soc/st/stm32/stm32wbax/Kconfig.defconfig b/soc/st/stm32/stm32wbax/Kconfig.defconfig index 498292d79ce8f..8f856904fd873 100644 --- a/soc/st/stm32/stm32wbax/Kconfig.defconfig +++ b/soc/st/stm32/stm32wbax/Kconfig.defconfig @@ -50,6 +50,29 @@ config SYSTEM_WORKQUEUE_STACK_SIZE endif +config IEEE802154_STM32WBA + select DYNAMIC_INTERRUPTS + select DYNAMIC_DIRECT_INTERRUPTS + select ENTROPY_GENERATOR + select HAS_STM32LIB + +if IEEE802154_STM32WBA + +config FPU + default y + +choice LIBC_IMPLEMENTATION + default NEWLIB_LIBC +endchoice + +config ENTROPY_STM32_CLK_CHECK + default n + +config SYSTEM_WORKQUEUE_STACK_SIZE + default 2048 + +endif # IEEE802154_STM32WBA + if PM_S2RAM config COUNTER diff --git a/soc/st/stm32/stm32wbax/hci_if/host_stack_if.c b/soc/st/stm32/stm32wbax/hci_if/host_stack_if.c index 2c9f1e57b2c4f..493687e69abfe 100644 --- a/soc/st/stm32/stm32wbax/hci_if/host_stack_if.c +++ b/soc/st/stm32/stm32wbax/hci_if/host_stack_if.c @@ -7,28 +7,41 @@ #include #include +#include "app_conf.h" +#if defined(CONFIG_BT_STM32WBA) #include "blestack.h" #include "bpka.h" +#endif /* CONFIG_BT_STM32WBA */ #include "ll_intf.h" K_MUTEX_DEFINE(ble_ctrl_stack_mutex); -struct k_work_q ble_ctrl_work_q, ll_work_q; +#if defined(CONFIG_BT_STM32WBA) +struct k_work_q ble_ctrl_work_q; struct k_work ble_ctrl_stack_work, bpka_work; - +#endif /* CONFIG_BT_STM32WBA */ +struct k_work_q ll_work_q; uint8_t ll_state_busy; /* TODO: More tests to be done to optimize thread stacks' sizes */ +#if defined(CONFIG_BT_STM32WBA) #define BLE_CTRL_THREAD_STACK_SIZE (256 * 7) -#define LL_THREAD_STACK_SIZE (256 * 7) #define BLE_CTRL_THREAD_PRIO (14) +#endif /* CONFIG_BT_STM32WBA */ +#define LL_THREAD_STACK_SIZE (256 * 7) + /* The LL thread has higher priority than the BLE CTRL thread and the Zephyr BLE stack threads */ #define LL_THREAD_PRIO (4) +#if defined(CONFIG_BT_STM32WBA) K_THREAD_STACK_DEFINE(ble_ctrl_work_area, BLE_CTRL_THREAD_STACK_SIZE); +#endif /* CONFIG_BT_STM32WBA */ K_THREAD_STACK_DEFINE(ll_work_area, LL_THREAD_STACK_SIZE); +#if defined(CONFIG_BT_STM32WBA) void HostStack_Process(void); +#endif /* CONFIG_BT_STM32WBA */ +#if defined(CONFIG_BT_STM32WBA) static void ble_ctrl_stack_handler(struct k_work *work) { uint8_t running = 0x00; @@ -53,25 +66,33 @@ static void bpka_work_handler(struct k_work *work) { BPKA_BG_Process(); } +#endif /* CONFIG_BT_STM32WBA */ -static int stm32wba_ble_ctrl_init(void) +static int stm32wba_ctrl_init(void) { + struct k_work_queue_config ll_cfg = {.name = "LL thread"}; + +#if defined(CONFIG_BT_STM32WBA) + struct k_work_queue_config ble_ctrl_cfg = {.name = "ble ctrl thread"}; + k_work_queue_init(&ble_ctrl_work_q); k_work_queue_start(&ble_ctrl_work_q, ble_ctrl_work_area, K_THREAD_STACK_SIZEOF(ble_ctrl_work_area), - BLE_CTRL_THREAD_PRIO, NULL); + BLE_CTRL_THREAD_PRIO, &ble_ctrl_cfg); + + k_work_init(&ble_ctrl_stack_work, &ble_ctrl_stack_handler); + k_work_init(&bpka_work, &bpka_work_handler); +#endif /* CONFIG_BT_STM32WBA */ k_work_queue_init(&ll_work_q); k_work_queue_start(&ll_work_q, ll_work_area, K_THREAD_STACK_SIZEOF(ll_work_area), - LL_THREAD_PRIO, NULL); - - k_work_init(&ble_ctrl_stack_work, &ble_ctrl_stack_handler); - k_work_init(&bpka_work, &bpka_work_handler); + LL_THREAD_PRIO, &ll_cfg); return 0; } +#if defined(CONFIG_BT_STM32WBA) void HostStack_Process(void) { k_work_submit_to_queue(&ble_ctrl_work_q, &ble_ctrl_stack_work); @@ -81,5 +102,6 @@ void BPKACB_Process(void) { k_work_submit_to_queue(&ble_ctrl_work_q, &bpka_work); } +#endif /* CONFIG_BT_STM32WBA */ -SYS_INIT(stm32wba_ble_ctrl_init, POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT); +SYS_INIT(stm32wba_ctrl_init, POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT); diff --git a/soc/st/stm32/stm32wbax/hci_if/linklayer_plat_adapt.c b/soc/st/stm32/stm32wbax/hci_if/linklayer_plat_adapt.c index 48bacb00ca885..d091af9388188 100644 --- a/soc/st/stm32/stm32wbax/hci_if/linklayer_plat_adapt.c +++ b/soc/st/stm32/stm32wbax/hci_if/linklayer_plat_adapt.c @@ -285,6 +285,16 @@ void LINKLAYER_PLAT_DisableOSContextSwitch(void) **/ } +uint32_t LINKLAYER_PLAT_GetSTCompanyID(void) +{ + return LL_FLASH_GetSTCompanyID(); +} + +uint32_t LINKLAYER_PLAT_GetUDN(void) +{ + return LL_FLASH_GetUDN(); +} + void LINKLAYER_PLAT_EnableBackupDomainAccess(void) { stm32_backup_domain_enable_access(); diff --git a/soc/st/stm32/stm32wbax/hci_if/ll_sys_if_adapt.c b/soc/st/stm32/stm32wbax/hci_if/ll_sys_if_adapt.c index 498bacab60be3..318e04b7d4a4c 100644 --- a/soc/st/stm32/stm32wbax/hci_if/ll_sys_if_adapt.c +++ b/soc/st/stm32/stm32wbax/hci_if/ll_sys_if_adapt.c @@ -15,8 +15,9 @@ LOG_MODULE_REGISTER(ll_sys_if_adapt); #if defined(CONFIG_BT_STM32WBA_USE_TEMP_BASED_CALIB) #include #endif /* defined(CONFIG_BT_STM32WBA_USE_TEMP_BASED_CALIB) */ - +#if defined(CONFIG_BT_STM32WBA) extern struct k_mutex ble_ctrl_stack_mutex; +#endif /* CONFIG_BT_STM32WBA */ extern struct k_work_q ll_work_q; struct k_work ll_sys_work; @@ -27,9 +28,13 @@ static struct k_work temp_calibration_work; static void ll_sys_bg_process_handler(struct k_work *work) { +#if defined(CONFIG_BT_STM32WBA) k_mutex_lock(&ble_ctrl_stack_mutex, K_FOREVER); ll_sys_bg_process(); k_mutex_unlock(&ble_ctrl_stack_mutex); +#else + ll_sys_bg_process(); +#endif /* CONFIG_BT_STM32WBA */ } void ll_sys_schedule_bg_process(void) diff --git a/soc/st/stm32/stm32wbax/hci_if/bleplat.c b/soc/st/stm32/stm32wbax/hci_if/sys_wireless_plat.c similarity index 94% rename from soc/st/stm32/stm32wbax/hci_if/bleplat.c rename to soc/st/stm32/stm32wbax/hci_if/sys_wireless_plat.c index 12de3b8f4b5ec..5169e770633db 100644 --- a/soc/st/stm32/stm32wbax/hci_if/bleplat.c +++ b/soc/st/stm32/stm32wbax/hci_if/sys_wireless_plat.c @@ -11,13 +11,14 @@ #include #include - +#if defined(CONFIG_BT_STM32WBA) #include "bleplat.h" #include "bpka.h" +#endif /* CONFIG_BT_STM32WBA */ #include "linklayer_plat.h" #define LOG_LEVEL CONFIG_SOC_LOG_LEVEL -LOG_MODULE_REGISTER(ble_plat); +LOG_MODULE_REGISTER(sys_wireless_plat); RAMCFG_HandleTypeDef hramcfg_SRAM1; const struct device *rng_dev = DEVICE_DT_GET(DT_CHOSEN(zephyr_entropy)); @@ -30,6 +31,7 @@ struct entropy_stm32_rng_dev_cfg { struct stm32_pclken *pclken; }; +#if defined(CONFIG_BT_STM32WBA) void BLEPLAT_Init(void) { BPKA_Reset(); @@ -70,6 +72,7 @@ void BPKACB_Complete(void) { BLEPLATCB_PkaComplete(); } +#endif /* CONFIG_BT_STM32WBA */ void MX_RAMCFG_Init(void) { diff --git a/west.yml b/west.yml index e56baad71cc6e..7776ed8eb06fb 100644 --- a/west.yml +++ b/west.yml @@ -250,7 +250,7 @@ manifest: groups: - hal - name: hal_stm32 - revision: 2459cda20429a15d4a5eb5ca4140a0c19618f3bf + revision: dc7c2543a079c57a4e7dbee54ed7877880dc235b path: modules/hal/stm32 groups: - hal