mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
drivers/kw2xrf: add IEEE 802.15.4 Radio HAL support
Co-authored-by: Michel Rottleuthner <michel.rottleuthner@haw-hamburg.de>
This commit is contained in:
parent
4ace2ed778
commit
4ebcd7c055
@ -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
|
||||
|
@ -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"
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -1,2 +1,4 @@
|
||||
USEMODULE_INCLUDES_kw2xrf := $(LAST_MAKEFILEDIR)/include
|
||||
USEMODULE_INCLUDES += $(USEMODULE_INCLUDES_kw2xrf)
|
||||
|
||||
PSEUDOMODULES += kw2xrf_testmode
|
||||
|
@ -19,6 +19,7 @@
|
||||
#ifndef KW2XRF_GETSET_H
|
||||
#define KW2XRF_GETSET_H
|
||||
|
||||
#include "kw2xrf_reg.h"
|
||||
#include "kw2xrf.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
@ -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
|
||||
/**@}*/
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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:
|
||||
|
@ -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 <j.fischer@phytec.de>
|
||||
* @author Peter Kietzmann <peter.kietzmann@haw-hamburg.de>
|
||||
* @author Joakim Nohlgård <joakim.nohlgard@eistec.se>
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
|
||||
#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,
|
||||
};
|
||||
|
||||
/** @} */
|
729
drivers/kw2xrf/kw2xrf_radio_hal.c
Normal file
729
drivers/kw2xrf/kw2xrf_radio_hal.c
Normal file
@ -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 <michel.rottleuthner@haw-hamburg.de>
|
||||
*
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#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,
|
||||
};
|
@ -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)
|
||||
|
@ -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:
|
||||
|
@ -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);
|
||||
|
||||
}
|
||||
}
|
||||
/** @} */
|
||||
|
Loading…
Reference in New Issue
Block a user