diff --git a/drivers/Makefile.include b/drivers/Makefile.include index b7de874d6..b4c590257 100644 --- a/drivers/Makefile.include +++ b/drivers/Makefile.include @@ -16,6 +16,9 @@ endif ifneq (,$(filter ng_at86rf2xx,$(USEMODULE))) USEMODULE_INCLUDES += $(RIOTBASE)/drivers/ng_at86rf2xx/include endif +ifneq (,$(filter kw2xrf,$(USEMODULE))) + USEMODULE_INCLUDES += $(RIOTBASE)/drivers/kw2xrf/include +endif ifneq (,$(filter isl29020,$(USEMODULE))) USEMODULE_INCLUDES += $(RIOTBASE)/drivers/isl29020/include endif diff --git a/drivers/include/kw2xrf.h b/drivers/include/kw2xrf.h new file mode 100644 index 000000000..61313b5b4 --- /dev/null +++ b/drivers/include/kw2xrf.h @@ -0,0 +1,147 @@ +/* + * 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. + */ + +/** + * @defgroup drivers_kw2xrf kw2x radio-driver + * @ingroup drivers + * @brief Device driver for the Freescale KW2xD radio + * @{ + * + * @file + * @brief Interface definition for the KW2xD device driver + * + * @author Johann Fischer + * @author Jonas Remmert + */ + +#ifndef MKW2XDRF_H_ +#define MKW2XDRF_H_ + +#include + +#include "board.h" +#include "periph/spi.h" +#include "periph/gpio.h" +#include "net/ng_netdev.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Maximum packet length + */ +#define KW2XRF_MAX_PKT_LENGTH (127U) + +/** + * @brief Default protocol for data that is coming in + */ +#ifdef MODULE_NG_SIXLOWPAN +#define KW2XRF_DEFAULT_PROTOCOL NG_NETTYPE_SIXLOWPAN +#else +#define KW2XRF_DEFAULT_PROTOCOL NG_NETTYPE_UNDEF +#endif + +/** + * @brief Default short address used after initialization + */ +#define KW2XRF_DEFAULT_SHORT_ADDR (0x0002) + +/** + * @brief Default short address used after initialization + */ +#define KW2XRF_DEFAULT_ADDR_LONG (0x0000000000000000) + +/** + * @brief Default PAN ID used after initialization + */ +#define KW2XRF_DEFAULT_PANID (0x0023) + +/** + * @brief Default channel used after initialization + */ +#define KW2XRF_DEFAULT_CHANNEL (17U) + +/** + * @brief Default TX_POWER in dbm used after initialization + */ +#define KW2XRF_DEFAULT_TX_POWER (0) + +/** + * @brief Maximum output power of the kw2x device in dBm + */ +#define MKW2XDRF_OUTPUT_POWER_MAX (8) + +/** + * @brief Minimum output power of the kw2x device in dBm + */ +#define MKW2XDRF_OUTPUT_POWER_MIN (-35) + +/** + * @brief Internal device option flags + * @{ + */ +#define KW2XRF_OPT_AUTOACK (0x0001) /**< auto ACKs active */ +#define KW2XRF_OPT_CSMA (0x0002) /**< CSMA active */ +#define KW2XRF_OPT_PROMISCUOUS (0x0004) /**< promiscuous mode active */ +#define KW2XRF_OPT_PRELOADING (0x0008) /**< preloading enabled */ +#define KW2XRF_OPT_TELL_TX_START (0x0010) /**< notify MAC layer on TX start */ +#define KW2XRF_OPT_TELL_TX_END (0x0020) /**< notify MAC layer on TX finished */ +#define KW2XRF_OPT_TELL_RX_START (0x0040) /**< notify MAC layer on RX start */ +#define KW2XRF_OPT_TELL_RX_END (0x0080) /**< notify MAC layer on RX finished */ +#define KW2XRF_OPT_RAWDUMP (0x0100) /**< pass RAW frame data to upper layer */ +#define KW2XRF_OPT_SRC_ADDR_LONG (0x0200) /**< send data using long source address */ +#define KW2XRF_OPT_USE_SRC_PAN (0x0400) /**< do not compress source PAN ID */ +/** @} */ + +/** + * @brief kw2xrf device descriptor + */ +typedef struct { + /* netdev fields */ + ng_netdev_driver_t const *driver; /**< Pointer to the devices interface */ + ng_netdev_event_cb_t event_cb; /**< Netdev event callback */ + kernel_pid_t mac_pid; /**< The driver's thread's PID */ + /* driver specific fields */ + uint8_t buf[KW2XRF_MAX_PKT_LENGTH]; /**< Buffer for incoming or outgoing packets */ + ng_netconf_state_t state; /**< Variable to keep radio driver's state */ + uint8_t seq_nr; /**< Next packets sequence number */ + uint16_t radio_pan; /**< The PAN the radio device is using */ + uint8_t radio_channel; /**< The channel the radio device is using */ + uint8_t addr_short[2]; /**< The short address the radio device is using */ + uint8_t addr_long[8]; /**< The long address the radio device is using */ + uint16_t option; /**< Bit field to save enable/disable options */ + int8_t tx_power; /**< The current tx-power setting of the device */ + ng_nettype_t proto; /**< Protocol the interface speaks */ +} kw2xrf_t; + +/** + * @brief Initialize the given KW2XRF device + * @param[out] dev device descriptor + * @param[in] spi SPI bus the device is connected to + * @param[in] spi_speed SPI speed to use + * @param[in] cs_pin GPIO pin connected to chip select + * @param[in] int_pin GPIO pin connected to the interrupt pin + * + * @return 0 on success + * @return <0 on error + */ +int kw2xrf_init(kw2xrf_t *dev, spi_t spi, spi_speed_t spi_speed, + gpio_t cs_pin, gpio_t int_pin); + +/** + * @brief Reference to the KW2XRF driver interface + */ +extern const ng_netdev_driver_t kw2xrf_driver; + +#ifdef __cplusplus +} +#endif + +#endif +/** @} */ diff --git a/drivers/kw2xrf/Makefile b/drivers/kw2xrf/Makefile new file mode 100644 index 000000000..48422e909 --- /dev/null +++ b/drivers/kw2xrf/Makefile @@ -0,0 +1 @@ +include $(RIOTBASE)/Makefile.base diff --git a/drivers/kw2xrf/include/kw2xrf_reg.h b/drivers/kw2xrf/include/kw2xrf_reg.h new file mode 100644 index 000000000..3fa1b0f7b --- /dev/null +++ b/drivers/kw2xrf/include/kw2xrf_reg.h @@ -0,0 +1,356 @@ +/* + * 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 drivers_kw2xrf + * @{ + * + * @file + * @brief Register and command definitions for the MKW2XD Modem + * + * @author Johann Fischer + */ + +#ifndef MKW2XD_MODEM_REG_H +#define MKW2XD_MODEM_REG_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** Option to for SPI-IF to read a register */ +#define MKW2XDRF_REG_READ (uint8_t)(1 << 7) + +/** Option to for SPI-IF to write a register */ +#define MKW2XDRF_REG_WRITE (uint8_t)(0) + +/** Option to for SPI-IF to read data from the RX/TX-Buffer */ +#define MKW2XDRF_BUF_READ (uint8_t)(1 << 7 | 1 << 6) + +/** Option to for SPI-IF to write data to RX/TX-Buffer */ +#define MKW2XDRF_BUF_WRITE (uint8_t)(1 << 6) + +/** Option for SPI-IF */ +#define MKW2XDRF_BUF_BYTE_READ (uint8_t)(1 << 7 | 1 << 6 | 1 << 5) + +/** Option for SPI-IF */ +#define MKW2XDRF_BUF_BYTE_WRITE (uint8_t)(1 << 6 | 1 << 5) + +/** Transceiver Sequence Selector, define Values for XCVSEQ */ +typedef enum { + XCVSEQ_IDLE = 0, + XCVSEQ_RECEIVE, + XCVSEQ_TRANSMIT, + XCVSEQ_CCA, + XCVSEQ_TX_RX, + XCVSEQ_CONTINUOUS_CCA, +} +kw2xrf_physeq_t; + +enum mkw2xdrf_dregister { + MKW2XDM_IRQSTS1 = 0x0, /**< Interrupt Request Status 1 */ + MKW2XDM_IRQSTS2 = 0x1, /**< Interrupt Request Status 2 */ + MKW2XDM_IRQSTS3 = 0x2, /**< Interrupt Request Status 3 */ + MKW2XDM_PHY_CTRL1 = 0x3, /**< PHY Control 1 */ + MKW2XDM_PHY_CTRL2 = 0x4, /**< PHY Control 2 */ + MKW2XDM_PHY_CTRL3 = 0x5, /**< PHY Control 3 */ + MKW2XDM_RX_FRM_LEN = 0x6, /**< Receive Frame Length */ + MKW2XDM_PHY_CTRL4 = 0x7, /**< PHY Control 4 */ + MKW2XDM_SRC_CTRL = 0x8, /**< SRC Control */ + MKW2XDM_SRC_ADDRS_SUM_LSB = 0x9, /**< SRC Address SUM LSB */ + MKW2XDM_SRC_ADDRS_SUM_MSB = 0xa, /**< SRC Address SUM MSB */ + MKW2XDM_CCA1_ED_FNL = 0xb, /**< CCA1 ED FNL */ + MKW2XDM_EVENT_TIMER_LSB = 0xc, /**< Event Timer LSB */ + MKW2XDM_EVENT_TIMER_MSB = 0xd, /**< Event Timer MSB */ + MKW2XDM_EVENT_TIMER_USB = 0xe, /**< Event Timer USB */ + MKW2XDM_TIMESTAMP_LSB = 0xf, /**< Timestamp LSB */ + MKW2XDM_TIMESTAMP_MSB = 0x10, /**< Timestamp MSB */ + MKW2XDM_TIMESTAMP_USB = 0x11, /**< Timestamp USB */ + MKW2XDM_T3CMP_LSB = 0x12, /**< Timer 3 Compare Value LSB */ + MKW2XDM_T3CMP_MSB = 0x13, /**< Timer 3 Compare Value MSB */ + MKW2XDM_T3CMP_USB = 0x14, /**< Timer 3 Compare Value USB */ + MKW2XDM_T2PRIMECMP_LSB = 0x15, /**< Timer 2-Prime Compare Value LSB */ + MKW2XDM_T2PRIMECMP_MSB = 0x16, /**< Timer 2-Prime Compare Value MSB */ + MKW2XDM_T1CMP_LSB = 0x17, /**< Timer 1 Compare Value LSB */ + MKW2XDM_T1CMP_MSB = 0x18, /**< Timer 1 Compare Value MSB */ + MKW2XDM_T1CMP_USB = 0x19, /**< Timer 1 Compare Value USB */ + MKW2XDM_T2CMP_LSB = 0x1a, /**< Timer 2 Compare Value LSB */ + MKW2XDM_T2CMP_MSB = 0x1b, /**< Timer 2 Compare Value MSB */ + MKW2XDM_T2CMP_USB = 0x1c, /**< Timer 2 Compare Value USB */ + MKW2XDM_T4CMP_LSB = 0x1d, /**< Timer 4 Compare Value LSB */ + MKW2XDM_T4CMP_MSB = 0x1e, /**< Timer 4 Compare Value MSB */ + MKW2XDM_T4CMP_USB = 0x1f, /**< Timer 4 Compare Value USB */ + MKW2XDM_PLL_INT0 = 0x20, /**< PLL Integer Value for PAN0 */ + MKW2XDM_PLL_FRAC0_LSB = 0x21, /**< PLL Frequency Fractional Value for PAN0 */ + MKW2XDM_PLL_FRAC0_MSB = 0x22, /**< PLL Frequency Fractional Value for PAN0 */ + MKW2XDM_PA_PWR = 0x23, /**< PA Power Control (PA_PWR) */ + MKW2XDM_SEQ_STATE = 0x24, /**< Sequence Manager State */ + MKW2XDM_LQI_VALUE = 0x25, /**< Link Quality Indicator */ + MKW2XDM_RSSI_CCA_CNT = 0x26, /**< RSSI CCA CNT */ + MKW2XDM_OVERWRITE_VER = 0x3b, /**< Overwrite Version Number */ + MKW2XDM_CLK_OUT_CTRL = 0x3c, /**< CLK_OUT Control */ + MKW2XDM_PWR_MODES = 0x3d, /**< Power Modes */ + MKW2XDM_IAR_INDEX = 0x3e, /**< IAR Index */ + MKW2XDM_IAR_DATA = 0x3f, /**< IAR Data */ +}; + +#define MKW2XDM_IRQSTS1_RX_FRM_PEND (1 << 7) +#define MKW2XDM_IRQSTS1_PLL_UNLOCK_IRQ (1 << 6) +#define MKW2XDM_IRQSTS1_FILTERFAIL_IRQ (1 << 5) +#define MKW2XDM_IRQSTS1_RXWTRMRKIRQ (1 << 4) +#define MKW2XDM_IRQSTS1_CCAIRQ (1 << 3) +#define MKW2XDM_IRQSTS1_RXIRQ (1 << 2) +#define MKW2XDM_IRQSTS1_TXIRQ (1 << 1) +#define MKW2XDM_IRQSTS1_SEQIRQ (1 << 0) + +#define MKW2XDM_IRQSTS2_CRCVALID (1 << 7) +#define MKW2XDM_IRQSTS2_CCA (1 << 6) +#define MKW2XDM_IRQSTS2_SRCADDR (1 << 5) +#define MKW2XDM_IRQSTS2_PI (1 << 4) +#define MKW2XDM_IRQSTS2_TMRSTATUS (1 << 3) +#define MKW2XDM_IRQSTS2_PB_ERR_IRQ (1 << 1) +#define MKW2XDM_IRQSTS2_WAKE_IRQ (1 << 0) + +#define MKW2XDM_IRQSTS3_TMR4MSK (1 << 7) +#define MKW2XDM_IRQSTS3_TMR3MSK (1 << 6) +#define MKW2XDM_IRQSTS3_TMR2MSK (1 << 5) +#define MKW2XDM_IRQSTS3_TMR1MSK (1 << 4) +#define MKW2XDM_IRQSTS3_TMR4IRQ (1 << 3) +#define MKW2XDM_IRQSTS3_TMR3IRQ (1 << 2) +#define MKW2XDM_IRQSTS3_TMR2IRQ (1 << 1) +#define MKW2XDM_IRQSTS3_TMR1IRQ (1 << 0) + +#define MKW2XDM_PHY_CTRL1_TMRTRIGEN (1 << 7) +#define MKW2XDM_PHY_CTRL1_SLOTTED (1 << 6) +#define MKW2XDM_PHY_CTRL1_CCABFRTX (1 << 5) +#define MKW2XDM_PHY_CTRL1_RXACKRQD (1 << 4) +#define MKW2XDM_PHY_CTRL1_AUTOACK (1 << 3) +#define MKW2XDM_PHY_CTRL1_XCVSEQ_MASK 0x03u +#define MKW2XDM_PHY_CTRL1_XCVSEQ(x) (((uint8_t)(((uint8_t)(x))<<0))&MKW2XDM_PHY_CTRL1_XCVSEQ_MASK) + +#define MKW2XDM_PHY_CTRL2_CRC_MSK (1 << 7) +#define MKW2XDM_PHY_CTRL2_PLL_UNLOCK_MSK (1 << 6) +#define MKW2XDM_PHY_CTRL2_FILTERFAIL_MSK (1 << 5) +#define MKW2XDM_PHY_CTRL2_RX_WMRK_MSK (1 << 4) +#define MKW2XDM_PHY_CTRL2_CCAMSK (1 << 3) +#define MKW2XDM_PHY_CTRL2_RXMSK (1 << 2) +#define MKW2XDM_PHY_CTRL2_TXMSK (1 << 1) +#define MKW2XDM_PHY_CTRL2_SEQMSK (1 << 0) + +#define MKW2XDM_PHY_CTRL3_TMR4CMP_EN (1 << 7) +#define MKW2XDM_PHY_CTRL3_TMR3CMP_EN (1 << 6) +#define MKW2XDM_PHY_CTRL3_TMR2CMP_EN (1 << 5) +#define MKW2XDM_PHY_CTRL3_TMR1CMP_EN (1 << 4) +#define MKW2XDM_PHY_CTRL3_PB_ERR_MSK (1 << 1) +#define MKW2XDM_PHY_CTRL3_WAKE_MSK (1 << 0) + +#define MKW2XDM_RX_FRM_LENGTH_MASK 0x7Fu + +#define MKW2XDM_PHY_CTRL4_TRCV_MSK (1 << 7) +#define MKW2XDM_PHY_CTRL4_TC3TMOUT (1 << 6) +#define MKW2XDM_PHY_CTRL4_PANCORDNTR0 (1 << 5) +#define MKW2XDM_PHY_CTRL4_CCATYPE_MASK 0x18u +#define MKW2XDM_PHY_CTRL4_CCATYPE_SHIFT 3 +#define MKW2XDM_PHY_CTRL4_CCATYPE(x) (((uint8_t)(((uint8_t)(x))< + */ +#ifndef KW2XRF_SPI_H +#define KW2XRF_SPI_H + +#include +#include "board.h" +#include "cpu.h" +#include "periph/spi.h" +#include "periph_conf.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief SPI interface initialization + * @param[in] spi SPI bus the device is connected to + * @param[in] spi_speed SPI speed to use + * @param[in] cs_pin GPIO pin connected to chip select + * + * @return 0 on success + * @return -1 on error + */ +int kw2xrf_spi_init(spi_t spi, spi_speed_t spi_speed, + gpio_t cs_pin); + +/** + * @brief Writes a byte to the kw2xrf register. + * + * @param[in] addr Address of the register to write. + * @param[in] value The value to write in the register. + */ +void kw2xrf_write_dreg(uint8_t addr, uint8_t value); + +/** + * @brief Reads a byte from the kw2xrf register. + * + * @param[in] addr Address of the register to read. + * @return Value of the register. + */ +uint8_t kw2xrf_read_dreg(uint8_t addr); + +/** + * @brief Writes to a byte from the kw2xrf indirect register. + * + * @param[in] addr Address of the register to write into. + * @param[in] value Value that shall be written. + */ +void kw2xrf_write_ireg(uint8_t addr, uint8_t value); + +/** + * @brief Reads a byte from the kw2xrf indirect register. + * + * @param[in] addr Address of the register to read. + * + * @return value in the register + */ +uint8_t kw2xrf_read_ireg(uint8_t addr); + +/** + * @brief Writes to kw2xrf indirect registers. + * + * @param[in] addr Address of the register to write into. + * @param[in] buf Value that shall be written. + * @param[in] length Length of the register. + */ +void kw2xrf_write_iregs(uint8_t addr, uint8_t *buf, uint8_t length); + +/** + * @brief Reads a byte from the kw2xrf indirect register. + * + * @param[in] addr Address of the register to read. + * @param[in] buf Buffer, where the content of the reg shall be written to. + * @param[in] length Length of the register. + */ +void kw2xrf_read_iregs(uint8_t addr, uint8_t *buf, uint8_t length); + +/** + * @brief Writes multiple bytes to the kw2xrf fifo. + * + * @param[in] data A buffer with the value to write to the fifo. + * @param[in] data_length The count of bytes which should be written. + * + * @return number of bytes written. + */ +void kw2xrf_write_fifo(uint8_t *data, uint8_t data_length); + +/** + * @brief Reads multiple bytes from the kw2xrf fifo. + * + * @param[out] data A buffer to store the value of the fifo. + * @param[in] data_length The count of bytes which should be read. + * + * @return number of bytes read. + */ +void kw2xrf_read_fifo(uint8_t *data, uint8_t data_length); + +#ifdef __cplusplus +} +#endif + +#endif +/** @} */ diff --git a/drivers/kw2xrf/kw2xrf.c b/drivers/kw2xrf/kw2xrf.c new file mode 100644 index 000000000..b7cff7c3c --- /dev/null +++ b/drivers/kw2xrf/kw2xrf.c @@ -0,0 +1,1173 @@ +/* + * 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 kw2xrf.c + * @brief Basic functionality of kw2xrf driver + * + * @author Johann Fischer + * @author Jonas Remmert + * @} + */ +#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 "net/ng_netbase.h" +#include "net/ng_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 */ + +/* 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 +}; + +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(-EBUSY, "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 = NETCONF_STATE_SLEEP; + break; + + case XCVSEQ_RECEIVE: + dev->state = NETCONF_STATE_IDLE; + break; + + case XCVSEQ_TRANSMIT: + dev->state = NETCONF_STATE_TX; + break; + + case XCVSEQ_CCA: + dev->state = NETCONF_STATE_TX; + break; + + case XCVSEQ_TX_RX: + dev->state = NETCONF_STATE_TX; + break; + + case XCVSEQ_CONTINUOUS_CCA: + dev->state = NETCONF_STATE_TX; + break; + + default: + DEBUG("kw2xrf: undefined state assigned to phy\n"); + dev->state = NETCONF_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 = NG_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(ng_nettype_t)) { + return -EOVERFLOW; + } + + memcpy(val, &(dev->proto), sizeof(ng_nettype_t)); + return sizeof(ng_nettype_t); +} + +int kw2xrf_set_proto(kw2xrf_t *dev, uint8_t *val, size_t len) +{ + if (len != sizeof(ng_nettype_t)) { + return -EINVAL; + } + + memcpy(&(dev->proto), val, sizeof(ng_nettype_t)); + return sizeof(ng_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 = NETCONF_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]; + 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; + + /* check device parameters */ + if (dev == NULL) { + return -ENODEV; + } + + kw2xrf_spi_init(spi, spi_speed, cs_pin); + + if (kw2xrf_on(dev) != 0) { + core_panic(0x42, "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; + + /* set default short address */ + uint16_t addr_conv = KW2XRF_DEFAULT_SHORT_ADDR >> 8; + addr_conv |= (KW2XRF_DEFAULT_SHORT_ADDR << 8) & 0xff00; + kw2xrf_set_addr(dev, addr_conv); + + /* set default long address */ + kw2xrf_set_addr_long(dev, KW2XRF_DEFAULT_ADDR_LONG); + + /* 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(ng_netdev_t *dev, ng_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(ng_netdev_t *dev, ng_netdev_event_cb_t cb) +{ + if (dev == NULL) { + return -ENODEV; + } + + if (dev->event_cb != cb) { + return -ENOENT; + } + + dev->event_cb = NULL; + return 0; +} + +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; +} + +int kw2xrf_get(ng_netdev_t *netdev, ng_netconf_opt_t opt, void *value, size_t max_len) +{ + kw2xrf_t *dev = (kw2xrf_t *)netdev; + + if (dev == NULL) { + return -ENODEV; + } + + switch (opt) { + case NETCONF_OPT_ADDRESS: + if (max_len < sizeof(uint16_t)) { + return -EOVERFLOW; + } + + *((uint16_t *)value) = ((dev->addr_short[0] << 8) | dev->addr_short[1]); + return sizeof(uint16_t); + + case NETCONF_OPT_ADDRESS_LONG: + if (max_len < sizeof(uint64_t)) { + return -EOVERFLOW; + } + + *((uint64_t *)value) = kw2xrf_get_addr_long(dev); + return sizeof(uint64_t); + + case NETCONF_OPT_ADDR_LEN: + if (max_len < sizeof(uint16_t)) { + return -EOVERFLOW; + } + + *((uint16_t *)value) = 2; + return sizeof(uint16_t); + + case NETCONF_OPT_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 NETCONF_OPT_NID: + if (max_len < sizeof(uint16_t)) { + return -EOVERFLOW; + } + + *((uint16_t *)value) = dev->radio_pan; + return sizeof(uint16_t); + + case NETCONF_OPT_CHANNEL: + return kw2xrf_get_channel(dev, (uint8_t *)value, max_len); + + case NETCONF_OPT_PROTO: + return kw2xrf_get_proto(dev, (uint8_t *)value, max_len); + + case NETCONF_OPT_STATE: + if (max_len < sizeof(ng_netconf_state_t)) { + return -EOVERFLOW; + } + + *(ng_netconf_state_t *)value = *(ng_netconf_state_t *) & (dev->state); + return 0; + + case NETCONF_OPT_TX_POWER: + if (max_len < 1) { + return -EOVERFLOW; + } + + *(int16_t *)value = dev->tx_power; + return 0; + + case NETCONF_OPT_MAX_PACKET_SIZE: + if (max_len < sizeof(int16_t)) { + return -EOVERFLOW; + } + + *((uint16_t *)value) = KW2XRF_MAX_PKT_LENGTH; + return sizeof(uint16_t); + + case NETCONF_OPT_PRELOADING: + *((ng_netconf_enable_t *)value) = !!(dev->option & KW2XRF_OPT_PRELOADING); + return sizeof(ng_netconf_enable_t); + + case NETCONF_OPT_AUTOACK: + *((ng_netconf_enable_t *)value) = !!(dev->option & KW2XRF_OPT_AUTOACK); + return sizeof(ng_netconf_enable_t); + + case NETCONF_OPT_PROMISCUOUSMODE: + *((ng_netconf_enable_t *)value) = !!(dev->option & KW2XRF_OPT_PROMISCUOUS); + return sizeof(ng_netconf_enable_t); + + case NETCONF_OPT_RAWMODE: + *((ng_netconf_enable_t *)value) = !!(dev->option & KW2XRF_OPT_RAWDUMP); + return sizeof(ng_netconf_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(ng_netdev_t *netdev, ng_netconf_opt_t opt, void *value, size_t value_len) +{ + kw2xrf_t *dev = (kw2xrf_t *)netdev; + + if (dev == NULL) { + return -ENODEV; + } + + switch (opt) { + case NETCONF_OPT_CHANNEL: + return kw2xrf_set_channel(dev, (uint8_t *)value, value_len); + + case NETCONF_OPT_ADDRESS: + if (value_len > sizeof(uint16_t)) { + return -EOVERFLOW; + } + + return kw2xrf_set_addr(dev, *((uint16_t *)value)); + + case NETCONF_OPT_ADDRESS_LONG: + if (value_len > sizeof(uint64_t)) { + return -EOVERFLOW; + } + + return kw2xrf_set_addr_long(dev, *((uint64_t *)value)); + + case NETCONF_OPT_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 NETCONF_OPT_NID: + if (value_len > sizeof(uint16_t)) { + return -EOVERFLOW; + } + + return kw2xrf_set_pan(dev, *((uint16_t *)value)); + + case NETCONF_OPT_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 NETCONF_OPT_PROTO: + return kw2xrf_set_proto(dev, (uint8_t *)value, value_len); + + case NETCONF_OPT_AUTOACK: + /* Set up HW generated automatic ACK after Receive */ + kw2xrf_set_option(dev, KW2XRF_OPT_AUTOACK, + ((bool *)value)[0]); + return sizeof(ng_netconf_enable_t); + + case NETCONF_OPT_PROMISCUOUSMODE: + kw2xrf_set_option(dev, KW2XRF_OPT_PROMISCUOUS, + ((bool *)value)[0]); + return sizeof(ng_netconf_enable_t); + + case NETCONF_OPT_RAWMODE: + kw2xrf_set_option(dev, KW2XRF_OPT_RAWDUMP, + ((bool *)value)[0]); + return sizeof(ng_netconf_enable_t); + + case NETCONF_OPT_PRELOADING: + kw2xrf_set_option(dev, KW2XRF_OPT_PRELOADING, + ((bool *)value)[0]); + return sizeof(ng_netconf_enable_t); + + case NETCONF_OPT_AUTOCCA: + kw2xrf_set_option(dev, KW2XRF_OPT_CSMA, + ((bool *)value)[0]); + return sizeof(ng_netconf_enable_t); + + case NETCONF_OPT_STATE: + if (*((ng_netconf_state_t *)value) == NETCONF_STATE_TX) { + DEBUG("kw2xrf: Sending now.\n"); + kw2xrf_set_sequence(dev, XCVSEQ_TRANSMIT); + return sizeof(ng_netconf_state_t); + } + else if (*((ng_netconf_state_t *)value) == NETCONF_STATE_SLEEP) { + kw2xrf_set_sequence(dev, XCVSEQ_IDLE); + return sizeof(ng_netconf_state_t); + } + else if (*((ng_netconf_state_t *)value) == NETCONF_STATE_IDLE) { + kw2xrf_set_sequence(dev, XCVSEQ_RECEIVE); + return sizeof(ng_netconf_state_t); + } + + /* TODO: Implement Off state here, when LPM functions are implemented */ + + default: + return -ENOTSUP; + } +} + +/* TODO: generalize and move to ng_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] & NG_IEEE802154_FCF_DST_ADDR_MASK); + + if (tmp == NG_IEEE802154_FCF_DST_ADDR_SHORT) { + len += 4; + } + else if (tmp == NG_IEEE802154_FCF_DST_ADDR_LONG) { + len += 10; + } + else if (tmp != NG_IEEE802154_FCF_DST_ADDR_VOID) { + return 0; + } + + tmp = (mhr[1] & NG_IEEE802154_FCF_SRC_ADDR_MASK); + + if (tmp == NG_IEEE802154_FCF_SRC_ADDR_VOID) { + return len; + } + else { + if (!(mhr[0] & NG_IEEE802154_FCF_PAN_COMP)) { + len += 2; + } + + if (tmp == NG_IEEE802154_FCF_SRC_ADDR_SHORT) { + return (len + 2); + } + else if (tmp == NG_IEEE802154_FCF_SRC_ADDR_LONG) { + return (len + 8); + } + } + + return 0; +} + +/* TODO: generalize and move to ng_ieee802154 */ +static ng_pktsnip_t *_make_netif_hdr(uint8_t *mhr) +{ + uint8_t tmp; + uint8_t *addr; + uint8_t src_len, dst_len; + ng_pktsnip_t *snip; + ng_netif_hdr_t *hdr; + + /* figure out address sizes */ + tmp = mhr[1] & NG_IEEE802154_FCF_SRC_ADDR_MASK; + + if (tmp == NG_IEEE802154_FCF_SRC_ADDR_SHORT) { + src_len = 2; + } + else if (tmp == NG_IEEE802154_FCF_SRC_ADDR_LONG) { + src_len = 8; + } + else if (tmp == 0) { + src_len = 0; + } + else { + return NULL; + } + + tmp = mhr[1] & NG_IEEE802154_FCF_DST_ADDR_MASK; + + if (tmp == NG_IEEE802154_FCF_DST_ADDR_SHORT) { + dst_len = 2; + } + else if (tmp == NG_IEEE802154_FCF_DST_ADDR_LONG) { + dst_len = 8; + } + else if (tmp == 0) { + dst_len = 0; + } + else { + return NULL; + } + + /* allocate space for header */ + snip = ng_pktbuf_add(NULL, NULL, sizeof(ng_netif_hdr_t) + src_len + dst_len, + NG_NETTYPE_NETIF); + + if (snip == NULL) { + return NULL; + } + + /* fill header */ + hdr = (ng_netif_hdr_t *)snip->data; + ng_netif_hdr_init(hdr, src_len, dst_len); + + if (dst_len > 0) { + tmp = 5 + dst_len; + addr = ng_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] & NG_IEEE802154_FCF_PAN_COMP)) { + tmp += 2; + } + + if (src_len > 0) { + addr = ng_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; + ng_pktsnip_t *hdr, *payload = NULL; + ng_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 = ng_pktbuf_add(NULL, NULL, pkt_len, NG_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 = (ng_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 = ng_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"); + ng_pktbuf_release(hdr); + return; + } + + dev->event_cb(NETDEV_EVENT_RX_COMPLETE, payload); +} + +void kw2xrf_isr_event(ng_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"); + } + + 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 ng_ieee802.15.4 as soon as ready */ +uint8_t _assemble_tx_buf(kw2xrf_t *dev, ng_pktsnip_t *pkt) +{ + ng_netif_hdr_t *hdr; + hdr = (ng_netif_hdr_t *)pkt->data; + int index = 0; + + if (dev == NULL) { + ng_pktbuf_release(pkt); + return -ENODEV; + } + + /* get netif header check address length */ + hdr = (ng_netif_hdr_t *)pkt->data; + + if (!(hdr->dst_l2addr_len == 2 || hdr->dst_l2addr_len == 8)) { + ng_pktbuf_release(pkt); + return -ENOMSG; + } + + /* FCF, set up data frame, request for ack, panid_compression */ + /* TODO: Currently we don´t request for Ack in this device. + * since this is a soft_mac device this has to be + * handled in a upcoming CSMA-MAC layer. + */ + /*if(dev->option & KW2XRF_OPT_AUTOACK) { + dev->buf[1] = 0x61; + } + else { + dev->buf[1] = 0x51; + }*/ + dev->buf[1] = 0x51; + + /* set sequence number */ + dev->buf[3] = dev->seq_nr++; + + index = 4; + + if (hdr->dst_l2addr_len == 2) { + /* set to short addressing mode */ + dev->buf[2] = 0x88; + /* set destination pan_id */ + dev->buf[index++] = (uint8_t)((dev->radio_pan) & 0xff); + dev->buf[index++] = (uint8_t)((dev->radio_pan) >> 8); + /* set destination address, byte order is inverted */ + dev->buf[index++] = (ng_netif_hdr_get_dst_addr(hdr))[1]; + dev->buf[index++] = (ng_netif_hdr_get_dst_addr(hdr))[0]; + /* set source pan_id */ + //dev->buf[index++] = (uint8_t)((dev->radio_pan) >> 8); + //dev->buf[index++] = (uint8_t)((dev->radio_pan) & 0xff); + /* set source address */ + dev->buf[index++] = (uint8_t)(dev->addr_short[0]); + dev->buf[index++] = (uint8_t)(dev->addr_short[1]); + } + + if (hdr->dst_l2addr_len == 8) { + /* set destination pan_id, wireshark expects it there */ + dev->buf[index++] = (uint8_t)((dev->radio_pan) & 0xff); + dev->buf[index++] = (uint8_t)((dev->radio_pan) >> 8); + /* default to use long address mode for src and dst */ + dev->buf[2] |= 0xcc; + /* set destination address located directly after ng_ifhrd_t in memory */ + memcpy(&(dev->buf)[index], ng_netif_hdr_get_dst_addr(hdr), 8); + index += 8; + /* set source pan_id, wireshark expects it there */ + //dev->buf[index++] = (uint8_t)((dev->radio_pan) >> 8); + //dev->buf[index++] = (uint8_t)((dev->radio_pan) & 0xff); + + /* set source address */ + memcpy(&(dev->buf[index]), dev->addr_long, 8); + index += 8; + } + + return index; +} + +int kw2xrf_send(ng_netdev_t *netdev, ng_pktsnip_t *pkt) +{ + uint8_t index = 0; + kw2xrf_t *dev = (kw2xrf_t *) netdev; + + if (pkt == NULL) { + return -ENOMSG; + } + + ng_pktsnip_t *payload = pkt->next; + + if (netdev == NULL) { + ng_pktbuf_release(pkt); + return -ENODEV; + } + + if (pkt->type == NG_NETTYPE_NETIF) { + /* Build header and fills this already into the tx-buf */ + index = _assemble_tx_buf(dev, pkt); + DEBUG("Assembled header for NG_NETTYPE_UNDEF to tx-buf, index: %i\n", index); + } + else if (pkt->type == NG_NETTYPE_UNDEF) { + /* IEEE packet is already included in the header, + * no need to build the header manually */ + DEBUG("Incoming packet of type NG_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) { + ng_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 */ + + ng_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) == NETCONF_DISABLE) { + DEBUG("kw2xrf: Sending now.\n"); + kw2xrf_set_sequence(dev, XCVSEQ_TRANSMIT); + } + + return index; +} + +/* implementation of the netdev interface */ +const ng_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, +}; diff --git a/drivers/kw2xrf/kw2xrf_spi.c b/drivers/kw2xrf/kw2xrf_spi.c new file mode 100644 index 000000000..2c2ff4834 --- /dev/null +++ b/drivers/kw2xrf/kw2xrf_spi.c @@ -0,0 +1,153 @@ +/* + * 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 drivers_kw2xrf + * @{ + * @file kw2xrf_spi.c + * @brief Implementation of SPI-functions for the kw2xrf driver + * + * @author Johann Fischer + * @author Jonas Remmert + * @} + */ +#include "kw2xrf.h" +#include "kw2xrf_reg.h" +#include "kw2xrf_spi.h" +#include "periph/spi.h" +#include "periph/gpio.h" +#include "cpu-conf.h" +#include "irq.h" + +#define ENABLE_DEBUG (0) +#include "debug.h" + +#define KW2XRF_IBUF_LENGTH 9 + +static uint8_t ibuf[KW2XRF_IBUF_LENGTH]; + +/** Set up in kw2xrf_spi_init during initialization */ +static gpio_t kw2xrf_cs_pin; +static spi_t kw2xrf_spi; + +void kw2xrf_spi_transfer_head(void) +{ +#if KW2XRF_SHARED_SPI + spi_acquire(kw2xrf_spi); + gpio_clear(kw2xrf_cs_pin); +#endif +} + +void kw2xrf_spi_transfer_tail(void) +{ +#if KW2XRF_SHARED_SPI + gpio_set(kw2xrf_cs_pin); + spi_release(kw2xrf_spi); +#endif +} + +int kw2xrf_spi_init(spi_t spi, spi_speed_t spi_speed, + gpio_t cs_pin) +{ + int res; + kw2xrf_cs_pin = cs_pin; /**< for later reference */ + kw2xrf_spi = spi; + +#if KW2XRF_SHARED_SPI + spi_acquire(kw2xrf_spi); +#endif + res = spi_init_master(kw2xrf_spi, SPI_CONF_FIRST_RISING, spi_speed); +#if KW2XRF_SHARED_SPI + spi_release(kw2xrf_spi); + gpio_init_out(kw2xrf_cs_pin, GPIO_NOPULL); + gpio_set(kw2xrf_cs_pin); +#endif + + if (res < 0) { + DEBUG("kw2xrf_spi_init: error initializing SPI_%i device (code %i)\n", + kw2xrf_spi, res); + return -1; + } + + return 0; +} + +void kw2xrf_write_dreg(uint8_t addr, uint8_t value) +{ + kw2xrf_spi_transfer_head(); + spi_transfer_reg(kw2xrf_spi, addr, value, NULL); + kw2xrf_spi_transfer_tail(); + return; +} + +uint8_t kw2xrf_read_dreg(uint8_t addr) +{ + uint8_t value; + kw2xrf_spi_transfer_head(); + spi_transfer_reg(kw2xrf_spi, (addr | MKW2XDRF_REG_READ), + 0x0, (char *)&value); + kw2xrf_spi_transfer_tail(); + return value; +} + +void kw2xrf_write_iregs(uint8_t addr, uint8_t *buf, uint8_t length) +{ + if (length > (KW2XRF_IBUF_LENGTH - 1)) { + length = KW2XRF_IBUF_LENGTH - 1; + } + + ibuf[0] = addr; + + for (uint8_t i = 0; i < length; i++) { + ibuf[i + 1] = buf[i]; + } + + kw2xrf_spi_transfer_head(); + spi_transfer_regs(kw2xrf_spi, MKW2XDM_IAR_INDEX, + (char *)ibuf, NULL, length + 1); + kw2xrf_spi_transfer_tail(); + + return; +} + +void kw2xrf_read_iregs(uint8_t addr, uint8_t *buf, uint8_t length) +{ + if (length > (KW2XRF_IBUF_LENGTH - 1)) { + length = KW2XRF_IBUF_LENGTH - 1; + } + + ibuf[0] = addr; + + kw2xrf_spi_transfer_head(); + spi_transfer_regs(kw2xrf_spi, MKW2XDM_IAR_INDEX | MKW2XDRF_REG_READ, + (char *)ibuf, (char *)ibuf, length + 1); + kw2xrf_spi_transfer_tail(); + + for (uint8_t i = 0; i < length; i++) { + buf[i] = ibuf[i + 1]; + } + + return; +} + +void kw2xrf_write_fifo(uint8_t *data, uint8_t length) +{ + kw2xrf_spi_transfer_head(); + spi_transfer_regs(kw2xrf_spi, MKW2XDRF_BUF_WRITE, + (char *)data, NULL, length); + kw2xrf_spi_transfer_tail(); +} + +void kw2xrf_read_fifo(uint8_t *data, uint8_t length) +{ + kw2xrf_spi_transfer_head(); + spi_transfer_regs(kw2xrf_spi, MKW2XDRF_BUF_READ, NULL, + (char *)data, length); + kw2xrf_spi_transfer_tail(); +} +/** @} */ diff --git a/sys/include/net/ng_netconf.h b/sys/include/net/ng_netconf.h index b3ed5ffcf..167b22119 100644 --- a/sys/include/net/ng_netconf.h +++ b/sys/include/net/ng_netconf.h @@ -117,6 +117,8 @@ typedef enum { * @note not all transceivers may support this interrupt */ NETCONF_OPT_TX_END_IRQ, + NETCONF_OPT_AUTOCCA, /**< en/disable to check automatically + before sending the channel is clear. */ /* add more options if needed */ } ng_netconf_opt_t; diff --git a/tests/driver_kw2xrf/Makefile b/tests/driver_kw2xrf/Makefile new file mode 100644 index 000000000..67eef65e8 --- /dev/null +++ b/tests/driver_kw2xrf/Makefile @@ -0,0 +1,51 @@ +APPLICATION = driver_kw2xrf +include ../Makefile.tests_common + +FEATURES_REQUIRED = periph_spi periph_gpio + +BOARD_INSUFFICIENT_RAM := stm32f0discovery + +ifneq (,$(filter pba-d-01-kw2x,$(BOARD))) + export KWRF_SPI ?= SPI_1 + export KWRF_CS ?= GPIO_24 + export KWRF_INT ?= GPIO_23 + export KWRF_SPI_SPEED ?= SPI_SPEED_10MHZ + export KW2XRF_SHARED_SPI ?= 0 +endif + +USEMODULE += ng_netif +USEMODULE += ng_nomac +USEMODULE += ng_pktdump +USEMODULE += uart0 +USEMODULE += shell +USEMODULE += shell_commands +USEMODULE += ps +USEMODULE += kw2xrf + +CFLAGS += -DDEVELHELP + +ifneq (,$(KWRF_SHARED_SPI)) + CFLAGS += -DKW2XRF_SHARED_SPI=$(DKW2XRF_SHARED_SPI) +else + CFLAGS += -DKW2XRFD_SHARED_SPI=1 # activate spi-lock +endif +ifneq (,$(KWRF_SPI)) + CFLAGS += -DKWRF_SPI=$(KWRF_SPI) +else + CFLAGS += -DKWRF_SPI=SPI_0 # set default +endif +ifneq (,$(KWRF_CS)) + CFLAGS += -DKWRF_CS=$(KWRF_CS) +else + CFLAGS += -DKWRF_CS=GPIO_0 # set default +endif +ifneq (,$(KWRF_INT)) + CFLAGS += -DKWRF_INT=$(KWRF_INT) +else + CFLAGS += -DKWRF_INT=GPIO_1 # set default +endif +ifneq (,$(KWRF_SPI_SPEED)) + CFLAGS += -DKWRF_SPI_SPEED=$(KWRF_SPI_SPEED) +endif + +include $(RIOTBASE)/Makefile.include diff --git a/tests/driver_kw2xrf/README.md b/tests/driver_kw2xrf/README.md new file mode 100644 index 000000000..9a237d983 --- /dev/null +++ b/tests/driver_kw2xrf/README.md @@ -0,0 +1,12 @@ +# About +This is a manual test application for testing the KW2xrf network device driver. + +For running this test, you need to connect/configure the following pins of your +radio device: +- SPI DEV +- CS (chip select) +- INT (external interrupt) + +# Usage +For testing the radio driver you can use the netif and txtsnd shell commands +that are included in this application. diff --git a/tests/driver_kw2xrf/auto_init_ng_netif/Makefile b/tests/driver_kw2xrf/auto_init_ng_netif/Makefile new file mode 100644 index 000000000..48422e909 --- /dev/null +++ b/tests/driver_kw2xrf/auto_init_ng_netif/Makefile @@ -0,0 +1 @@ +include $(RIOTBASE)/Makefile.base diff --git a/tests/driver_kw2xrf/auto_init_ng_netif/netif_app.c b/tests/driver_kw2xrf/auto_init_ng_netif/netif_app.c new file mode 100644 index 000000000..3ffdbd65a --- /dev/null +++ b/tests/driver_kw2xrf/auto_init_ng_netif/netif_app.c @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2015 Freie Universität Berlin + * 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 tests + * @{ + * + * @file + * @brief Test application for KW2xRF network device driver + * + * @author Hauke Petersen + * @author Jonas Remmert + * + * @} + */ + +#include + +#include "kernel.h" +#include "kw2xrf.h" +#include "net/ng_nomac.h" +#include "net/ng_netbase.h" + +/* make sure the SPI port and the needed GPIO pins are defined */ +#ifndef KWRF_SPI +#error "SPI not defined" +#endif +#ifndef KWRF_CS +#error "Chip select pin not defined" +#endif +#ifndef KWRF_INT +#error "Interrupt pin not defined" +#endif +#ifndef KWRF_SPI_SPEED +#define KWRF_SPI_SPEED (SPI_SPEED_10MHZ) +#endif + +/** + * @brief MAC layer stack configuration + * @{ + */ +#define STACKSIZE (KERNEL_CONF_STACKSIZE_MAIN) +#define PRIO (0) +/** @} */ + +/** + * @brief Allocate the KW2XRF device descriptor + */ +static kw2xrf_t dev; + +/** + * @brief Stack for the nomac thread + */ +static char nomac_stack[STACKSIZE]; + + +void auto_init_ng_netif(void) +{ + kernel_pid_t iface; + int res; + + /* initialize the KW2XRF device */ + printf("Initializing the KW2XRF radio at SPI_%i... \n", KWRF_SPI); + res = kw2xrf_init(&dev, KWRF_SPI, KWRF_SPI_SPEED, + KWRF_CS, KWRF_INT); + if (res < 0) { + puts("Error initializing KW2XRF radio device"); + return; + } + + /* start MAC layer */ + puts("Starting the NOMAC layer on top of the driver"); + iface = ng_nomac_init(nomac_stack, sizeof(nomac_stack), PRIO, "kw2xrf", + (ng_netdev_t *)(&dev)); + if (iface <= KERNEL_PID_UNDEF) { + puts("Error initializing MAC layer"); + return; + } + +} diff --git a/tests/driver_kw2xrf/main.c b/tests/driver_kw2xrf/main.c new file mode 100644 index 000000000..7549b8236 --- /dev/null +++ b/tests/driver_kw2xrf/main.c @@ -0,0 +1,72 @@ +/* + * 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 tests + * @file + * @brief Test application for KW2x network device driver + * + * @author Hauke Petersen + * @author Jonas Remmert + * @} + */ + +#include + +#include "shell.h" +#include "shell_commands.h" +#include "posix_io.h" +#include "board_uart0.h" +#include "net/ng_netbase.h" +#include "net/ng_pktdump.h" + +/** + * @brief Buffer size used by the shell + */ +#define SHELL_BUFSIZE (64U) + +/** + * @brief Read chars from STDIO + */ +int shell_read(void) +{ + char c = 0; + (void) posix_read(uart0_handler_pid, &c, 1); + return c; +} + +/** + * @brief Write chars to STDIO + */ +void shell_put(int c) +{ + putchar((char)c); +} + +int main(void) +{ + shell_t shell; + ng_netreg_entry_t dump; + + puts("KW2XRF device driver test"); + + /* register the pktdump thread */ + puts("Register the packet dump thread for NG_NETTYPE_UNDEF packets"); + dump.pid = ng_pktdump_getpid(); + dump.demux_ctx = NG_NETREG_DEMUX_CTX_ALL; + ng_netreg_register(NG_NETTYPE_UNDEF, &dump); + + /* start the shell */ + puts("Initialization successful - starting the shell now"); + (void) posix_open(uart0_handler_pid, 0); + shell_init(&shell, NULL, SHELL_BUFSIZE, shell_read, shell_put); + shell_run(&shell); + + return 0; +}