62396443fc
"Sleeping" has a well defined meaning in Zephyr related to threading and power management. This differs from OpenThread's definition: - Deprecates the "SLEEP_TO_TX" capability as it is redundant and conflicts with all of Zephyr's nomenclature, #61227, RFC 2863, Thread standard and IEEE 802.15.4. This binds the API to an implementation detail of OpenThread, instead. See #63670 for the agreed migration path. - Renames the "SLEEP" event to "RX_OFF" which conforms to the nomenclature in Zephyr, this API and IEEE 802.15.4. Fixes: #62995 Signed-off-by: Florian Grandel <fgrandel@code-for-humans.de>
1202 lines
32 KiB
C
1202 lines
32 KiB
C
/* ieee802154_nrf5.c - nRF5 802.15.4 driver */
|
|
|
|
/*
|
|
* Copyright (c) 2017-2023 Nordic Semiconductor ASA
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#define DT_DRV_COMPAT nordic_nrf_ieee802154
|
|
|
|
#define LOG_MODULE_NAME ieee802154_nrf5
|
|
#if defined(CONFIG_IEEE802154_DRIVER_LOG_LEVEL)
|
|
#define LOG_LEVEL CONFIG_IEEE802154_DRIVER_LOG_LEVEL
|
|
#else
|
|
#define LOG_LEVEL LOG_LEVEL_NONE
|
|
#endif
|
|
|
|
#include <zephyr/logging/log.h>
|
|
LOG_MODULE_REGISTER(LOG_MODULE_NAME);
|
|
|
|
#include <errno.h>
|
|
|
|
#include <zephyr/kernel.h>
|
|
#include <zephyr/arch/cpu.h>
|
|
#include <zephyr/debug/stack.h>
|
|
|
|
#include <soc.h>
|
|
#include <soc_secure.h>
|
|
#include <zephyr/device.h>
|
|
#include <zephyr/init.h>
|
|
#include <zephyr/debug/stack.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 <string.h>
|
|
#include <zephyr/random/random.h>
|
|
|
|
#include <zephyr/net/ieee802154_radio.h>
|
|
#include <zephyr/irq.h>
|
|
|
|
#include "ieee802154_nrf5.h"
|
|
#include "nrf_802154.h"
|
|
#include "nrf_802154_const.h"
|
|
|
|
#if defined(CONFIG_NRF_802154_SER_HOST)
|
|
#include "nrf_802154_serialization_error.h"
|
|
#endif
|
|
|
|
struct nrf5_802154_config {
|
|
void (*irq_config_func)(const struct device *dev);
|
|
};
|
|
|
|
static struct nrf5_802154_data nrf5_data;
|
|
|
|
#define ACK_REQUEST_BYTE 1
|
|
#define ACK_REQUEST_BIT (1 << 5)
|
|
#define FRAME_PENDING_BYTE 1
|
|
#define FRAME_PENDING_BIT (1 << 4)
|
|
|
|
#define DRX_SLOT_RX 0 /* Delayed reception window ID */
|
|
|
|
#if defined(CONFIG_IEEE802154_NRF5_UICR_EUI64_ENABLE)
|
|
#if defined(CONFIG_SOC_NRF5340_CPUAPP)
|
|
#if defined(CONFIG_TRUSTED_EXECUTION_NONSECURE)
|
|
#error "NRF_UICR->OTP is not supported to read from non-secure"
|
|
#else
|
|
#define EUI64_ADDR (NRF_UICR->OTP)
|
|
#endif /* CONFIG_TRUSTED_EXECUTION_NONSECURE */
|
|
#else
|
|
#define EUI64_ADDR (NRF_UICR->CUSTOMER)
|
|
#endif /* CONFIG_SOC_NRF5340_CPUAPP */
|
|
#endif /* CONFIG_IEEE802154_NRF5_UICR_EUI64_ENABLE */
|
|
|
|
#if defined(CONFIG_IEEE802154_NRF5_UICR_EUI64_ENABLE)
|
|
#define EUI64_ADDR_HIGH CONFIG_IEEE802154_NRF5_UICR_EUI64_REG
|
|
#define EUI64_ADDR_LOW (CONFIG_IEEE802154_NRF5_UICR_EUI64_REG + 1)
|
|
#else
|
|
#define EUI64_ADDR_HIGH 0
|
|
#define EUI64_ADDR_LOW 1
|
|
#endif /* CONFIG_IEEE802154_NRF5_UICR_EUI64_ENABLE */
|
|
|
|
/* Convenience defines for RADIO */
|
|
#define NRF5_802154_DATA(dev) \
|
|
((struct nrf5_802154_data * const)(dev)->data)
|
|
|
|
#define NRF5_802154_CFG(dev) \
|
|
((const struct nrf5_802154_config * const)(dev)->config)
|
|
|
|
#if CONFIG_IEEE802154_VENDOR_OUI_ENABLE
|
|
#define IEEE802154_NRF5_VENDOR_OUI CONFIG_IEEE802154_VENDOR_OUI
|
|
#else
|
|
#define IEEE802154_NRF5_VENDOR_OUI (uint32_t)0xF4CE36
|
|
#endif
|
|
|
|
static void nrf5_get_eui64(uint8_t *mac)
|
|
{
|
|
uint64_t factoryAddress;
|
|
uint32_t index = 0;
|
|
|
|
#if !defined(CONFIG_IEEE802154_NRF5_UICR_EUI64_ENABLE)
|
|
uint32_t deviceid[2];
|
|
|
|
/* Set the MAC Address Block Larger (MA-L) formerly called OUI. */
|
|
mac[index++] = (IEEE802154_NRF5_VENDOR_OUI >> 16) & 0xff;
|
|
mac[index++] = (IEEE802154_NRF5_VENDOR_OUI >> 8) & 0xff;
|
|
mac[index++] = IEEE802154_NRF5_VENDOR_OUI & 0xff;
|
|
|
|
soc_secure_read_deviceid(deviceid);
|
|
|
|
factoryAddress = (uint64_t)deviceid[EUI64_ADDR_HIGH] << 32;
|
|
factoryAddress |= deviceid[EUI64_ADDR_LOW];
|
|
#else
|
|
/* Use device identifier assigned during the production. */
|
|
factoryAddress = (uint64_t)EUI64_ADDR[EUI64_ADDR_HIGH] << 32;
|
|
factoryAddress |= EUI64_ADDR[EUI64_ADDR_LOW];
|
|
#endif
|
|
memcpy(mac + index, &factoryAddress, sizeof(factoryAddress) - index);
|
|
}
|
|
|
|
static void nrf5_rx_thread(void *arg1, void *arg2, void *arg3)
|
|
{
|
|
struct nrf5_802154_data *nrf5_radio = (struct nrf5_802154_data *)arg1;
|
|
struct net_pkt *pkt;
|
|
struct nrf5_802154_rx_frame *rx_frame;
|
|
uint8_t pkt_len;
|
|
uint8_t *psdu;
|
|
|
|
ARG_UNUSED(arg2);
|
|
ARG_UNUSED(arg3);
|
|
|
|
while (1) {
|
|
pkt = NULL;
|
|
rx_frame = NULL;
|
|
|
|
LOG_DBG("Waiting for frame");
|
|
|
|
rx_frame = k_fifo_get(&nrf5_radio->rx_fifo, K_FOREVER);
|
|
|
|
__ASSERT_NO_MSG(rx_frame->psdu);
|
|
|
|
/* rx_mpdu contains length, psdu, fcs|lqi
|
|
* The last 2 bytes contain LQI or FCS, depending if
|
|
* automatic CRC handling is enabled or not, respectively.
|
|
*/
|
|
if (IS_ENABLED(CONFIG_IEEE802154_NRF5_FCS_IN_LENGTH)) {
|
|
pkt_len = rx_frame->psdu[0];
|
|
} else {
|
|
pkt_len = rx_frame->psdu[0] - 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");
|
|
|
|
/* 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 nRF 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(nrf5_radio->iface, pkt_len,
|
|
AF_UNSPEC, 0, K_FOREVER);
|
|
|
|
if (net_pkt_write(pkt, rx_frame->psdu + 1, pkt_len)) {
|
|
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->time * NSEC_PER_USEC);
|
|
#endif
|
|
|
|
LOG_DBG("Caught a packet (%u) (LQI: %u)",
|
|
pkt_len, rx_frame->lqi);
|
|
|
|
if (net_recv_data(nrf5_radio->iface, pkt) < 0) {
|
|
LOG_ERR("Packet dropped by NET stack");
|
|
goto drop;
|
|
}
|
|
|
|
psdu = rx_frame->psdu;
|
|
rx_frame->psdu = NULL;
|
|
nrf_802154_buffer_free_raw(psdu);
|
|
|
|
if (LOG_LEVEL >= LOG_LEVEL_DBG) {
|
|
log_stack_usage(&nrf5_radio->rx_thread);
|
|
}
|
|
|
|
continue;
|
|
|
|
drop:
|
|
psdu = rx_frame->psdu;
|
|
rx_frame->psdu = NULL;
|
|
nrf_802154_buffer_free_raw(psdu);
|
|
|
|
net_pkt_unref(pkt);
|
|
}
|
|
}
|
|
|
|
static void nrf5_get_capabilities_at_boot(void)
|
|
{
|
|
nrf_802154_capabilities_t caps = nrf_802154_capabilities_get();
|
|
|
|
nrf5_data.capabilities =
|
|
IEEE802154_HW_FCS |
|
|
IEEE802154_HW_PROMISC |
|
|
IEEE802154_HW_FILTER |
|
|
((caps & NRF_802154_CAPABILITY_CSMA) ? IEEE802154_HW_CSMA : 0UL) |
|
|
IEEE802154_HW_TX_RX_ACK |
|
|
IEEE802154_HW_RX_TX_ACK |
|
|
IEEE802154_HW_ENERGY_SCAN |
|
|
((caps & NRF_802154_CAPABILITY_DELAYED_TX) ? IEEE802154_HW_TXTIME : 0UL) |
|
|
((caps & NRF_802154_CAPABILITY_DELAYED_RX) ? IEEE802154_HW_RXTIME : 0UL) |
|
|
IEEE802154_HW_SLEEP_TO_TX |
|
|
((caps & NRF_802154_CAPABILITY_SECURITY) ? IEEE802154_HW_TX_SEC : 0UL)
|
|
#if defined(CONFIG_IEEE802154_NRF5_MULTIPLE_CCA)
|
|
| IEEE802154_OPENTHREAD_HW_MULTIPLE_CCA
|
|
#endif
|
|
;
|
|
}
|
|
|
|
/* Radio device API */
|
|
|
|
static enum ieee802154_hw_caps nrf5_get_capabilities(const struct device *dev)
|
|
{
|
|
return nrf5_data.capabilities;
|
|
}
|
|
|
|
static int nrf5_cca(const struct device *dev)
|
|
{
|
|
struct nrf5_802154_data *nrf5_radio = NRF5_802154_DATA(dev);
|
|
|
|
if (!nrf_802154_cca()) {
|
|
LOG_DBG("CCA failed");
|
|
return -EBUSY;
|
|
}
|
|
|
|
/* The nRF driver guarantees that a callback will be called once
|
|
* the CCA function is done, thus unlocking the semaphore.
|
|
*/
|
|
k_sem_take(&nrf5_radio->cca_wait, K_FOREVER);
|
|
|
|
LOG_DBG("Channel free? %d", nrf5_radio->channel_free);
|
|
|
|
return nrf5_radio->channel_free ? 0 : -EBUSY;
|
|
}
|
|
|
|
static int nrf5_set_channel(const struct device *dev, uint16_t channel)
|
|
{
|
|
ARG_UNUSED(dev);
|
|
|
|
LOG_DBG("%u", channel);
|
|
|
|
if (channel < 11 || channel > 26) {
|
|
return channel < 11 ? -ENOTSUP : -EINVAL;
|
|
}
|
|
|
|
nrf_802154_channel_set(channel);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int nrf5_energy_scan_start(const struct device *dev,
|
|
uint16_t duration,
|
|
energy_scan_done_cb_t done_cb)
|
|
{
|
|
int err = 0;
|
|
|
|
ARG_UNUSED(dev);
|
|
|
|
if (nrf5_data.energy_scan_done == NULL) {
|
|
nrf5_data.energy_scan_done = done_cb;
|
|
|
|
if (nrf_802154_energy_detection(duration * 1000) == false) {
|
|
nrf5_data.energy_scan_done = NULL;
|
|
err = -EBUSY;
|
|
}
|
|
} else {
|
|
err = -EALREADY;
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
static int nrf5_set_pan_id(const struct device *dev, uint16_t pan_id)
|
|
{
|
|
uint8_t pan_id_le[2];
|
|
|
|
ARG_UNUSED(dev);
|
|
|
|
sys_put_le16(pan_id, pan_id_le);
|
|
nrf_802154_pan_id_set(pan_id_le);
|
|
|
|
LOG_DBG("0x%x", pan_id);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int nrf5_set_short_addr(const struct device *dev, uint16_t short_addr)
|
|
{
|
|
uint8_t short_addr_le[2];
|
|
|
|
ARG_UNUSED(dev);
|
|
|
|
sys_put_le16(short_addr, short_addr_le);
|
|
nrf_802154_short_address_set(short_addr_le);
|
|
|
|
LOG_DBG("0x%x", short_addr);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int nrf5_set_ieee_addr(const struct device *dev,
|
|
const uint8_t *ieee_addr)
|
|
{
|
|
ARG_UNUSED(dev);
|
|
|
|
LOG_DBG("IEEE address %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x",
|
|
ieee_addr[7], ieee_addr[6], ieee_addr[5], ieee_addr[4],
|
|
ieee_addr[3], ieee_addr[2], ieee_addr[1], ieee_addr[0]);
|
|
|
|
nrf_802154_extended_address_set(ieee_addr);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int nrf5_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 nrf5_set_ieee_addr(dev, filter->ieee_addr);
|
|
} else if (type == IEEE802154_FILTER_TYPE_SHORT_ADDR) {
|
|
return nrf5_set_short_addr(dev, filter->short_addr);
|
|
} else if (type == IEEE802154_FILTER_TYPE_PAN_ID) {
|
|
return nrf5_set_pan_id(dev, filter->pan_id);
|
|
}
|
|
|
|
return -ENOTSUP;
|
|
}
|
|
|
|
static int nrf5_set_txpower(const struct device *dev, int16_t dbm)
|
|
{
|
|
ARG_UNUSED(dev);
|
|
|
|
LOG_DBG("%d", dbm);
|
|
|
|
nrf_802154_tx_power_set(dbm);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int handle_ack(struct nrf5_802154_data *nrf5_radio)
|
|
{
|
|
uint8_t ack_len;
|
|
struct net_pkt *ack_pkt;
|
|
int err = 0;
|
|
|
|
if (IS_ENABLED(CONFIG_IEEE802154_NRF5_FCS_IN_LENGTH)) {
|
|
ack_len = nrf5_radio->ack_frame.psdu[0];
|
|
} else {
|
|
ack_len = nrf5_radio->ack_frame.psdu[0] - IEEE802154_FCS_LENGTH;
|
|
}
|
|
|
|
ack_pkt = net_pkt_rx_alloc_with_buffer(nrf5_radio->iface, ack_len,
|
|
AF_UNSPEC, 0, K_NO_WAIT);
|
|
if (!ack_pkt) {
|
|
LOG_ERR("No free packet available.");
|
|
err = -ENOMEM;
|
|
goto free_nrf_ack;
|
|
}
|
|
|
|
/* Upper layers expect the frame to start at the MAC header, skip the
|
|
* PHY header (1 byte).
|
|
*/
|
|
if (net_pkt_write(ack_pkt, nrf5_radio->ack_frame.psdu + 1,
|
|
ack_len) < 0) {
|
|
LOG_ERR("Failed to write to a packet.");
|
|
err = -ENOMEM;
|
|
goto free_net_ack;
|
|
}
|
|
|
|
net_pkt_set_ieee802154_lqi(ack_pkt, nrf5_radio->ack_frame.lqi);
|
|
net_pkt_set_ieee802154_rssi_dbm(ack_pkt, nrf5_radio->ack_frame.rssi);
|
|
|
|
#if defined(CONFIG_NET_PKT_TIMESTAMP)
|
|
net_pkt_set_timestamp_ns(ack_pkt, nrf5_radio->ack_frame.time * NSEC_PER_USEC);
|
|
#endif
|
|
|
|
net_pkt_cursor_init(ack_pkt);
|
|
|
|
if (ieee802154_handle_ack(nrf5_radio->iface, ack_pkt) != NET_OK) {
|
|
LOG_INF("ACK packet not handled - releasing.");
|
|
}
|
|
|
|
free_net_ack:
|
|
net_pkt_unref(ack_pkt);
|
|
|
|
free_nrf_ack:
|
|
nrf_802154_buffer_free_raw(nrf5_radio->ack_frame.psdu);
|
|
nrf5_radio->ack_frame.psdu = NULL;
|
|
|
|
return err;
|
|
}
|
|
|
|
static void nrf5_tx_started(const struct device *dev,
|
|
struct net_pkt *pkt,
|
|
struct net_buf *frag)
|
|
{
|
|
ARG_UNUSED(pkt);
|
|
|
|
if (nrf5_data.event_handler) {
|
|
nrf5_data.event_handler(dev, IEEE802154_EVENT_TX_STARTED,
|
|
(void *)frag);
|
|
}
|
|
}
|
|
|
|
static bool nrf5_tx_immediate(struct net_pkt *pkt, uint8_t *payload, bool cca)
|
|
{
|
|
nrf_802154_transmit_metadata_t metadata = {
|
|
.frame_props = {
|
|
.is_secured = net_pkt_ieee802154_frame_secured(pkt),
|
|
.dynamic_data_is_set = net_pkt_ieee802154_mac_hdr_rdy(pkt),
|
|
},
|
|
.cca = cca,
|
|
.tx_power = {
|
|
.use_metadata_value = IS_ENABLED(CONFIG_IEEE802154_SELECTIVE_TXPOWER),
|
|
#if defined(CONFIG_IEEE802154_SELECTIVE_TXPOWER)
|
|
.power = net_pkt_ieee802154_txpwr(pkt),
|
|
#endif
|
|
},
|
|
};
|
|
|
|
return nrf_802154_transmit_raw(payload, &metadata);
|
|
}
|
|
|
|
#if NRF_802154_CSMA_CA_ENABLED
|
|
static bool nrf5_tx_csma_ca(struct net_pkt *pkt, uint8_t *payload)
|
|
{
|
|
nrf_802154_transmit_csma_ca_metadata_t metadata = {
|
|
.frame_props = {
|
|
.is_secured = net_pkt_ieee802154_frame_secured(pkt),
|
|
.dynamic_data_is_set = net_pkt_ieee802154_mac_hdr_rdy(pkt),
|
|
},
|
|
.tx_power = {
|
|
.use_metadata_value = IS_ENABLED(CONFIG_IEEE802154_SELECTIVE_TXPOWER),
|
|
#if defined(CONFIG_IEEE802154_SELECTIVE_TXPOWER)
|
|
.power = net_pkt_ieee802154_txpwr(pkt),
|
|
#endif
|
|
},
|
|
};
|
|
|
|
return nrf_802154_transmit_csma_ca_raw(payload, &metadata);
|
|
}
|
|
#endif
|
|
|
|
#if defined(CONFIG_NET_PKT_TXTIME)
|
|
static bool nrf5_tx_at(struct nrf5_802154_data *nrf5_radio, struct net_pkt *pkt,
|
|
uint8_t *payload, enum ieee802154_tx_mode mode)
|
|
{
|
|
bool cca = false;
|
|
#if defined(CONFIG_IEEE802154_NRF5_MULTIPLE_CCA)
|
|
uint8_t max_extra_cca_attempts = 0;
|
|
#endif
|
|
|
|
switch (mode) {
|
|
case IEEE802154_TX_MODE_TXTIME:
|
|
break;
|
|
case IEEE802154_TX_MODE_TXTIME_CCA:
|
|
cca = true;
|
|
break;
|
|
#if defined(CONFIG_IEEE802154_NRF5_MULTIPLE_CCA)
|
|
case IEEE802154_OPENTHREAD_TX_MODE_TXTIME_MULTIPLE_CCA:
|
|
cca = true;
|
|
max_extra_cca_attempts = nrf5_data.max_extra_cca_attempts;
|
|
break;
|
|
#endif
|
|
break;
|
|
default:
|
|
__ASSERT_NO_MSG(false);
|
|
return false;
|
|
}
|
|
|
|
nrf_802154_transmit_at_metadata_t metadata = {
|
|
.frame_props = {
|
|
.is_secured = net_pkt_ieee802154_frame_secured(pkt),
|
|
.dynamic_data_is_set = net_pkt_ieee802154_mac_hdr_rdy(pkt),
|
|
},
|
|
.cca = cca,
|
|
.channel = nrf_802154_channel_get(),
|
|
.tx_power = {
|
|
.use_metadata_value = IS_ENABLED(CONFIG_IEEE802154_SELECTIVE_TXPOWER),
|
|
#if defined(CONFIG_IEEE802154_SELECTIVE_TXPOWER)
|
|
.power = net_pkt_ieee802154_txpwr(pkt),
|
|
#endif
|
|
},
|
|
#if defined(CONFIG_IEEE802154_NRF5_MULTIPLE_CCA)
|
|
.extra_cca_attempts = max_extra_cca_attempts,
|
|
#endif
|
|
};
|
|
|
|
/* The timestamp points to the start of PHR but `nrf_802154_transmit_raw_at`
|
|
* expects a timestamp pointing to start of SHR.
|
|
*/
|
|
uint64_t tx_at = nrf_802154_timestamp_phr_to_shr_convert(
|
|
net_pkt_timestamp_ns(pkt) / NSEC_PER_USEC);
|
|
|
|
return nrf_802154_transmit_raw_at(payload, tx_at, &metadata);
|
|
}
|
|
#endif /* CONFIG_NET_PKT_TXTIME */
|
|
|
|
static int nrf5_tx(const struct device *dev,
|
|
enum ieee802154_tx_mode mode,
|
|
struct net_pkt *pkt,
|
|
struct net_buf *frag)
|
|
{
|
|
struct nrf5_802154_data *nrf5_radio = NRF5_802154_DATA(dev);
|
|
uint8_t payload_len = frag->len;
|
|
uint8_t *payload = frag->data;
|
|
bool ret = true;
|
|
|
|
if (payload_len > IEEE802154_MTU) {
|
|
LOG_ERR("Payload too large: %d", payload_len);
|
|
return -EMSGSIZE;
|
|
}
|
|
|
|
LOG_DBG("%p (%u)", payload, payload_len);
|
|
|
|
nrf5_radio->tx_psdu[0] = payload_len + IEEE802154_FCS_LENGTH;
|
|
memcpy(nrf5_radio->tx_psdu + 1, payload, payload_len);
|
|
|
|
/* Reset semaphore in case ACK was received after timeout */
|
|
k_sem_reset(&nrf5_radio->tx_wait);
|
|
|
|
switch (mode) {
|
|
case IEEE802154_TX_MODE_DIRECT:
|
|
case IEEE802154_TX_MODE_CCA:
|
|
ret = nrf5_tx_immediate(pkt, nrf5_radio->tx_psdu,
|
|
mode == IEEE802154_TX_MODE_CCA);
|
|
break;
|
|
#if NRF_802154_CSMA_CA_ENABLED
|
|
case IEEE802154_TX_MODE_CSMA_CA:
|
|
ret = nrf5_tx_csma_ca(pkt, nrf5_radio->tx_psdu);
|
|
break;
|
|
#endif
|
|
#if defined(CONFIG_NET_PKT_TXTIME)
|
|
case IEEE802154_TX_MODE_TXTIME:
|
|
case IEEE802154_TX_MODE_TXTIME_CCA:
|
|
#if defined(CONFIG_IEEE802154_NRF5_MULTIPLE_CCA)
|
|
case IEEE802154_OPENTHREAD_TX_MODE_TXTIME_MULTIPLE_CCA:
|
|
#endif
|
|
__ASSERT_NO_MSG(pkt);
|
|
ret = nrf5_tx_at(nrf5_radio, pkt, nrf5_radio->tx_psdu, mode);
|
|
break;
|
|
#endif /* CONFIG_NET_PKT_TXTIME */
|
|
default:
|
|
NET_ERR("TX mode %d not supported", mode);
|
|
return -ENOTSUP;
|
|
}
|
|
|
|
if (!ret) {
|
|
LOG_ERR("Cannot send frame");
|
|
return -EIO;
|
|
}
|
|
|
|
nrf5_tx_started(dev, pkt, frag);
|
|
|
|
LOG_DBG("Sending frame (ch:%d, txpower:%d)",
|
|
nrf_802154_channel_get(), nrf_802154_tx_power_get());
|
|
|
|
/* Wait for the callback from the radio driver. */
|
|
k_sem_take(&nrf5_radio->tx_wait, K_FOREVER);
|
|
|
|
LOG_DBG("Result: %d", nrf5_data.tx_result);
|
|
|
|
#if defined(CONFIG_NRF_802154_ENCRYPTION)
|
|
/*
|
|
* When frame encryption by the radio driver is enabled, the frame stored in
|
|
* the tx_psdu buffer is:
|
|
* 1) authenticated and encrypted in place which causes that after an unsuccessful
|
|
* TX attempt, this frame must be propagated back to the upper layer for retransmission.
|
|
* The upper layer must ensure that the exact same secured frame is used for
|
|
* retransmission
|
|
* 2) frame counters are updated in place and for keeping the link frame counter up to date,
|
|
* this information must be propagated back to the upper layer
|
|
*/
|
|
memcpy(payload, nrf5_radio->tx_psdu + 1, payload_len);
|
|
#endif
|
|
net_pkt_set_ieee802154_frame_secured(pkt, nrf5_radio->tx_frame_is_secured);
|
|
net_pkt_set_ieee802154_mac_hdr_rdy(pkt, nrf5_radio->tx_frame_mac_hdr_rdy);
|
|
|
|
switch (nrf5_radio->tx_result) {
|
|
case NRF_802154_TX_ERROR_NONE:
|
|
if (nrf5_radio->ack_frame.psdu == NULL) {
|
|
/* No ACK was requested. */
|
|
return 0;
|
|
}
|
|
/* Handle ACK packet. */
|
|
return handle_ack(nrf5_radio);
|
|
case NRF_802154_TX_ERROR_NO_MEM:
|
|
return -ENOBUFS;
|
|
case NRF_802154_TX_ERROR_BUSY_CHANNEL:
|
|
return -EBUSY;
|
|
case NRF_802154_TX_ERROR_INVALID_ACK:
|
|
case NRF_802154_TX_ERROR_NO_ACK:
|
|
return -ENOMSG;
|
|
case NRF_802154_TX_ERROR_ABORTED:
|
|
case NRF_802154_TX_ERROR_TIMESLOT_DENIED:
|
|
case NRF_802154_TX_ERROR_TIMESLOT_ENDED:
|
|
default:
|
|
return -EIO;
|
|
}
|
|
}
|
|
|
|
static net_time_t nrf5_get_time(const struct device *dev)
|
|
{
|
|
ARG_UNUSED(dev);
|
|
|
|
return (net_time_t)nrf_802154_time_get() * NSEC_PER_USEC;
|
|
}
|
|
|
|
static uint8_t nrf5_get_acc(const struct device *dev)
|
|
{
|
|
ARG_UNUSED(dev);
|
|
|
|
return CONFIG_IEEE802154_NRF5_DELAY_TRX_ACC;
|
|
}
|
|
|
|
static int nrf5_start(const struct device *dev)
|
|
{
|
|
ARG_UNUSED(dev);
|
|
|
|
if (!nrf_802154_receive()) {
|
|
LOG_ERR("Failed to enter receive state");
|
|
return -EIO;
|
|
}
|
|
|
|
LOG_DBG("nRF5 802154 radio started (channel: %d)",
|
|
nrf_802154_channel_get());
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int nrf5_stop(const struct device *dev)
|
|
{
|
|
#if defined(CONFIG_IEEE802154_CSL_ENDPOINT)
|
|
if (nrf_802154_sleep_if_idle() != NRF_802154_SLEEP_ERROR_NONE) {
|
|
if (nrf5_data.event_handler) {
|
|
nrf5_data.event_handler(dev, IEEE802154_EVENT_RX_OFF, NULL);
|
|
} else {
|
|
LOG_WRN("Transition to radio sleep cannot be handled.");
|
|
}
|
|
return 0;
|
|
}
|
|
#else
|
|
ARG_UNUSED(dev);
|
|
|
|
if (!nrf_802154_sleep()) {
|
|
LOG_ERR("Error while stopping radio");
|
|
return -EIO;
|
|
}
|
|
#endif
|
|
|
|
LOG_DBG("nRF5 802154 radio stopped");
|
|
|
|
return 0;
|
|
}
|
|
|
|
#if defined(CONFIG_NRF_802154_CARRIER_FUNCTIONS)
|
|
static int nrf5_continuous_carrier(const struct device *dev)
|
|
{
|
|
ARG_UNUSED(dev);
|
|
|
|
if (!nrf_802154_continuous_carrier()) {
|
|
LOG_ERR("Failed to enter continuous carrier state");
|
|
return -EIO;
|
|
}
|
|
|
|
LOG_DBG("Continuous carrier wave transmission started (channel: %d)",
|
|
nrf_802154_channel_get());
|
|
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
#if !IS_ENABLED(CONFIG_IEEE802154_NRF5_EXT_IRQ_MGMT)
|
|
static void nrf5_radio_irq(const void *arg)
|
|
{
|
|
ARG_UNUSED(arg);
|
|
|
|
nrf_802154_radio_irq_handler();
|
|
}
|
|
#endif
|
|
|
|
static void nrf5_irq_config(const struct device *dev)
|
|
{
|
|
ARG_UNUSED(dev);
|
|
|
|
#if !IS_ENABLED(CONFIG_IEEE802154_NRF5_EXT_IRQ_MGMT)
|
|
IRQ_CONNECT(RADIO_IRQn, NRF_802154_IRQ_PRIORITY,
|
|
nrf5_radio_irq, NULL, 0);
|
|
irq_enable(RADIO_IRQn);
|
|
#endif
|
|
}
|
|
|
|
static int nrf5_init(const struct device *dev)
|
|
{
|
|
const struct nrf5_802154_config *nrf5_radio_cfg = NRF5_802154_CFG(dev);
|
|
struct nrf5_802154_data *nrf5_radio = NRF5_802154_DATA(dev);
|
|
|
|
k_fifo_init(&nrf5_radio->rx_fifo);
|
|
k_sem_init(&nrf5_radio->tx_wait, 0, 1);
|
|
k_sem_init(&nrf5_radio->cca_wait, 0, 1);
|
|
|
|
nrf_802154_init();
|
|
|
|
nrf5_get_capabilities_at_boot();
|
|
|
|
nrf5_radio_cfg->irq_config_func(dev);
|
|
|
|
k_thread_create(&nrf5_radio->rx_thread, nrf5_radio->rx_stack,
|
|
CONFIG_IEEE802154_NRF5_RX_STACK_SIZE,
|
|
nrf5_rx_thread, nrf5_radio, NULL, NULL,
|
|
K_PRIO_COOP(2), 0, K_NO_WAIT);
|
|
|
|
k_thread_name_set(&nrf5_radio->rx_thread, "nrf5_rx");
|
|
|
|
LOG_INF("nRF5 802154 radio initialized");
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void nrf5_iface_init(struct net_if *iface)
|
|
{
|
|
const struct device *dev = net_if_get_device(iface);
|
|
struct nrf5_802154_data *nrf5_radio = NRF5_802154_DATA(dev);
|
|
|
|
nrf5_get_eui64(nrf5_radio->mac);
|
|
net_if_set_link_addr(iface, nrf5_radio->mac, sizeof(nrf5_radio->mac),
|
|
NET_LINK_IEEE802154);
|
|
|
|
nrf5_radio->iface = iface;
|
|
|
|
ieee802154_init(iface);
|
|
}
|
|
|
|
#if defined(CONFIG_NRF_802154_ENCRYPTION)
|
|
static void nrf5_config_mac_keys(struct ieee802154_key *mac_keys)
|
|
{
|
|
static nrf_802154_key_id_t stored_key_ids[NRF_802154_SECURITY_KEY_STORAGE_SIZE];
|
|
static uint8_t stored_ids[NRF_802154_SECURITY_KEY_STORAGE_SIZE];
|
|
uint8_t i;
|
|
|
|
for (i = 0; i < NRF_802154_SECURITY_KEY_STORAGE_SIZE && stored_key_ids[i].p_key_id; i++) {
|
|
nrf_802154_security_key_remove(&stored_key_ids[i]);
|
|
stored_key_ids[i].p_key_id = NULL;
|
|
}
|
|
|
|
i = 0;
|
|
for (struct ieee802154_key *keys = mac_keys; keys->key_value
|
|
&& i < NRF_802154_SECURITY_KEY_STORAGE_SIZE; keys++, i++) {
|
|
nrf_802154_key_t key = {
|
|
.value.p_cleartext_key = keys->key_value,
|
|
.id.mode = keys->key_id_mode,
|
|
.id.p_key_id = &(keys->key_index),
|
|
.type = NRF_802154_KEY_CLEARTEXT,
|
|
.frame_counter = 0,
|
|
.use_global_frame_counter = !(keys->frame_counter_per_key),
|
|
};
|
|
|
|
__ASSERT_EVAL((void)nrf_802154_security_key_store(&key),
|
|
nrf_802154_security_error_t err = nrf_802154_security_key_store(&key),
|
|
err == NRF_802154_SECURITY_ERROR_NONE ||
|
|
err == NRF_802154_SECURITY_ERROR_ALREADY_PRESENT,
|
|
"Storing key failed, err: %d", err);
|
|
|
|
stored_ids[i] = *key.id.p_key_id;
|
|
stored_key_ids[i].mode = key.id.mode;
|
|
stored_key_ids[i].p_key_id = &stored_ids[i];
|
|
};
|
|
}
|
|
#endif /* CONFIG_NRF_802154_ENCRYPTION */
|
|
|
|
static int nrf5_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.enabled) {
|
|
switch (config->auto_ack_fpb.mode) {
|
|
case IEEE802154_FPB_ADDR_MATCH_THREAD:
|
|
nrf_802154_src_addr_matching_method_set(
|
|
NRF_802154_SRC_ADDR_MATCH_THREAD);
|
|
break;
|
|
|
|
case IEEE802154_FPB_ADDR_MATCH_ZIGBEE:
|
|
nrf_802154_src_addr_matching_method_set(
|
|
NRF_802154_SRC_ADDR_MATCH_ZIGBEE);
|
|
break;
|
|
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
nrf_802154_auto_pending_bit_set(config->auto_ack_fpb.enabled);
|
|
break;
|
|
|
|
case IEEE802154_CONFIG_ACK_FPB:
|
|
if (config->ack_fpb.enabled) {
|
|
if (!nrf_802154_pending_bit_for_addr_set(
|
|
config->ack_fpb.addr,
|
|
config->ack_fpb.extended)) {
|
|
return -ENOMEM;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
if (config->ack_fpb.addr != NULL) {
|
|
if (!nrf_802154_pending_bit_for_addr_clear(
|
|
config->ack_fpb.addr,
|
|
config->ack_fpb.extended)) {
|
|
return -ENOENT;
|
|
}
|
|
} else {
|
|
nrf_802154_pending_bit_for_addr_reset(
|
|
config->ack_fpb.extended);
|
|
}
|
|
|
|
break;
|
|
|
|
case IEEE802154_CONFIG_PAN_COORDINATOR:
|
|
nrf_802154_pan_coord_set(config->pan_coordinator);
|
|
break;
|
|
|
|
case IEEE802154_CONFIG_PROMISCUOUS:
|
|
nrf_802154_promiscuous_set(config->promiscuous);
|
|
break;
|
|
|
|
case IEEE802154_CONFIG_EVENT_HANDLER:
|
|
nrf5_data.event_handler = config->event_handler;
|
|
break;
|
|
|
|
#if defined(CONFIG_NRF_802154_ENCRYPTION)
|
|
case IEEE802154_CONFIG_MAC_KEYS:
|
|
nrf5_config_mac_keys(config->mac_keys);
|
|
break;
|
|
|
|
case IEEE802154_CONFIG_FRAME_COUNTER:
|
|
nrf_802154_security_global_frame_counter_set(config->frame_counter);
|
|
break;
|
|
|
|
case IEEE802154_CONFIG_FRAME_COUNTER_IF_LARGER:
|
|
nrf_802154_security_global_frame_counter_set_if_larger(config->frame_counter);
|
|
break;
|
|
#endif /* CONFIG_NRF_802154_ENCRYPTION */
|
|
|
|
case IEEE802154_CONFIG_ENH_ACK_HEADER_IE: {
|
|
uint8_t short_addr_le[SHORT_ADDRESS_SIZE];
|
|
uint8_t ext_addr_le[EXTENDED_ADDRESS_SIZE];
|
|
|
|
sys_put_le16(config->ack_ie.short_addr, short_addr_le);
|
|
/**
|
|
* The extended address field passed to this function starts
|
|
* with the most significant octet and ends with the least
|
|
* significant octet (i.e. big endian byte order).
|
|
* The IEEE 802.15.4 transmission order mandates this order to be
|
|
* reversed (i.e. little endian byte order) in a transmitted frame.
|
|
*
|
|
* The nrf_802154_ack_data_set expects extended address in transmission
|
|
* order.
|
|
*/
|
|
sys_memcpy_swap(ext_addr_le, config->ack_ie.ext_addr, EXTENDED_ADDRESS_SIZE);
|
|
|
|
if (config->ack_ie.data_len > 0) {
|
|
nrf_802154_ack_data_set(short_addr_le, false, config->ack_ie.data,
|
|
config->ack_ie.data_len, NRF_802154_ACK_DATA_IE);
|
|
nrf_802154_ack_data_set(ext_addr_le, true, config->ack_ie.data,
|
|
config->ack_ie.data_len, NRF_802154_ACK_DATA_IE);
|
|
} else {
|
|
nrf_802154_ack_data_clear(short_addr_le, false, NRF_802154_ACK_DATA_IE);
|
|
nrf_802154_ack_data_clear(ext_addr_le, true, NRF_802154_ACK_DATA_IE);
|
|
}
|
|
} break;
|
|
|
|
#if defined(CONFIG_IEEE802154_CSL_ENDPOINT)
|
|
case IEEE802154_CONFIG_CSL_RX_TIME: {
|
|
nrf_802154_csl_writer_anchor_time_set(config->csl_rx_time / NSEC_PER_USEC);
|
|
} break;
|
|
|
|
case IEEE802154_CONFIG_RX_SLOT: {
|
|
/* Note that even if the nrf_802154_receive_at function is not called in time
|
|
* (for example due to the call being blocked by higher priority threads) and
|
|
* the delayed reception window is not scheduled, the CSL phase will still be
|
|
* calculated as if the following reception windows were at times
|
|
* anchor_time + n * csl_period. The previously set
|
|
* anchor_time will be used for calculations.
|
|
*/
|
|
nrf_802154_receive_at(config->rx_slot.start / NSEC_PER_USEC,
|
|
config->rx_slot.duration / NSEC_PER_USEC,
|
|
config->rx_slot.channel, DRX_SLOT_RX);
|
|
} break;
|
|
|
|
case IEEE802154_CONFIG_CSL_PERIOD:
|
|
nrf_802154_csl_writer_period_set(config->csl_period);
|
|
break;
|
|
#endif /* CONFIG_IEEE802154_CSL_ENDPOINT */
|
|
|
|
#if defined(CONFIG_IEEE802154_NRF5_MULTIPLE_CCA)
|
|
case IEEE802154_OPENTHREAD_CONFIG_MAX_EXTRA_CCA_ATTEMPTS:
|
|
nrf5_data.max_extra_cca_attempts =
|
|
((const struct ieee802154_openthread_config *)config)
|
|
->max_extra_cca_attempts;
|
|
break;
|
|
#endif /* CONFIG_IEEE802154_NRF5_MULTIPLE_CCA */
|
|
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* driver-allocated attribute memory - constant across all driver instances */
|
|
IEEE802154_DEFINE_PHY_SUPPORTED_CHANNELS(drv_attr, 11, 26);
|
|
|
|
static int nrf5_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;
|
|
}
|
|
|
|
switch ((uint32_t)attr) {
|
|
#if defined(CONFIG_IEEE802154_NRF5_MULTIPLE_CCA)
|
|
/* TODO: t_recca and t_ccatx should be provided by the public API of the
|
|
* nRF 802.15.4 Radio Driver.
|
|
*/
|
|
case IEEE802154_OPENTHREAD_ATTR_T_RECCA:
|
|
((struct ieee802154_openthread_attr_value *)value)->t_recca = 0;
|
|
break;
|
|
case IEEE802154_OPENTHREAD_ATTR_T_CCATX:
|
|
((struct ieee802154_openthread_attr_value *)value)->t_ccatx = 20;
|
|
break;
|
|
#endif
|
|
default:
|
|
return -ENOENT;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* nRF5 radio driver callbacks */
|
|
|
|
void nrf_802154_received_timestamp_raw(uint8_t *data, int8_t power, uint8_t lqi, uint64_t time)
|
|
{
|
|
for (uint32_t i = 0; i < ARRAY_SIZE(nrf5_data.rx_frames); i++) {
|
|
if (nrf5_data.rx_frames[i].psdu != NULL) {
|
|
continue;
|
|
}
|
|
|
|
nrf5_data.rx_frames[i].psdu = data;
|
|
nrf5_data.rx_frames[i].rssi = power;
|
|
nrf5_data.rx_frames[i].lqi = lqi;
|
|
|
|
#if defined(CONFIG_NET_PKT_TIMESTAMP)
|
|
nrf5_data.rx_frames[i].time =
|
|
nrf_802154_timestamp_end_to_phr_convert(time, data[0]);
|
|
#endif
|
|
|
|
if (data[ACK_REQUEST_BYTE] & ACK_REQUEST_BIT) {
|
|
nrf5_data.rx_frames[i].ack_fpb = nrf5_data.last_frame_ack_fpb;
|
|
} else {
|
|
nrf5_data.rx_frames[i].ack_fpb = false;
|
|
}
|
|
|
|
nrf5_data.last_frame_ack_fpb = false;
|
|
|
|
k_fifo_put(&nrf5_data.rx_fifo, &nrf5_data.rx_frames[i]);
|
|
|
|
return;
|
|
}
|
|
|
|
__ASSERT(false, "Not enough rx frames allocated for 15.4 driver");
|
|
}
|
|
|
|
void nrf_802154_receive_failed(nrf_802154_rx_error_t error, uint32_t id)
|
|
{
|
|
const struct device *dev = net_if_get_device(nrf5_data.iface);
|
|
|
|
#if defined(CONFIG_IEEE802154_CSL_ENDPOINT)
|
|
if (id == DRX_SLOT_RX) {
|
|
__ASSERT_NO_MSG(nrf5_data.event_handler);
|
|
#if !defined(CONFIG_IEEE802154_CSL_DEBUG)
|
|
/* When CSL debug option is used we intentionally avoid notifying the higher layer
|
|
* about the finalization of a DRX slot, so that the radio stays in receive state
|
|
* for receiving "out of slot" frames.
|
|
* As a side effect, regular failure notifications would be reported with the
|
|
* incorrect ID.
|
|
*/
|
|
nrf5_data.event_handler(dev, IEEE802154_EVENT_RX_OFF, NULL);
|
|
#endif
|
|
if (error == NRF_802154_RX_ERROR_DELAYED_TIMEOUT) {
|
|
return;
|
|
}
|
|
}
|
|
#else
|
|
ARG_UNUSED(id);
|
|
#endif
|
|
|
|
enum ieee802154_rx_fail_reason reason;
|
|
|
|
switch (error) {
|
|
case NRF_802154_RX_ERROR_INVALID_FRAME:
|
|
case NRF_802154_RX_ERROR_DELAYED_TIMEOUT:
|
|
reason = IEEE802154_RX_FAIL_NOT_RECEIVED;
|
|
break;
|
|
|
|
case NRF_802154_RX_ERROR_INVALID_FCS:
|
|
reason = IEEE802154_RX_FAIL_INVALID_FCS;
|
|
break;
|
|
|
|
case NRF_802154_RX_ERROR_INVALID_DEST_ADDR:
|
|
reason = IEEE802154_RX_FAIL_ADDR_FILTERED;
|
|
break;
|
|
|
|
default:
|
|
reason = IEEE802154_RX_FAIL_OTHER;
|
|
break;
|
|
}
|
|
|
|
if (IS_ENABLED(CONFIG_IEEE802154_NRF5_LOG_RX_FAILURES)) {
|
|
LOG_INF("Rx failed, error = %d", error);
|
|
}
|
|
|
|
nrf5_data.last_frame_ack_fpb = false;
|
|
if (nrf5_data.event_handler) {
|
|
nrf5_data.event_handler(dev, IEEE802154_EVENT_RX_FAILED, (void *)&reason);
|
|
}
|
|
}
|
|
|
|
void nrf_802154_tx_ack_started(const uint8_t *data)
|
|
{
|
|
nrf5_data.last_frame_ack_fpb =
|
|
data[FRAME_PENDING_BYTE] & FRAME_PENDING_BIT;
|
|
}
|
|
|
|
void nrf_802154_transmitted_raw(uint8_t *frame,
|
|
const nrf_802154_transmit_done_metadata_t *metadata)
|
|
{
|
|
ARG_UNUSED(frame);
|
|
|
|
nrf5_data.tx_result = NRF_802154_TX_ERROR_NONE;
|
|
nrf5_data.tx_frame_is_secured = metadata->frame_props.is_secured;
|
|
nrf5_data.tx_frame_mac_hdr_rdy = metadata->frame_props.dynamic_data_is_set;
|
|
nrf5_data.ack_frame.psdu = metadata->data.transmitted.p_ack;
|
|
|
|
if (nrf5_data.ack_frame.psdu) {
|
|
nrf5_data.ack_frame.rssi = metadata->data.transmitted.power;
|
|
nrf5_data.ack_frame.lqi = metadata->data.transmitted.lqi;
|
|
|
|
#if defined(CONFIG_NET_PKT_TIMESTAMP)
|
|
nrf5_data.ack_frame.time = nrf_802154_timestamp_end_to_phr_convert(
|
|
metadata->data.transmitted.time, nrf5_data.ack_frame.psdu[0]);
|
|
#endif
|
|
}
|
|
|
|
k_sem_give(&nrf5_data.tx_wait);
|
|
}
|
|
|
|
void nrf_802154_transmit_failed(uint8_t *frame,
|
|
nrf_802154_tx_error_t error,
|
|
const nrf_802154_transmit_done_metadata_t *metadata)
|
|
{
|
|
ARG_UNUSED(frame);
|
|
|
|
nrf5_data.tx_result = error;
|
|
nrf5_data.tx_frame_is_secured = metadata->frame_props.is_secured;
|
|
nrf5_data.tx_frame_mac_hdr_rdy = metadata->frame_props.dynamic_data_is_set;
|
|
|
|
k_sem_give(&nrf5_data.tx_wait);
|
|
}
|
|
|
|
void nrf_802154_cca_done(bool channel_free)
|
|
{
|
|
nrf5_data.channel_free = channel_free;
|
|
|
|
k_sem_give(&nrf5_data.cca_wait);
|
|
}
|
|
|
|
void nrf_802154_cca_failed(nrf_802154_cca_error_t error)
|
|
{
|
|
ARG_UNUSED(error);
|
|
|
|
nrf5_data.channel_free = false;
|
|
|
|
k_sem_give(&nrf5_data.cca_wait);
|
|
}
|
|
|
|
void nrf_802154_energy_detected(const nrf_802154_energy_detected_t *result)
|
|
{
|
|
if (nrf5_data.energy_scan_done != NULL) {
|
|
energy_scan_done_cb_t callback = nrf5_data.energy_scan_done;
|
|
|
|
nrf5_data.energy_scan_done = NULL;
|
|
callback(net_if_get_device(nrf5_data.iface), result->ed_dbm);
|
|
}
|
|
}
|
|
|
|
void nrf_802154_energy_detection_failed(nrf_802154_ed_error_t error)
|
|
{
|
|
if (nrf5_data.energy_scan_done != NULL) {
|
|
energy_scan_done_cb_t callback = nrf5_data.energy_scan_done;
|
|
|
|
nrf5_data.energy_scan_done = NULL;
|
|
callback(net_if_get_device(nrf5_data.iface), SHRT_MAX);
|
|
}
|
|
}
|
|
|
|
#if defined(CONFIG_NRF_802154_SER_HOST)
|
|
void nrf_802154_serialization_error(const nrf_802154_ser_err_data_t *err)
|
|
{
|
|
__ASSERT(false, "802.15.4 serialization error: %d", err->reason);
|
|
k_oops();
|
|
}
|
|
#endif
|
|
|
|
static const struct nrf5_802154_config nrf5_radio_cfg = {
|
|
.irq_config_func = nrf5_irq_config,
|
|
};
|
|
|
|
static struct ieee802154_radio_api nrf5_radio_api = {
|
|
.iface_api.init = nrf5_iface_init,
|
|
|
|
.get_capabilities = nrf5_get_capabilities,
|
|
.cca = nrf5_cca,
|
|
.set_channel = nrf5_set_channel,
|
|
.filter = nrf5_filter,
|
|
.set_txpower = nrf5_set_txpower,
|
|
.start = nrf5_start,
|
|
.stop = nrf5_stop,
|
|
#if defined(CONFIG_NRF_802154_CARRIER_FUNCTIONS)
|
|
.continuous_carrier = nrf5_continuous_carrier,
|
|
#endif
|
|
.tx = nrf5_tx,
|
|
.ed_scan = nrf5_energy_scan_start,
|
|
.get_time = nrf5_get_time,
|
|
.get_sch_acc = nrf5_get_acc,
|
|
.configure = nrf5_configure,
|
|
.attr_get = nrf5_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, nrf5_init, NULL, &nrf5_data, &nrf5_radio_cfg,
|
|
CONFIG_IEEE802154_NRF5_INIT_PRIO, &nrf5_radio_api, L2,
|
|
L2_CTX_TYPE, MTU);
|
|
#else
|
|
DEVICE_DT_INST_DEFINE(0, nrf5_init, NULL, &nrf5_data, &nrf5_radio_cfg,
|
|
POST_KERNEL, CONFIG_IEEE802154_NRF5_INIT_PRIO,
|
|
&nrf5_radio_api);
|
|
#endif
|