diff --git a/drivers/include/kw2xrf.h b/drivers/include/kw2xrf.h index 45af585936..11aca9b00d 100644 --- a/drivers/include/kw2xrf.h +++ b/drivers/include/kw2xrf.h @@ -32,6 +32,7 @@ #include "net/netdev/ieee802154.h" #include "net/gnrc/nettype.h" #include "thread.h" +#include "net/ieee802154/radio.h" #ifdef __cplusplus extern "C" { @@ -107,6 +108,7 @@ typedef struct kw2xrf_params { spi_clk_t spi_clk; /**< SPI clock speed to use */ gpio_t cs_pin; /**< GPIO pin connected to chip select */ gpio_t int_pin; /**< GPIO pin connected to the interrupt pin */ + gpio_t rst_pin; /**< GPIO pin connected to RST_B */ } kw2xrf_params_t; /** @@ -115,13 +117,12 @@ typedef struct kw2xrf_params { * @extends netdev_ieee802154_t */ typedef struct { - netdev_ieee802154_t netdev; /**< netdev parent struct */ /** * @brief device specific fields * @{ */ thread_t *thread; /**< Network driver thread, for providing feedback from IRQ handler */ - kw2xrf_params_t params; /**< parameters for initialization */ + const kw2xrf_params_t *params; /**< parameters for initialization */ uint8_t buf[KW2XRF_MAX_PKT_LENGTH]; /**< Buffer for incoming or outgoing packets */ uint8_t state; /**< current state of the radio */ uint8_t tx_frame_len; /**< length of the current TX frame */ @@ -130,6 +131,13 @@ typedef struct { this is required to know when to return to @ref kw2xrf_t::idle_state */ int16_t tx_power; /**< The current tx-power setting of the device */ + bool ack_requested; /**< ACK was requested for last frame */ + bool ch_clear; /**< CCA indicated channel clear */ + bool waiting_for_cca; /**< Indicate whether CCA is still ongoing */ + bool tx_done; /**< Indicate whether TX completed */ + bool ack_rcvd; /**< Indicate if ACK was received for last transmission */ + bool cca_before_tx; /**< true if CCA shall be performed before TX */ + bool tx_cca_pending; /**< true a manual CCA was started and a TX should be triggered on channel clear indication */ /** @} */ } kw2xrf_t; @@ -146,12 +154,16 @@ void kw2xrf_setup(kw2xrf_t *dev, const kw2xrf_params_t *params, uint8_t index); /** * @brief Initialize the given KW2XRF device * @param[out] dev device descriptor - * @param[in] cb irq callback + * @param[in] params parameters for device initialization + * @param[in] hal pointer to IEEE 802.15.4 Radio HAL descriptor + * @param[in] cb isr callback + * @param[in] ctx context pointer handed to isr * * @return 0 on success * @return <0 on error */ -int kw2xrf_init(kw2xrf_t *dev, gpio_cb_t cb); +int kw2xrf_init(kw2xrf_t *dev, const kw2xrf_params_t *params, ieee802154_dev_t *hal, + gpio_cb_t cb, void *ctx); /** * @brief Configure radio with default values @@ -160,6 +172,13 @@ int kw2xrf_init(kw2xrf_t *dev, gpio_cb_t cb); */ void kw2xrf_reset_phy(kw2xrf_t *dev); +/** + * @brief IRQ Handler for the KW2XRF device + * + * @param[in] dev pointer to the IEEE 802.15.4 Radio HAL descriptor + */ +void kw2xrf_radio_hal_irq_handler(void *dev); + #ifdef __cplusplus } #endif diff --git a/drivers/kw2xrf/Kconfig b/drivers/kw2xrf/Kconfig index 88d694b5b4..2d4b2eafa7 100644 --- a/drivers/kw2xrf/Kconfig +++ b/drivers/kw2xrf/Kconfig @@ -21,6 +21,8 @@ menuconfig MODULE_KW2XRF select MODULE_NETDEV select MODULE_NETDEV_IEEE802154 select MODULE_CORE_THREAD_FLAGS + select MODULE_IOLIST + select HAVE_BHP_IRQ_HANDLER config MODULE_KW2XRF_TESTMODE bool "Test mode" diff --git a/drivers/kw2xrf/Makefile b/drivers/kw2xrf/Makefile index 77bdb4e016..0d24e2c106 100644 --- a/drivers/kw2xrf/Makefile +++ b/drivers/kw2xrf/Makefile @@ -1,5 +1,9 @@ SUBMODULES := 1 -SRC := kw2xrf.c kw2xrf_getset.c kw2xrf_intern.c kw2xrf_netdev.c kw2xrf_spi.c +SRC := kw2xrf.c kw2xrf_getset.c kw2xrf_intern.c kw2xrf_radio_hal.c kw2xrf_spi.c + +ifneq (,$(filter kw2xrf_testmode,$(USEMODULE))) + SRC += kw2xrf_tm.c +endif include $(RIOTBASE)/Makefile.base diff --git a/drivers/kw2xrf/Makefile.dep b/drivers/kw2xrf/Makefile.dep index 200f241c46..b5a48e2e81 100644 --- a/drivers/kw2xrf/Makefile.dep +++ b/drivers/kw2xrf/Makefile.dep @@ -1,7 +1,13 @@ USEMODULE += luid USEMODULE += ieee802154 -USEMODULE += netdev_ieee802154 USEMODULE += core_thread_flags +USEMODULE += iolist +USEMODULE += bhp + +ifneq (,$(filter netdev,$(USEMODULE))) + USEMODULE += netdev_ieee802154_submac +endif + FEATURES_REQUIRED += periph_spi FEATURES_REQUIRED += periph_gpio FEATURES_REQUIRED += periph_gpio_irq diff --git a/drivers/kw2xrf/Makefile.include b/drivers/kw2xrf/Makefile.include index 2639e8d1ec..f74e0ce253 100644 --- a/drivers/kw2xrf/Makefile.include +++ b/drivers/kw2xrf/Makefile.include @@ -1,2 +1,4 @@ USEMODULE_INCLUDES_kw2xrf := $(LAST_MAKEFILEDIR)/include USEMODULE_INCLUDES += $(USEMODULE_INCLUDES_kw2xrf) + +PSEUDOMODULES += kw2xrf_testmode diff --git a/drivers/kw2xrf/include/kw2xrf_getset.h b/drivers/kw2xrf/include/kw2xrf_getset.h index 682f5201a6..d540522d12 100644 --- a/drivers/kw2xrf/include/kw2xrf_getset.h +++ b/drivers/kw2xrf/include/kw2xrf_getset.h @@ -19,6 +19,7 @@ #ifndef KW2XRF_GETSET_H #define KW2XRF_GETSET_H +#include "kw2xrf_reg.h" #include "kw2xrf.h" #ifdef __cplusplus diff --git a/drivers/kw2xrf/include/kw2xrf_params.h b/drivers/kw2xrf/include/kw2xrf_params.h index e282c00d8b..c56b5c9aee 100644 --- a/drivers/kw2xrf/include/kw2xrf_params.h +++ b/drivers/kw2xrf/include/kw2xrf_params.h @@ -53,7 +53,8 @@ extern "C" { #define KW2XRF_PARAMS { .spi = KW2XRF_PARAM_SPI, \ .spi_clk = KW2XRF_PARAM_SPI_CLK, \ .cs_pin = KW2XRF_PARAM_CS, \ - .int_pin = KW2XRF_PARAM_INT } + .int_pin = KW2XRF_PARAM_INT, \ + .rst_pin = KW2XRF_PARAM_RESET } #endif /**@}*/ diff --git a/drivers/kw2xrf/kw2xrf.c b/drivers/kw2xrf/kw2xrf.c index 808d2f9b9a..7b7de8314f 100644 --- a/drivers/kw2xrf/kw2xrf.c +++ b/drivers/kw2xrf/kw2xrf.c @@ -54,51 +54,8 @@ static void kw2xrf_set_address(kw2xrf_t *dev) kw2xrf_set_addr_short(dev, ntohs(addr_long.uint16[0].u16)); } -void kw2xrf_setup(kw2xrf_t *dev, const kw2xrf_params_t *params, uint8_t index) -{ - netdev_t *netdev = &dev->netdev.netdev; - - netdev->driver = &kw2xrf_driver; - /* initialize device descriptor */ - dev->params = *params; - dev->idle_state = XCVSEQ_RECEIVE; - dev->state = 0; - dev->pending_tx = 0; - kw2xrf_spi_init(dev); - kw2xrf_set_power_mode(dev, KW2XRF_IDLE); - DEBUG("[kw2xrf] enabling RX/TX completion and start events"); - kw2xrf_clear_dreg_bit(dev, MKW2XDM_PHY_CTRL2, MKW2XDM_PHY_CTRL2_RX_WMRK_MSK); - kw2xrf_clear_dreg_bit(dev, MKW2XDM_PHY_CTRL2, MKW2XDM_PHY_CTRL2_RXMSK); - kw2xrf_clear_dreg_bit(dev, MKW2XDM_PHY_CTRL2, MKW2XDM_PHY_CTRL2_TXMSK); - DEBUG("[kw2xrf] setup finished\n"); - - /* register with netdev */ - netdev_register(netdev, NETDEV_KW2XRF, index); -} - -int kw2xrf_init(kw2xrf_t *dev, gpio_cb_t cb) -{ - if (dev == NULL) { - return -ENODEV; - } - - kw2xrf_set_out_clk(dev); - kw2xrf_disable_interrupts(dev); - /* set up GPIO-pin used for IRQ */ - gpio_init_int(dev->params.int_pin, GPIO_IN, GPIO_FALLING, cb, dev); - - kw2xrf_abort_sequence(dev); - kw2xrf_update_overwrites(dev); - kw2xrf_timer_init(dev, KW2XRF_TIMEBASE_62500HZ); - DEBUG("[kw2xrf] init finished\n"); - - return 0; -} - void kw2xrf_reset_phy(kw2xrf_t *dev) { - netdev_ieee802154_reset(&dev->netdev); - dev->tx_power = KW2XRF_DEFAULT_TX_POWER; kw2xrf_set_tx_power(dev, dev->tx_power); @@ -110,7 +67,10 @@ void kw2xrf_reset_phy(kw2xrf_t *dev) kw2xrf_set_rx_watermark(dev, 1); - kw2xrf_set_option(dev, KW2XRF_OPT_AUTOACK, true); + if (!IS_ACTIVE(CONFIG_IEEE802154_AUTO_ACK_DISABLE)) { + kw2xrf_set_option(dev, KW2XRF_OPT_AUTOACK, true); + } + kw2xrf_set_option(dev, KW2XRF_OPT_ACK_REQ, true); kw2xrf_set_option(dev, KW2XRF_OPT_AUTOCCA, true); diff --git a/drivers/kw2xrf/kw2xrf_getset.c b/drivers/kw2xrf/kw2xrf_getset.c index 3e2d628475..d117dd81df 100644 --- a/drivers/kw2xrf/kw2xrf_getset.c +++ b/drivers/kw2xrf/kw2xrf_getset.c @@ -368,8 +368,6 @@ void kw2xrf_set_option(kw2xrf_t *dev, uint16_t option, bool state) /* set option field */ if (state) { - dev->netdev.flags |= option; - /* trigger option specific actions */ switch (option) { case KW2XRF_OPT_AUTOCCA: @@ -404,7 +402,6 @@ void kw2xrf_set_option(kw2xrf_t *dev, uint16_t option, bool state) } } else { - dev->netdev.flags &= ~(option); /* trigger option specific actions */ switch (option) { case KW2XRF_OPT_AUTOCCA: @@ -416,15 +413,6 @@ void kw2xrf_set_option(kw2xrf_t *dev, uint16_t option, bool state) /* disable promiscuous mode */ kw2xrf_clear_dreg_bit(dev, MKW2XDM_PHY_CTRL4, MKW2XDM_PHY_CTRL4_PROMISCUOUS); - /* re-enable AUTOACK only if the option is set */ - if (dev->netdev.flags & KW2XRF_OPT_AUTOACK) { - kw2xrf_set_dreg_bit(dev, MKW2XDM_PHY_CTRL1, - MKW2XDM_PHY_CTRL1_AUTOACK); - } - if (dev->netdev.flags & KW2XRF_OPT_ACK_REQ) { - kw2xrf_set_dreg_bit(dev, MKW2XDM_PHY_CTRL1, - MKW2XDM_PHY_CTRL1_RXACKRQD); - } break; case KW2XRF_OPT_AUTOACK: diff --git a/drivers/kw2xrf/kw2xrf_netdev.c b/drivers/kw2xrf/kw2xrf_netdev.c deleted file mode 100644 index 386571a74c..0000000000 --- a/drivers/kw2xrf/kw2xrf_netdev.c +++ /dev/null @@ -1,804 +0,0 @@ -/* - * Copyright (C) 2016 Phytec Messtechnik GmbH - 2017 HAW Hamburg - 2017 SKF AB - * - * This file is subject to the terms and conditions of the GNU Lesser - * General Public License v2.1. See the file LICENSE in the top level - * directory for more details. - */ - -/** - * @ingroup drivers_kw2xrf - * @{ - * - * @file - * @brief Netdev interface for kw2xrf drivers - * - * @author Johann Fischer - * @author Peter Kietzmann - * @author Joakim NohlgÄrd - */ - -#include -#include -#include - -#include "log.h" -#include "thread_flags.h" -#include "net/eui64.h" -#include "net/ieee802154.h" -#include "net/netdev.h" -#include "net/netdev/ieee802154.h" - -#include "kw2xrf.h" -#include "kw2xrf_spi.h" -#include "kw2xrf_reg.h" -#include "kw2xrf_netdev.h" -#include "kw2xrf_getset.h" -#include "kw2xrf_tm.h" -#include "kw2xrf_intern.h" - -#define ENABLE_DEBUG 0 -#include "debug.h" - -#define _MACACKWAITDURATION (864 / 16) /* 864us * 62500Hz */ - -#define KW2XRF_THREAD_FLAG_ISR (1 << 8) - -static volatile unsigned int num_irqs_queued = 0; -static volatile unsigned int num_irqs_handled = 0; -static unsigned int spinning_for_irq = 0; -static uint8_t _send_last_fcf; - -static void _isr(netdev_t *netdev); - -static void _irq_handler(void *arg) -{ - netdev_t *netdev = arg; - netdev_ieee802154_t *netdev_ieee802154 = container_of(netdev, netdev_ieee802154_t, netdev); - kw2xrf_t *dev = container_of(netdev_ieee802154, kw2xrf_t, netdev); - - thread_flags_set(dev->thread, KW2XRF_THREAD_FLAG_ISR); - - /* We use this counter to avoid filling the message queue with redundant ISR events */ - if (num_irqs_queued == num_irqs_handled) { - ++num_irqs_queued; - netdev_trigger_event_isr(netdev); - } -} - -static int _init(netdev_t *netdev) -{ - netdev_ieee802154_t *netdev_ieee802154 = container_of(netdev, netdev_ieee802154_t, netdev); - kw2xrf_t *dev = container_of(netdev_ieee802154, kw2xrf_t, netdev); - - dev->thread = thread_get_active(); - - /* initialize SPI and GPIOs */ - if (kw2xrf_init(dev, &_irq_handler)) { - LOG_ERROR("[kw2xrf] unable to initialize device\n"); - return -1; - } - - /* reset device to default values and put it into RX state */ - kw2xrf_reset_phy(dev); - - /* enable TX End IRQ: the driver uses the event and gnrc_netif_ieee802154 - * only enables this when MODULE_NETSTATS_L2 is active */ - kw2xrf_clear_dreg_bit(dev, MKW2XDM_PHY_CTRL2, MKW2XDM_PHY_CTRL2_TXMSK); - - return 0; -} - -static size_t kw2xrf_tx_load(uint8_t *pkt_buf, uint8_t *buf, size_t len, size_t offset) -{ - for (unsigned i = 0; i < len; i++) { - pkt_buf[i + offset] = buf[i]; - } - return offset + len; -} - -static void kw2xrf_tx_exec(kw2xrf_t *dev) -{ - if ((dev->netdev.flags & KW2XRF_OPT_ACK_REQ) && - (_send_last_fcf & IEEE802154_FCF_ACK_REQ)) { - kw2xrf_set_sequence(dev, XCVSEQ_TX_RX); - } - else { - kw2xrf_set_sequence(dev, XCVSEQ_TRANSMIT); - } -} - -static void kw2xrf_wait_idle(kw2xrf_t *dev) -{ - /* make sure any ongoing T or TR sequence is finished */ - if (kw2xrf_can_switch_to_idle(dev) == 0) { - DEBUG("[kw2xrf] TX already in progress\n"); - num_irqs_handled = num_irqs_queued; - spinning_for_irq = 1; - thread_flags_clear(KW2XRF_THREAD_FLAG_ISR); - while (1) { - /* TX in progress */ - /* Handle any outstanding IRQ first */ - _isr(&dev->netdev.netdev); - /* _isr() will switch the transceiver back to idle after - * handling the TX complete IRQ */ - if (kw2xrf_can_switch_to_idle(dev)) { - break; - } - /* Block until we get another IRQ */ - thread_flags_wait_any(KW2XRF_THREAD_FLAG_ISR); - DEBUG("[kw2xrf] waited ISR\n"); - } - spinning_for_irq = 0; - DEBUG("[kw2xrf] previous TX done\n"); - } -} - -static int _send(netdev_t *netdev, const iolist_t *iolist) -{ - netdev_ieee802154_t *netdev_ieee802154 = container_of(netdev, netdev_ieee802154_t, netdev); - kw2xrf_t *dev = container_of(netdev_ieee802154, kw2xrf_t, netdev); - uint8_t *pkt_buf = &(dev->buf[1]); - size_t len = 0; - - /* wait for ongoing transmissions to finish */ - kw2xrf_wait_idle(dev); - - /* load packet data into buffer */ - for (const iolist_t *iol = iolist; iol; iol = iol->iol_next) { - /* current packet data + FCS too long */ - if ((len + iol->iol_len + IEEE802154_FCS_LEN) > KW2XRF_MAX_PKT_LENGTH) { - LOG_ERROR("[kw2xrf] packet too large (%u byte) to be send\n", - (unsigned)len + IEEE802154_FCS_LEN); - return -EOVERFLOW; - } - len = kw2xrf_tx_load(pkt_buf, iol->iol_base, iol->iol_len, len); - } - - kw2xrf_set_sequence(dev, XCVSEQ_IDLE); - dev->pending_tx++; - - /* - * Nbytes = FRAME_LEN - 2 -> FRAME_LEN = Nbytes + 2 - * MKW2xD Reference Manual, P.192 - */ - dev->buf[0] = len + IEEE802154_FCS_LEN; - - /* Help for decision to use T or TR sequenz */ - _send_last_fcf = dev->buf[1]; - - kw2xrf_write_fifo(dev, dev->buf, dev->buf[0]); - - /* send data out directly if pre-loading id disabled */ - if (!(dev->netdev.flags & KW2XRF_OPT_PRELOADING)) { - kw2xrf_tx_exec(dev); - } - - return (int)len; -} - -static int _recv(netdev_t *netdev, void *buf, size_t len, void *info) -{ - netdev_ieee802154_t *netdev_ieee802154 = container_of(netdev, netdev_ieee802154_t, netdev); - kw2xrf_t *dev = container_of(netdev_ieee802154, kw2xrf_t, netdev); - size_t pkt_len = 0; - - /* get size of the received packet */ - pkt_len = kw2xrf_read_dreg(dev, MKW2XDM_RX_FRM_LEN); - - /* just return length when buf == NULL */ - if (buf == NULL) { - return pkt_len + 1; - } - - if (pkt_len > len) { - /* not enough space in buf */ - return -ENOBUFS; - } - - kw2xrf_read_fifo(dev, (uint8_t *)buf, pkt_len + 1); - - if (info != NULL) { - netdev_ieee802154_rx_info_t *radio_info = info; - radio_info->lqi = ((uint8_t*)buf)[pkt_len]; - radio_info->rssi = kw2xrf_get_rssi(radio_info->lqi); - } - - /* skip FCS and LQI */ - return pkt_len - 2; -} - -static int _set_state(kw2xrf_t *dev, netopt_state_t state) -{ - switch (state) { - case NETOPT_STATE_SLEEP: - kw2xrf_set_power_mode(dev, KW2XRF_DOZE); - break; - case NETOPT_STATE_IDLE: - kw2xrf_set_power_mode(dev, KW2XRF_AUTODOZE); - kw2xrf_set_sequence(dev, dev->idle_state); - break; - case NETOPT_STATE_TX: - if (dev->netdev.flags & KW2XRF_OPT_PRELOADING) { - kw2xrf_tx_exec(dev); - } - break; - case NETOPT_STATE_RESET: - kw2xrf_reset_phy(dev); - break; - case NETOPT_STATE_OFF: - /* TODO: Replace with powerdown (set reset input low) */ - kw2xrf_set_power_mode(dev, KW2XRF_HIBERNATE); - break; - default: - return -ENOTSUP; - } - return sizeof(netopt_state_t); -} - -static netopt_state_t _get_state(kw2xrf_t *dev) -{ - return dev->state; -} - -int _get(netdev_t *netdev, netopt_t opt, void *value, size_t len) -{ - netdev_ieee802154_t *netdev_ieee802154 = container_of(netdev, netdev_ieee802154_t, netdev); - kw2xrf_t *dev = container_of(netdev_ieee802154, kw2xrf_t, netdev); - - if (dev == NULL) { - return -ENODEV; - } - - switch (opt) { - case NETOPT_ADDRESS: - if (len < sizeof(uint16_t)) { - return -EOVERFLOW; - } - *((uint16_t *)value) = kw2xrf_get_addr_short(dev); - return sizeof(uint16_t); - - case NETOPT_ADDRESS_LONG: - if (len < sizeof(uint64_t)) { - return -EOVERFLOW; - } - *((uint64_t *)value) = kw2xrf_get_addr_long(dev); - return sizeof(uint64_t); - - case NETOPT_STATE: - if (len < sizeof(netopt_state_t)) { - return -EOVERFLOW; - } - *((netopt_state_t *)value) = _get_state(dev); - return sizeof(netopt_state_t); - - case NETOPT_AUTOACK: - if (dev->netdev.flags & KW2XRF_OPT_AUTOACK) { - *((netopt_enable_t *)value) = NETOPT_ENABLE; - } - else { - *((netopt_enable_t *)value) = NETOPT_DISABLE; - } - return sizeof(netopt_enable_t); - - case NETOPT_PRELOADING: - if (dev->netdev.flags & KW2XRF_OPT_PRELOADING) { - *((netopt_enable_t *)value) = NETOPT_ENABLE; - } - else { - *((netopt_enable_t *)value) = NETOPT_DISABLE; - } - return sizeof(netopt_enable_t); - - case NETOPT_PROMISCUOUSMODE: - if (dev->netdev.flags & KW2XRF_OPT_PROMISCUOUS) { - *((netopt_enable_t *)value) = NETOPT_ENABLE; - } - else { - *((netopt_enable_t *)value) = NETOPT_DISABLE; - } - return sizeof(netopt_enable_t); - - case NETOPT_RX_START_IRQ: - case NETOPT_TX_START_IRQ: - case NETOPT_TX_END_IRQ: - *((netopt_enable_t *)value) = NETOPT_ENABLE; - return sizeof(netopt_enable_t); - - case NETOPT_AUTOCCA: - *((netopt_enable_t *)value) = - !!(dev->netdev.flags & KW2XRF_OPT_AUTOCCA); - return sizeof(netopt_enable_t); - - case NETOPT_CHANNEL: - if (len < sizeof(uint16_t)) { - return -EOVERFLOW; - } - *((uint16_t *)value) = kw2xrf_get_channel(dev); - return sizeof(uint16_t); - - case NETOPT_TX_POWER: - if (len < sizeof(int16_t)) { - return -EOVERFLOW; - } - *((uint16_t *)value) = kw2xrf_get_txpower(dev); - return sizeof(uint16_t); - - case NETOPT_IS_CHANNEL_CLR: - if (kw2xrf_cca(dev)) { - *((netopt_enable_t *)value) = NETOPT_ENABLE; - } - else { - *((netopt_enable_t *)value) = NETOPT_DISABLE; - } - return sizeof(netopt_enable_t); - - case NETOPT_CCA_THRESHOLD: - if (len < sizeof(uint8_t)) { - return -EOVERFLOW; - } - else { - *(int8_t *)value = kw2xrf_get_cca_threshold(dev); - } - return sizeof(int8_t); - - case NETOPT_CCA_MODE: - if (len < sizeof(uint8_t)) { - return -EOVERFLOW; - } - else { - *(uint8_t *)value = kw2xrf_get_cca_mode(dev); - switch (*((int8_t *)value)) { - case NETDEV_IEEE802154_CCA_MODE_1: - case NETDEV_IEEE802154_CCA_MODE_2: - case NETDEV_IEEE802154_CCA_MODE_3: - return sizeof(uint8_t); - default: - break; - } - return -EOVERFLOW; - } - break; - - case NETOPT_CHANNEL_PAGE: - default: - break; - } - - return netdev_ieee802154_get(container_of(netdev, netdev_ieee802154_t, netdev), - opt, value, len); -} - -static int _set(netdev_t *netdev, netopt_t opt, const void *value, size_t len) -{ - netdev_ieee802154_t *netdev_ieee802154 = container_of(netdev, netdev_ieee802154_t, netdev); - kw2xrf_t *dev = container_of(netdev_ieee802154, kw2xrf_t, netdev); - int res = -ENOTSUP; - - if (dev == NULL) { - return -ENODEV; - } - - switch (opt) { - case NETOPT_ADDRESS: - if (len > sizeof(uint16_t)) { - res = -EOVERFLOW; - } - else { - kw2xrf_set_addr_short(dev, *((uint16_t *)value)); - res = sizeof(uint16_t); - } - break; - - case NETOPT_ADDRESS_LONG: - if (len > sizeof(uint64_t)) { - return -EOVERFLOW; - } - else { - kw2xrf_set_addr_long(dev, *((uint64_t *)value)); - res = sizeof(uint64_t); - } - break; - - case NETOPT_NID: - if (len > sizeof(uint16_t)) { - return -EOVERFLOW; - } - - else { - kw2xrf_set_pan(dev, *((uint16_t *)value)); - /* don't set res to set netdev_ieee802154_t::pan */ - } - break; - - case NETOPT_CHANNEL: - if (len != sizeof(uint16_t)) { - res = -EINVAL; - } - else { - uint8_t chan = ((uint8_t *)value)[0]; - if (kw2xrf_set_channel(dev, chan)) { - res = -EINVAL; - break; - } - res = sizeof(uint16_t); - } - break; - - case NETOPT_CHANNEL_PAGE: - res = -EINVAL; - break; - - case NETOPT_TX_POWER: - if (len < sizeof(uint16_t)) { - res = -EOVERFLOW; - } - else { - kw2xrf_set_tx_power(dev, *(int16_t *)value); - res = sizeof(uint16_t); - } - break; - - case NETOPT_STATE: - if (len > sizeof(netopt_state_t)) { - res = -EOVERFLOW; - } - else { - res = _set_state(dev, *((netopt_state_t *)value)); - } - break; - - case NETOPT_AUTOACK: - /* Set up HW generated automatic ACK after Receive */ - kw2xrf_set_option(dev, KW2XRF_OPT_AUTOACK, - ((bool *)value)[0]); - res = sizeof(netopt_enable_t); - break; - - case NETOPT_ACK_REQ: - kw2xrf_set_option(dev, KW2XRF_OPT_ACK_REQ, - ((bool *)value)[0]); - break; - - case NETOPT_PRELOADING: - kw2xrf_set_option(dev, KW2XRF_OPT_PRELOADING, - ((bool *)value)[0]); - res = sizeof(netopt_enable_t); - break; - - case NETOPT_PROMISCUOUSMODE: - kw2xrf_set_option(dev, KW2XRF_OPT_PROMISCUOUS, - ((bool *)value)[0]); - res = sizeof(netopt_enable_t); - break; - - case NETOPT_AUTOCCA: - kw2xrf_set_option(dev, KW2XRF_OPT_AUTOCCA, - ((bool *)value)[0]); - res = sizeof(netopt_enable_t); - break; - - case NETOPT_CCA_THRESHOLD: - if (len < sizeof(uint8_t)) { - res = -EOVERFLOW; - } - else { - kw2xrf_set_cca_threshold(dev, *((int8_t*)value)); - res = sizeof(uint8_t); - } - break; - - case NETOPT_CCA_MODE: - if (len < sizeof(uint8_t)) { - res = -EOVERFLOW; - } - else { - switch (*((int8_t*)value)) { - case NETDEV_IEEE802154_CCA_MODE_1: - case NETDEV_IEEE802154_CCA_MODE_2: - case NETDEV_IEEE802154_CCA_MODE_3: - kw2xrf_set_cca_mode(dev, *((int8_t*)value)); - res = sizeof(uint8_t); - break; - case NETDEV_IEEE802154_CCA_MODE_4: - case NETDEV_IEEE802154_CCA_MODE_5: - case NETDEV_IEEE802154_CCA_MODE_6: - default: - break; - } - } - break; - - case NETOPT_RF_TESTMODE: -#ifdef MODULE_KW2XRF_TESTMODE - if (len < sizeof(uint8_t)) { - res = -EOVERFLOW; - } - else { - kw2xrf_set_test_mode(dev, *((uint8_t *)value)); - res = sizeof(uint8_t); - } -#endif - break; - - default: - break; - } - - if (res == -ENOTSUP) { - res = netdev_ieee802154_set(container_of(netdev, netdev_ieee802154_t, netdev), - opt, value, len); - } - - return res; -} - -static void _isr_event_seq_r(netdev_t *netdev, uint8_t *dregs) -{ - netdev_ieee802154_t *netdev_ieee802154 = container_of(netdev, netdev_ieee802154_t, netdev); - kw2xrf_t *dev = container_of(netdev_ieee802154, kw2xrf_t, netdev); - uint8_t irqsts1 = 0; - - if (dregs[MKW2XDM_IRQSTS1] & MKW2XDM_IRQSTS1_RXWTRMRKIRQ) { - DEBUG("[kw2xrf] got RXWTRMRKIRQ\n"); - irqsts1 |= MKW2XDM_IRQSTS1_RXWTRMRKIRQ; - netdev->event_callback(netdev, NETDEV_EVENT_RX_STARTED); - } - - if (dregs[MKW2XDM_IRQSTS1] & MKW2XDM_IRQSTS1_RXIRQ) { - DEBUG("[kw2xrf] finished RXSEQ\n"); - dev->state = NETOPT_STATE_RX; - irqsts1 |= MKW2XDM_IRQSTS1_RXIRQ; - netdev->event_callback(netdev, NETDEV_EVENT_RX_COMPLETE); - if (dregs[MKW2XDM_PHY_CTRL1] & MKW2XDM_PHY_CTRL1_AUTOACK) { - DEBUG("[kw2xrf]: perform TX ACK\n"); - } - } - - if (dregs[MKW2XDM_IRQSTS1] & MKW2XDM_IRQSTS1_TXIRQ) { - DEBUG("[kw2xrf] finished (ACK) TXSEQ\n"); - irqsts1 |= MKW2XDM_IRQSTS1_TXIRQ; - } - - if (dregs[MKW2XDM_IRQSTS1] & MKW2XDM_IRQSTS1_SEQIRQ) { - DEBUG("[kw2xrf] SEQIRQ\n"); - irqsts1 |= MKW2XDM_IRQSTS1_SEQIRQ; - kw2xrf_set_idle_sequence(dev); - } - - kw2xrf_write_dreg(dev, MKW2XDM_IRQSTS1, irqsts1); - dregs[MKW2XDM_IRQSTS1] &= ~irqsts1; -} - -static void _isr_event_seq_t(netdev_t *netdev, uint8_t *dregs) -{ - netdev_ieee802154_t *netdev_ieee802154 = container_of(netdev, netdev_ieee802154_t, netdev); - kw2xrf_t *dev = container_of(netdev_ieee802154, kw2xrf_t, netdev); - uint8_t irqsts1 = 0; - - if (dregs[MKW2XDM_IRQSTS1] & MKW2XDM_IRQSTS1_TXIRQ) { - DEBUG("[kw2xrf] finished TXSEQ\n"); - irqsts1 |= MKW2XDM_IRQSTS1_TXIRQ; - } - - if (dregs[MKW2XDM_IRQSTS1] & MKW2XDM_IRQSTS1_SEQIRQ) { - DEBUG("[kw2xrf] SEQIRQ\n"); - irqsts1 |= MKW2XDM_IRQSTS1_SEQIRQ; - - if (dregs[MKW2XDM_IRQSTS1] & MKW2XDM_IRQSTS1_CCAIRQ) { - irqsts1 |= MKW2XDM_IRQSTS1_CCAIRQ; - if (dregs[MKW2XDM_IRQSTS2] & MKW2XDM_IRQSTS2_CCA) { - DEBUG("[kw2xrf] CCA CH busy\n"); - netdev->event_callback(netdev, NETDEV_EVENT_TX_MEDIUM_BUSY); - } - else { - netdev->event_callback(netdev, NETDEV_EVENT_TX_COMPLETE); - } - } - - assert(dev->pending_tx != 0); - dev->pending_tx--; - kw2xrf_set_idle_sequence(dev); - } - - kw2xrf_write_dreg(dev, MKW2XDM_IRQSTS1, irqsts1); - dregs[MKW2XDM_IRQSTS1] &= ~irqsts1; -} - -/* Standalone CCA */ -static void _isr_event_seq_cca(netdev_t *netdev, uint8_t *dregs) -{ - netdev_ieee802154_t *netdev_ieee802154 = container_of(netdev, netdev_ieee802154_t, netdev); - kw2xrf_t *dev = container_of(netdev_ieee802154, kw2xrf_t, netdev); - uint8_t irqsts1 = 0; - - if ((dregs[MKW2XDM_IRQSTS1] & MKW2XDM_IRQSTS1_CCAIRQ) && - (dregs[MKW2XDM_IRQSTS1] & MKW2XDM_IRQSTS1_SEQIRQ)) { - irqsts1 |= MKW2XDM_IRQSTS1_CCAIRQ | MKW2XDM_IRQSTS1_SEQIRQ; - if (dregs[MKW2XDM_IRQSTS2] & MKW2XDM_IRQSTS2_CCA) { - DEBUG("[kw2xrf] SEQIRQ, CCA CH busy\n"); - } - else { - DEBUG("[kw2xrf] SEQIRQ, CCA CH idle\n"); - } - kw2xrf_set_idle_sequence(dev); - } - kw2xrf_write_dreg(dev, MKW2XDM_IRQSTS1, irqsts1); - dregs[MKW2XDM_IRQSTS1] &= ~irqsts1; -} - -static void _isr_event_seq_tr(netdev_t *netdev, uint8_t *dregs) -{ - netdev_ieee802154_t *netdev_ieee802154 = container_of(netdev, netdev_ieee802154_t, netdev); - kw2xrf_t *dev = container_of(netdev_ieee802154, kw2xrf_t, netdev); - uint8_t irqsts1 = 0; - - if (dregs[MKW2XDM_IRQSTS1] & MKW2XDM_IRQSTS1_TXIRQ) { - DEBUG("[kw2xrf] finished TXSEQ\n"); - irqsts1 |= MKW2XDM_IRQSTS1_TXIRQ; - if (dregs[MKW2XDM_PHY_CTRL1] & MKW2XDM_PHY_CTRL1_RXACKRQD) { - DEBUG("[kw2xrf] wait for RX ACK\n"); - /* Allow TMR3IRQ to cancel RX operation */ - kw2xrf_timer3_seq_abort_on(dev); - /* Enable interrupt for TMR3 and set timer */ - kw2xrf_abort_rx_ops_enable(dev, _MACACKWAITDURATION); - } - } - - if (dregs[MKW2XDM_IRQSTS1] & MKW2XDM_IRQSTS1_RXWTRMRKIRQ) { - DEBUG("[kw2xrf] got RXWTRMRKIRQ\n"); - irqsts1 |= MKW2XDM_IRQSTS1_RXWTRMRKIRQ; - } - - if (dregs[MKW2XDM_IRQSTS1] & MKW2XDM_IRQSTS1_FILTERFAIL_IRQ) { - DEBUG("[kw2xrf] got FILTERFAILIRQ\n"); - irqsts1 |= MKW2XDM_IRQSTS1_FILTERFAIL_IRQ; - } - - if (dregs[MKW2XDM_IRQSTS1] & MKW2XDM_IRQSTS1_RXIRQ) { - DEBUG("[kw2xrf] got RX ACK\n"); - irqsts1 |= MKW2XDM_IRQSTS1_RXIRQ; - } - - if (dregs[MKW2XDM_IRQSTS1] & MKW2XDM_IRQSTS1_SEQIRQ) { - if (dregs[MKW2XDM_IRQSTS1] & MKW2XDM_IRQSTS1_CCAIRQ) { - irqsts1 |= MKW2XDM_IRQSTS1_CCAIRQ; - if (dregs[MKW2XDM_IRQSTS2] & MKW2XDM_IRQSTS2_CCA) { - DEBUG("[kw2xrf] CCA CH busy\n"); - netdev->event_callback(netdev, NETDEV_EVENT_TX_MEDIUM_BUSY); - } - } - - irqsts1 |= MKW2XDM_IRQSTS1_SEQIRQ; - assert(dev->pending_tx != 0); - dev->pending_tx--; - - if (dregs[MKW2XDM_IRQSTS3] & MKW2XDM_IRQSTS3_TMR3IRQ) { - /* if the sequence was aborted by timer 3, ACK timed out */ - DEBUG("[kw2xrf] TC3TMOUT, SEQIRQ, TX failed\n"); - netdev->event_callback(netdev, NETDEV_EVENT_TX_NOACK); - } else { - DEBUG("[kw2xrf] SEQIRQ\n"); - netdev->event_callback(netdev, NETDEV_EVENT_TX_COMPLETE); - } - - /* Disallow TMR3IRQ to cancel RX operation */ - kw2xrf_timer3_seq_abort_off(dev); - /* Disable interrupt for TMR3 and reset TMR3IRQ */ - kw2xrf_abort_rx_ops_disable(dev); - /* Go back to idle state */ - kw2xrf_set_idle_sequence(dev); - } - - kw2xrf_write_dreg(dev, MKW2XDM_IRQSTS1, irqsts1); - dregs[MKW2XDM_IRQSTS1] &= ~irqsts1; -} - -static void _isr_event_seq_ccca(netdev_t *netdev, uint8_t *dregs) -{ - netdev_ieee802154_t *netdev_ieee802154 = container_of(netdev, netdev_ieee802154_t, netdev); - kw2xrf_t *dev = container_of(netdev_ieee802154, kw2xrf_t, netdev); - uint8_t irqsts1 = 0; - - if ((dregs[MKW2XDM_IRQSTS1] & MKW2XDM_IRQSTS1_CCAIRQ) && - (dregs[MKW2XDM_IRQSTS1] & MKW2XDM_IRQSTS1_SEQIRQ)) { - irqsts1 |= MKW2XDM_IRQSTS1_CCAIRQ | MKW2XDM_IRQSTS1_SEQIRQ; - DEBUG("[kw2xrf] CCCA CH idle\n"); - kw2xrf_seq_timeout_off(dev); - kw2xrf_set_sequence(dev, dev->idle_state); - } - else if (dregs[MKW2XDM_IRQSTS3] & MKW2XDM_IRQSTS3_TMR4IRQ) { - irqsts1 |= MKW2XDM_IRQSTS1_CCAIRQ | MKW2XDM_IRQSTS1_SEQIRQ; - DEBUG("[kw2xrf] CCCA timeout\n"); - kw2xrf_seq_timeout_off(dev); - kw2xrf_set_sequence(dev, dev->idle_state); - } - kw2xrf_write_dreg(dev, MKW2XDM_IRQSTS1, irqsts1); - dregs[MKW2XDM_IRQSTS1] &= ~irqsts1; -} - -static void _isr(netdev_t *netdev) -{ - uint8_t dregs[MKW2XDM_PHY_CTRL4 + 1]; - netdev_ieee802154_t *netdev_ieee802154 = container_of(netdev, netdev_ieee802154_t, netdev); - kw2xrf_t *dev = container_of(netdev_ieee802154, kw2xrf_t, netdev); - if (!spinning_for_irq) { - num_irqs_handled = num_irqs_queued; - } - - kw2xrf_read_dregs(dev, MKW2XDM_IRQSTS1, dregs, MKW2XDM_PHY_CTRL4 + 1); - kw2xrf_mask_irq_b(dev); - - DEBUG("[kw2xrf] CTRL1 %0x, IRQSTS1 %0x, IRQSTS2 %0x\n", - dregs[MKW2XDM_PHY_CTRL1], dregs[MKW2XDM_IRQSTS1], dregs[MKW2XDM_IRQSTS2]); - - switch (dregs[MKW2XDM_PHY_CTRL1] & MKW2XDM_PHY_CTRL1_XCVSEQ_MASK) { - case XCVSEQ_RECEIVE: - _isr_event_seq_r(netdev, dregs); - break; - - case XCVSEQ_TRANSMIT: - _isr_event_seq_t(netdev, dregs); - break; - - case XCVSEQ_CCA: - _isr_event_seq_cca(netdev, dregs); - break; - - case XCVSEQ_TX_RX: - _isr_event_seq_tr(netdev, dregs); - break; - - case XCVSEQ_CONTINUOUS_CCA: - _isr_event_seq_ccca(netdev, dregs); - break; - - case XCVSEQ_IDLE: - default: - DEBUG("[kw2xrf] undefined seq state in isr\n"); - break; - } - - uint8_t irqsts2 = 0; - if (dregs[MKW2XDM_IRQSTS2] & MKW2XDM_IRQSTS2_PB_ERR_IRQ) { - DEBUG("[kw2xrf] untreated PB_ERR_IRQ\n"); - irqsts2 |= MKW2XDM_IRQSTS2_PB_ERR_IRQ; - } - if (dregs[MKW2XDM_IRQSTS2] & MKW2XDM_IRQSTS2_WAKE_IRQ) { - DEBUG("[kw2xrf] untreated WAKE_IRQ\n"); - irqsts2 |= MKW2XDM_IRQSTS2_WAKE_IRQ; - } - kw2xrf_write_dreg(dev, MKW2XDM_IRQSTS2, irqsts2); - - if (IS_ACTIVE(ENABLE_DEBUG)) { - /* for debugging only */ - kw2xrf_read_dregs(dev, MKW2XDM_IRQSTS1, dregs, MKW2XDM_IRQSTS1 + 3); - if (dregs[MKW2XDM_IRQSTS1] & 0x7f) { - DEBUG("[kw2xrf] IRQSTS1 contains untreated IRQs: 0x%02x\n", - dregs[MKW2XDM_IRQSTS1]); - } - if (dregs[MKW2XDM_IRQSTS2] & 0x02) { - DEBUG("[kw2xrf] IRQSTS2 contains untreated IRQs: 0x%02x\n", - dregs[MKW2XDM_IRQSTS2]); - } - if (dregs[MKW2XDM_IRQSTS3] & 0x0f) { - DEBUG("[kw2xrf] IRQSTS3 contains untreated IRQs: 0x%02x\n", - dregs[MKW2XDM_IRQSTS3]); - } - } - - kw2xrf_enable_irq_b(dev); -} - -const netdev_driver_t kw2xrf_driver = { - .init = _init, - .send = _send, - .recv = _recv, - .get = _get, - .set = _set, - .isr = _isr, -}; - -/** @} */ diff --git a/drivers/kw2xrf/kw2xrf_radio_hal.c b/drivers/kw2xrf/kw2xrf_radio_hal.c new file mode 100644 index 0000000000..e66f47bfb0 --- /dev/null +++ b/drivers/kw2xrf/kw2xrf_radio_hal.c @@ -0,0 +1,729 @@ +/* + * Copyright (C) 2020 HAW Hamburg + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + * + */ + +/** + * @ingroup drivers_kw2xrf + * @{ + * + * @file + * @brief IEEE 802.15.4 Radio HAL implementation for the KW2x RF driver + * + * @author Michel Rottleuthner + * + * @} + */ + +#include +#include +#include + +#include "net/gnrc.h" + +#define LOG_LEVEL LOG_NONE +#include "log.h" +#include "kw2xrf.h" +#include "kw2xrf_spi.h" +#include "kw2xrf_getset.h" +#include "kw2xrf_intern.h" +#include "net/ieee802154/radio.h" +#include "kw2xrf_params.h" +#include "event/thread.h" +#include "xtimer.h" + +static const ieee802154_radio_ops_t kw2xrf_ops; + +void _print_sts1_state(uint8_t sts1, uint8_t phyctl2) { + printf("MKW2XDM_IRQSTS1_RX_FRM_PEND: %s\n", sts1 & MKW2XDM_IRQSTS1_RX_FRM_PEND ? "SET" : "off"); + printf("MKW2XDM_IRQSTS1_PLL_UNLOCK_IRQ: %s (%s)\n", sts1 & MKW2XDM_IRQSTS1_PLL_UNLOCK_IRQ ? "SET" : "off", + phyctl2 & MKW2XDM_PHY_CTRL2_PLL_UNLOCK_MSK ? "masked" : "ACTIVE"); + printf("MKW2XDM_IRQSTS1_FILTERFAIL_IRQ: %s (%s)\n", sts1 & MKW2XDM_IRQSTS1_FILTERFAIL_IRQ ? "SET" : "off", + phyctl2 & MKW2XDM_PHY_CTRL2_FILTERFAIL_MSK ? "masked" : "ACTIVE"); + printf("MKW2XDM_IRQSTS1_RXWTRMRKIRQ: %s (%s)\n", sts1 & MKW2XDM_IRQSTS1_RXWTRMRKIRQ ? "SET" : "off", + phyctl2 & MKW2XDM_PHY_CTRL2_RX_WMRK_MSK ? "masked" : "ACTIVE"); + printf("MKW2XDM_IRQSTS1_CCAIRQ: %s (%s)\n", sts1 & MKW2XDM_IRQSTS1_CCAIRQ ? "SET" : "off", + phyctl2 & MKW2XDM_PHY_CTRL2_CCAMSK ? "masked" : "ACTIVE"); + printf("MKW2XDM_IRQSTS1_RXIRQ: %s (%s)\n", sts1 & MKW2XDM_IRQSTS1_RXIRQ ? "SET" : "off", + phyctl2 & MKW2XDM_PHY_CTRL2_RXMSK ? "masked" : "ACTIVE"); + printf("MKW2XDM_IRQSTS1_TXIRQ: %s (%s)\n", sts1 & MKW2XDM_IRQSTS1_TXIRQ ? "SET" : "off", + phyctl2 & MKW2XDM_PHY_CTRL2_TXMSK ? "masked" : "ACTIVE"); + printf("MKW2XDM_IRQSTS1_SEQIRQ: %s (%s)\n", sts1 & MKW2XDM_IRQSTS1_SEQIRQ ? "SET" : "off", + phyctl2 & MKW2XDM_PHY_CTRL2_SEQMSK ? "masked" : "ACTIVE"); +} + +void _print_irq_state(uint8_t *dregs) { + + printf("MKW2XDM_IRQSTS1: 0x%02X\n", dregs[MKW2XDM_IRQSTS1]); + printf("MKW2XDM_IRQSTS2: 0x%02X\n", dregs[MKW2XDM_IRQSTS2]); + printf("MKW2XDM_IRQSTS3: 0x%02X\n", dregs[MKW2XDM_IRQSTS3]); + printf("MKW2XDM_PHY_CTRL1: 0x%02X\n", dregs[MKW2XDM_PHY_CTRL1]); + + printf(" TMRTRIGEN: %s\n", dregs[MKW2XDM_PHY_CTRL1] & MKW2XDM_PHY_CTRL1_TMRTRIGEN ? "on" : "off"); + printf(" SLOTTED: %s\n", dregs[MKW2XDM_PHY_CTRL1] & MKW2XDM_PHY_CTRL1_SLOTTED ? "on" : "off"); + printf(" CCABFRTX: %s\n", dregs[MKW2XDM_PHY_CTRL1] & MKW2XDM_PHY_CTRL1_CCABFRTX ? "on" : "off"); + printf(" RXACKRQD: %s\n", dregs[MKW2XDM_PHY_CTRL1] & MKW2XDM_PHY_CTRL1_RXACKRQD ? "on" : "off"); + printf(" AUTOACK: %s\n", dregs[MKW2XDM_PHY_CTRL1] & MKW2XDM_PHY_CTRL1_AUTOACK ? "on" : "off"); + + printf("MKW2XDM_PHY_CTRL2: 0x%02X\n", dregs[MKW2XDM_PHY_CTRL2]); + printf("MKW2XDM_PHY_CTRL3: 0x%02X\n", dregs[MKW2XDM_PHY_CTRL3]); + printf("MKW2XDM_RX_FRM_LEN: 0x%02X\n", dregs[MKW2XDM_RX_FRM_LEN]); + printf("MKW2XDM_PHY_CTRL4: 0x%02X\n", dregs[MKW2XDM_PHY_CTRL4]); + printf("MKW2XDM_PHY_CTRL4_TRCV_MSK %s\n", dregs[MKW2XDM_PHY_CTRL4] & MKW2XDM_PHY_CTRL4_TRCV_MSK ? "off" : "on"); + printf("MKW2XDM_PHY_CTRL1_TMRTRIGEN %s\n", dregs[MKW2XDM_PHY_CTRL1] & MKW2XDM_PHY_CTRL1_TMRTRIGEN ? "on" : "off"); + printf("-------------------------------------\n"); + + _print_sts1_state(dregs[MKW2XDM_IRQSTS1], dregs[MKW2XDM_PHY_CTRL2]); + + printf("MKW2XDM_IRQSTS2_CRCVALID: %s (%s)\n", dregs[MKW2XDM_IRQSTS2] & MKW2XDM_IRQSTS2_CRCVALID ? "SET" : "off", + dregs[MKW2XDM_PHY_CTRL2] & MKW2XDM_PHY_CTRL2_CRC_MSK ? "masked" : "ACTIVE"); + printf("MKW2XDM_IRQSTS2_CCA: %s\n", dregs[MKW2XDM_IRQSTS2] & MKW2XDM_IRQSTS2_CCA ? "SET" : "off"); + printf("MKW2XDM_IRQSTS2_SRCADDR: %s\n", dregs[MKW2XDM_IRQSTS2] & MKW2XDM_IRQSTS2_SRCADDR ? "SET" : "off"); + printf("MKW2XDM_IRQSTS2_PI: %s\n", dregs[MKW2XDM_IRQSTS2] & MKW2XDM_IRQSTS2_PI ? "SET" : "off"); + printf("MKW2XDM_IRQSTS2_TMRSTATUS: %s\n", dregs[MKW2XDM_IRQSTS2] & MKW2XDM_IRQSTS2_TMRSTATUS ? "SET" : "off"); + + printf("MKW2XDM_IRQSTS2_PB_ERR_IRQ: %s (%s)\n", dregs[MKW2XDM_IRQSTS2] & MKW2XDM_IRQSTS2_PB_ERR_IRQ ? "SET" : "off", + dregs[MKW2XDM_PHY_CTRL3] & MKW2XDM_PHY_CTRL3_PB_ERR_MSK ? "masked" : "ACTIVE"); + printf("MKW2XDM_IRQSTS2_WAKE_IRQ: %s (%s)\n", dregs[MKW2XDM_IRQSTS2] & MKW2XDM_IRQSTS2_WAKE_IRQ ? "SET" : "off", + dregs[MKW2XDM_PHY_CTRL3] & MKW2XDM_PHY_CTRL3_WAKE_MSK ? "masked" : "ACTIVE"); + + printf("MKW2XDM_IRQSTS3_TMR4IRQ: %s (%s|%s)\n", dregs[MKW2XDM_IRQSTS3] & MKW2XDM_IRQSTS3_TMR4IRQ ? "SET" : "off", + dregs[MKW2XDM_IRQSTS3] & MKW2XDM_IRQSTS3_TMR4MSK ? "masked" : "ACTIVE", + dregs[MKW2XDM_PHY_CTRL3] & MKW2XDM_PHY_CTRL3_TMR4CMP_EN ? "compare" : "no-compare"); + printf("MKW2XDM_IRQSTS3_TMR3IRQ: %s (%s|%s)\n", dregs[MKW2XDM_IRQSTS3] & MKW2XDM_IRQSTS3_TMR3IRQ ? "SET" : "off", + dregs[MKW2XDM_IRQSTS3] & MKW2XDM_IRQSTS3_TMR3MSK ? "masked" : "ACTIVE", + dregs[MKW2XDM_PHY_CTRL3] & MKW2XDM_PHY_CTRL3_TMR3CMP_EN ? "compare" : "no-compare"); + printf("MKW2XDM_IRQSTS3_TMR2IRQ: %s (%s|%s)\n", dregs[MKW2XDM_IRQSTS3] & MKW2XDM_IRQSTS3_TMR2IRQ ? "SET" : "off", + dregs[MKW2XDM_IRQSTS3] & MKW2XDM_IRQSTS3_TMR2MSK ? "masked" : "ACTIVE", + dregs[MKW2XDM_PHY_CTRL3] & MKW2XDM_PHY_CTRL3_TMR2CMP_EN ? "compare" : "no-compare"); + printf("MKW2XDM_IRQSTS3_TMR1IRQ: %s (%s|%s)\n", dregs[MKW2XDM_IRQSTS3] & MKW2XDM_IRQSTS3_TMR1IRQ ? "SET" : "off", + dregs[MKW2XDM_IRQSTS3] & MKW2XDM_IRQSTS3_TMR1MSK ? "masked" : "ACTIVE", + dregs[MKW2XDM_PHY_CTRL3] & MKW2XDM_PHY_CTRL3_TMR1CMP_EN ? "compare" : "no-compare"); +} + +static void _set_sequence(kw2xrf_t *kw_dev, kw2xrf_physeq_t seq) +{ + uint8_t ctl1 = kw2xrf_read_dreg(kw_dev, MKW2XDM_PHY_CTRL1); + ctl1 &= ~(MKW2XDM_PHY_CTRL1_XCVSEQ_MASK); + ctl1 |= MKW2XDM_PHY_CTRL1_XCVSEQ(seq); + kw2xrf_write_dreg(kw_dev, MKW2XDM_PHY_CTRL1, ctl1); +} + +/* The first byte returned by the radio is always IRQSTS1. Thus, giving IRQSTS2 + as the start address (first byte written) reduces communicaiton overhead by + one byte and reduces turnaround time for radio handling. + (Starting at address IRQSTS1 instead, effectively returns IRQSTS1 twice) */ +void _kw2xrf_read_dregs_from_sts1(kw2xrf_t *dev, uint8_t *buf, uint8_t length) +{ + spi_acquire(dev->params->spi, dev->params->cs_pin, SPI_MODE_0, + dev->params->spi_clk); + uint8_t cmd = (MKW2XDM_IRQSTS2 | MKW2XDRF_REG_READ); + spi_transfer_bytes(dev->params->spi, dev->params->cs_pin, true, &cmd, buf, 1); + if (length > 1) { + spi_transfer_bytes(dev->params->spi, dev->params->cs_pin, false, NULL, + &buf[1], length - 1); + } + spi_release(dev->params->spi); +} + +static void _start_tx(kw2xrf_t *kw_dev) +{ + uint8_t pctl1 = kw2xrf_read_dreg(kw_dev, MKW2XDM_PHY_CTRL1); + + if (kw_dev->ack_requested) { + /* expect an ACK after TX */ + pctl1 |= MKW2XDM_PHY_CTRL1_RXACKRQD; + } else { + /* don't expect an ACK after TX */ + pctl1 &= ~MKW2XDM_PHY_CTRL1_RXACKRQD; + } + + /* A (T) sequence performs a simple transmit and returns to idle, a (TR) + sequence waits for the requested ACK response after the transmission */ + pctl1 &= ~MKW2XDM_PHY_CTRL1_XCVSEQ_MASK; + pctl1 |= MKW2XDM_PHY_CTRL1_XCVSEQ(kw_dev->ack_requested ? XCVSEQ_TX_RX : + XCVSEQ_TRANSMIT); + kw2xrf_write_dreg(kw_dev, MKW2XDM_PHY_CTRL1, pctl1); +} + +void kw2xrf_radio_hal_irq_handler(void *arg) +{ + ieee802154_dev_t *dev = arg; + kw2xrf_t *kw_dev = dev->priv; + kw2xrf_mask_irq_b(kw_dev); + + uint8_t dregs[MKW2XDM_PHY_CTRL2 +1]; + _kw2xrf_read_dregs_from_sts1(kw_dev, dregs, ARRAY_SIZE(dregs)); + LOG_DEBUG("0x%02X\n", dregs[MKW2XDM_IRQSTS1]); + + uint8_t sts1_clr = 0; + bool indicate_hal_event = false; + ieee802154_trx_ev_t hal_event; + + switch (dregs[MKW2XDM_PHY_CTRL1] & MKW2XDM_PHY_CTRL1_XCVSEQ_MASK) { + case XCVSEQ_RECEIVE: + if (dregs[MKW2XDM_IRQSTS1] & MKW2XDM_IRQSTS1_RXWTRMRKIRQ) { + sts1_clr |= MKW2XDM_IRQSTS1_RXWTRMRKIRQ; + hal_event = IEEE802154_RADIO_INDICATION_RX_START; + indicate_hal_event = true; + break; /* don't process further events on RX_START */ + } + + /* SEQ assertion during this state indicates a reception */ + if (dregs[MKW2XDM_IRQSTS1] & MKW2XDM_IRQSTS1_SEQIRQ) { + /* clear all pending IRQs asserted during RX sequence */ + sts1_clr |= dregs[MKW2XDM_IRQSTS1]; + + if (!(dregs[MKW2XDM_IRQSTS2] & MKW2XDM_IRQSTS2_CRCVALID)) { + hal_event = IEEE802154_RADIO_INDICATION_CRC_ERROR; + indicate_hal_event = true; + } + + if (dregs[MKW2XDM_IRQSTS1] & MKW2XDM_IRQSTS1_RXIRQ) { + sts1_clr |= MKW2XDM_IRQSTS1_RXIRQ; + hal_event = IEEE802154_RADIO_INDICATION_RX_DONE; + indicate_hal_event = true; + } + } + break; + case XCVSEQ_TRANSMIT: + if (dregs[MKW2XDM_IRQSTS1] & MKW2XDM_IRQSTS1_TXIRQ) { + sts1_clr |= MKW2XDM_IRQSTS1_TXIRQ; + } + + if (dregs[MKW2XDM_IRQSTS1] & MKW2XDM_IRQSTS1_SEQIRQ) { + sts1_clr |= MKW2XDM_IRQSTS1_SEQIRQ; + kw_dev->tx_done = true; + hal_event = IEEE802154_RADIO_CONFIRM_TX_DONE; + indicate_hal_event = true; + } + break; + case XCVSEQ_CCA: + /* handle after CCA *and* sequence (warmdown) finished */ + if ((dregs[MKW2XDM_IRQSTS1] & MKW2XDM_IRQSTS1_CCAIRQ) && + (dregs[MKW2XDM_IRQSTS1] & MKW2XDM_IRQSTS1_SEQIRQ)) { + sts1_clr |= (MKW2XDM_IRQSTS1_CCAIRQ | MKW2XDM_IRQSTS1_SEQIRQ); + + kw_dev->ch_clear = !(dregs[MKW2XDM_IRQSTS2] & + MKW2XDM_IRQSTS2_CCA); + kw_dev->waiting_for_cca = false; + + /* if this cca was performed as "CCA-before TX" */ + if (kw_dev->tx_cca_pending) { + kw_dev->tx_cca_pending = false; + if (kw_dev->ch_clear) { + /* clear pending interrupts before TX */ + kw2xrf_write_dreg(kw_dev, MKW2XDM_IRQSTS1, sts1_clr); + _start_tx(kw_dev); + kw2xrf_enable_irq_b(kw_dev); + return; + } else { + kw_dev->tx_done = true; + /* indicate TX_DONE. The confirm function will return + channel busy */ + hal_event = IEEE802154_RADIO_CONFIRM_TX_DONE; + indicate_hal_event = true; + break; + } + } + + hal_event = IEEE802154_RADIO_CONFIRM_CCA; + indicate_hal_event = true; + } + break; + case XCVSEQ_TX_RX: + if (dregs[MKW2XDM_IRQSTS1] & MKW2XDM_IRQSTS1_TXIRQ) { + sts1_clr |= MKW2XDM_IRQSTS1_TXIRQ; + if (dregs[MKW2XDM_PHY_CTRL1] & MKW2XDM_PHY_CTRL1_RXACKRQD) { + /* Allow TMR3IRQ to cancel RX operation */ + kw2xrf_timer3_seq_abort_on(kw_dev); + /* Enable interrupt for TMR3 and set timer */ + kw2xrf_abort_rx_ops_enable(kw_dev, IEEE802154_ACK_TIMEOUT_SYMS); + } + } + + if (dregs[MKW2XDM_IRQSTS1] & MKW2XDM_IRQSTS1_RXIRQ) { + sts1_clr |= MKW2XDM_IRQSTS1_RXIRQ; + } + + if (dregs[MKW2XDM_IRQSTS1] & MKW2XDM_IRQSTS1_CCAIRQ) { + kw_dev->ch_clear = !(dregs[MKW2XDM_IRQSTS2] & + MKW2XDM_IRQSTS2_CCA); + sts1_clr |= MKW2XDM_IRQSTS1_CCAIRQ; + } + + if (dregs[MKW2XDM_IRQSTS1] & MKW2XDM_IRQSTS1_SEQIRQ) { + sts1_clr |= MKW2XDM_IRQSTS1_SEQIRQ; + + kw_dev->ack_rcvd = !(dregs[MKW2XDM_IRQSTS3] & + MKW2XDM_IRQSTS3_TMR3IRQ); + + /* Disallow TMR3IRQ to cancel RX operation */ + kw2xrf_timer3_seq_abort_off(kw_dev); + /* Disable interrupt for TMR3 and reset TMR3IRQ */ + kw2xrf_abort_rx_ops_disable(kw_dev); + + kw_dev->tx_done = true; + hal_event = IEEE802154_RADIO_CONFIRM_TX_DONE; + indicate_hal_event = true; + } + break; + case XCVSEQ_IDLE: + /* clear SEQ interrupt for explicit transitions to IDLE */ + sts1_clr |= MKW2XDM_IRQSTS1_SEQIRQ; + break; + default: + break; + } + + /* clear handled IRQs */ + kw2xrf_write_dreg(kw_dev, MKW2XDM_IRQSTS1, sts1_clr); + + kw2xrf_enable_irq_b(kw_dev); + + if (indicate_hal_event) { + dev->cb(dev, hal_event); + } +} + +int kw2xrf_init(kw2xrf_t *dev, const kw2xrf_params_t *params, ieee802154_dev_t *hal, + gpio_cb_t cb, void *ctx) +{ + /* initialize device descriptor */ + dev->params = params; + dev->idle_state = XCVSEQ_RECEIVE; + dev->state = 0; + dev->pending_tx = 0; + + kw2xrf_spi_init(dev); + kw2xrf_set_power_mode(dev, KW2XRF_IDLE); + LOG_DEBUG("[kw2xrf] enabling RX/TX completion and start events"); + kw2xrf_clear_dreg_bit(dev, MKW2XDM_PHY_CTRL2, MKW2XDM_PHY_CTRL2_RX_WMRK_MSK); + kw2xrf_clear_dreg_bit(dev, MKW2XDM_PHY_CTRL2, MKW2XDM_PHY_CTRL2_RXMSK); + kw2xrf_clear_dreg_bit(dev, MKW2XDM_PHY_CTRL2, MKW2XDM_PHY_CTRL2_TXMSK); + LOG_DEBUG("[kw2xrf] setup finished\n"); + + hal->driver = &kw2xrf_ops; + hal->priv = dev; + + dev->cca_before_tx = true; + kw2xrf_set_out_clk(dev); + kw2xrf_disable_interrupts(dev); + + /* set up GPIO-pin used for IRQ */ + gpio_init_int(dev->params->int_pin, GPIO_IN, GPIO_FALLING, cb, ctx); + + kw2xrf_abort_sequence(dev); + kw2xrf_update_overwrites(dev); + kw2xrf_timer_init(dev, KW2XRF_TIMEBASE_62500HZ); + + kw2xrf_reset_phy(dev); + + /* Disable automatic CCA before TX (for T and TR sequences). + Auto CCA was found to cause poor performance (significant packet loss) + when performed before the transmission - even on clearchannel. + As a workaround we perform manual CCA directly before transmission + which doesn't show degraded performance. + Note: the poor performance was only visible when transmitting data + to other models of radios. I.e., kw2xrf to kw2xrf was fine, while + kw2xrf to at862xx and nrf52 performs very bad. */ + kw2xrf_clear_dreg_bit(dev, MKW2XDM_PHY_CTRL1, MKW2XDM_PHY_CTRL1_CCABFRTX); + + LOG_DEBUG("[kw2xrf] init finished\n"); + return 0; +} + +static int _write(ieee802154_dev_t *dev, const iolist_t *iolist) +{ + kw2xrf_t *kw_dev = dev->priv; + + /* get length */ + uint8_t len = iolist_size(iolist) + IEEE802154_FCS_LEN; + + if (len > KW2XRF_MAX_PKT_LENGTH) { + LOG_ERROR("[kw2xrf] packet too large (%u byte) to be send\n", len); + return -EOVERFLOW; + } + + /* check if ack req bit is set to decide which transmit sequence to use to + send the frame */ + uint8_t *data = iolist->iol_base; + kw_dev->ack_requested = *data & IEEE802154_FCF_ACK_REQ; + + /* transfer packet data to radio buffer */ + spi_acquire(kw_dev->params->spi, kw_dev->params->cs_pin, SPI_MODE_0, + kw_dev->params->spi_clk); + spi_transfer_byte(kw_dev->params->spi, kw_dev->params->cs_pin, true, + MKW2XDRF_BUF_WRITE); + spi_transfer_byte(kw_dev->params->spi, kw_dev->params->cs_pin, true, len); + + for (const iolist_t *iol = iolist; iol; iol = iol->iol_next) { + /* start after pkt len byte */ + bool cont = iol->iol_next; + spi_transfer_bytes(kw_dev->params->spi, kw_dev->params->cs_pin, cont, + iol->iol_base, NULL, iol->iol_len); + } + + spi_release(kw_dev->params->spi); + + return 0; +} + +static int _request_op(ieee802154_dev_t *dev, ieee802154_hal_op_t op, void *ctx) +{ + kw2xrf_t *kw_dev = dev->priv; + int res = -EINVAL; + (void) ctx; + + kw2xrf_mask_irq_b(kw_dev); + switch (op) { + case IEEE802154_HAL_OP_TRANSMIT: + kw_dev->tx_done = false; + if (kw_dev->cca_before_tx) { + kw_dev->tx_cca_pending = true; + _set_sequence(kw_dev, XCVSEQ_CCA); + } else { + _start_tx(kw_dev); + } + kw2xrf_enable_irq_b(kw_dev); + res = 0; + break; + case IEEE802154_HAL_OP_SET_RX: + kw2xrf_write_dreg(kw_dev, MKW2XDM_IRQSTS1, MKW2XDM_IRQSTS1_RXIRQ | + MKW2XDM_IRQSTS1_RXWTRMRKIRQ); + + /* enable WTMRK and SEQ as indication for RX_START and RX_DONE */ + kw2xrf_write_dreg(kw_dev, MKW2XDM_PHY_CTRL2, + ~(MKW2XDM_PHY_CTRL2_SEQMSK | + MKW2XDM_PHY_CTRL2_RX_WMRK_MSK)); + + _set_sequence(kw_dev, XCVSEQ_RECEIVE); + res = 0; + break; + case IEEE802154_HAL_OP_SET_IDLE: + kw2xrf_set_power_mode(kw_dev, KW2XRF_IDLE); + + /* clear any pending Tx interrupts */ + kw2xrf_write_dreg(kw_dev, MKW2XDM_IRQSTS1, MKW2XDM_IRQSTS1_TXIRQ | + MKW2XDM_IRQSTS1_SEQIRQ); + + /* enable SEQ IRQ as indication for TX_DONE and TX IRQ to + indicate when the frame was transmitted to then possibly + schedule an ACK timeout */ + kw2xrf_write_dreg(kw_dev, MKW2XDM_PHY_CTRL2, + ~(MKW2XDM_PHY_CTRL2_SEQMSK | + MKW2XDM_PHY_CTRL2_TXMSK)); + + _set_sequence(kw_dev, XCVSEQ_IDLE); + res = 0; + break; + case IEEE802154_HAL_OP_CCA: + kw_dev->waiting_for_cca = true; + kw2xrf_set_sequence(kw_dev, XCVSEQ_CCA); + res = 0; + break; + default: + assert(false); + } + + return res; +} + +static int _confirm_op(ieee802154_dev_t *dev, ieee802154_hal_op_t op, void *ctx) +{ + kw2xrf_t *kw_dev = dev->priv; + int res = -EAGAIN; + switch (op) { + case IEEE802154_HAL_OP_TRANSMIT: + if (!kw_dev->tx_done) { + break; + } + if (ctx) { + ieee802154_tx_info_t *info = ctx; + if (!kw_dev->ch_clear) { + info->status = TX_STATUS_MEDIUM_BUSY; + } + else if (kw_dev->ack_rcvd || !kw_dev->ack_requested) { + info->status = TX_STATUS_SUCCESS; + } else { + info->status = TX_STATUS_NO_ACK; + } + } + res = 0; + break; + case IEEE802154_HAL_OP_SET_RX: + kw2xrf_enable_irq_b(kw_dev); + res = 0; + break; + case IEEE802154_HAL_OP_SET_IDLE: + if (kw2xrf_read_dreg(kw_dev, MKW2XDM_SEQ_STATE) == XCVSEQ_IDLE) { + kw2xrf_write_dreg(kw_dev, MKW2XDM_IRQSTS1, MKW2XDM_IRQSTS1_SEQIRQ); + kw2xrf_enable_irq_b(kw_dev); + res = 0; + } + else { + res = -EAGAIN; + } + break; + case IEEE802154_HAL_OP_CCA: + if (!kw_dev->waiting_for_cca) { + *((bool*) ctx) = kw_dev->ch_clear; + res = 0; + } + break; + default: + assert(false); + } + + return res; +} + +static int _len(ieee802154_dev_t *dev) +{ + kw2xrf_t *kw_dev = dev->priv; + size_t pkt_len = kw2xrf_read_dreg(kw_dev, MKW2XDM_RX_FRM_LEN) - + IEEE802154_FCS_LEN; + return pkt_len; +} + +static int _read(ieee802154_dev_t *dev, void *buf, size_t size, ieee802154_rx_info_t *info) +{ + kw2xrf_t *kw_dev = dev->priv; + + size_t rxlen = _len(dev); + if (!buf) { + return 0; + } + rxlen = size < rxlen ? size : rxlen; + kw2xrf_read_fifo(kw_dev, (uint8_t *)buf, rxlen); + + if (info != NULL) { + info->lqi = kw2xrf_read_dreg(kw_dev, MKW2XDM_LQI_VALUE); + info->rssi = kw2xrf_get_rssi(info->lqi); + } + + return rxlen; +} + +static int _set_cca_threshold(ieee802154_dev_t *dev, int8_t threshold) +{ + kw2xrf_t *kw_dev = dev->priv; + /* normalize to absolute value */ + if (threshold < 0) { + threshold = -threshold; + } + + kw2xrf_write_iregs(kw_dev, MKW2XDMI_CCA1_THRESH, (uint8_t*)&threshold, 1); + kw2xrf_write_iregs(kw_dev, MKW2XDMI_CCA2_THRESH, (uint8_t*)&threshold, 1); + + return 0; +} + +/* PLL integer and fractional lookup tables + * + * Fc = 2405 + 5(k - 11) , k = 11,12,...,26 + * + * Equation for PLL frequency, MKW2xD Reference Manual, p.255 : + * F = ((PLL_INT0 + 64) + (PLL_FRAC0/65536))32MHz + * + */ +static const uint8_t pll_int_lt[16] = { + 11, 11, 11, 11, + 11, 11, 12, 12, + 12, 12, 12, 12, + 13, 13, 13, 13 +}; + +static const uint16_t pll_frac_lt[16] = { + 10240, 20480, 30720, 40960, + 51200, 61440, 6144, 16384, + 26624, 36864, 47104, 57344, + 2048, 12288, 22528, 32768 +}; + +static int _config_phy(ieee802154_dev_t *dev, const ieee802154_phy_conf_t *conf) +{ + kw2xrf_t *kw_dev = dev->priv; + kw2xrf_set_tx_power(kw_dev, conf->pow); + uint8_t tmp = conf->channel - 11; + kw2xrf_write_dreg(kw_dev, MKW2XDM_PLL_INT0, MKW2XDM_PLL_INT0_VAL(pll_int_lt[tmp])); + kw2xrf_write_dreg(kw_dev, MKW2XDM_PLL_FRAC0_LSB, (uint8_t)pll_frac_lt[tmp]); + kw2xrf_write_dreg(kw_dev, MKW2XDM_PLL_FRAC0_MSB, (uint8_t)(pll_frac_lt[tmp] >> 8)); + return 0; +} + +static int _off(ieee802154_dev_t *dev) +{ + kw2xrf_t *kw_dev = dev->priv; + /* TODO: check if power down via RST_B is possible */ + kw2xrf_set_power_mode(kw_dev, KW2XRF_HIBERNATE); + return 0; +} + +static int _config_addr_filter(ieee802154_dev_t *dev, ieee802154_af_cmd_t cmd, const void *value) +{ + const uint16_t *pan_id = value; + const network_uint16_t *short_addr = value; + const eui64_t *ext_addr = value; + kw2xrf_t *kw_dev = dev->priv; + switch(cmd) { + case IEEE802154_AF_SHORT_ADDR: + kw2xrf_set_addr_short(kw_dev, byteorder_ntohs(*short_addr)); + break; + case IEEE802154_AF_EXT_ADDR: + kw2xrf_set_addr_long(kw_dev, ext_addr->uint64.u64); + break; + case IEEE802154_AF_PANID: + kw2xrf_set_pan(kw_dev, *pan_id); + break; + case IEEE802154_AF_PAN_COORD: + return -ENOTSUP; + } + + return 0; +} + +static int _request_on(ieee802154_dev_t *dev) +{ + kw2xrf_t *kw_dev = dev->priv; + /* enable xtal and put power management controller to high power mode */ + kw2xrf_set_power_mode(kw_dev, KW2XRF_IDLE); + return 0; +} + +static int _confirm_on(ieee802154_dev_t *dev) +{ + kw2xrf_t *kw_dev = dev->priv; + size_t pwr_modes = kw2xrf_read_dreg(kw_dev, MKW2XDM_PWR_MODES); + return (pwr_modes & MKW2XDM_PWR_MODES_XTAL_READY) ? 0 : -EAGAIN; +} + +static int _set_cca_mode(ieee802154_dev_t *dev, ieee802154_cca_mode_t mode) +{ + kw2xrf_t *kw_dev = dev->priv; + + uint8_t cca_ctl_val = 0; + uint8_t dev_mode = 0; + switch (mode) { + case IEEE802154_CCA_MODE_ED_THRESHOLD: + dev_mode = 1; + break; + case IEEE802154_CCA_MODE_CARRIER_SENSING: + dev_mode = 2; + break; + case IEEE802154_CCA_MODE_ED_THRESH_AND_CS: + dev_mode = 3; + kw2xrf_read_iregs(kw_dev, MKW2XDMI_CCA_CTRL, &cca_ctl_val, 1); + cca_ctl_val |= MKW2XDMI_CCA_CTRL_CCA3_AND_NOT_OR; + kw2xrf_write_iregs(kw_dev, MKW2XDMI_CCA_CTRL, &cca_ctl_val, 1); + break; + case IEEE802154_CCA_MODE_ED_THRESH_OR_CS: + dev_mode = 3; + kw2xrf_read_iregs(kw_dev, MKW2XDMI_CCA_CTRL, &cca_ctl_val, 1); + cca_ctl_val &= ~(MKW2XDMI_CCA_CTRL_CCA3_AND_NOT_OR); + kw2xrf_write_iregs(kw_dev, MKW2XDMI_CCA_CTRL, &cca_ctl_val, 1); + break; + } + + kw2xrf_set_cca_mode(kw_dev, dev_mode); + return 0; +} + +static int _config_src_addr_match(ieee802154_dev_t *dev, ieee802154_src_match_t cmd, const void *value) +{ + kw2xrf_t *kw_dev = dev->priv; + switch(cmd) { + case IEEE802154_SRC_MATCH_EN: { + + const bool en = *((const bool*) value); + if (en) { + kw2xrf_set_dreg_bit(kw_dev, MKW2XDM_SRC_CTRL, + MKW2XDM_SRC_CTRL_ACK_FRM_PND); + } + else { + kw2xrf_clear_dreg_bit(kw_dev, MKW2XDM_SRC_CTRL, + MKW2XDM_SRC_CTRL_ACK_FRM_PND); + } + break; + } + default: + return -ENOTSUP; + } + return 0; +} + +static int _set_frame_filter_mode(ieee802154_dev_t *dev, ieee802154_filter_mode_t mode) +{ + kw2xrf_t *kw_dev = dev->priv; + + bool _promisc = false; + + switch (mode) { + case IEEE802154_FILTER_ACCEPT: + break; + case IEEE802154_FILTER_PROMISC: + _promisc = true; + break; + case IEEE802154_FILTER_ACK_ONLY: + /* Not implemented */ + break; + default: + return -ENOTSUP; + } + + if (_promisc) { + kw2xrf_set_dreg_bit(kw_dev, MKW2XDM_PHY_CTRL4, + MKW2XDM_PHY_CTRL4_PROMISCUOUS); + } + else { + kw2xrf_clear_dreg_bit(kw_dev, MKW2XDM_PHY_CTRL4, + MKW2XDM_PHY_CTRL4_PROMISCUOUS); + } + + return 0; +} + +static int _set_csma_params(ieee802154_dev_t *dev, const ieee802154_csma_be_t *bd, int8_t retries) +{ + kw2xrf_t *kw_dev = dev->priv; + (void) bd; + + if (retries < 0) { + kw_dev->cca_before_tx = false; + return 0; + } else if (retries == 0) { + kw_dev->cca_before_tx = true; + return 0; + } + + return -EINVAL; +} + +static const ieee802154_radio_ops_t kw2xrf_ops = { + .caps = IEEE802154_CAP_24_GHZ + | IEEE802154_CAP_IRQ_CRC_ERROR + | IEEE802154_CAP_IRQ_RX_START + | IEEE802154_CAP_IRQ_TX_DONE + | IEEE802154_CAP_IRQ_CCA_DONE + | IEEE802154_CAP_IRQ_ACK_TIMEOUT + | IEEE802154_CAP_PHY_OQPSK, + .write = _write, + .read = _read, + .request_on = _request_on, + .confirm_on = _confirm_on, + .len = _len, + .off = _off, + .request_op = _request_op, + .confirm_op = _confirm_op, + .set_cca_threshold = _set_cca_threshold, + .set_cca_mode = _set_cca_mode, + .config_phy = _config_phy, + .set_csma_params = _set_csma_params, + .config_addr_filter = _config_addr_filter, + .config_src_addr_match = _config_src_addr_match, + .set_frame_filter_mode = _set_frame_filter_mode, +}; diff --git a/drivers/kw2xrf/kw2xrf_spi.c b/drivers/kw2xrf/kw2xrf_spi.c index fe8470d101..3d29a0b52b 100644 --- a/drivers/kw2xrf/kw2xrf_spi.c +++ b/drivers/kw2xrf/kw2xrf_spi.c @@ -30,9 +30,9 @@ #define ENABLE_DEBUG 0 #include "debug.h" -#define SPIDEV (dev->params.spi) -#define SPICLK (dev->params.spi_clk) -#define CSPIN (dev->params.cs_pin) +#define SPIDEV (dev->params->spi) +#define SPICLK (dev->params->spi_clk) +#define CSPIN (dev->params->cs_pin) #define SPIMODE (SPI_MODE_0) #define KW2XRF_IBUF_LENGTH (9) diff --git a/drivers/kw2xrf/kw2xrf_tm.c b/drivers/kw2xrf/kw2xrf_tm.c index c3d3f3c351..f0f1b64497 100644 --- a/drivers/kw2xrf/kw2xrf_tm.c +++ b/drivers/kw2xrf/kw2xrf_tm.c @@ -54,7 +54,6 @@ int kw2xrf_set_test_mode(kw2xrf_t *dev, uint8_t mode) kw2xrf_abort_sequence(dev); disable_xcvr_test_mode(dev); - kw2xrf_set_channel(dev, dev->netdev.chan); switch (mode) { case NETOPT_RF_TESTMODE_IDLE: diff --git a/sys/net/gnrc/netif/init_devs/auto_init_kw2xrf.c b/sys/net/gnrc/netif/init_devs/auto_init_kw2xrf.c index 5288b32c42..d92d617861 100644 --- a/sys/net/gnrc/netif/init_devs/auto_init_kw2xrf.c +++ b/sys/net/gnrc/netif/init_devs/auto_init_kw2xrf.c @@ -25,6 +25,8 @@ #include "net/gnrc/netif/ieee802154.h" #include "net/gnrc.h" #include "include/init_devs.h" +#include "net/netdev/ieee802154_submac.h" +#include "bhp/event.h" #include "kw2xrf.h" #include "kw2xrf_params.h" @@ -41,9 +43,10 @@ #define KW2XRF_NUM ARRAY_SIZE(kw2xrf_params) static kw2xrf_t kw2xrf_devs[KW2XRF_NUM]; +static netdev_ieee802154_submac_t kw2xrf_netdev[KW2XRF_NUM]; static char _kw2xrf_stacks[KW2XRF_NUM][KW2XRF_MAC_STACKSIZE]; - static gnrc_netif_t _netif[KW2XRF_NUM]; +static bhp_event_t kw2xrf_bhp[KW2XRF_NUM]; void auto_init_kw2xrf(void) { @@ -51,10 +54,20 @@ void auto_init_kw2xrf(void) const kw2xrf_params_t *p = &kw2xrf_params[i]; LOG_DEBUG("[auto_init_netif] initializing kw2xrf #%u\n", i); - kw2xrf_setup(&kw2xrf_devs[i], (kw2xrf_params_t*) p, i); + + /* Init Bottom Half Processor (with events module) and radio */ + bhp_event_init(&kw2xrf_bhp[i], &_netif[i].evq, &kw2xrf_radio_hal_irq_handler, &kw2xrf_netdev[i].submac.dev); + kw2xrf_init(&kw2xrf_devs[i], (kw2xrf_params_t*) p,&kw2xrf_netdev[i].submac.dev, + bhp_event_isr_cb, &kw2xrf_bhp[i]); + + + netdev_register(&kw2xrf_netdev[i].dev.netdev, NETDEV_KW2XRF, i); + netdev_ieee802154_submac_init(&kw2xrf_netdev[i]); + gnrc_netif_ieee802154_create(&_netif[i], _kw2xrf_stacks[i], KW2XRF_MAC_STACKSIZE, KW2XRF_MAC_PRIO, "kw2xrf", - &kw2xrf_devs[i].netdev.netdev); + &kw2xrf_netdev[i].dev.netdev); + } } /** @} */