mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
Koen Zandberg
e3eaf4671f
This modification changes the netdev::set function for the kw2xrf to not propagate the NETOPT_CHANNEL to the generic netdev_ieee802154 code
820 lines
24 KiB
C
820 lines
24 KiB
C
/*
|
|
* 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 = (netdev_t *) arg;
|
|
kw2xrf_t *dev = (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;
|
|
if (netdev->event_callback) {
|
|
netdev->event_callback(netdev, NETDEV_EVENT_ISR);
|
|
}
|
|
}
|
|
}
|
|
|
|
static int _init(netdev_t *netdev)
|
|
{
|
|
kw2xrf_t *dev = (kw2xrf_t *)netdev;
|
|
|
|
dev->thread = (thread_t *)thread_get(thread_getpid());
|
|
|
|
/* initialize SPI and GPIOs */
|
|
if (kw2xrf_init(dev, &_irq_handler)) {
|
|
LOG_ERROR("[kw2xrf] unable to initialize device\n");
|
|
return -1;
|
|
}
|
|
|
|
#ifdef MODULE_NETSTATS_L2
|
|
memset(&netdev->stats, 0, sizeof(netstats_t));
|
|
#endif
|
|
|
|
/* reset device to default values and put it into RX state */
|
|
kw2xrf_reset_phy(dev);
|
|
|
|
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((netdev_t *)dev);
|
|
/* _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)
|
|
{
|
|
kw2xrf_t *dev = (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]);
|
|
#ifdef MODULE_NETSTATS_L2
|
|
netdev->stats.tx_bytes += len;
|
|
#endif
|
|
|
|
/* 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)
|
|
{
|
|
kw2xrf_t *dev = (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;
|
|
}
|
|
|
|
#ifdef MODULE_NETSTATS_L2
|
|
netdev->stats.rx_count++;
|
|
netdev->stats.rx_bytes += pkt_len;
|
|
#endif
|
|
|
|
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)
|
|
{
|
|
kw2xrf_t *dev = (kw2xrf_t *)netdev;
|
|
|
|
if (dev == NULL) {
|
|
return -ENODEV;
|
|
}
|
|
|
|
switch (opt) {
|
|
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:
|
|
*((netopt_enable_t *)value) =
|
|
!!(dev->netdev.flags & KW2XRF_OPT_TELL_RX_START);
|
|
return sizeof(netopt_enable_t);
|
|
|
|
case NETOPT_RX_END_IRQ:
|
|
*((netopt_enable_t *)value) =
|
|
!!(dev->netdev.flags & KW2XRF_OPT_TELL_RX_END);
|
|
return sizeof(netopt_enable_t);
|
|
|
|
case NETOPT_TX_START_IRQ:
|
|
*((netopt_enable_t *)value) =
|
|
!!(dev->netdev.flags & KW2XRF_OPT_TELL_TX_START);
|
|
return sizeof(netopt_enable_t);
|
|
|
|
case NETOPT_TX_END_IRQ:
|
|
*((netopt_enable_t *)value) =
|
|
!!(dev->netdev.flags & KW2XRF_OPT_TELL_TX_END);
|
|
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((netdev_ieee802154_t *)netdev, opt, value, len);
|
|
}
|
|
|
|
static int _set(netdev_t *netdev, netopt_t opt, const void *value, size_t len)
|
|
{
|
|
kw2xrf_t *dev = (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));
|
|
/* don't set res to set netdev_ieee802154_t::short_addr */
|
|
}
|
|
break;
|
|
|
|
case NETOPT_ADDRESS_LONG:
|
|
if (len > sizeof(uint64_t)) {
|
|
return -EOVERFLOW;
|
|
}
|
|
else {
|
|
kw2xrf_set_addr_long(dev, *((uint64_t *)value));
|
|
/* don't set res to set netdev_ieee802154_t::short_addr */
|
|
}
|
|
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_RX_START_IRQ:
|
|
kw2xrf_set_option(dev, KW2XRF_OPT_TELL_RX_START,
|
|
((bool *)value)[0]);
|
|
res = sizeof(netopt_enable_t);
|
|
break;
|
|
|
|
case NETOPT_RX_END_IRQ:
|
|
kw2xrf_set_option(dev, KW2XRF_OPT_TELL_RX_END,
|
|
((bool *)value)[0]);
|
|
res = sizeof(netopt_enable_t);
|
|
break;
|
|
|
|
case NETOPT_TX_START_IRQ:
|
|
kw2xrf_set_option(dev, KW2XRF_OPT_TELL_TX_START,
|
|
((bool *)value)[0]);
|
|
res = sizeof(netopt_enable_t);
|
|
break;
|
|
|
|
case NETOPT_TX_END_IRQ:
|
|
kw2xrf_set_option(dev, KW2XRF_OPT_TELL_TX_END,
|
|
((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 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((netdev_ieee802154_t *)netdev, opt,
|
|
value, len);
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
static void _isr_event_seq_r(netdev_t *netdev, uint8_t *dregs)
|
|
{
|
|
kw2xrf_t *dev = (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)
|
|
{
|
|
kw2xrf_t *dev = (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)
|
|
{
|
|
kw2xrf_t *dev = (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)
|
|
{
|
|
kw2xrf_t *dev = (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");
|
|
kw2xrf_seq_timeout_on(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);
|
|
}
|
|
}
|
|
|
|
DEBUG("[kw2xrf] SEQIRQ\n");
|
|
irqsts1 |= MKW2XDM_IRQSTS1_SEQIRQ;
|
|
assert(dev->pending_tx != 0);
|
|
dev->pending_tx--;
|
|
netdev->event_callback(netdev, NETDEV_EVENT_TX_COMPLETE);
|
|
kw2xrf_seq_timeout_off(dev);
|
|
kw2xrf_set_idle_sequence(dev);
|
|
}
|
|
else if (dregs[MKW2XDM_IRQSTS3] & MKW2XDM_IRQSTS3_TMR4IRQ) {
|
|
DEBUG("[kw2xrf] TC4TMOUT, no SEQIRQ, TX failed\n");
|
|
assert(dev->pending_tx != 0);
|
|
dev->pending_tx--;
|
|
netdev->event_callback(netdev, NETDEV_EVENT_TX_NOACK);
|
|
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_event_seq_ccca(netdev_t *netdev, uint8_t *dregs)
|
|
{
|
|
kw2xrf_t *dev = (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];
|
|
kw2xrf_t *dev = (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 (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,
|
|
};
|
|
|
|
/** @} */
|