From 49f09477af3606b6849b6039093758347e353856 Mon Sep 17 00:00:00 2001 From: Jonas Date: Thu, 2 Apr 2015 17:09:29 +0200 Subject: [PATCH 1/2] Initial commit of ng_kw2xrf radio driver squash: removed tab indent --- drivers/include/kw2xrf.h | 110 ++++ drivers/kw2xrf/Makefile | 3 + drivers/kw2xrf/include/kw2xrf_reg.h | 344 +++++++++++++ drivers/kw2xrf/include/kw2xrf_spi.h | 114 +++++ drivers/kw2xrf/kw2xrf.c | 749 ++++++++++++++++++++++++++++ drivers/kw2xrf/kw2xrf_spi.c | 147 ++++++ 6 files changed, 1467 insertions(+) create mode 100644 drivers/include/kw2xrf.h create mode 100644 drivers/kw2xrf/Makefile create mode 100644 drivers/kw2xrf/include/kw2xrf_reg.h create mode 100644 drivers/kw2xrf/include/kw2xrf_spi.h create mode 100644 drivers/kw2xrf/kw2xrf.c create mode 100644 drivers/kw2xrf/kw2xrf_spi.c diff --git a/drivers/include/kw2xrf.h b/drivers/include/kw2xrf.h new file mode 100644 index 000000000..49574650d --- /dev/null +++ b/drivers/include/kw2xrf.h @@ -0,0 +1,110 @@ +/* + * 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 "net/ng_netdev.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Maximum packet length, including XBee API frame overhead + */ +#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 PAN ID used after initialization + */ +#define KW2XRF_DEFAULT_PANID (0x0001) + +/** + * @brief Default channel used after initialization + */ +#define KW2XRF_DEFAULT_CHANNEL (13U) + +/** + * @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 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 */ + /* Devide driver specific fields */ + uint8_t buf[KW2XRF_MAX_PKT_LENGTH]; /**> Buffer for the kw2x radio device */ + 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 */ + uint8_t options; /**< Bit field to save enable/disable options */ + ng_nettype_t proto; /**< Protocol the interface speaks */ +} kw2xrf_t; + +/** + * @brief Initialize the given KW2XRF device + * + * @param[out] dev KW2XRF device to initialize + * + * @return 0 on success + * @return -ENODEV on invalid device descriptor + */ +int kw2xrf_init(kw2xrf_t *dev); + +/** + * 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..e36d07d1c --- /dev/null +++ b/drivers/kw2xrf/Makefile @@ -0,0 +1,3 @@ +MODULE = kw2xrf + +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..9d80df24c --- /dev/null +++ b/drivers/kw2xrf/include/kw2xrf_reg.h @@ -0,0 +1,344 @@ +/* + * Copyright (C) 2014 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 + +#define MKW2XDRF_REG_READ (uint8_t)(1 << 7) +#define MKW2XDRF_REG_WRITE (uint8_t)(0) +#define MKW2XDRF_BUF_READ (uint8_t)(1 << 7 | 1 << 6) +#define MKW2XDRF_BUF_WRITE (uint8_t)(1 << 6) +#define MKW2XDRF_BUF_BYTE_READ (uint8_t)(1 << 7 | 1 << 6 | 1 << 5) +#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 + * + * @return 0 on success + * @return -1 on error + */ +int kw2xrf_spi_init(void); + +/** + * @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, radio_packet_length_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, radio_packet_length_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..79e223689 --- /dev/null +++ b/drivers/kw2xrf/kw2xrf.c @@ -0,0 +1,749 @@ +/* + * 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 "crash.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" + +#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 +}; + +int _set_tx_power(kw2xrf_t *dev, uint8_t *val, size_t len) +{ + + if (len < 1) { + return -EOVERFLOW; + } + + 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 _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 _get_sequence(void) +{ + int reg = 0; + reg = kw2xrf_read_dreg(MKW2XDM_PHY_CTRL1); + reg &= MKW2XDM_PHY_CTRL1_XCVSEQ_MASK; + return reg; +} + +void _set_sequence(kw2xrf_physeq_t seq) +{ + /* Mask all interrupts; disable interrupts for exclusive SPI access */ + int tmp_irq_state = disableIRQ(); + uint8_t reg = kw2xrf_read_dreg(MKW2XDM_PHY_CTRL4); + reg |= MKW2XDM_PHY_CTRL4_TRCV_MSK; + kw2xrf_write_dreg(MKW2XDM_PHY_CTRL4, reg); + restoreIRQ(tmp_irq_state); + + if (kw2xrf_read_dreg(MKW2XDM_SEQ_STATE)) { + /* abort any ongoing sequence */ + DEBUG("tx: abort SEQ_STATE: %x\n", kw2xrf_read_dreg(MKW2XDM_SEQ_STATE)); + kw2xrf_write_dreg(MKW2XDM_PHY_CTRL1, reg); + while (kw2xrf_read_dreg(MKW2XDM_SEQ_STATE)); + + /* Clear all pending interrupts */ + kw2xrf_write_dreg(MKW2XDM_IRQSTS1, 0x7f); + kw2xrf_write_dreg(MKW2XDM_IRQSTS2, 0x03); + kw2xrf_write_dreg(MKW2XDM_IRQSTS3, 0xff); + + /* Mask all possible interrupts */ + reg = kw2xrf_read_dreg(MKW2XDM_PHY_CTRL2); + reg |= ~(MKW2XDM_PHY_CTRL2_CRC_MSK); + kw2xrf_write_dreg(MKW2XDM_PHY_CTRL2, reg); + + reg = kw2xrf_read_dreg(MKW2XDM_PHY_CTRL3); + reg |= (MKW2XDM_PHY_CTRL3_PB_ERR_MSK | MKW2XDM_PHY_CTRL3_WAKE_MSK); + reg &= (MKW2XDM_PHY_CTRL3_PB_ERR_MSK | MKW2XDM_PHY_CTRL3_WAKE_MSK); + kw2xrf_write_dreg(MKW2XDM_PHY_CTRL2, reg); + } + /* For all sequences only enable SEQ-irq, that is set when sequence was completed */ + reg = kw2xrf_read_dreg(MKW2XDM_PHY_CTRL2); + reg &= ~(MKW2XDM_PHY_CTRL2_SEQMSK); + kw2xrf_write_dreg(MKW2XDM_PHY_CTRL2, reg); + + /* Progrmm new sequence */ + 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); + DEBUG("kw2xrf: Set sequence to %i\n", seq); + + /* Unmask all interrupts */ + reg = kw2xrf_read_dreg(MKW2XDM_PHY_CTRL4); + reg &= ~(MKW2XDM_PHY_CTRL4_TRCV_MSK); + kw2xrf_write_dreg(MKW2XDM_PHY_CTRL4, reg); +} + +int _set_channel(kw2xrf_t *dev, uint8_t *val, size_t len) +{ + /* Save old sequence to restore this state later */ + uint8_t old_seq = _get_sequence(); + if(old_seq){ + _set_sequence(XCVSEQ_IDLE); + } + if (val[0] < 11 || val[0] > 26) { + DEBUG("Invalid channel %i set. Valid channels are 11 through 26\n", val[0]); + return -EINVAL; + } + + 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)); + + if(old_seq){ + _set_sequence(old_seq); + } + + return 2; +} + +void kw2xrf_irq_handler(void *args) +{ + msg_t msg; + kw2xrf_t *dev = (kw2xrf_t *)args; + uint8_t irqst1 = kw2xrf_read_dreg(MKW2XDM_IRQSTS1); + DEBUG("kw2xrf_irq_handler: irqst1: %x\n",irqst1); + if ((irqst1 & MKW2XDM_IRQSTS1_RXIRQ) && (irqst1 & MKW2XDM_IRQSTS1_SEQIRQ)) { + msg.content.value = NETDEV_EVENT_RX_COMPLETE; + kw2xrf_write_dreg(MKW2XDM_IRQSTS1, MKW2XDM_IRQSTS1_RXIRQ | MKW2XDM_IRQSTS1_SEQIRQ); + } + else if ((irqst1 & MKW2XDM_IRQSTS1_TXIRQ) && (irqst1 & MKW2XDM_IRQSTS1_SEQIRQ)) { + msg.content.value = NETDEV_EVENT_TX_COMPLETE; + kw2xrf_write_dreg(MKW2XDM_IRQSTS1, MKW2XDM_IRQSTS1_TXIRQ | MKW2XDM_IRQSTS1_SEQIRQ); + } + else if ((irqst1 & MKW2XDM_IRQSTS1_CCAIRQ) && (irqst1 & MKW2XDM_IRQSTS1_SEQIRQ)) { + msg.content.value = NETDEV_EVENT_TX_STARTED; + kw2xrf_write_dreg(MKW2XDM_IRQSTS1, MKW2XDM_IRQSTS1_CCAIRQ | MKW2XDM_IRQSTS1_SEQIRQ); + } + else { + msg.content.value = ISR_EVENT_UNKNOWN; + /* 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); + } + + /* packet is complete */ + 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) +{ + /* Disable all interrups: + * Selectively enable only one interrupt source selectively in sequence manager. + * After reset state all interrupts are disabled, except WAKE_IRQ. + */ + kw2xrf_write_dreg(MKW2XDM_IRQSTS1, 0x7f); + kw2xrf_write_dreg(MKW2XDM_IRQSTS2, 0x03); + kw2xrf_write_dreg(MKW2XDM_IRQSTS3, 0xff); + + /* Mask all possible interrupts */ + int reg = kw2xrf_read_dreg(MKW2XDM_PHY_CTRL2); + reg |= ~(MKW2XDM_PHY_CTRL2_CRC_MSK); + kw2xrf_write_dreg(MKW2XDM_PHY_CTRL2, reg); + + reg = kw2xrf_read_dreg(MKW2XDM_PHY_CTRL3); + reg |= (MKW2XDM_PHY_CTRL3_PB_ERR_MSK | MKW2XDM_PHY_CTRL3_WAKE_MSK); + reg &= (MKW2XDM_PHY_CTRL3_PB_ERR_MSK | MKW2XDM_PHY_CTRL3_WAKE_MSK); + kw2xrf_write_dreg(MKW2XDM_PHY_CTRL2, reg); + + /* set up GPIO-pin used for IRQ */ + gpio_init_int(GPIO_KW2XDRF, GPIO_NOPULL, GPIO_FALLING, &kw2xrf_irq_handler, dev); +} + +int _set_pan(kw2xrf_t *dev, uint8_t *val, size_t len) +{ + if (len != 2) { + return -EINVAL; + } + kw2xrf_write_iregs(MKW2XDMI_MACPANID0_LSB, val, 2); + dev->radio_pan = val[1]; + dev->radio_pan = (dev->radio_pan) <<8; + dev->radio_pan |= val[0]; + return 2; +} + +int _get_pan(kw2xrf_t *dev, uint8_t *val, size_t max) +{ + if (max < 2) { + return -EOVERFLOW; + } + kw2xrf_read_iregs(MKW2XDMI_MACPANID0_LSB, val, 2); + return 2; +} + +int _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 _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 */ + _set_sequence(XCVSEQ_IDLE); + + dev->state = NETCONF_STATE_SLEEP; + return 0; +} + +int _get_addr_short(kw2xrf_t *dev, uint8_t *val, size_t len) +{ + if (len < 2) { + return -EOVERFLOW; + } + kw2xrf_read_iregs(MKW2XDMI_MACSHORTADDRS0_LSB, val, 2); + return 2; +} + +int _get_addr_long(kw2xrf_t *dev, uint8_t *val, size_t len) +{ + if (len < 8) { + return -EOVERFLOW; + } + kw2xrf_read_iregs(MKW2XDMI_MACLONGADDRS0_0, val, 8); + return 8; +} + +int _set_addr(kw2xrf_t *dev, uint8_t *val, size_t len) +{ + if (len == 2) { + kw2xrf_write_iregs(MKW2XDMI_MACSHORTADDRS0_LSB, val, 2); + dev->addr_short[0] = val[0]; + dev->addr_short[1] = val[1]; + return 2; + } + else if (len == 8){ + kw2xrf_write_iregs(MKW2XDMI_MACLONGADDRS0_0, val, 8); + for (int i=0; i<8; i++) { + dev->addr_long[i] = val[i]; + } + return 8; + } + return -ENOTSUP; +} + +int kw2xrf_init(kw2xrf_t *dev) { + uint8_t reg = 0; + uint8_t tmp[2]; + uint8_t tx_pwr = 0; + + /* check device parameters */ + if (dev == NULL) { + return -ENODEV; + } + + kw2xrf_spi_init(); + if (!(kw2xrf_on(dev) == 0)) { + core_panic(0x42, "Could not start MKW2XD radio transceiver"); + } + + /* Gerneral initialization of interrupt sources. + * sets radio to idle modewith all interrupt masked + */ + kw2xrf_init_interrupts(dev); + + /* set device driver */ + dev->driver = &kw2xrf_driver; + /* set default options */ + dev->proto = KW2XRF_DEFAULT_PROTOCOL; + dev->options = 0; + dev->addr_short[0] = (uint8_t)(KW2XRF_DEFAULT_SHORT_ADDR >> 8); + dev->addr_short[1] = (uint8_t)(KW2XRF_DEFAULT_SHORT_ADDR); + /* set default short address */ + _set_addr(dev, dev->addr_short, 2); + /* load long address */ + _get_addr_long(dev, dev->addr_long, 8); + + _set_tx_power(dev, &tx_pwr, 1); + + /* set default channel */ + tmp[0] = KW2XRF_DEFAULT_CHANNEL; + tmp[1] = 0; + _set_channel(dev, tmp, 2); + /* set default PAN ID */ + tmp[0] = (uint8_t)(KW2XRF_DEFAULT_PANID & 0xff); + tmp[1] = (uint8_t)(KW2XRF_DEFAULT_PANID >> 8); + _set_pan(dev, tmp, 2); + + /* CCA Setup */ + reg = kw2xrf_read_dreg(MKW2XDM_PHY_CTRL4); + reg |= MKW2XDM_PHY_CTRL4_CCATYPE(1); /* Set up CCA mode 1 (RSSI threshold) */ + 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); + + /* TODO: For testing, ignores CRCVALID in receive mode */ + reg = kw2xrf_read_dreg(MKW2XDM_PHY_CTRL2); + reg &= ~(MKW2XDM_PHY_CTRL2_CRC_MSK); + kw2xrf_write_dreg(MKW2XDM_PHY_CTRL2, reg); + + /* TODO: For testing, set up promiscous mode */ + reg = kw2xrf_read_dreg(MKW2XDM_PHY_CTRL4); + reg |= MKW2XDM_PHY_CTRL4_PROMISCUOUS; + kw2xrf_write_dreg(MKW2XDM_PHY_CTRL4, reg); + return 0; +} + +int _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 _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; +} + +int _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: + return _get_addr_short(dev, (uint8_t *)value, max_len); + case NETCONF_OPT_ADDRESS_LONG: + return _get_addr_long(dev, (uint8_t *)value, max_len); + case NETCONF_OPT_CHANNEL: + return _get_channel(dev, (uint8_t *)value, max_len); + case NETCONF_OPT_NID: + return _get_pan(dev, (uint8_t *)value, max_len); + case NETCONF_OPT_PROTO: + return _get_proto(dev, (uint8_t *)value, max_len); + case NETCONF_OPT_STATE: + if (max_len < sizeof(ng_netconf_state_t)) { + return -EOVERFLOW; + } + value = (int *)&(dev->state); + return 0; + default: + return -ENOTSUP; + } +} + +int _set(ng_netdev_t *netdev, ng_netconf_opt_t opt, void *value, size_t value_len) +{ + uint8_t reg; + kw2xrf_t *dev = (kw2xrf_t *)netdev; + if (dev == NULL) { + return -ENODEV; + } + + switch (opt) { + case NETCONF_OPT_CHANNEL: + return _set_channel(dev, (uint8_t *)value, value_len); + case NETCONF_OPT_ADDRESS: + return _set_addr(dev, (uint8_t *)value, value_len); + case NETCONF_OPT_NID: + return _set_pan(dev, (uint8_t *)value, value_len); + case NETCONF_OPT_IS_CHANNEL_CLR: + _set_sequence(XCVSEQ_CCA); + return 0; + case NETCONF_OPT_TX_POWER: + _set_tx_power(dev, (uint8_t *)value, value_len); + return 0; + case NETCONF_OPT_PROTO: + return _set_proto(dev, (uint8_t *)value, value_len); + case NETCONF_OPT_CCA_BEFORE_TX: + reg = kw2xrf_read_dreg(MKW2XDM_PHY_CTRL1); + reg |= MKW2XDM_PHY_CTRL1_CCABFRTX; /* Set up CCA before TX */ + kw2xrf_write_dreg(MKW2XDM_PHY_CTRL1, reg); + return 0; + case NETCONF_OPT_AUTOACK: + reg = kw2xrf_read_dreg(MKW2XDM_PHY_CTRL1); + /* Set up HW generated automatic ACK after Receive */ + reg |= MKW2XDM_PHY_CTRL1_AUTOACK; + kw2xrf_write_dreg(MKW2XDM_PHY_CTRL1, reg); + return 0; + case NETCONF_OPT_STATE: + if(*((ng_netconf_state_t *)value) == NETCONF_STATE_TX) { + DEBUG("kw2xrf: Sending now.\n"); + _set_sequence(XCVSEQ_TRANSMIT); + dev->state = NETCONF_STATE_TX; + return 0; + } + else if(*((ng_netconf_state_t *)value) == NETCONF_STATE_SLEEP) { + _set_sequence(XCVSEQ_IDLE); + dev->state = NETCONF_STATE_SLEEP; + return 0; + } + else if(*((ng_netconf_state_t *)value) == NETCONF_STATE_IDLE) { + _set_sequence(XCVSEQ_RECEIVE); + dev->state = NETCONF_STATE_IDLE; + return 0; + } + /* TODO: Implement Off state here, when LPM functions are implemented */ + default: + return -ENOTSUP; + } +} + +void _isr_event(ng_netdev_t *netdev, uint32_t event_type) +{ + kw2xrf_t *dev = (kw2xrf_t *)netdev; + uint8_t pkt_len = 0; + ng_netif_hdr_t *hdr; + uint8_t index = 0; + uint8_t src_addr_len = 0; + uint8_t dst_addr_len = 0; + + /* check device */ + if (dev == NULL) { + return; + } + + if (event_type == NETDEV_EVENT_RX_COMPLETE) { + pkt_len = kw2xrf_read_dreg(MKW2XDM_RX_FRM_LEN); + + /* read PSDU */ + kw2xrf_read_fifo(dev->buf, pkt_len + 1); + if((dev->buf[0] & 0x07) == 0x00) { + DEBUG("kw2xrf: Beacon received; not handled yed -> dischard message\n"); + _set_sequence(XCVSEQ_RECEIVE); + return; + } + if((dev->buf[0] & 0x07) == 0x02) { + DEBUG("kw2xrf: ACK received; not handled yed -> dischard message\n"); + _set_sequence(XCVSEQ_RECEIVE); + return; + } + if((dev->buf[0] & 0x07) == 0x03) { + DEBUG("kw2xrf: MAC-cmd received; not handled yed -> dischard message\n"); + _set_sequence(XCVSEQ_RECEIVE); + return; + } + if((dev->buf[0] & 0x07) != 0x01) { /* No Data frame either */ + DEBUG("kw2xrf: undefined message received; -> dischard message\n"); + _set_sequence(XCVSEQ_RECEIVE); + return; + } + DEBUG("kw2xrf: Message received: size %i\n", pkt_len); + + /* src 16bit addr */ + if((dev->buf[1] & 0x0c) == 0x08) { + src_addr_len = 0x02; + } + /* src 64bit addr */ + else if((dev->buf[1] & 0x0c) == 0x0c) { + src_addr_len = 0x08; + } + else { + DEBUG("Bogus src address length.\n"); + } + /* dst 16bit addr */ + if((dev->buf[1] & 0xc0) == 0x80) { + dst_addr_len = 0x02; + } + /* dst 64bit addr */ + else if((dev->buf[1] & 0xc0) == 0xc0) { + dst_addr_len = 0x08; + } + else { + DEBUG("Bogus src address length.\n"); + } + DEBUG("Src addr len: %i, Dst addr len: %i", src_addr_len, dst_addr_len); + /* allocate a pktsnip for generic header */ + ng_pktsnip_t *hdr_snip = ng_pktbuf_add(NULL, NULL, sizeof(ng_netif_hdr_t)+ + src_addr_len, NG_NETTYPE_UNDEF); + if (hdr_snip == NULL) { + DEBUG("kw2xrf: ERROR allocating header in packet buffer on RX\n"); + ng_pktbuf_release(hdr_snip); + _set_sequence(XCVSEQ_RECEIVE); + return; + } + hdr = (ng_netif_hdr_t *)hdr_snip->data; + /* init generic header */ + ng_netif_hdr_init(hdr, src_addr_len, dst_addr_len); + /* append src address into memory */ + if(hdr->dst_l2addr_len == 2) { + ng_netif_hdr_set_src_addr(hdr, &(dev->buf[7]), hdr->src_l2addr_len); + index = 7 + hdr->src_l2addr_len; + } + else { + ng_netif_hdr_set_src_addr(hdr, &(dev->buf[11]), hdr->src_l2addr_len); + index = 11 + hdr->src_l2addr_len; + } + hdr->if_pid = thread_getpid(); + hdr->rssi = kw2xrf_read_dreg(MKW2XDM_RSSI_CCA_CNT); + hdr->lqi = dev->buf[pkt_len]; + + ng_pktsnip_t *payload_snip = ng_pktbuf_add(hdr_snip, (void *)&(dev->buf[index + 1]), + pkt_len - index, dev->proto); + if(payload_snip == NULL) { + DEBUG("kw2xrf: ERROR allocating payload in packet buffer on RX\n"); + ng_pktbuf_release(hdr_snip); + _set_sequence(XCVSEQ_RECEIVE); + return; + } + dev->event_cb(NETDEV_EVENT_RX_COMPLETE, payload_snip); + _set_sequence(XCVSEQ_RECEIVE); + return; + } + + if (event_type == NETDEV_EVENT_TX_STARTED) { + uint8_t irqst2 = kw2xrf_read_dreg(MKW2XDM_IRQSTS2); + if (irqst2 & MKW2XDM_IRQSTS2_CCA) { + DEBUG("kw2xrf: CCA done -> Channel busy\n"); + } + DEBUG("kw2xrf: CCA done -> Channel idle\n"); + } + + if (event_type == NETDEV_EVENT_TX_COMPLETE) { + /* Device is automatically in Radio-idle state when TX is done */ + dev->state = NETCONF_STATE_SLEEP; + DEBUG("kw2xrf: TX Complete\n"); + } +} + +int _send(ng_netdev_t *netdev, ng_pktsnip_t *pkt) +{ + uint8_t index=0; + kw2xrf_t *dev = (kw2xrf_t*) netdev; + ng_netif_hdr_t *hdr; + hdr = (ng_netif_hdr_t *)pkt->data; + + if (pkt == NULL) { + return -ENOMSG; + } + if (netdev == 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 */ + dev->buf[1] = 0x01; + /* set sequence number */ + dev->buf[3] = dev->seq_nr++; + + + if (hdr->dst_l2addr_len == 2) { + index = 4; + /* set to short addressing mode */ + dev->buf[2] = 0x88; + /* set destination pan_id TODO: not currect currently */ + dev->buf[index++] = (uint8_t)dev->radio_pan; + dev->buf[index++] = (uint8_t)((dev->radio_pan)>>8); + index += 2; + /* set source pan_id */ + dev->buf[index++] = (uint8_t)dev->radio_pan; + dev->buf[index++] = (uint8_t)((dev->radio_pan)>>8); + /* set source address */ + dev->buf[index++] = dev->addr_short[1]; + dev->buf[index++] = dev->addr_short[0]; + } + else if (hdr->dst_l2addr_len == 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 */ + index = 4; + index += 8; + /* set source address */ + for(int i=0; i<8; i++) { + dev->buf[index++] = dev->addr_long[i]; + } + } + + while (pkt) { + /* check we don't exceed FIFO size */ + if (index+2+pkt->size > KW2XRF_MAX_PKT_LENGTH) { + ng_pktbuf_release(pkt); + DEBUG("Packet exceeded FIFO size.\n"); + return -ENOBUFS; + } + 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; + + /* next snip */ + pkt = pkt->next; + } + dev->buf[0] = index+2; /* set packet size, reserve additional */ + + DEBUG("kw2xrf: send packet with size %i\n", dev->buf[0]); + kw2xrf_write_fifo(dev->buf, dev->buf[0]); + + if ((dev->options&(1<state = NETCONF_STATE_TX; + + return index; +} + +/* implementation of the netdev interface */ +const ng_netdev_driver_t kw2xrf_driver = { + .send_data = _send, + .add_event_callback = _add_cb, + .rem_event_callback = _rem_cb, + .get = _get, + .set = _set, + .isr_event = _isr_event, +}; diff --git a/drivers/kw2xrf/kw2xrf_spi.c b/drivers/kw2xrf/kw2xrf_spi.c new file mode 100644 index 000000000..dc9de6553 --- /dev/null +++ b/drivers/kw2xrf/kw2xrf_spi.c @@ -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. + */ + +/** + * @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]; + +#ifndef KW2XRF_SPI_SPEED +#define KW2XRF_SPI_SPEED SPI_SPEED_5MHZ +#endif + +inline void kw2xrf_spi_transfer_head(void) +{ +#if KW2XRF_SHARED_SPI + spi_acquire(KW2XRF_SPI); + gpio_clear(KW2XRF_CS_GPIO); +#endif +} + +inline void kw2xrf_spi_transfer_tail(void) +{ +#if KW2XRF_SHARED_SPI + gpio_set(KW2XRF_CS_GPIO); + spi_release(KW2XRF_SPI); +#endif +} + +int kw2xrf_spi_init(void) +{ + int res; + +#if KW2XRF_SHARED_SPI + spi_acquire(KW2XRF_SPI); +#endif + res = spi_init_master(KW2XRF_SPI, SPI_CONF_FIRST_RISING, KW2XRF_SPI_SPEED); +#if KW2XRF_SHARED_SPI + spi_release(KW2XRF_SPI); + gpio_init_out(KW2XRF_CS_GPIO, GPIO_NOPULL); + gpio_set(KW2XRF_CS_GPIO); +#endif + if (res < 0) { + DEBUG("kw2xrf_spi_init: error initializing SPI_%i device (code %i)\n", + spi_dev, 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, radio_packet_length_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, radio_packet_length_t length) +{ + kw2xrf_spi_transfer_head(); + spi_transfer_regs(KW2XRF_SPI, MKW2XDRF_BUF_READ, NULL, + (char *)data, length); + kw2xrf_spi_transfer_tail(); +} +/** @} */ From 3287e71d45da591bc54f4400700004287014002d Mon Sep 17 00:00:00 2001 From: Jonas Date: Thu, 2 Apr 2015 17:39:10 +0200 Subject: [PATCH 2/2] Initial import of tests for kw2x radio squash: driver: fixed typo to pass doccheck squash: driver: bug fixes after test squash: adapted Makefiles for correct build behaviour restructured, no spi interaction in isr anymore major bugfixes and restructurization comments addressed introduce new netconf option, ..AUTOCCA squash: minor bugfix and add auto-init mechanism squash: minor fixes to make travis happy --- drivers/Makefile.include | 3 + drivers/include/kw2xrf.h | 69 +- drivers/kw2xrf/Makefile | 2 - drivers/kw2xrf/include/kw2xrf_reg.h | 214 ++-- drivers/kw2xrf/include/kw2xrf_spi.h | 10 +- drivers/kw2xrf/kw2xrf.c | 1108 ++++++++++++----- drivers/kw2xrf/kw2xrf_spi.c | 60 +- sys/include/net/ng_netconf.h | 2 + tests/driver_kw2xrf/Makefile | 51 + tests/driver_kw2xrf/README.md | 12 + .../driver_kw2xrf/auto_init_ng_netif/Makefile | 1 + .../auto_init_ng_netif/netif_app.c | 86 ++ tests/driver_kw2xrf/main.c | 72 ++ 13 files changed, 1199 insertions(+), 491 deletions(-) create mode 100644 tests/driver_kw2xrf/Makefile create mode 100644 tests/driver_kw2xrf/README.md create mode 100644 tests/driver_kw2xrf/auto_init_ng_netif/Makefile create mode 100644 tests/driver_kw2xrf/auto_init_ng_netif/netif_app.c create mode 100644 tests/driver_kw2xrf/main.c diff --git a/drivers/Makefile.include b/drivers/Makefile.include index 25a18bd2c..17f74e4c7 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 index 49574650d..61313b5b4 100644 --- a/drivers/include/kw2xrf.h +++ b/drivers/include/kw2xrf.h @@ -22,6 +22,11 @@ #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 @@ -29,7 +34,7 @@ extern "C" { #endif /** - * @brief Maximum packet length, including XBee API frame overhead + * @brief Maximum packet length */ #define KW2XRF_MAX_PKT_LENGTH (127U) @@ -47,58 +52,90 @@ extern "C" { */ #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 (0x0001) +#define KW2XRF_DEFAULT_PANID (0x0023) /** * @brief Default channel used after initialization */ -#define KW2XRF_DEFAULT_CHANNEL (13U) +#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 +#define MKW2XDRF_OUTPUT_POWER_MAX (8) /** * @brief Minimum output power of the kw2x device in dBm */ -#define MKW2XDRF_OUTPUT_POWER_MIN (-35) +#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 */ - /* Devide driver specific fields */ - uint8_t buf[KW2XRF_MAX_PKT_LENGTH]; /**> Buffer for the kw2x radio device */ + 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 */ - uint8_t options; /**< Bit field to save enable/disable options */ + 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 KW2XRF device to initialize + * @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 -ENODEV on invalid device descriptor + * @return <0 on error */ -int kw2xrf_init(kw2xrf_t *dev); +int kw2xrf_init(kw2xrf_t *dev, spi_t spi, spi_speed_t spi_speed, + gpio_t cs_pin, gpio_t int_pin); /** - * Reference to the KW2XRF driver interface. + * @brief Reference to the KW2XRF driver interface */ extern const ng_netdev_driver_t kw2xrf_driver; diff --git a/drivers/kw2xrf/Makefile b/drivers/kw2xrf/Makefile index e36d07d1c..48422e909 100644 --- a/drivers/kw2xrf/Makefile +++ b/drivers/kw2xrf/Makefile @@ -1,3 +1 @@ -MODULE = kw2xrf - include $(RIOTBASE)/Makefile.base diff --git a/drivers/kw2xrf/include/kw2xrf_reg.h b/drivers/kw2xrf/include/kw2xrf_reg.h index 9d80df24c..3fa1b0f7b 100644 --- a/drivers/kw2xrf/include/kw2xrf_reg.h +++ b/drivers/kw2xrf/include/kw2xrf_reg.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2014 Phytec Messtechnik GmbH + * 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 @@ -23,68 +23,80 @@ 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 */ +/** 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; + 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 */ + 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) @@ -176,54 +188,54 @@ enum mkw2xdrf_dregister { #define MKW2XDM_PWR_MODES_PMC_MODE (1 << 0) enum mkw2xdrf_iregister { - MKW2XDMI_PART_ID = 0x00, /* Part Identification */ - MKW2XDMI_XTAL_TRIM = 0x01, /* XTAL 32 MHz Trim */ - MKW2XDMI_MACPANID0_LSB = 0x03, /* MAC PAN ID for PAN0 */ - MKW2XDMI_MACPANID0_MSB = 0x04, /* MAC PAN ID for PAN0 */ - MKW2XDMI_MACSHORTADDRS0_LSB = 0x05, /* MAC Short Address for PAN0 */ - MKW2XDMI_MACSHORTADDRS0_MSB = 0x06, /* MAC Short Address for PAN0 */ - MKW2XDMI_MACLONGADDRS0_0 = 0x07, /* MAC Long Address for PAN0 */ - MKW2XDMI_MACLONGADDRS0_1 = 0x08, /* MAC Long Address for PAN0 */ - MKW2XDMI_MACLONGADDRS0_2 = 0x09, /* MAC Long Address for PAN0 */ - MKW2XDMI_MACLONGADDRS0_3 = 0x0a, /* MAC Long Address for PAN0 */ - MKW2XDMI_MACLONGADDRS0_4 = 0x0b, /* MAC Long Address for PAN0 */ - MKW2XDMI_MACLONGADDRS0_5 = 0x0c, /* MAC Long Address for PAN0 */ - MKW2XDMI_MACLONGADDRS0_6 = 0x0d, /* MAC Long Address for PAN0 */ - MKW2XDMI_MACLONGADDRS0_7 = 0x0e, /* MAC Long Address for PAN0 */ - MKW2XDMI_RX_FRAME_FILTER = 0x0f, /* Receive Frame Filter */ - MKW2XDMI_PLL_INT1 = 0x10, /* Frequency Integer for PAN1 */ - MKW2XDMI_PLL_FRAC1_LSB = 0x11, /* Frequency Fractional Value for PAN1 */ - MKW2XDMI_PLL_FRAC1_MSB = 0x12, /* Frequency Fractional Value for PAN1 */ - MKW2XDMI_MACPANID1_LSB = 0x13, /* Frequency Fractional Value for PAN1 */ - MKW2XDMI_MACPANID1_MSB = 0x14, /* Frequency Fractional Value for PAN1 */ - MKW2XDMI_MACSHORTADDRS1_LSB = 0x15, /* MAC Short Address for PAN1 */ - MKW2XDMI_MACSHORTADDRS1_MSB = 0x16, /* MAC Short Address for PAN1 */ - MKW2XDMI_MACLONGADDRS1_0 = 0x17, /* MAC Long Address for PAN1 */ - MKW2XDMI_MACLONGADDRS1_1 = 0x18, /* MAC Long Address for PAN1 */ - MKW2XDMI_MACLONGADDRS1_2 = 0x19, /* MAC Long Address for PAN1 */ - MKW2XDMI_MACLONGADDRS1_3 = 0x1a, /* MAC Long Address for PAN1 */ - MKW2XDMI_MACLONGADDRS1_4 = 0x1b, /* MAC Long Address for PAN1 */ - MKW2XDMI_MACLONGADDRS1_5 = 0x1c, /* MAC Long Address for PAN1 */ - MKW2XDMI_MACLONGADDRS1_6 = 0x1d, /* MAC Long Address for PAN1 */ - MKW2XDMI_MACLONGADDRS1_7 = 0x1e, /* MAC Long Address for PAN1 */ - MKW2XDMI_DUAL_PAN_CTRL = 0x1f, /* Dual PAN Control */ - MKW2XDMI_DUAL_PAN_DWELL = 0x20, /* Channel Frequency Dwell Time */ - MKW2XDMI_DUAL_PAN_STS = 0x21, /* Dual PAN Status */ - MKW2XDMI_CCA1_THRESH = 0x22, /* Clear Channel Assessment 1 Threshold */ - MKW2XDMI_CCA1_ED_OFFSET_COMP = 0x23, /* Clear Channel Assessment / ED Offset Computation */ - MKW2XDMI_LQI_OFFSET_COMP = 0x24, /* LQI Offset Computation */ - MKW2XDMI_CCA_CTRL = 0x25, /* CCA Control */ - MKW2XDMI_CCA2_CORR_PEAKS = 0x26, /* Clear Channel Assessment 2 Threshold Peak Compare */ - MKW2XDMI_CCA2_THRESH = 0x27, /* Clear Channel Assessment 2 Threshold */ - MKW2XDMI_GPIO_DATA = 0x2a, /* GPIO Data */ - MKW2XDMI_GPIO_DIR = 0x2b, /* GPIO Direction Control */ - MKW2XDMI_GPIO_PUL_EN = 0x2c, /* GPIO Pullup Enable */ - MKW2XDMI_GPIO_SEL = 0x2d, /* GPIO Pullup Select */ - MKW2XDMI_GPIO_DS = 0x2e, /* GPIO Drive Strength */ - MKW2XDMI_ANT_PAD_CTRL = 0x30, /* Antenna Control */ - MKW2XDMI_MISC_PAD_CTRL = 0x31, /* Miscellaneous Pad Control */ - MKW2XDMI_ANT_AGC_CTRL = 0x51, /* Antenna AGC and FAD Control */ - MKW2XDMI_LPPS_CTRL = 0x56, /* LPPS_CTRL */ + MKW2XDMI_PART_ID = 0x00, /**< Part Identification */ + MKW2XDMI_XTAL_TRIM = 0x01, /**< XTAL 32 MHz Trim */ + MKW2XDMI_MACPANID0_LSB = 0x03, /**< MAC PAN ID for PAN0 */ + MKW2XDMI_MACPANID0_MSB = 0x04, /**< MAC PAN ID for PAN0 */ + MKW2XDMI_MACSHORTADDRS0_LSB = 0x05, /**< MAC Short Address for PAN0 */ + MKW2XDMI_MACSHORTADDRS0_MSB = 0x06, /**< MAC Short Address for PAN0 */ + MKW2XDMI_MACLONGADDRS0_0 = 0x07, /**< MAC Long Address for PAN0 */ + MKW2XDMI_MACLONGADDRS0_1 = 0x08, /**< MAC Long Address for PAN0 */ + MKW2XDMI_MACLONGADDRS0_2 = 0x09, /**< MAC Long Address for PAN0 */ + MKW2XDMI_MACLONGADDRS0_3 = 0x0a, /**< MAC Long Address for PAN0 */ + MKW2XDMI_MACLONGADDRS0_4 = 0x0b, /**< MAC Long Address for PAN0 */ + MKW2XDMI_MACLONGADDRS0_5 = 0x0c, /**< MAC Long Address for PAN0 */ + MKW2XDMI_MACLONGADDRS0_6 = 0x0d, /**< MAC Long Address for PAN0 */ + MKW2XDMI_MACLONGADDRS0_7 = 0x0e, /**< MAC Long Address for PAN0 */ + MKW2XDMI_RX_FRAME_FILTER = 0x0f, /**< Receive Frame Filter */ + MKW2XDMI_PLL_INT1 = 0x10, /**< Frequency Integer for PAN1 */ + MKW2XDMI_PLL_FRAC1_LSB = 0x11, /**< Frequency Fractional Value for PAN1 */ + MKW2XDMI_PLL_FRAC1_MSB = 0x12, /**< Frequency Fractional Value for PAN1 */ + MKW2XDMI_MACPANID1_LSB = 0x13, /**< Frequency Fractional Value for PAN1 */ + MKW2XDMI_MACPANID1_MSB = 0x14, /**< Frequency Fractional Value for PAN1 */ + MKW2XDMI_MACSHORTADDRS1_LSB = 0x15, /**< MAC Short Address for PAN1 */ + MKW2XDMI_MACSHORTADDRS1_MSB = 0x16, /**< MAC Short Address for PAN1 */ + MKW2XDMI_MACLONGADDRS1_0 = 0x17, /**< MAC Long Address for PAN1 */ + MKW2XDMI_MACLONGADDRS1_1 = 0x18, /**< MAC Long Address for PAN1 */ + MKW2XDMI_MACLONGADDRS1_2 = 0x19, /**< MAC Long Address for PAN1 */ + MKW2XDMI_MACLONGADDRS1_3 = 0x1a, /**< MAC Long Address for PAN1 */ + MKW2XDMI_MACLONGADDRS1_4 = 0x1b, /**< MAC Long Address for PAN1 */ + MKW2XDMI_MACLONGADDRS1_5 = 0x1c, /**< MAC Long Address for PAN1 */ + MKW2XDMI_MACLONGADDRS1_6 = 0x1d, /**< MAC Long Address for PAN1 */ + MKW2XDMI_MACLONGADDRS1_7 = 0x1e, /**< MAC Long Address for PAN1 */ + MKW2XDMI_DUAL_PAN_CTRL = 0x1f, /**< Dual PAN Control */ + MKW2XDMI_DUAL_PAN_DWELL = 0x20, /**< Channel Frequency Dwell Time */ + MKW2XDMI_DUAL_PAN_STS = 0x21, /**< Dual PAN Status */ + MKW2XDMI_CCA1_THRESH = 0x22, /**< Clear Channel Assessment 1 Threshold */ + MKW2XDMI_CCA1_ED_OFFSET_COMP = 0x23, /**< Clear Channel Assessment / ED Offset Computation */ + MKW2XDMI_LQI_OFFSET_COMP = 0x24, /**< LQI Offset Computation */ + MKW2XDMI_CCA_CTRL = 0x25, /**< CCA Control */ + MKW2XDMI_CCA2_CORR_PEAKS = 0x26, /**< Clear Channel Assessment 2 Threshold Peak Compare */ + MKW2XDMI_CCA2_THRESH = 0x27, /**< Clear Channel Assessment 2 Threshold */ + MKW2XDMI_GPIO_DATA = 0x2a, /**< GPIO Data */ + MKW2XDMI_GPIO_DIR = 0x2b, /**< GPIO Direction Control */ + MKW2XDMI_GPIO_PUL_EN = 0x2c, /**< GPIO Pullup Enable */ + MKW2XDMI_GPIO_SEL = 0x2d, /**< GPIO Pullup Select */ + MKW2XDMI_GPIO_DS = 0x2e, /**< GPIO Drive Strength */ + MKW2XDMI_ANT_PAD_CTRL = 0x30, /**< Antenna Control */ + MKW2XDMI_MISC_PAD_CTRL = 0x31, /**< Miscellaneous Pad Control */ + MKW2XDMI_ANT_AGC_CTRL = 0x51, /**< Antenna AGC and FAD Control */ + MKW2XDMI_LPPS_CTRL = 0x56, /**< LPPS_CTRL */ }; #define MKW2XDMI_PART_ID_MANUF_ID_MASK 0x60u diff --git a/drivers/kw2xrf/include/kw2xrf_spi.h b/drivers/kw2xrf/include/kw2xrf_spi.h index 49baa354e..b7a29e101 100644 --- a/drivers/kw2xrf/include/kw2xrf_spi.h +++ b/drivers/kw2xrf/include/kw2xrf_spi.h @@ -29,11 +29,15 @@ extern "C" { /** * @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(void); +int kw2xrf_spi_init(spi_t spi, spi_speed_t spi_speed, + gpio_t cs_pin); /** * @brief Writes a byte to the kw2xrf register. @@ -94,7 +98,7 @@ void kw2xrf_read_iregs(uint8_t addr, uint8_t *buf, uint8_t length); * * @return number of bytes written. */ -void kw2xrf_write_fifo(uint8_t *data, radio_packet_length_t data_length); +void kw2xrf_write_fifo(uint8_t *data, uint8_t data_length); /** * @brief Reads multiple bytes from the kw2xrf fifo. @@ -104,7 +108,7 @@ void kw2xrf_write_fifo(uint8_t *data, radio_packet_length_t data_length); * * @return number of bytes read. */ -void kw2xrf_read_fifo(uint8_t *data, radio_packet_length_t data_length); +void kw2xrf_read_fifo(uint8_t *data, uint8_t data_length); #ifdef __cplusplus } diff --git a/drivers/kw2xrf/kw2xrf.c b/drivers/kw2xrf/kw2xrf.c index 79e223689..b7cff7c3c 100644 --- a/drivers/kw2xrf/kw2xrf.c +++ b/drivers/kw2xrf/kw2xrf.c @@ -16,7 +16,7 @@ * @author Jonas Remmert * @} */ -#include "crash.h" +#include "panic.h" #include "kw2xrf.h" #include "kw2xrf_spi.h" #include "kw2xrf_reg.h" @@ -24,6 +24,7 @@ #include "msg.h" #include "periph/gpio.h" #include "net/ng_netbase.h" +#include "net/ng_ieee802154.h" #define ENABLE_DEBUG (0) #include "debug.h" @@ -78,13 +79,12 @@ static const int level_lt[29] = { 7 }; -int _set_tx_power(kw2xrf_t *dev, uint8_t *val, size_t len) -{ +static gpio_t kw2xrf_gpio_int; - if (len < 1) { - return -EOVERFLOW; - } +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; } @@ -98,11 +98,12 @@ int _set_tx_power(kw2xrf_t *dev, uint8_t *val, size_t len) return 2; } -int _get_channel(kw2xrf_t *dev, uint8_t *val, size_t max) +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); @@ -114,78 +115,137 @@ int _get_channel(kw2xrf_t *dev, uint8_t *val, size_t max) return 2; } } + return -EINVAL; } -int _get_sequence(void) +int kw2xrf_get_sequence(void) { - int reg = 0; - reg = kw2xrf_read_dreg(MKW2XDM_PHY_CTRL1); - reg &= MKW2XDM_PHY_CTRL1_XCVSEQ_MASK; - return reg; + int reg = 0; + reg = kw2xrf_read_dreg(MKW2XDM_PHY_CTRL1); + reg &= MKW2XDM_PHY_CTRL1_XCVSEQ_MASK; + return reg; } -void _set_sequence(kw2xrf_physeq_t seq) +void kw2xrf_set_sequence(kw2xrf_t *dev, kw2xrf_physeq_t seq) { - /* Mask all interrupts; disable interrupts for exclusive SPI access */ - int tmp_irq_state = disableIRQ(); - uint8_t reg = kw2xrf_read_dreg(MKW2XDM_PHY_CTRL4); - reg |= MKW2XDM_PHY_CTRL4_TRCV_MSK; - kw2xrf_write_dreg(MKW2XDM_PHY_CTRL4, reg); - restoreIRQ(tmp_irq_state); + 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); - if (kw2xrf_read_dreg(MKW2XDM_SEQ_STATE)) { /* abort any ongoing sequence */ - DEBUG("tx: abort SEQ_STATE: %x\n", kw2xrf_read_dreg(MKW2XDM_SEQ_STATE)); + 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); - while (kw2xrf_read_dreg(MKW2XDM_SEQ_STATE)); - /* Clear all pending interrupts */ + /* 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); - /* Mask all possible interrupts */ - reg = kw2xrf_read_dreg(MKW2XDM_PHY_CTRL2); - reg |= ~(MKW2XDM_PHY_CTRL2_CRC_MSK); - kw2xrf_write_dreg(MKW2XDM_PHY_CTRL2, reg); + gpio_irq_enable(kw2xrf_gpio_int); + } - reg = kw2xrf_read_dreg(MKW2XDM_PHY_CTRL3); - reg |= (MKW2XDM_PHY_CTRL3_PB_ERR_MSK | MKW2XDM_PHY_CTRL3_WAKE_MSK); - reg &= (MKW2XDM_PHY_CTRL3_PB_ERR_MSK | MKW2XDM_PHY_CTRL3_WAKE_MSK); - kw2xrf_write_dreg(MKW2XDM_PHY_CTRL2, reg); +#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, that is set when sequence was completed */ + + /* 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); - DEBUG("kw2xrf: Set sequence to %i\n", seq); - - /* Unmask all interrupts */ - reg = kw2xrf_read_dreg(MKW2XDM_PHY_CTRL4); - reg &= ~(MKW2XDM_PHY_CTRL4_TRCV_MSK); - kw2xrf_write_dreg(MKW2XDM_PHY_CTRL4, reg); } -int _set_channel(kw2xrf_t *dev, uint8_t *val, size_t len) +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 = _get_sequence(); - if(old_seq){ - _set_sequence(XCVSEQ_IDLE); + uint8_t old_seq = kw2xrf_get_sequence(); + + if (old_seq) { + kw2xrf_set_sequence(dev, XCVSEQ_IDLE); } - if (val[0] < 11 || val[0] > 26) { - DEBUG("Invalid channel %i set. Valid channels are 11 through 26\n", val[0]); - return -EINVAL; + + 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) { + if ((len != 2) || (val[1] != 0)) { DEBUG("kw2xrf: set channel failed, len: %u, val[0]:%u\n", len, val[0]); return -EINVAL; } @@ -202,8 +262,10 @@ int _set_channel(kw2xrf_t *dev, uint8_t *val, size_t len) 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)); - if(old_seq){ - _set_sequence(old_seq); + DEBUG("kw2xrf: set channel to %u\n", val[0]); + + if (old_seq) { + kw2xrf_set_sequence(dev, old_seq); } return 2; @@ -213,93 +275,59 @@ void kw2xrf_irq_handler(void *args) { msg_t msg; kw2xrf_t *dev = (kw2xrf_t *)args; - uint8_t irqst1 = kw2xrf_read_dreg(MKW2XDM_IRQSTS1); - DEBUG("kw2xrf_irq_handler: irqst1: %x\n",irqst1); - if ((irqst1 & MKW2XDM_IRQSTS1_RXIRQ) && (irqst1 & MKW2XDM_IRQSTS1_SEQIRQ)) { - msg.content.value = NETDEV_EVENT_RX_COMPLETE; - kw2xrf_write_dreg(MKW2XDM_IRQSTS1, MKW2XDM_IRQSTS1_RXIRQ | MKW2XDM_IRQSTS1_SEQIRQ); - } - else if ((irqst1 & MKW2XDM_IRQSTS1_TXIRQ) && (irqst1 & MKW2XDM_IRQSTS1_SEQIRQ)) { - msg.content.value = NETDEV_EVENT_TX_COMPLETE; - kw2xrf_write_dreg(MKW2XDM_IRQSTS1, MKW2XDM_IRQSTS1_TXIRQ | MKW2XDM_IRQSTS1_SEQIRQ); - } - else if ((irqst1 & MKW2XDM_IRQSTS1_CCAIRQ) && (irqst1 & MKW2XDM_IRQSTS1_SEQIRQ)) { - msg.content.value = NETDEV_EVENT_TX_STARTED; - kw2xrf_write_dreg(MKW2XDM_IRQSTS1, MKW2XDM_IRQSTS1_CCAIRQ | MKW2XDM_IRQSTS1_SEQIRQ); - } - else { - msg.content.value = ISR_EVENT_UNKNOWN; - /* 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); - } - /* packet is complete */ + /* 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) +void kw2xrf_init_interrupts(kw2xrf_t *dev, gpio_t int_pin) { - /* Disable all interrups: - * Selectively enable only one interrupt source selectively in sequence manager. - * After reset state all interrupts are disabled, except WAKE_IRQ. - */ + /* Clear all pending interrupts */ kw2xrf_write_dreg(MKW2XDM_IRQSTS1, 0x7f); kw2xrf_write_dreg(MKW2XDM_IRQSTS2, 0x03); kw2xrf_write_dreg(MKW2XDM_IRQSTS3, 0xff); - /* Mask all possible interrupts */ - int reg = kw2xrf_read_dreg(MKW2XDM_PHY_CTRL2); - reg |= ~(MKW2XDM_PHY_CTRL2_CRC_MSK); - kw2xrf_write_dreg(MKW2XDM_PHY_CTRL2, reg); - - reg = kw2xrf_read_dreg(MKW2XDM_PHY_CTRL3); - reg |= (MKW2XDM_PHY_CTRL3_PB_ERR_MSK | MKW2XDM_PHY_CTRL3_WAKE_MSK); - reg &= (MKW2XDM_PHY_CTRL3_PB_ERR_MSK | MKW2XDM_PHY_CTRL3_WAKE_MSK); - kw2xrf_write_dreg(MKW2XDM_PHY_CTRL2, reg); + /* 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(GPIO_KW2XDRF, GPIO_NOPULL, GPIO_FALLING, &kw2xrf_irq_handler, dev); + gpio_init_int(int_pin, GPIO_NOPULL, GPIO_FALLING, &kw2xrf_irq_handler, dev); } -int _set_pan(kw2xrf_t *dev, uint8_t *val, size_t len) +int kw2xrf_set_pan(kw2xrf_t *dev, uint16_t pan) { - if (len != 2) { - return -EINVAL; - } - kw2xrf_write_iregs(MKW2XDMI_MACPANID0_LSB, val, 2); - dev->radio_pan = val[1]; - dev->radio_pan = (dev->radio_pan) <<8; - dev->radio_pan |= val[0]; - return 2; -} + dev->radio_pan = pan; -int _get_pan(kw2xrf_t *dev, uint8_t *val, size_t max) -{ - if (max < 2) { - return -EOVERFLOW; - } - kw2xrf_read_iregs(MKW2XDMI_MACPANID0_LSB, val, 2); + 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 _get_proto(kw2xrf_t *dev, uint8_t *val, size_t max) +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 _set_proto(kw2xrf_t *dev, uint8_t *val, size_t len) +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); } @@ -321,429 +349,825 @@ int kw2xrf_on(kw2xrf_t *dev) (MKW2XDM_PWR_MODES_XTALEN | MKW2XDM_PWR_MODES_PMC_MODE)); /* abort any ongoing sequence */ - _set_sequence(XCVSEQ_IDLE); + kw2xrf_set_sequence(dev, XCVSEQ_IDLE); dev->state = NETCONF_STATE_SLEEP; return 0; } -int _get_addr_short(kw2xrf_t *dev, uint8_t *val, size_t len) +int kw2xrf_set_addr(kw2xrf_t *dev, uint16_t addr) { - if (len < 2) { - return -EOVERFLOW; - } - kw2xrf_read_iregs(MKW2XDMI_MACSHORTADDRS0_LSB, val, 2); - return 2; + 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 _get_addr_long(kw2xrf_t *dev, uint8_t *val, size_t len) +int kw2xrf_set_addr_long(kw2xrf_t *dev, uint64_t addr) { - if (len < 8) { - return -EOVERFLOW; + for (int i = 0; i < 8; i++) { + dev->addr_long[i] = (addr >> ((7 - i) * 8)); } - kw2xrf_read_iregs(MKW2XDMI_MACLONGADDRS0_0, val, 8); - return 8; -} -int _set_addr(kw2xrf_t *dev, uint8_t *val, size_t len) -{ - if (len == 2) { - kw2xrf_write_iregs(MKW2XDMI_MACSHORTADDRS0_LSB, val, 2); - dev->addr_short[0] = val[0]; - dev->addr_short[1] = val[1]; - return 2; - } - else if (len == 8){ - kw2xrf_write_iregs(MKW2XDMI_MACLONGADDRS0_0, val, 8); - for (int i=0; i<8; i++) { - dev->addr_long[i] = val[i]; - } - return 8; - } - return -ENOTSUP; + kw2xrf_write_iregs(MKW2XDMI_MACLONGADDRS0_0, (dev->addr_long), 8); + + return sizeof(uint64_t); } -int kw2xrf_init(kw2xrf_t *dev) { +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]; - uint8_t tx_pwr = 0; + kw2xrf_gpio_int = int_pin; /* check device parameters */ if (dev == NULL) { return -ENODEV; } - kw2xrf_spi_init(); - if (!(kw2xrf_on(dev) == 0)) { + kw2xrf_spi_init(spi, spi_speed, cs_pin); + + if (kw2xrf_on(dev) != 0) { core_panic(0x42, "Could not start MKW2XD radio transceiver"); } - /* Gerneral initialization of interrupt sources. - * sets radio to idle modewith all interrupt masked + /* General initialization of interrupt sources. + * sets radio to idle mode with all interrupt masked */ - kw2xrf_init_interrupts(dev); + kw2xrf_init_interrupts(dev, kw2xrf_gpio_int); /* set device driver */ dev->driver = &kw2xrf_driver; - /* set default options */ + /* set default option */ dev->proto = KW2XRF_DEFAULT_PROTOCOL; - dev->options = 0; - dev->addr_short[0] = (uint8_t)(KW2XRF_DEFAULT_SHORT_ADDR >> 8); - dev->addr_short[1] = (uint8_t)(KW2XRF_DEFAULT_SHORT_ADDR); + dev->option = 0; + /* set default short address */ - _set_addr(dev, dev->addr_short, 2); - /* load long address */ - _get_addr_long(dev, dev->addr_long, 8); + uint16_t addr_conv = KW2XRF_DEFAULT_SHORT_ADDR >> 8; + addr_conv |= (KW2XRF_DEFAULT_SHORT_ADDR << 8) & 0xff00; + kw2xrf_set_addr(dev, addr_conv); - _set_tx_power(dev, &tx_pwr, 1); + /* 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 */ - tmp[0] = KW2XRF_DEFAULT_CHANNEL; + dev->radio_channel = KW2XRF_DEFAULT_CHANNEL; + tmp[0] = dev->radio_channel; tmp[1] = 0; - _set_channel(dev, tmp, 2); + kw2xrf_set_channel(dev, tmp, 2); /* set default PAN ID */ - tmp[0] = (uint8_t)(KW2XRF_DEFAULT_PANID & 0xff); - tmp[1] = (uint8_t)(KW2XRF_DEFAULT_PANID >> 8); - _set_pan(dev, tmp, 2); + kw2xrf_set_pan(dev, KW2XRF_DEFAULT_PANID); /* CCA Setup */ reg = kw2xrf_read_dreg(MKW2XDM_PHY_CTRL4); - reg |= MKW2XDM_PHY_CTRL4_CCATYPE(1); /* Set up CCA mode 1 (RSSI threshold) */ + /* 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_DEFAULT_CHANNEL, KW2XRF_DEFAULT_PANID); - /* TODO: For testing, ignores CRCVALID in receive mode */ - reg = kw2xrf_read_dreg(MKW2XDM_PHY_CTRL2); - reg &= ~(MKW2XDM_PHY_CTRL2_CRC_MSK); - kw2xrf_write_dreg(MKW2XDM_PHY_CTRL2, reg); + kw2xrf_set_option(dev, KW2XRF_OPT_AUTOACK, true); + kw2xrf_set_option(dev, KW2XRF_OPT_CSMA, true); - /* TODO: For testing, set up promiscous mode */ - reg = kw2xrf_read_dreg(MKW2XDM_PHY_CTRL4); - reg |= MKW2XDM_PHY_CTRL4_PROMISCUOUS; - kw2xrf_write_dreg(MKW2XDM_PHY_CTRL4, reg); + /* Switch to Receive state per default after initialization */ + kw2xrf_set_sequence(dev, XCVSEQ_RECEIVE); return 0; } -int _add_cb(ng_netdev_t *dev, ng_netdev_event_cb_t cb) +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 _rem_cb(ng_netdev_t *dev, ng_netdev_event_cb_t cb) +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; } -int _get(ng_netdev_t *netdev, ng_netconf_opt_t opt, void *value, size_t max_len) +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: - return _get_addr_short(dev, (uint8_t *)value, max_len); + 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: - return _get_addr_long(dev, (uint8_t *)value, max_len); - case NETCONF_OPT_CHANNEL: - return _get_channel(dev, (uint8_t *)value, max_len); + 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: - return _get_pan(dev, (uint8_t *)value, max_len); + 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 _get_proto(dev, (uint8_t *)value, max_len); + return kw2xrf_get_proto(dev, (uint8_t *)value, max_len); + case NETCONF_OPT_STATE: if (max_len < sizeof(ng_netconf_state_t)) { return -EOVERFLOW; } - value = (int *)&(dev->state); + + *(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; } } -int _set(ng_netdev_t *netdev, ng_netconf_opt_t opt, void *value, size_t value_len) +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 _set_channel(dev, (uint8_t *)value, value_len); + return kw2xrf_set_channel(dev, (uint8_t *)value, value_len); + case NETCONF_OPT_ADDRESS: - return _set_addr(dev, (uint8_t *)value, value_len); + 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: - return _set_pan(dev, (uint8_t *)value, value_len); - case NETCONF_OPT_IS_CHANNEL_CLR: - _set_sequence(XCVSEQ_CCA); - return 0; + if (value_len > sizeof(uint16_t)) { + return -EOVERFLOW; + } + + return kw2xrf_set_pan(dev, *((uint16_t *)value)); + case NETCONF_OPT_TX_POWER: - _set_tx_power(dev, (uint8_t *)value, value_len); - return 0; + 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 _set_proto(dev, (uint8_t *)value, value_len); - case NETCONF_OPT_CCA_BEFORE_TX: - reg = kw2xrf_read_dreg(MKW2XDM_PHY_CTRL1); - reg |= MKW2XDM_PHY_CTRL1_CCABFRTX; /* Set up CCA before TX */ - kw2xrf_write_dreg(MKW2XDM_PHY_CTRL1, reg); - return 0; + return kw2xrf_set_proto(dev, (uint8_t *)value, value_len); + case NETCONF_OPT_AUTOACK: - reg = kw2xrf_read_dreg(MKW2XDM_PHY_CTRL1); /* Set up HW generated automatic ACK after Receive */ - reg |= MKW2XDM_PHY_CTRL1_AUTOACK; - kw2xrf_write_dreg(MKW2XDM_PHY_CTRL1, reg); - return 0; + 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"); - _set_sequence(XCVSEQ_TRANSMIT); - dev->state = NETCONF_STATE_TX; - return 0; - } - else if(*((ng_netconf_state_t *)value) == NETCONF_STATE_SLEEP) { - _set_sequence(XCVSEQ_IDLE); - dev->state = NETCONF_STATE_SLEEP; - return 0; - } - else if(*((ng_netconf_state_t *)value) == NETCONF_STATE_IDLE) { - _set_sequence(XCVSEQ_RECEIVE); - dev->state = NETCONF_STATE_IDLE; - return 0; - } - /* TODO: Implement Off state here, when LPM functions are implemented */ + 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; } } -void _isr_event(ng_netdev_t *netdev, uint32_t event_type) +/* TODO: generalize and move to ng_ieee802154 */ +/* TODO: include security header implications */ +static size_t _get_frame_hdr_len(uint8_t *mhr) { - kw2xrf_t *dev = (kw2xrf_t *)netdev; - uint8_t pkt_len = 0; - ng_netif_hdr_t *hdr; - uint8_t index = 0; - uint8_t src_addr_len = 0; - uint8_t dst_addr_len = 0; + uint8_t tmp; + size_t len = 3; - /* check device */ - if (dev == NULL) { - return; + /* 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; } - if (event_type == NETDEV_EVENT_RX_COMPLETE) { - pkt_len = kw2xrf_read_dreg(MKW2XDM_RX_FRM_LEN); + tmp = (mhr[1] & NG_IEEE802154_FCF_SRC_ADDR_MASK); - /* read PSDU */ - kw2xrf_read_fifo(dev->buf, pkt_len + 1); - if((dev->buf[0] & 0x07) == 0x00) { - DEBUG("kw2xrf: Beacon received; not handled yed -> dischard message\n"); - _set_sequence(XCVSEQ_RECEIVE); - return; - } - if((dev->buf[0] & 0x07) == 0x02) { - DEBUG("kw2xrf: ACK received; not handled yed -> dischard message\n"); - _set_sequence(XCVSEQ_RECEIVE); - return; - } - if((dev->buf[0] & 0x07) == 0x03) { - DEBUG("kw2xrf: MAC-cmd received; not handled yed -> dischard message\n"); - _set_sequence(XCVSEQ_RECEIVE); - return; - } - if((dev->buf[0] & 0x07) != 0x01) { /* No Data frame either */ - DEBUG("kw2xrf: undefined message received; -> dischard message\n"); - _set_sequence(XCVSEQ_RECEIVE); - return; + if (tmp == NG_IEEE802154_FCF_SRC_ADDR_VOID) { + return len; + } + else { + if (!(mhr[0] & NG_IEEE802154_FCF_PAN_COMP)) { + len += 2; } - DEBUG("kw2xrf: Message received: size %i\n", pkt_len); - /* src 16bit addr */ - if((dev->buf[1] & 0x0c) == 0x08) { - src_addr_len = 0x02; - } - /* src 64bit addr */ - else if((dev->buf[1] & 0x0c) == 0x0c) { - src_addr_len = 0x08; - } - else { - DEBUG("Bogus src address length.\n"); + if (tmp == NG_IEEE802154_FCF_SRC_ADDR_SHORT) { + return (len + 2); } - /* dst 16bit addr */ - if((dev->buf[1] & 0xc0) == 0x80) { - dst_addr_len = 0x02; + else if (tmp == NG_IEEE802154_FCF_SRC_ADDR_LONG) { + return (len + 8); } - /* dst 64bit addr */ - else if((dev->buf[1] & 0xc0) == 0xc0) { - dst_addr_len = 0x08; - } - else { - DEBUG("Bogus src address length.\n"); - } - DEBUG("Src addr len: %i, Dst addr len: %i", src_addr_len, dst_addr_len); - /* allocate a pktsnip for generic header */ - ng_pktsnip_t *hdr_snip = ng_pktbuf_add(NULL, NULL, sizeof(ng_netif_hdr_t)+ - src_addr_len, NG_NETTYPE_UNDEF); - if (hdr_snip == NULL) { - DEBUG("kw2xrf: ERROR allocating header in packet buffer on RX\n"); - ng_pktbuf_release(hdr_snip); - _set_sequence(XCVSEQ_RECEIVE); - return; - } - hdr = (ng_netif_hdr_t *)hdr_snip->data; - /* init generic header */ - ng_netif_hdr_init(hdr, src_addr_len, dst_addr_len); - /* append src address into memory */ - if(hdr->dst_l2addr_len == 2) { - ng_netif_hdr_set_src_addr(hdr, &(dev->buf[7]), hdr->src_l2addr_len); - index = 7 + hdr->src_l2addr_len; + } + + 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 { - ng_netif_hdr_set_src_addr(hdr, &(dev->buf[11]), hdr->src_l2addr_len); - index = 11 + hdr->src_l2addr_len; + } + 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]; } - hdr->if_pid = thread_getpid(); - hdr->rssi = kw2xrf_read_dreg(MKW2XDM_RSSI_CCA_CNT); - hdr->lqi = dev->buf[pkt_len]; - - ng_pktsnip_t *payload_snip = ng_pktbuf_add(hdr_snip, (void *)&(dev->buf[index + 1]), - pkt_len - index, dev->proto); - if(payload_snip == NULL) { - DEBUG("kw2xrf: ERROR allocating payload in packet buffer on RX\n"); - ng_pktbuf_release(hdr_snip); - _set_sequence(XCVSEQ_RECEIVE); + } + + 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; } - dev->event_cb(NETDEV_EVENT_RX_COMPLETE, payload_snip); - _set_sequence(XCVSEQ_RECEIVE); + + 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; } - if (event_type == NETDEV_EVENT_TX_STARTED) { - uint8_t irqst2 = kw2xrf_read_dreg(MKW2XDM_IRQSTS2); + /* 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); } - - if (event_type == NETDEV_EVENT_TX_COMPLETE) { - /* Device is automatically in Radio-idle state when TX is done */ - dev->state = NETCONF_STATE_SLEEP; - DEBUG("kw2xrf: TX Complete\n"); + 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; } } -int _send(ng_netdev_t *netdev, ng_pktsnip_t *pkt) +/* TODO: Move to ng_ieee802.15.4 as soon as ready */ +uint8_t _assemble_tx_buf(kw2xrf_t *dev, ng_pktsnip_t *pkt) { - uint8_t index=0; - kw2xrf_t *dev = (kw2xrf_t*) netdev; ng_netif_hdr_t *hdr; hdr = (ng_netif_hdr_t *)pkt->data; + int index = 0; - if (pkt == NULL) { - return -ENOMSG; - } - if (netdev == NULL) { + 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 */ - dev->buf[1] = 0x01; + /* 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) { - index = 4; /* set to short addressing mode */ dev->buf[2] = 0x88; - /* set destination pan_id TODO: not currect currently */ - dev->buf[index++] = (uint8_t)dev->radio_pan; - dev->buf[index++] = (uint8_t)((dev->radio_pan)>>8); - index += 2; + /* 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; - dev->buf[index++] = (uint8_t)((dev->radio_pan)>>8); + //dev->buf[index++] = (uint8_t)((dev->radio_pan) >> 8); + //dev->buf[index++] = (uint8_t)((dev->radio_pan) & 0xff); /* set source address */ - dev->buf[index++] = dev->addr_short[1]; - dev->buf[index++] = dev->addr_short[0]; + dev->buf[index++] = (uint8_t)(dev->addr_short[0]); + dev->buf[index++] = (uint8_t)(dev->addr_short[1]); } - else if (hdr->dst_l2addr_len == 8) { + + 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; + dev->buf[2] |= 0xcc; /* set destination address located directly after ng_ifhrd_t in memory */ - index = 4; + 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 */ - for(int i=0; i<8; i++) { - dev->buf[index++] = dev->addr_long[i]; + 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 (pkt) { + while (payload) { /* check we don't exceed FIFO size */ - if (index+2+pkt->size > KW2XRF_MAX_PKT_LENGTH) { + 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 < pkt->size; i++) { - uint8_t *tmp = pkt->data; - dev->buf[index+i+1] = tmp[i]; + + for (int i = 0; i < payload->size; i++) { + uint8_t *tmp = payload->data; + dev->buf[index + i] = tmp[i]; } + /* count bytes */ - index += pkt->size; + index += payload->size; /* next snip */ - pkt = pkt->next; + payload = payload->next; } - dev->buf[0] = index+2; /* set packet size, reserve additional */ - DEBUG("kw2xrf: send packet with size %i\n", dev->buf[0]); + 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->options&(1<option & KW2XRF_OPT_PRELOADING) == NETCONF_DISABLE) { DEBUG("kw2xrf: Sending now.\n"); - _set_sequence(XCVSEQ_TRANSMIT); + kw2xrf_set_sequence(dev, XCVSEQ_TRANSMIT); } - dev->state = NETCONF_STATE_TX; return index; } /* implementation of the netdev interface */ const ng_netdev_driver_t kw2xrf_driver = { - .send_data = _send, - .add_event_callback = _add_cb, - .rem_event_callback = _rem_cb, - .get = _get, - .set = _set, - .isr_event = _isr_event, + .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 index dc9de6553..2c2ff4834 100644 --- a/drivers/kw2xrf/kw2xrf_spi.c +++ b/drivers/kw2xrf/kw2xrf_spi.c @@ -31,42 +31,46 @@ static uint8_t ibuf[KW2XRF_IBUF_LENGTH]; -#ifndef KW2XRF_SPI_SPEED -#define KW2XRF_SPI_SPEED SPI_SPEED_5MHZ -#endif +/** Set up in kw2xrf_spi_init during initialization */ +static gpio_t kw2xrf_cs_pin; +static spi_t kw2xrf_spi; -inline void kw2xrf_spi_transfer_head(void) +void kw2xrf_spi_transfer_head(void) { #if KW2XRF_SHARED_SPI - spi_acquire(KW2XRF_SPI); - gpio_clear(KW2XRF_CS_GPIO); + spi_acquire(kw2xrf_spi); + gpio_clear(kw2xrf_cs_pin); #endif } -inline void kw2xrf_spi_transfer_tail(void) +void kw2xrf_spi_transfer_tail(void) { #if KW2XRF_SHARED_SPI - gpio_set(KW2XRF_CS_GPIO); - spi_release(KW2XRF_SPI); + gpio_set(kw2xrf_cs_pin); + spi_release(kw2xrf_spi); #endif } -int kw2xrf_spi_init(void) +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); + spi_acquire(kw2xrf_spi); #endif - res = spi_init_master(KW2XRF_SPI, SPI_CONF_FIRST_RISING, KW2XRF_SPI_SPEED); + 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_GPIO, GPIO_NOPULL); - gpio_set(KW2XRF_CS_GPIO); + 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", - spi_dev, res); + kw2xrf_spi, res); return -1; } @@ -76,7 +80,7 @@ int kw2xrf_spi_init(void) void kw2xrf_write_dreg(uint8_t addr, uint8_t value) { kw2xrf_spi_transfer_head(); - spi_transfer_reg(KW2XRF_SPI, addr, value, NULL); + spi_transfer_reg(kw2xrf_spi, addr, value, NULL); kw2xrf_spi_transfer_tail(); return; } @@ -85,8 +89,8 @@ 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); + spi_transfer_reg(kw2xrf_spi, (addr | MKW2XDRF_REG_READ), + 0x0, (char *)&value); kw2xrf_spi_transfer_tail(); return value; } @@ -96,6 +100,7 @@ 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++) { @@ -103,7 +108,7 @@ void kw2xrf_write_iregs(uint8_t addr, uint8_t *buf, uint8_t length) } kw2xrf_spi_transfer_head(); - spi_transfer_regs(KW2XRF_SPI, MKW2XDM_IAR_INDEX, + spi_transfer_regs(kw2xrf_spi, MKW2XDM_IAR_INDEX, (char *)ibuf, NULL, length + 1); kw2xrf_spi_transfer_tail(); @@ -115,10 +120,11 @@ 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, + spi_transfer_regs(kw2xrf_spi, MKW2XDM_IAR_INDEX | MKW2XDRF_REG_READ, (char *)ibuf, (char *)ibuf, length + 1); kw2xrf_spi_transfer_tail(); @@ -129,19 +135,19 @@ void kw2xrf_read_iregs(uint8_t addr, uint8_t *buf, uint8_t length) return; } -void kw2xrf_write_fifo(uint8_t *data, radio_packet_length_t length) +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); + spi_transfer_regs(kw2xrf_spi, MKW2XDRF_BUF_WRITE, + (char *)data, NULL, length); kw2xrf_spi_transfer_tail(); } -void kw2xrf_read_fifo(uint8_t *data, radio_packet_length_t length) +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); + 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; +}