/* * 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 * @author Jonas Remmert * @author Oliver Hahm * @} */ #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) { 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_IN, 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, IEEE802154_SHORT_ADDRESS_LEN); return sizeof(uint16_t); } int kw2xrf_set_addr_long(kw2xrf_t *dev, uint64_t addr) { for (int i = 0; i < IEEE802154_LONG_ADDRESS_LEN; i++) { dev->addr_long[i] = (addr >> ((IEEE802154_LONG_ADDRESS_LEN - 1 - i) * 8)); } kw2xrf_write_iregs(MKW2XDMI_MACLONGADDRS0_0, (dev->addr_long), IEEE802154_LONG_ADDRESS_LEN); 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 /* make sure that the buffer is always big enough to store a 64bit value */ # if CPUID_LEN < IEEE802154_LONG_ADDRESS_LEN uint8_t cpuid[IEEE802154_LONG_ADDRESS_LEN]; # else uint8_t cpuid[CPUID_LEN]; #endif uint16_t addr_short = 0; 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 /* in case CPUID_LEN < 8, fill missing bytes with zeros */ memset(cpuid, 0, CPUID_LEN); cpuid_get(cpuid); /* generate short hardware address if CPUID_LEN > 0 */ for (int i = 0; i < CPUID_LEN; i++) { /* XOR each even byte of the CPUID with LSB of short address and each odd byte with MSB */ addr_short ^= (uint16_t)(cpuid[i] << ((i & 0x01) * 8)); } #if CPUID_LEN > IEEE802154_LONG_ADDRESS_LEN for (int i = IEEE802154_LONG_ADDRESS_LEN; 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, IEEE802154_LONG_ADDRESS_LEN); kw2xrf_set_addr_long(dev, NTOHLL(addr_long.uint64.u64)); kw2xrf_set_addr(dev, addr_short); #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 < IEEE802154_LONG_ADDRESS_LEN; i++) { ap[i] = dev->addr_long[IEEE802154_LONG_ADDRESS_LEN - 1 - 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) = IEEE802154_LONG_ADDRESS_LEN; } else { *((uint16_t *)value) = IEEE802154_SHORT_ADDRESS_LEN; } 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, IEEE802154_LONG_ADDRESS_LEN); } else { uint16_t addr = kw2xrf_get_addr_short(dev); ieee802154_get_iid(value, (uint8_t *)&addr, IEEE802154_SHORT_ADDRESS_LEN); } 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) == IEEE802154_SHORT_ADDRESS_LEN) { kw2xrf_set_option(dev, KW2XRF_OPT_SRC_ADDR_LONG, false); } else if (*((uint16_t *)value) == IEEE802154_LONG_ADDRESS_LEN) { 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; } /* correct value if out of valid range */ if (*(int16_t *)value > MKW2XDRF_OUTPUT_POWER_MAX) { *(int16_t *)value = MKW2XDRF_OUTPUT_POWER_MAX; } else if (*(int16_t *)value < MKW2XDRF_OUTPUT_POWER_MIN) { *(int16_t *)value = MKW2XDRF_OUTPUT_POWER_MIN; } kw2xrf_set_tx_power(dev, (int8_t *)value, value_len); dev->tx_power = *((uint16_t *)value); 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 + IEEE802154_SHORT_ADDRESS_LEN); } else if (tmp == IEEE802154_FCF_SRC_ADDR_LONG) { return (len + IEEE802154_LONG_ADDRESS_LEN); } } 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 = IEEE802154_SHORT_ADDRESS_LEN; } else if (tmp == IEEE802154_FCF_SRC_ADDR_LONG) { src_len = IEEE802154_LONG_ADDRESS_LEN; } 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 = IEEE802154_SHORT_ADDRESS_LEN; } else if (tmp == IEEE802154_FCF_DST_ADDR_LONG) { dst_len = IEEE802154_LONG_ADDRESS_LEN; } 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 directly 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 always negative, 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 == IEEE802154_SHORT_ADDRESS_LEN) { /* 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 == IEEE802154_LONG_ADDRESS_LEN) { /* 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 = IEEE802154_LONG_ADDRESS_LEN - 1; 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 == IEEE802154_SHORT_ADDRESS_LEN) { 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, IEEE802154_LONG_ADDRESS_LEN); index += IEEE802154_LONG_ADDRESS_LEN; } /* 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, };