mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
1282 lines
36 KiB
C
1282 lines
36 KiB
C
/*
|
|
* Copyright (C) 2015 PHYTEC Messtechnik GmbH
|
|
*
|
|
* 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 kw2xrf
|
|
* @{
|
|
* @file
|
|
* @brief Basic functionality of kw2xrf driver
|
|
*
|
|
* @author Johann Fischer <j.fischer@phytec.de>
|
|
* @author Jonas Remmert <j.remmert@phytec.de>
|
|
* @author Oliver Hahm <oliver.hahm@inria.fr>
|
|
* @}
|
|
*/
|
|
#include "panic.h"
|
|
#include "kw2xrf.h"
|
|
#include "kw2xrf_spi.h"
|
|
#include "kw2xrf_reg.h"
|
|
#include "mutex.h"
|
|
#include "msg.h"
|
|
#include "periph/gpio.h"
|
|
#include "periph/cpuid.h"
|
|
#include "net/gnrc.h"
|
|
#include "net/ieee802154.h"
|
|
|
|
#define ENABLE_DEBUG (0)
|
|
#include "debug.h"
|
|
|
|
/**
|
|
* @brief Internal driver event type in case of an unexpected interrupt
|
|
*/
|
|
#define ISR_EVENT_UNKNOWN (0x0020)
|
|
|
|
/* Modem_PA_PWR Register (PA Power Control) has a valid range from 3-31 */
|
|
#define MKW2XDRF_PA_RANGE_MAX 31 /**< Maximum value of PA Power Control Register */
|
|
#define MKW2XDRF_PA_RANGE_MIN 3 /**< Minimum value of PA Power Control Register */
|
|
|
|
#define _MAX_MHR_OVERHEAD (25)
|
|
|
|
/* PLL integer lookup table */
|
|
static const uint8_t pll_int_lt[16] = {
|
|
11, 11, 11, 11,
|
|
11, 11, 12, 12,
|
|
12, 12, 12, 12,
|
|
13, 13, 13, 13
|
|
};
|
|
|
|
/* PLL frequency fractional lookup table */
|
|
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 const uint8_t pow_lt[44] = {
|
|
3, 4, 4, 5,
|
|
6, 6, 7, 7,
|
|
8, 9, 9, 10,
|
|
11, 11, 12, 13,
|
|
13, 14, 14, 15,
|
|
16, 16, 17, 18,
|
|
18, 19, 20, 20,
|
|
21, 21, 22, 23,
|
|
23, 24, 25, 25,
|
|
26, 27, 27, 28,
|
|
28, 29, 30, 31
|
|
};
|
|
|
|
/* TODO: Implement this
|
|
static const int level_lt[29] = {
|
|
-35, -34, -32, -31,
|
|
-29, -28, -26, -25,
|
|
-23, -22, -20, -19,
|
|
-17, -16, -14, -13,
|
|
-11, -10, -8, -7,
|
|
-5, -4, -2, -1,
|
|
1, 2, 4, 5,
|
|
7
|
|
};
|
|
*/
|
|
|
|
static gpio_t kw2xrf_gpio_int;
|
|
|
|
void kw2xrf_set_option(kw2xrf_t *dev, uint16_t option, bool state);
|
|
|
|
int kw2xrf_set_tx_power(kw2xrf_t *dev, int8_t *val, size_t len)
|
|
{
|
|
if (val[0] > MKW2XDRF_OUTPUT_POWER_MAX) {
|
|
val[0] = MKW2XDRF_OUTPUT_POWER_MAX;
|
|
}
|
|
|
|
if (val[0] < MKW2XDRF_OUTPUT_POWER_MIN) {
|
|
val[0] = MKW2XDRF_OUTPUT_POWER_MIN;
|
|
}
|
|
|
|
uint8_t level = pow_lt[val[0] - MKW2XDRF_OUTPUT_POWER_MIN];
|
|
kw2xrf_write_dreg(MKW2XDM_PA_PWR, MKW2XDM_PA_PWR(level));
|
|
return 2;
|
|
}
|
|
|
|
int kw2xrf_get_channel(kw2xrf_t *dev, uint8_t *val, size_t max)
|
|
{
|
|
if (max < 2) {
|
|
return -EOVERFLOW;
|
|
}
|
|
|
|
uint8_t pll_int = kw2xrf_read_dreg(MKW2XDM_PLL_INT0);
|
|
uint16_t pll_frac = kw2xrf_read_dreg(MKW2XDM_PLL_FRAC0_LSB);
|
|
pll_frac |= ((uint16_t)kw2xrf_read_dreg(MKW2XDM_PLL_FRAC0_MSB) << 8);
|
|
|
|
for (int i = 0; i < 16; i++) {
|
|
if ((pll_frac_lt[i] == pll_frac) && (pll_int_lt[i] == pll_int)) {
|
|
val[0] = i + 11;
|
|
val[1] = 0;
|
|
return 2;
|
|
}
|
|
}
|
|
|
|
return -EINVAL;
|
|
}
|
|
|
|
int kw2xrf_get_sequence(void)
|
|
{
|
|
int reg = 0;
|
|
reg = kw2xrf_read_dreg(MKW2XDM_PHY_CTRL1);
|
|
reg &= MKW2XDM_PHY_CTRL1_XCVSEQ_MASK;
|
|
return reg;
|
|
}
|
|
|
|
void kw2xrf_set_sequence(kw2xrf_t *dev, kw2xrf_physeq_t seq)
|
|
{
|
|
uint8_t reg = 0;
|
|
|
|
/* Only interrupt interruptable states */
|
|
uint8_t curr_seq = kw2xrf_read_dreg(MKW2XDM_PHY_CTRL1);
|
|
curr_seq &= (MKW2XDM_PHY_CTRL1_XCVSEQ_MASK);
|
|
|
|
if ((curr_seq == XCVSEQ_RECEIVE) || (curr_seq == XCVSEQ_CONTINUOUS_CCA)) {
|
|
/* Clear all pending interrupts */
|
|
gpio_irq_disable(kw2xrf_gpio_int);
|
|
|
|
/* abort any ongoing sequence */
|
|
DEBUG("kw2xrf_tx: abort SEQ_STATE: %x\n", kw2xrf_read_dreg(MKW2XDM_SEQ_STATE));
|
|
reg = kw2xrf_read_dreg(MKW2XDM_PHY_CTRL1);
|
|
reg &= ~(MKW2XDM_PHY_CTRL1_XCVSEQ_MASK);
|
|
kw2xrf_write_dreg(MKW2XDM_PHY_CTRL1, reg);
|
|
|
|
/* Mask all possible interrupts */
|
|
reg = kw2xrf_read_dreg(MKW2XDM_PHY_CTRL3);
|
|
reg |= MKW2XDM_PHY_CTRL3_WAKE_MSK;
|
|
kw2xrf_write_dreg(MKW2XDM_PHY_CTRL3, reg);
|
|
|
|
kw2xrf_write_dreg(MKW2XDM_IRQSTS1, 0x7f);
|
|
kw2xrf_write_dreg(MKW2XDM_IRQSTS2, 0x03);
|
|
kw2xrf_write_dreg(MKW2XDM_IRQSTS3, 0xff);
|
|
|
|
gpio_irq_enable(kw2xrf_gpio_int);
|
|
}
|
|
|
|
#ifdef DEVELHELP
|
|
uint16_t max_tries = 0;
|
|
#endif
|
|
|
|
/* Wait for all other states finish */
|
|
while (kw2xrf_read_dreg(MKW2XDM_SEQ_STATE)) {
|
|
#ifdef DEVELHELP
|
|
max_tries++;
|
|
|
|
/* At 10MHz SPI-Clock, 40000 should be in the magnitue of 0.1s */
|
|
if (max_tries == 40000) {
|
|
DEBUG("kw2xrf_error: device does not finish sequence\n");
|
|
core_panic(PANIC_GENERAL_ERROR, "kw2xrf_error: device does not finish sequence");
|
|
}
|
|
|
|
#endif
|
|
}
|
|
|
|
/* For all sequences only enable SEQ-irq will be set when sequence completed */
|
|
reg = kw2xrf_read_dreg(MKW2XDM_PHY_CTRL2);
|
|
reg &= ~(MKW2XDM_PHY_CTRL2_SEQMSK);
|
|
kw2xrf_write_dreg(MKW2XDM_PHY_CTRL2, reg);
|
|
|
|
/* Progrmm new sequence */
|
|
switch (seq) {
|
|
case XCVSEQ_IDLE:
|
|
dev->state = NETOPT_STATE_SLEEP;
|
|
break;
|
|
|
|
case XCVSEQ_RECEIVE:
|
|
dev->state = NETOPT_STATE_RX;
|
|
break;
|
|
|
|
case XCVSEQ_TRANSMIT:
|
|
dev->state = NETOPT_STATE_TX;
|
|
break;
|
|
|
|
case XCVSEQ_CCA:
|
|
dev->state = NETOPT_STATE_TX;
|
|
break;
|
|
|
|
case XCVSEQ_TX_RX:
|
|
dev->state = NETOPT_STATE_TX;
|
|
break;
|
|
|
|
case XCVSEQ_CONTINUOUS_CCA:
|
|
dev->state = NETOPT_STATE_TX;
|
|
break;
|
|
|
|
default:
|
|
DEBUG("kw2xrf: undefined state assigned to phy\n");
|
|
dev->state = NETOPT_STATE_IDLE;
|
|
}
|
|
|
|
/* Mapping of TX-sequences depending on AUTOACK flag */
|
|
/* TODO: This should only used in combination with
|
|
* an CSMA-MAC layer. Currently not working
|
|
*/
|
|
/*if ((seq == XCVSEQ_TRANSMIT) || (seq == XCVSEQ_TX_RX)) {
|
|
if ((dev->option) & KW2XRF_OPT_AUTOACK) {
|
|
seq = XCVSEQ_TX_RX;
|
|
}
|
|
else {
|
|
seq = XCVSEQ_TRANSMIT;
|
|
}
|
|
}*/
|
|
|
|
DEBUG("kw2xrf: Set sequence to %i\n", seq);
|
|
reg = kw2xrf_read_dreg(MKW2XDM_PHY_CTRL1);
|
|
reg &= ~(MKW2XDM_PHY_CTRL1_XCVSEQ_MASK);
|
|
reg |= MKW2XDM_PHY_CTRL1_XCVSEQ(seq);
|
|
kw2xrf_write_dreg(MKW2XDM_PHY_CTRL1, reg);
|
|
}
|
|
|
|
int kw2xrf_set_channel(kw2xrf_t *dev, uint8_t *val, size_t len)
|
|
{
|
|
/* Save old sequence to restore this state later */
|
|
uint8_t old_seq = kw2xrf_get_sequence();
|
|
|
|
if (old_seq) {
|
|
kw2xrf_set_sequence(dev, XCVSEQ_IDLE);
|
|
}
|
|
|
|
if ((val[0] < 11) || (val[0] > 26)) {
|
|
DEBUG("kw2xrf: Invalid channel %i set. Valid channels are 11 through 26\n", val[0]);
|
|
return -ENOTSUP;
|
|
}
|
|
|
|
if ((len != 2) || (val[1] != 0)) {
|
|
DEBUG("kw2xrf: set channel failed, len: %u, val[0]:%u\n", len, val[0]);
|
|
return -EINVAL;
|
|
}
|
|
|
|
/*
|
|
* 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
|
|
*
|
|
*/
|
|
uint8_t tmp = val[0] - 11;
|
|
kw2xrf_write_dreg(MKW2XDM_PLL_INT0, MKW2XDM_PLL_INT0_VAL(pll_int_lt[tmp]));
|
|
kw2xrf_write_dreg(MKW2XDM_PLL_FRAC0_LSB, (uint8_t)pll_frac_lt[tmp]);
|
|
kw2xrf_write_dreg(MKW2XDM_PLL_FRAC0_MSB, (uint8_t)(pll_frac_lt[tmp] >> 8));
|
|
|
|
DEBUG("kw2xrf: set channel to %u\n", val[0]);
|
|
|
|
if (old_seq) {
|
|
kw2xrf_set_sequence(dev, old_seq);
|
|
}
|
|
|
|
return 2;
|
|
}
|
|
|
|
void kw2xrf_irq_handler(void *args)
|
|
{
|
|
msg_t msg;
|
|
kw2xrf_t *dev = (kw2xrf_t *)args;
|
|
|
|
/* notify driver thread about the interrupt */
|
|
msg.type = GNRC_NETDEV_MSG_TYPE_EVENT;
|
|
msg_send_int(&msg, dev->mac_pid);
|
|
}
|
|
|
|
/* Set up interrupt sources, triggered by the radio-module */
|
|
void kw2xrf_init_interrupts(kw2xrf_t *dev, gpio_t int_pin)
|
|
{
|
|
/* Clear all pending interrupts */
|
|
kw2xrf_write_dreg(MKW2XDM_IRQSTS1, 0x7f);
|
|
kw2xrf_write_dreg(MKW2XDM_IRQSTS2, 0x03);
|
|
kw2xrf_write_dreg(MKW2XDM_IRQSTS3, 0xff);
|
|
|
|
/* Disable all interrups:
|
|
* Selectively enable only one interrupt source selectively in sequence manager.
|
|
* After reset state all interrupts are disabled, except WAKE_IRQ.
|
|
*/
|
|
int reg = kw2xrf_read_dreg(MKW2XDM_PHY_CTRL3);
|
|
reg |= MKW2XDM_PHY_CTRL3_WAKE_MSK;
|
|
kw2xrf_write_dreg(MKW2XDM_PHY_CTRL3, reg);
|
|
|
|
/* set up GPIO-pin used for IRQ */
|
|
gpio_init_int(int_pin, GPIO_NOPULL, GPIO_FALLING, &kw2xrf_irq_handler, dev);
|
|
}
|
|
|
|
int kw2xrf_set_pan(kw2xrf_t *dev, uint16_t pan)
|
|
{
|
|
dev->radio_pan = pan;
|
|
|
|
uint8_t val_ar[2];
|
|
val_ar[1] = (pan >> 8);
|
|
val_ar[0] = (uint8_t)pan;
|
|
kw2xrf_write_iregs(MKW2XDMI_MACPANID0_LSB, val_ar, 2);
|
|
return 2;
|
|
}
|
|
|
|
int kw2xrf_get_proto(kw2xrf_t *dev, uint8_t *val, size_t max)
|
|
{
|
|
if (max < sizeof(gnrc_nettype_t)) {
|
|
return -EOVERFLOW;
|
|
}
|
|
|
|
memcpy(val, &(dev->proto), sizeof(gnrc_nettype_t));
|
|
return sizeof(gnrc_nettype_t);
|
|
}
|
|
|
|
int kw2xrf_set_proto(kw2xrf_t *dev, uint8_t *val, size_t len)
|
|
{
|
|
if (len != sizeof(gnrc_nettype_t)) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
memcpy(&(dev->proto), val, sizeof(gnrc_nettype_t));
|
|
return sizeof(gnrc_nettype_t);
|
|
}
|
|
|
|
int kw2xrf_on(kw2xrf_t *dev)
|
|
{
|
|
uint8_t tmp;
|
|
/* check modem's crystal oscillator, CLK_OUT shall be 4MHz */
|
|
tmp = kw2xrf_read_dreg(MKW2XDM_CLK_OUT_CTRL);
|
|
|
|
if (tmp != 0x8Bu) {
|
|
return -1;
|
|
}
|
|
|
|
DEBUG("SEQ_STATE: %x\n", kw2xrf_read_dreg(MKW2XDM_SEQ_STATE));
|
|
|
|
/* enable RFon mode */
|
|
kw2xrf_write_dreg(MKW2XDM_PWR_MODES,
|
|
(MKW2XDM_PWR_MODES_XTALEN | MKW2XDM_PWR_MODES_PMC_MODE));
|
|
|
|
/* abort any ongoing sequence */
|
|
kw2xrf_set_sequence(dev, XCVSEQ_IDLE);
|
|
|
|
dev->state = NETOPT_STATE_SLEEP;
|
|
return 0;
|
|
}
|
|
|
|
int kw2xrf_set_addr(kw2xrf_t *dev, uint16_t addr)
|
|
{
|
|
uint8_t val_ar[2];
|
|
val_ar[0] = (addr >> 8);
|
|
val_ar[1] = (uint8_t)addr;
|
|
dev->addr_short[0] = val_ar[0];
|
|
dev->addr_short[1] = val_ar[1];
|
|
#ifdef MODULE_SIXLOWPAN
|
|
/* https://tools.ietf.org/html/rfc4944#section-12 requires the first bit to
|
|
* 0 for unicast addresses */
|
|
dev->addr_short[1] &= 0x7F;
|
|
#endif
|
|
kw2xrf_write_iregs(MKW2XDMI_MACSHORTADDRS0_LSB, val_ar, 2);
|
|
return sizeof(uint16_t);
|
|
}
|
|
|
|
int kw2xrf_set_addr_long(kw2xrf_t *dev, uint64_t addr)
|
|
{
|
|
for (int i = 0; i < 8; i++) {
|
|
dev->addr_long[i] = (addr >> ((7 - i) * 8));
|
|
}
|
|
|
|
kw2xrf_write_iregs(MKW2XDMI_MACLONGADDRS0_0, (dev->addr_long), 8);
|
|
|
|
return sizeof(uint64_t);
|
|
}
|
|
|
|
int kw2xrf_init(kw2xrf_t *dev, spi_t spi, spi_speed_t spi_speed,
|
|
gpio_t cs_pin, gpio_t int_pin)
|
|
{
|
|
uint8_t reg = 0;
|
|
uint8_t tmp[2];
|
|
kw2xrf_gpio_int = int_pin;
|
|
#if CPUID_LEN
|
|
uint8_t cpuid[CPUID_LEN];
|
|
eui64_t addr_long;
|
|
#endif
|
|
|
|
/* check device parameters */
|
|
if (dev == NULL) {
|
|
return -ENODEV;
|
|
}
|
|
|
|
kw2xrf_spi_init(spi, spi_speed, cs_pin);
|
|
|
|
if (kw2xrf_on(dev) != 0) {
|
|
core_panic(PANIC_GENERAL_ERROR, "Could not start MKW2XD radio transceiver");
|
|
}
|
|
|
|
/* General initialization of interrupt sources.
|
|
* sets radio to idle mode with all interrupt masked
|
|
*/
|
|
kw2xrf_init_interrupts(dev, kw2xrf_gpio_int);
|
|
|
|
/* set device driver */
|
|
dev->driver = &kw2xrf_driver;
|
|
/* set default option */
|
|
dev->proto = KW2XRF_DEFAULT_PROTOCOL;
|
|
dev->option = 0;
|
|
|
|
#if CPUID_LEN
|
|
cpuid_get(cpuid);
|
|
|
|
#if CPUID_LEN < 8
|
|
|
|
/* in case CPUID_LEN < 8, fill missing bytes with zeros */
|
|
for (int i = CPUID_LEN; i < 8; i++) {
|
|
cpuid[i] = 0;
|
|
}
|
|
|
|
#else
|
|
|
|
for (int i = 8; i < CPUID_LEN; i++) {
|
|
cpuid[i & 0x07] ^= cpuid[i];
|
|
}
|
|
|
|
#endif
|
|
/* make sure we mark the address as non-multicast and not globally unique */
|
|
cpuid[0] &= ~(0x01);
|
|
cpuid[0] |= 0x02;
|
|
/* copy and set long address */
|
|
memcpy(&addr_long, cpuid, 8);
|
|
kw2xrf_set_addr_long(dev, NTOHLL(addr_long.uint64.u64));
|
|
kw2xrf_set_addr(dev, NTOHS(addr_long.uint16[0].u16));
|
|
#else
|
|
kw2xrf_set_addr_long(dev, KW2XRF_DEFAULT_SHORT_ADDR);
|
|
kw2xrf_set_addr(dev, KW2XRF_DEFAULT_ADDR_LONG);
|
|
#endif
|
|
|
|
/* set default TX-Power */
|
|
dev->tx_power = KW2XRF_DEFAULT_TX_POWER;
|
|
kw2xrf_set_tx_power(dev, &(dev->tx_power), sizeof(dev->tx_power));
|
|
|
|
/* set default channel */
|
|
dev->radio_channel = KW2XRF_DEFAULT_CHANNEL;
|
|
tmp[0] = dev->radio_channel;
|
|
tmp[1] = 0;
|
|
kw2xrf_set_channel(dev, tmp, 2);
|
|
/* set default PAN ID */
|
|
kw2xrf_set_pan(dev, KW2XRF_DEFAULT_PANID);
|
|
|
|
/* CCA Setup */
|
|
reg = kw2xrf_read_dreg(MKW2XDM_PHY_CTRL4);
|
|
/* Set up CCA mode 1 (RSSI threshold) */
|
|
reg |= MKW2XDM_PHY_CTRL4_CCATYPE(1);
|
|
kw2xrf_write_dreg(MKW2XDM_PHY_CTRL4, reg);
|
|
DEBUG("kw2xrf: Initialized and set to channel %i and pan %i.\n",
|
|
KW2XRF_DEFAULT_CHANNEL, KW2XRF_DEFAULT_PANID);
|
|
|
|
kw2xrf_set_option(dev, KW2XRF_OPT_AUTOACK, true);
|
|
kw2xrf_set_option(dev, KW2XRF_OPT_CSMA, true);
|
|
|
|
/* Switch to Receive state per default after initialization */
|
|
kw2xrf_set_sequence(dev, XCVSEQ_RECEIVE);
|
|
return 0;
|
|
}
|
|
|
|
int kw2xrf_add_cb(gnrc_netdev_t *dev, gnrc_netdev_event_cb_t cb)
|
|
{
|
|
if (dev == NULL) {
|
|
return -ENODEV;
|
|
}
|
|
|
|
if (dev->event_cb != NULL) {
|
|
return -ENOBUFS;
|
|
}
|
|
|
|
dev->event_cb = cb;
|
|
return 0;
|
|
}
|
|
|
|
int kw2xrf_rem_cb(gnrc_netdev_t *dev, gnrc_netdev_event_cb_t cb)
|
|
{
|
|
if (dev == NULL) {
|
|
return -ENODEV;
|
|
}
|
|
|
|
if (dev->event_cb != cb) {
|
|
return -ENOENT;
|
|
}
|
|
|
|
dev->event_cb = NULL;
|
|
return 0;
|
|
}
|
|
|
|
uint16_t kw2xrf_get_addr_short(kw2xrf_t *dev)
|
|
{
|
|
return (dev->addr_short[0] << 8) | dev->addr_short[1];
|
|
}
|
|
|
|
uint64_t kw2xrf_get_addr_long(kw2xrf_t *dev)
|
|
{
|
|
uint64_t addr;
|
|
uint8_t *ap = (uint8_t *)(&addr);
|
|
|
|
for (int i = 0; i < 8; i++) {
|
|
ap[i] = dev->addr_long[7 - i];
|
|
}
|
|
|
|
return addr;
|
|
}
|
|
|
|
int8_t kw2xrf_get_cca_threshold(kw2xrf_t *dev)
|
|
{
|
|
uint8_t tmp;
|
|
kw2xrf_read_iregs(MKW2XDMI_CCA1_THRESH, &tmp, 1);
|
|
/* KW2x register value represents absolute value in dBm
|
|
* default value: -75 dBm
|
|
*/
|
|
return (-tmp);
|
|
}
|
|
|
|
void kw2xrf_set_cca_threshold(kw2xrf_t *dev, int8_t value)
|
|
{
|
|
/* normalize to absolute value */
|
|
if (value < 0) {
|
|
value = -value;
|
|
}
|
|
kw2xrf_write_iregs(MKW2XDMI_CCA1_THRESH, (uint8_t*)&value, 1);
|
|
}
|
|
|
|
int kw2xrf_get(gnrc_netdev_t *netdev, netopt_t opt, void *value, size_t max_len)
|
|
{
|
|
kw2xrf_t *dev = (kw2xrf_t *)netdev;
|
|
|
|
if (dev == NULL) {
|
|
return -ENODEV;
|
|
}
|
|
|
|
switch (opt) {
|
|
case NETOPT_ADDRESS:
|
|
if (max_len < sizeof(uint16_t)) {
|
|
return -EOVERFLOW;
|
|
}
|
|
|
|
*((uint16_t *)value) = kw2xrf_get_addr_short(dev);
|
|
return sizeof(uint16_t);
|
|
|
|
case NETOPT_ADDRESS_LONG:
|
|
if (max_len < sizeof(uint64_t)) {
|
|
return -EOVERFLOW;
|
|
}
|
|
|
|
*((uint64_t *)value) = kw2xrf_get_addr_long(dev);
|
|
return sizeof(uint64_t);
|
|
|
|
case NETOPT_ADDR_LEN:
|
|
if (max_len < sizeof(uint16_t)) {
|
|
return -EOVERFLOW;
|
|
}
|
|
|
|
*((uint16_t *)value) = 2;
|
|
return sizeof(uint16_t);
|
|
|
|
case NETOPT_SRC_LEN:
|
|
if (max_len < sizeof(uint16_t)) {
|
|
return -EOVERFLOW;
|
|
}
|
|
|
|
if (dev->option & KW2XRF_OPT_SRC_ADDR_LONG) {
|
|
*((uint16_t *)value) = 8;
|
|
}
|
|
else {
|
|
*((uint16_t *)value) = 2;
|
|
}
|
|
|
|
return sizeof(uint16_t);
|
|
|
|
case NETOPT_NID:
|
|
if (max_len < sizeof(uint16_t)) {
|
|
return -EOVERFLOW;
|
|
}
|
|
|
|
*((uint16_t *)value) = dev->radio_pan;
|
|
return sizeof(uint16_t);
|
|
|
|
case NETOPT_IPV6_IID:
|
|
if (max_len < sizeof(eui64_t)) {
|
|
return -EOVERFLOW;
|
|
}
|
|
if (dev->option & KW2XRF_OPT_SRC_ADDR_LONG) {
|
|
uint64_t addr = kw2xrf_get_addr_long(dev);
|
|
ieee802154_get_iid(value, (uint8_t *)&addr, 8);
|
|
}
|
|
else {
|
|
uint16_t addr = kw2xrf_get_addr_short(dev);
|
|
ieee802154_get_iid(value, (uint8_t *)&addr, 2);
|
|
}
|
|
return sizeof(eui64_t);
|
|
|
|
case NETOPT_CHANNEL:
|
|
return kw2xrf_get_channel(dev, (uint8_t *)value, max_len);
|
|
|
|
case NETOPT_PROTO:
|
|
return kw2xrf_get_proto(dev, (uint8_t *)value, max_len);
|
|
|
|
case NETOPT_STATE:
|
|
if (max_len < sizeof(netopt_state_t)) {
|
|
return -EOVERFLOW;
|
|
}
|
|
|
|
*(netopt_state_t *)value = *(netopt_state_t *) & (dev->state);
|
|
return 0;
|
|
|
|
case NETOPT_TX_POWER:
|
|
if (max_len < 1) {
|
|
return -EOVERFLOW;
|
|
}
|
|
|
|
*(int16_t *)value = dev->tx_power;
|
|
return 0;
|
|
|
|
case NETOPT_CCA_THRESHOLD:
|
|
if (max_len < sizeof(uint8_t)) {
|
|
return -EOVERFLOW;
|
|
} else {
|
|
*(int8_t *)value = kw2xrf_get_cca_threshold(dev);
|
|
}
|
|
return 0;
|
|
|
|
case NETOPT_MAX_PACKET_SIZE:
|
|
if (max_len < sizeof(int16_t)) {
|
|
return -EOVERFLOW;
|
|
}
|
|
|
|
*((uint16_t *)value) = KW2XRF_MAX_PKT_LENGTH - _MAX_MHR_OVERHEAD;
|
|
return sizeof(uint16_t);
|
|
|
|
case NETOPT_PRELOADING:
|
|
*((netopt_enable_t *)value) = !!(dev->option & KW2XRF_OPT_PRELOADING);
|
|
return sizeof(netopt_enable_t);
|
|
|
|
case NETOPT_AUTOACK:
|
|
*((netopt_enable_t *)value) = !!(dev->option & KW2XRF_OPT_AUTOACK);
|
|
return sizeof(netopt_enable_t);
|
|
|
|
case NETOPT_PROMISCUOUSMODE:
|
|
*((netopt_enable_t *)value) = !!(dev->option & KW2XRF_OPT_PROMISCUOUS);
|
|
return sizeof(netopt_enable_t);
|
|
|
|
case NETOPT_RAWMODE:
|
|
*((netopt_enable_t *)value) = !!(dev->option & KW2XRF_OPT_RAWDUMP);
|
|
return sizeof(netopt_enable_t);
|
|
|
|
default:
|
|
return -ENOTSUP;
|
|
}
|
|
}
|
|
|
|
void kw2xrf_set_option(kw2xrf_t *dev, uint16_t option, bool state)
|
|
{
|
|
uint8_t reg;
|
|
|
|
DEBUG("set option %i to %i\n", option, state);
|
|
|
|
/* set option field */
|
|
if (state) {
|
|
dev->option |= option;
|
|
|
|
/* trigger option specific actions */
|
|
switch (option) {
|
|
case KW2XRF_OPT_CSMA:
|
|
DEBUG("[kw2xrf] opt: enabling CSMA mode\n");
|
|
reg = kw2xrf_read_dreg(MKW2XDM_PHY_CTRL1);
|
|
reg |= MKW2XDM_PHY_CTRL1_CCABFRTX;
|
|
kw2xrf_write_dreg(MKW2XDM_PHY_CTRL1, reg);
|
|
break;
|
|
|
|
case KW2XRF_OPT_PROMISCUOUS:
|
|
DEBUG("[kw2xrf] opt: enabling PROMISCUOUS mode\n");
|
|
/* disable auto ACKs in promiscuous mode */
|
|
reg = kw2xrf_read_dreg(MKW2XDM_PHY_CTRL1);
|
|
reg &= ~(MKW2XDM_PHY_CTRL1_AUTOACK);
|
|
kw2xrf_write_dreg(MKW2XDM_PHY_CTRL1, reg);
|
|
/* enable promiscuous mode */
|
|
reg = kw2xrf_read_dreg(MKW2XDM_PHY_CTRL4);
|
|
reg |= MKW2XDM_PHY_CTRL4_PROMISCUOUS;
|
|
kw2xrf_write_dreg(MKW2XDM_PHY_CTRL4, reg);
|
|
break;
|
|
|
|
case KW2XRF_OPT_AUTOACK:
|
|
DEBUG("[kw2xrf] opt: enabling auto ACKs\n");
|
|
reg = kw2xrf_read_dreg(MKW2XDM_PHY_CTRL1);
|
|
reg |= MKW2XDM_PHY_CTRL1_AUTOACK;
|
|
kw2xrf_write_dreg(MKW2XDM_PHY_CTRL1, reg);
|
|
break;
|
|
|
|
default:
|
|
/* do nothing */
|
|
break;
|
|
}
|
|
}
|
|
else {
|
|
dev->option &= ~(option);
|
|
|
|
/* trigger option specific actions */
|
|
switch (option) {
|
|
case KW2XRF_OPT_CSMA:
|
|
reg = kw2xrf_read_dreg(MKW2XDM_PHY_CTRL1);
|
|
reg &= ~(MKW2XDM_PHY_CTRL1_CCABFRTX);
|
|
kw2xrf_write_dreg(MKW2XDM_PHY_CTRL1, reg);
|
|
break;
|
|
|
|
case KW2XRF_OPT_PROMISCUOUS:
|
|
/* disable promiscuous mode */
|
|
reg = kw2xrf_read_dreg(MKW2XDM_PHY_CTRL4);
|
|
reg &= ~(MKW2XDM_PHY_CTRL4_PROMISCUOUS);
|
|
kw2xrf_write_dreg(MKW2XDM_PHY_CTRL4, reg);
|
|
|
|
/* re-enable AUTOACK only if the option is set */
|
|
if (dev->option & KW2XRF_OPT_AUTOACK) {
|
|
reg = kw2xrf_read_dreg(MKW2XDM_PHY_CTRL1);
|
|
reg |= MKW2XDM_PHY_CTRL1_AUTOACK;
|
|
kw2xrf_write_dreg(MKW2XDM_PHY_CTRL1, reg);
|
|
}
|
|
|
|
break;
|
|
|
|
case KW2XRF_OPT_AUTOACK:
|
|
reg = kw2xrf_read_dreg(MKW2XDM_PHY_CTRL1);
|
|
reg &= ~(MKW2XDM_PHY_CTRL1_AUTOACK);
|
|
kw2xrf_write_dreg(MKW2XDM_PHY_CTRL1, reg);
|
|
break;
|
|
|
|
default:
|
|
/* do nothing */
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
int kw2xrf_set(gnrc_netdev_t *netdev, netopt_t opt, void *value, size_t value_len)
|
|
{
|
|
kw2xrf_t *dev = (kw2xrf_t *)netdev;
|
|
|
|
if (dev == NULL) {
|
|
return -ENODEV;
|
|
}
|
|
|
|
switch (opt) {
|
|
case NETOPT_CHANNEL:
|
|
return kw2xrf_set_channel(dev, (uint8_t *)value, value_len);
|
|
|
|
case NETOPT_ADDRESS:
|
|
if (value_len > sizeof(uint16_t)) {
|
|
return -EOVERFLOW;
|
|
}
|
|
|
|
return kw2xrf_set_addr(dev, *((uint16_t *)value));
|
|
|
|
case NETOPT_ADDRESS_LONG:
|
|
if (value_len > sizeof(uint64_t)) {
|
|
return -EOVERFLOW;
|
|
}
|
|
|
|
return kw2xrf_set_addr_long(dev, *((uint64_t *)value));
|
|
|
|
case NETOPT_SRC_LEN:
|
|
if (value_len > sizeof(uint16_t)) {
|
|
return -EOVERFLOW;
|
|
}
|
|
|
|
if (*((uint16_t *)value) == 2) {
|
|
kw2xrf_set_option(dev, KW2XRF_OPT_SRC_ADDR_LONG,
|
|
false);
|
|
}
|
|
else if (*((uint16_t *)value) == 8) {
|
|
kw2xrf_set_option(dev, KW2XRF_OPT_SRC_ADDR_LONG,
|
|
true);
|
|
}
|
|
else {
|
|
return -ENOTSUP;
|
|
}
|
|
|
|
return sizeof(uint16_t);
|
|
|
|
case NETOPT_NID:
|
|
if (value_len > sizeof(uint16_t)) {
|
|
return -EOVERFLOW;
|
|
}
|
|
|
|
return kw2xrf_set_pan(dev, *((uint16_t *)value));
|
|
|
|
case NETOPT_TX_POWER:
|
|
if (value_len < sizeof(uint16_t)) {
|
|
return -EOVERFLOW;
|
|
}
|
|
|
|
kw2xrf_set_tx_power(dev, (int8_t *)value, value_len);
|
|
return sizeof(uint16_t);
|
|
|
|
case NETOPT_CCA_THRESHOLD:
|
|
if (value_len < sizeof(uint8_t)) {
|
|
return -EOVERFLOW;
|
|
} else {
|
|
kw2xrf_set_cca_threshold(dev, *((int8_t*)value));
|
|
}
|
|
return sizeof(uint8_t);
|
|
|
|
case NETOPT_PROTO:
|
|
return kw2xrf_set_proto(dev, (uint8_t *)value, value_len);
|
|
|
|
case NETOPT_AUTOACK:
|
|
/* Set up HW generated automatic ACK after Receive */
|
|
kw2xrf_set_option(dev, KW2XRF_OPT_AUTOACK,
|
|
((bool *)value)[0]);
|
|
return sizeof(netopt_enable_t);
|
|
|
|
case NETOPT_PROMISCUOUSMODE:
|
|
kw2xrf_set_option(dev, KW2XRF_OPT_PROMISCUOUS,
|
|
((bool *)value)[0]);
|
|
return sizeof(netopt_enable_t);
|
|
|
|
case NETOPT_RAWMODE:
|
|
kw2xrf_set_option(dev, KW2XRF_OPT_RAWDUMP,
|
|
((bool *)value)[0]);
|
|
return sizeof(netopt_enable_t);
|
|
|
|
case NETOPT_PRELOADING:
|
|
kw2xrf_set_option(dev, KW2XRF_OPT_PRELOADING,
|
|
((bool *)value)[0]);
|
|
return sizeof(netopt_enable_t);
|
|
|
|
case NETOPT_AUTOCCA:
|
|
kw2xrf_set_option(dev, KW2XRF_OPT_CSMA,
|
|
((bool *)value)[0]);
|
|
return sizeof(netopt_enable_t);
|
|
|
|
case NETOPT_STATE:
|
|
if (*((netopt_state_t *)value) == NETOPT_STATE_TX) {
|
|
DEBUG("kw2xrf: Sending now.\n");
|
|
kw2xrf_set_sequence(dev, XCVSEQ_TRANSMIT);
|
|
return sizeof(netopt_state_t);
|
|
}
|
|
else if (*((netopt_state_t *)value) == NETOPT_STATE_SLEEP) {
|
|
kw2xrf_set_sequence(dev, XCVSEQ_IDLE);
|
|
return sizeof(netopt_state_t);
|
|
}
|
|
else if (*((netopt_state_t *)value) == NETOPT_STATE_IDLE) {
|
|
kw2xrf_set_sequence(dev, XCVSEQ_RECEIVE);
|
|
return sizeof(netopt_state_t);
|
|
}
|
|
|
|
/* TODO: Implement Off state here, when LPM functions are implemented */
|
|
|
|
default:
|
|
return -ENOTSUP;
|
|
}
|
|
}
|
|
|
|
/* TODO: generalize and move to ieee802154 */
|
|
/* TODO: include security header implications */
|
|
static size_t _get_frame_hdr_len(uint8_t *mhr)
|
|
{
|
|
uint8_t tmp;
|
|
size_t len = 3;
|
|
|
|
/* figure out address sizes */
|
|
tmp = (mhr[1] & IEEE802154_FCF_DST_ADDR_MASK);
|
|
|
|
if (tmp == IEEE802154_FCF_DST_ADDR_SHORT) {
|
|
len += 4;
|
|
}
|
|
else if (tmp == IEEE802154_FCF_DST_ADDR_LONG) {
|
|
len += 10;
|
|
}
|
|
else if (tmp != IEEE802154_FCF_DST_ADDR_VOID) {
|
|
return 0;
|
|
}
|
|
|
|
tmp = (mhr[1] & IEEE802154_FCF_SRC_ADDR_MASK);
|
|
|
|
if (tmp == IEEE802154_FCF_SRC_ADDR_VOID) {
|
|
return len;
|
|
}
|
|
else {
|
|
if (!(mhr[0] & IEEE802154_FCF_PAN_COMP)) {
|
|
len += 2;
|
|
}
|
|
|
|
if (tmp == IEEE802154_FCF_SRC_ADDR_SHORT) {
|
|
return (len + 2);
|
|
}
|
|
else if (tmp == IEEE802154_FCF_SRC_ADDR_LONG) {
|
|
return (len + 8);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* TODO: generalize and move to (gnrc_)ieee802154 */
|
|
static gnrc_pktsnip_t *_make_netif_hdr(uint8_t *mhr)
|
|
{
|
|
uint8_t tmp;
|
|
uint8_t *addr;
|
|
uint8_t src_len, dst_len;
|
|
gnrc_pktsnip_t *snip;
|
|
gnrc_netif_hdr_t *hdr;
|
|
|
|
/* figure out address sizes */
|
|
tmp = mhr[1] & IEEE802154_FCF_SRC_ADDR_MASK;
|
|
|
|
if (tmp == IEEE802154_FCF_SRC_ADDR_SHORT) {
|
|
src_len = 2;
|
|
}
|
|
else if (tmp == IEEE802154_FCF_SRC_ADDR_LONG) {
|
|
src_len = 8;
|
|
}
|
|
else if (tmp == 0) {
|
|
src_len = 0;
|
|
}
|
|
else {
|
|
return NULL;
|
|
}
|
|
|
|
tmp = mhr[1] & IEEE802154_FCF_DST_ADDR_MASK;
|
|
|
|
if (tmp == IEEE802154_FCF_DST_ADDR_SHORT) {
|
|
dst_len = 2;
|
|
}
|
|
else if (tmp == IEEE802154_FCF_DST_ADDR_LONG) {
|
|
dst_len = 8;
|
|
}
|
|
else if (tmp == 0) {
|
|
dst_len = 0;
|
|
}
|
|
else {
|
|
return NULL;
|
|
}
|
|
|
|
/* allocate space for header */
|
|
snip = gnrc_pktbuf_add(NULL, NULL, sizeof(gnrc_netif_hdr_t) + src_len + dst_len,
|
|
GNRC_NETTYPE_NETIF);
|
|
|
|
if (snip == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
/* fill header */
|
|
hdr = (gnrc_netif_hdr_t *)snip->data;
|
|
gnrc_netif_hdr_init(hdr, src_len, dst_len);
|
|
|
|
if (dst_len > 0) {
|
|
tmp = 5 + dst_len;
|
|
addr = gnrc_netif_hdr_get_dst_addr(hdr);
|
|
|
|
for (int i = 0; i < dst_len; i++) {
|
|
addr[i] = mhr[5 + (dst_len - i) - 1];
|
|
}
|
|
}
|
|
else {
|
|
tmp = 3;
|
|
}
|
|
|
|
if (!(mhr[0] & IEEE802154_FCF_PAN_COMP)) {
|
|
tmp += 2;
|
|
}
|
|
|
|
if (src_len > 0) {
|
|
addr = gnrc_netif_hdr_get_src_addr(hdr);
|
|
|
|
for (int i = 0; i < src_len; i++) {
|
|
addr[i] = mhr[tmp + (src_len - i) - 1];
|
|
}
|
|
}
|
|
|
|
return snip;
|
|
}
|
|
|
|
void _receive_data(kw2xrf_t *dev)
|
|
{
|
|
size_t pkt_len, hdr_len;
|
|
gnrc_pktsnip_t *hdr, *payload = NULL;
|
|
gnrc_netif_hdr_t *netif;
|
|
|
|
/* get size of the received packet */
|
|
pkt_len = kw2xrf_read_dreg(MKW2XDM_RX_FRM_LEN);
|
|
|
|
/* read PSDU */
|
|
kw2xrf_read_fifo(dev->buf, pkt_len + 1);
|
|
|
|
/* abort here already if no event callback is registered */
|
|
if (!dev->event_cb) {
|
|
return;
|
|
}
|
|
|
|
/* If RAW-mode is selected direclty forward pkt, MAC does the rest */
|
|
if (dev->option & KW2XRF_OPT_RAWDUMP) {
|
|
payload = gnrc_pktbuf_add(NULL, NULL, pkt_len, GNRC_NETTYPE_UNDEF);
|
|
|
|
if (payload == NULL) {
|
|
DEBUG("kw2xf: error: unable to allocate RAW data\n");
|
|
return;
|
|
}
|
|
|
|
payload->data = dev->buf;
|
|
dev->event_cb(NETDEV_EVENT_RX_COMPLETE, payload);
|
|
return;
|
|
}
|
|
|
|
/* get FCF field and compute 802.15.4 header length */
|
|
hdr_len = _get_frame_hdr_len(dev->buf);
|
|
|
|
if (hdr_len == 0) {
|
|
DEBUG("kw2xrf error: unable parse incoming frame header\n");
|
|
return;
|
|
}
|
|
|
|
/* read the rest of the header and parse the netif header from it */
|
|
hdr = _make_netif_hdr(dev->buf);
|
|
|
|
if (hdr == NULL) {
|
|
DEBUG("kw2xrf error: unable to allocate netif header\n");
|
|
return;
|
|
}
|
|
|
|
/* fill missing fields in netif header */
|
|
netif = (gnrc_netif_hdr_t *)hdr->data;
|
|
netif->if_pid = thread_getpid();
|
|
netif->lqi = dev->buf[pkt_len];
|
|
/* lqi and rssi are directly related to each other in the kw2x-device.
|
|
* The rssi-unit is dBm and in this case alwaysnegative, nevertheless
|
|
* a positive value is reported.
|
|
*/
|
|
netif->rssi = -((netif->lqi) - 286.6) / 2.69333;
|
|
|
|
payload = gnrc_pktbuf_add(hdr, (void *) & (dev->buf[hdr_len]),
|
|
pkt_len - hdr_len - 2, dev->proto);
|
|
|
|
if (payload == NULL) {
|
|
DEBUG("kw2xrf: ERROR allocating payload in packet buffer on RX\n");
|
|
gnrc_pktbuf_release(hdr);
|
|
return;
|
|
}
|
|
|
|
dev->event_cb(NETDEV_EVENT_RX_COMPLETE, payload);
|
|
}
|
|
|
|
void kw2xrf_isr_event(gnrc_netdev_t *netdev, uint32_t event_type)
|
|
{
|
|
kw2xrf_t *dev = (kw2xrf_t *)netdev;
|
|
uint8_t irqst1 = kw2xrf_read_dreg(MKW2XDM_IRQSTS1);
|
|
uint8_t irqst2 = kw2xrf_read_dreg(MKW2XDM_IRQSTS2);
|
|
|
|
if ((irqst1 & MKW2XDM_IRQSTS1_RXIRQ) && (irqst1 & MKW2XDM_IRQSTS1_SEQIRQ)) {
|
|
/* RX */
|
|
DEBUG("kw2xrf: RX Int\n");
|
|
_receive_data(dev);
|
|
kw2xrf_set_sequence(dev, XCVSEQ_RECEIVE);
|
|
kw2xrf_write_dreg(MKW2XDM_IRQSTS1, MKW2XDM_IRQSTS1_RXIRQ | MKW2XDM_IRQSTS1_SEQIRQ);
|
|
}
|
|
else if ((irqst1 & MKW2XDM_IRQSTS1_TXIRQ) && (irqst1 & MKW2XDM_IRQSTS1_SEQIRQ)) {
|
|
/* TX_Complete */
|
|
/* Device is automatically in Radio-idle state when TX is done */
|
|
kw2xrf_set_sequence(dev, XCVSEQ_RECEIVE);
|
|
DEBUG("kw2xrf: TX Complete\n");
|
|
kw2xrf_write_dreg(MKW2XDM_IRQSTS1, MKW2XDM_IRQSTS1_TXIRQ | MKW2XDM_IRQSTS1_SEQIRQ);
|
|
}
|
|
else if ((irqst1 & MKW2XDM_IRQSTS1_CCAIRQ) && (irqst1 & MKW2XDM_IRQSTS1_SEQIRQ)) {
|
|
/* TX_Started (CCA_done) */
|
|
if (irqst2 & MKW2XDM_IRQSTS2_CCA) {
|
|
DEBUG("kw2xrf: CCA done -> Channel busy\n");
|
|
}
|
|
else {
|
|
DEBUG("kw2xrf: CCA done -> Channel idle\n");
|
|
}
|
|
|
|
kw2xrf_write_dreg(MKW2XDM_IRQSTS1, MKW2XDM_IRQSTS1_CCAIRQ | MKW2XDM_IRQSTS1_SEQIRQ);
|
|
kw2xrf_set_sequence(dev, XCVSEQ_RECEIVE);
|
|
}
|
|
else {
|
|
/* Unknown event */
|
|
/* Clear all interrupts to prevent ISR-loop */
|
|
kw2xrf_write_dreg(MKW2XDM_IRQSTS1, 0x7f);
|
|
kw2xrf_write_dreg(MKW2XDM_IRQSTS2, 0x03);
|
|
kw2xrf_write_dreg(MKW2XDM_IRQSTS3, 0xff);
|
|
DEBUG("kw2xrf_isr_event: unknown Interrupt\n");
|
|
kw2xrf_set_sequence(dev, XCVSEQ_RECEIVE);
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* TODO: Move to gnrc_ieee802.15.4 as soon as ready */
|
|
int _assemble_tx_buf(kw2xrf_t *dev, gnrc_pktsnip_t *pkt)
|
|
{
|
|
gnrc_netif_hdr_t *hdr;
|
|
int index = 4;
|
|
|
|
if (dev == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
/* get netif header check address length */
|
|
hdr = (gnrc_netif_hdr_t *)pkt->data;
|
|
|
|
/* we are building a data frame here */
|
|
dev->buf[1] = IEEE802154_FCF_TYPE_DATA;
|
|
dev->buf[2] = IEEE802154_FCF_VERS_V0;
|
|
|
|
/* if AUTOACK is enabled, then we also expect ACKs for this packet */
|
|
if (!(hdr->flags & GNRC_NETIF_HDR_FLAGS_BROADCAST) &&
|
|
!(hdr->flags & GNRC_NETIF_HDR_FLAGS_MULTICAST) &&
|
|
(dev->option & KW2XRF_OPT_AUTOACK)) {
|
|
dev->buf[1] |= IEEE802154_FCF_ACK_REQ;
|
|
}
|
|
|
|
/* set destination pan_id */
|
|
dev->buf[index++] = (uint8_t)((dev->radio_pan) & 0xff);
|
|
dev->buf[index++] = (uint8_t)((dev->radio_pan) >> 8);
|
|
|
|
/* fill in destination address */
|
|
if (hdr->flags &
|
|
(GNRC_NETIF_HDR_FLAGS_BROADCAST | GNRC_NETIF_HDR_FLAGS_MULTICAST)) {
|
|
dev->buf[2] |= IEEE802154_FCF_DST_ADDR_SHORT;
|
|
dev->buf[index++] = 0xff;
|
|
dev->buf[index++] = 0xff;
|
|
}
|
|
else if (hdr->dst_l2addr_len == 2) {
|
|
/* set to short addressing mode */
|
|
dev->buf[2] |= IEEE802154_FCF_DST_ADDR_SHORT;
|
|
/* set destination address, byte order is inverted */
|
|
uint8_t *dst_addr = gnrc_netif_hdr_get_dst_addr(hdr);
|
|
dev->buf[index++] = dst_addr[1];
|
|
dev->buf[index++] = dst_addr[0];
|
|
}
|
|
else if (hdr->dst_l2addr_len == 8) {
|
|
/* default to use long address mode for src and dst */
|
|
dev->buf[2] |= IEEE802154_FCF_DST_ADDR_LONG;
|
|
/* set destination address located directly after gnrc_ifhrd_t in memory */
|
|
uint8_t *dst_addr = gnrc_netif_hdr_get_dst_addr(hdr);
|
|
for (int i = 7; i >= 0; i--) {
|
|
dev->buf[index++] = dst_addr[i];
|
|
}
|
|
}
|
|
else {
|
|
return 0;
|
|
}
|
|
|
|
/* fill in source PAN ID (if applicable */
|
|
if (dev->option & KW2XRF_OPT_USE_SRC_PAN) {
|
|
dev->buf[index++] = (uint8_t)((dev->radio_pan) & 0xff);
|
|
dev->buf[index++] = (uint8_t)((dev->radio_pan) >> 8);
|
|
}
|
|
else {
|
|
dev->buf[1] |= IEEE802154_FCF_PAN_COMP;
|
|
}
|
|
|
|
/* insert source address according to length */
|
|
if (hdr->src_l2addr_len == 2) {
|
|
dev->buf[2] |= IEEE802154_FCF_SRC_ADDR_SHORT;
|
|
dev->buf[index++] = (uint8_t)(dev->addr_short[0]);
|
|
dev->buf[index++] = (uint8_t)(dev->addr_short[1]);
|
|
}
|
|
else {
|
|
dev->buf[2] |= IEEE802154_FCF_SRC_ADDR_LONG;
|
|
memcpy(&(dev->buf[index]), dev->addr_long, 8);
|
|
index += 8;
|
|
}
|
|
/* set sequence number */
|
|
dev->buf[3] = dev->seq_nr++;
|
|
|
|
/* return header size */
|
|
return index;
|
|
}
|
|
|
|
int kw2xrf_send(gnrc_netdev_t *netdev, gnrc_pktsnip_t *pkt)
|
|
{
|
|
int index = 0;
|
|
kw2xrf_t *dev = (kw2xrf_t *) netdev;
|
|
|
|
if ((dev->option & KW2XRF_OPT_PRELOADING) == NETOPT_DISABLE) {
|
|
kw2xrf_set_sequence(dev, XCVSEQ_TRANSMIT);
|
|
}
|
|
|
|
if (pkt == NULL) {
|
|
return -ENOMSG;
|
|
}
|
|
|
|
gnrc_pktsnip_t *payload = pkt->next;
|
|
|
|
if (netdev == NULL) {
|
|
gnrc_pktbuf_release(pkt);
|
|
return -ENODEV;
|
|
}
|
|
|
|
if (pkt->type == GNRC_NETTYPE_NETIF) {
|
|
/* Build header and fills this already into the tx-buf */
|
|
index = _assemble_tx_buf(dev, pkt);
|
|
if (index == 0) {
|
|
DEBUG("Unable to create 802.15.4 header\n");
|
|
gnrc_pktbuf_release(pkt);
|
|
return -ENOMSG;
|
|
}
|
|
DEBUG("Assembled header for GNRC_NETTYPE_UNDEF to tx-buf, index: %i\n", index);
|
|
}
|
|
else if (pkt->type == GNRC_NETTYPE_UNDEF) {
|
|
/* IEEE packet is already included in the header,
|
|
* no need to build the header manually */
|
|
DEBUG("Incoming packet of type GNRC_NETTYPE_802154: %i\n", index);
|
|
DEBUG("size of pktsnip: %i\n", pkt->size);
|
|
|
|
for (int i = 0; i < pkt->size; i++) {
|
|
uint8_t *tmp = pkt->data;
|
|
dev->buf[index + i + 1] = tmp[i];
|
|
}
|
|
|
|
/* count bytes */
|
|
index += pkt->size;
|
|
}
|
|
else {
|
|
DEBUG("Driver does not support this type of packet\n");
|
|
return -ENOTSUP;
|
|
}
|
|
|
|
while (payload) {
|
|
/* check we don't exceed FIFO size */
|
|
if (index + 2 + payload->size > KW2XRF_MAX_PKT_LENGTH) {
|
|
gnrc_pktbuf_release(pkt);
|
|
DEBUG("Packet exceeded FIFO size.\n");
|
|
return -ENOBUFS;
|
|
}
|
|
|
|
for (int i = 0; i < payload->size; i++) {
|
|
uint8_t *tmp = payload->data;
|
|
dev->buf[index + i] = tmp[i];
|
|
}
|
|
|
|
/* count bytes */
|
|
index += payload->size;
|
|
|
|
/* next snip */
|
|
payload = payload->next;
|
|
}
|
|
|
|
dev->buf[0] = index + 1; /* set packet size */
|
|
|
|
gnrc_pktbuf_release(pkt);
|
|
DEBUG("kw2xrf: packet with size %i loaded to tx_buf\n", dev->buf[0]);
|
|
kw2xrf_write_fifo(dev->buf, dev->buf[0]);
|
|
|
|
if ((dev->option & KW2XRF_OPT_PRELOADING) == NETOPT_DISABLE) {
|
|
DEBUG("kw2xrf: Sending now.\n");
|
|
kw2xrf_set_sequence(dev, XCVSEQ_TRANSMIT);
|
|
}
|
|
|
|
return index;
|
|
}
|
|
|
|
/* implementation of the netdev interface */
|
|
const gnrc_netdev_driver_t kw2xrf_driver = {
|
|
.send_data = kw2xrf_send,
|
|
.add_event_callback = kw2xrf_add_cb,
|
|
.rem_event_callback = kw2xrf_rem_cb,
|
|
.get = kw2xrf_get,
|
|
.set = kw2xrf_set,
|
|
.isr_event = kw2xrf_isr_event,
|
|
};
|