blob: a796fcc5c227814cbbb9a56404ddf41f022b10c8 [file] [log] [blame]
/* ieee802154_stm32wba.c - STM32WBxx 802.15.4 driver */
/*
* Copyright (c) 2025 STMicroelectronics
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <soc.h>
#include <errno.h>
#include <string.h>
#include <zephyr/arch/cpu.h>
#include <zephyr/debug/stack.h>
#include <zephyr/device.h>
#include <zephyr/init.h>
#include <zephyr/kernel.h>
#include <zephyr/logging/log.h>
#include <zephyr/net/net_if.h>
#include <zephyr/net/net_pkt.h>
#include <zephyr/sys/byteorder.h>
#if defined(CONFIG_NET_L2_OPENTHREAD)
#include <zephyr/net/openthread.h>
#include <zephyr/net/ieee802154_radio_openthread.h>
#endif
#ifdef CONFIG_PM_DEVICE
#include <zephyr/pm/policy.h>
#include <zephyr/pm/device.h>
#include <zephyr/pm/pm.h>
#include "app_conf.h"
#include "linklayer_plat.h"
#include <stm32wbaxx_ll_bus.h>
#include <stm32wbaxx_ll_pwr.h>
#endif
#include <linklayer_plat_local.h>
#include "ieee802154_stm32wba.h"
#include <stm32wba_802154_intf.h>
#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