You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1362 lines
36 KiB
1362 lines
36 KiB
/* 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 <zephyr/logging/log.h> |
|
LOG_MODULE_REGISTER(LOG_MODULE_NAME); |
|
|
|
#include <zephyr/kernel.h> |
|
#include <zephyr/arch/cpu.h> |
|
#include <zephyr/debug/stack.h> |
|
#include <zephyr/device.h> |
|
#include <zephyr/drivers/counter.h> |
|
#include <zephyr/init.h> |
|
#include <zephyr/net/net_if.h> |
|
#include <zephyr/net/net_pkt.h> |
|
|
|
#if defined(CONFIG_NET_L2_OPENTHREAD) |
|
#include <zephyr/net/openthread.h> |
|
#include <zephyr/net/ieee802154_radio_openthread.h> |
|
#endif |
|
|
|
#include <zephyr/sys/byteorder.h> |
|
#include <zephyr/random/random.h> |
|
|
|
#include <zephyr/net/ieee802154_radio.h> |
|
|
|
#include <soc.h> |
|
#include <errno.h> |
|
#include <string.h> |
|
|
|
#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; |
|
} |
|
|
|
net_pkt_set_ieee802154_lqi(pkt, mcxw_radio->rx_ack_frame.lqi); |
|
net_pkt_set_ieee802154_rssi_dbm(pkt, mcxw_radio->rx_ack_frame.rssi); |
|
|
|
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.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) / NSEC_PER_USEC) + |
|
(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) / NSEC_PER_USEC); |
|
|
|
/* 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) / NSEC_PER_USEC); |
|
} |
|
|
|
/* 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 > 0) ? -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 * NSEC_PER_USEC; |
|
} |
|
|
|
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) / NSEC_PER_USEC) - 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) / NSEC_PER_USEC); |
|
|
|
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.lqi = data_msg->msgData.dataCnf.ppduLinkQuality; |
|
mcxw_ctx.rx_ack_frame.rssi = data_msg->msgData.dataCnf.ppduRssi; |
|
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; |
|
|
|
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_HW_SELECTIVE_TXCHANNEL; |
|
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 |
|
|
|
#if defined(CONFIG_NET_L2_PHY_IEEE802154) |
|
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); |
|
#else |
|
DEVICE_DT_INST_DEFINE(0, mcxw_init, NULL, &mcxw_ctx, NULL, |
|
POST_KERNEL, CONFIG_IEEE802154_MCXW_INIT_PRIO, |
|
&mcxw71_radio_api); |
|
#endif
|
|
|