diff --git a/boards/airfy-beacon/Makefile.dep b/boards/airfy-beacon/Makefile.dep new file mode 100644 index 000000000..7de44689f --- /dev/null +++ b/boards/airfy-beacon/Makefile.dep @@ -0,0 +1,3 @@ +ifneq (,$(filter gnrc_netdev_default netdev_default,$(USEMODULE))) + USEMODULE += nrfmin +endif diff --git a/boards/microbit/Makefile.dep b/boards/microbit/Makefile.dep index fc6713414..e54ceaf6e 100644 --- a/boards/microbit/Makefile.dep +++ b/boards/microbit/Makefile.dep @@ -6,3 +6,7 @@ endif ifneq (,$(filter saul_default,$(USEMODULE))) USEMODULE += saul_gpio endif + +ifneq (,$(filter gnrc_netdev_default netdev_default,$(USEMODULE))) + USEMODULE += nrfmin +endif diff --git a/boards/microbit/include/periph_conf.h b/boards/microbit/include/periph_conf.h index 262e52199..cfc5582e8 100644 --- a/boards/microbit/include/periph_conf.h +++ b/boards/microbit/include/periph_conf.h @@ -33,7 +33,7 @@ extern "C" { * @{ */ #define CLOCK_CORECLOCK (16000000U) /* fixed for all NRF51822 */ -#define CLOCK_CRYSTAL (0U) /* set to 0: internal RC oscillator +#define CLOCK_CRYSTAL (16U) /* set to 0: internal RC oscillator 16: 16MHz crystal 32: 32MHz crystal */ /** @} */ diff --git a/boards/nrf51dongle/Makefile.dep b/boards/nrf51dongle/Makefile.dep new file mode 100644 index 000000000..7de44689f --- /dev/null +++ b/boards/nrf51dongle/Makefile.dep @@ -0,0 +1,3 @@ +ifneq (,$(filter gnrc_netdev_default netdev_default,$(USEMODULE))) + USEMODULE += nrfmin +endif diff --git a/boards/nrf52dk/Makefile.dep b/boards/nrf52dk/Makefile.dep index 27225b7b4..e9b18989d 100644 --- a/boards/nrf52dk/Makefile.dep +++ b/boards/nrf52dk/Makefile.dep @@ -2,6 +2,12 @@ ifneq (,$(filter saul_default,$(USEMODULE))) USEMODULE += saul_gpio endif -ifneq (,$(filter gnrc_netdev_default,$(USEMODULE))) - USEPKG += nordic_softdevice_ble +ifeq (,$(filter nrfmin,$(USEMODULE))) + ifneq (,$(filter gnrc_netdev_default,$(USEMODULE))) + USEPKG += nordic_softdevice_ble + endif +else + ifneq (,$(filter gnrc_netdev_default netdev_default,$(USEMODULE))) + USEMODULE += nrfmin + endif endif diff --git a/boards/nrf52dk/Makefile.features b/boards/nrf52dk/Makefile.features index b47349092..a12def18a 100644 --- a/boards/nrf52dk/Makefile.features +++ b/boards/nrf52dk/Makefile.features @@ -9,6 +9,7 @@ FEATURES_PROVIDED += periph_uart # Various other features (if any) FEATURES_PROVIDED += cpp +FEATURES_PROVIDED += radio_nrfmin # The board MPU family (used for grouping by the CI system) FEATURES_MCU_GROUP = cortex_m4_3 diff --git a/boards/nrf6310/Makefile.dep b/boards/nrf6310/Makefile.dep new file mode 100644 index 000000000..7de44689f --- /dev/null +++ b/boards/nrf6310/Makefile.dep @@ -0,0 +1,3 @@ +ifneq (,$(filter gnrc_netdev_default netdev_default,$(USEMODULE))) + USEMODULE += nrfmin +endif diff --git a/boards/pca10000/Makefile.dep b/boards/pca10000/Makefile.dep new file mode 100644 index 000000000..7de44689f --- /dev/null +++ b/boards/pca10000/Makefile.dep @@ -0,0 +1,3 @@ +ifneq (,$(filter gnrc_netdev_default netdev_default,$(USEMODULE))) + USEMODULE += nrfmin +endif diff --git a/boards/pca10005/Makefile.dep b/boards/pca10005/Makefile.dep new file mode 100644 index 000000000..7de44689f --- /dev/null +++ b/boards/pca10005/Makefile.dep @@ -0,0 +1,3 @@ +ifneq (,$(filter gnrc_netdev_default netdev_default,$(USEMODULE))) + USEMODULE += nrfmin +endif diff --git a/boards/yunjia-nrf51822/Makefile.dep b/boards/yunjia-nrf51822/Makefile.dep new file mode 100644 index 000000000..7de44689f --- /dev/null +++ b/boards/yunjia-nrf51822/Makefile.dep @@ -0,0 +1,3 @@ +ifneq (,$(filter gnrc_netdev_default netdev_default,$(USEMODULE))) + USEMODULE += nrfmin +endif diff --git a/cpu/nrf51/Makefile b/cpu/nrf51/Makefile index f16a1bd3f..7600b5893 100644 --- a/cpu/nrf51/Makefile +++ b/cpu/nrf51/Makefile @@ -4,11 +4,6 @@ MODULE = cpu # add a list of subdirectories, that should also be build DIRS = periph $(RIOTCPU)/cortexm_common $(RIOTCPU)/nrf5x_common -# build one of the radio drivers, if enabled -ifneq (,$(filter radio_nrfmin,$(USEMODULE))) - DIRS += radio/nrfmin -endif - # (file triggers compiler bug. see #5775) SRC_NOLTO += vectors.c diff --git a/cpu/nrf51/include/cpu_conf.h b/cpu/nrf51/include/cpu_conf.h index 56fc18011..cb4c5e1d9 100644 --- a/cpu/nrf51/include/cpu_conf.h +++ b/cpu/nrf51/include/cpu_conf.h @@ -49,6 +49,16 @@ extern "C" { #endif /** @} */ +/** + * @brief Due to RAM restrictions, we need to limit the default GNRC packet + * buffer size on these CPUs + * @{ + */ +#ifndef GNRC_PKTBUF_SIZE +#define GNRC_PKTBUF_SIZE (2048) +#endif +/** @} */ + #ifdef __cplusplus } #endif diff --git a/cpu/nrf51/include/nrfmin.h b/cpu/nrf51/include/nrfmin.h deleted file mode 100644 index b7fb49501..000000000 --- a/cpu/nrf51/include/nrfmin.h +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright (C) 2015 Freie Universität Berlin - * - * 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_nrf51822_nrfmin NRF Minimal Radio Driver - * @ingroup drivers_netdev - * @brief Minimal driver for the NRF51822 radio - * - * This driver enables the use of the NRF51822 radio in a IEEE802.15.4 like - * fashion. In the current state, the driver is only be meant to be used with - * the netdev/netapi based network stack, while only being able to communicate - * with other NRF51822 devices using the same driver. - * - * The driver is using a Nordic proprietary physical layer, configured to for a - * bitrate of 2Mbit. The payload length is set to a maximum length of 250 byte. - * The proprietary frame format used has the following format: - * - * byte0 | byte1 - byte2 | byte3 - byte4 | byte5 byte6 | byte7 - byteN - * ------ | ------------- | ------------- | ------------- | ------------- - * length | src_addr | dst_addr | proto | payload... - * - * An IEEE802.15.4 like behavior is reflected in the following way: the driver - * configures the radio device to use a fixed 5 byte addressing scheme. On this - * addresses, the first byte is set to a constant value, the same for all - * devices that use this driver. The next two bytes are set to the configured - * PAN ID, hereby simulating the use of PAN IDs. The last two bytes are set to - * a 16-bit short address, simulating IEEE802.15.4 short addresses. - * - * There is no support for EUIDs. Further there is no support for anything else - * than IEEE802.15.4 data frames, so no PAN coordinators, etc. - * - * The driver supports: - * - short address (16-bit) - * - using CPU-ID for default address - * - address broadcast (broadcast address is ff:ff) - * - PAN IDs (0 to 0xffff), PAN ID broadcast is not supported - * - setting of channel (0 to 0x3f) - * - setting of TX power (+4dBm to -20dBm) - * - packet type labeling - * - setting device state (RX, SLEEP) - * - * @{ - * - * @file - * @brief Interface definition for the nrfmin NRF51822 radio driver - * - * @author Hauke Petersen - */ - -#ifndef NRFMIN_H_ -#define NRFMIN_H_ - -#include "net/gnrc/netdev.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * @brief Default PAN ID used after initialization - */ -#define NRFMIN_DEFAULT_PAN (0x0550) - -/** - * @brief Default channel set after initialization - */ -#define NRFMIN_DEFAULT_CHANNEL (1U) /* 2401MHz */ - -/** - * @brief Default transmission power used - */ -#define NRFMIN_DEFAULT_TXPOWER (0) /* 0dBm */ - -/** - * @brief Reference to the netdev driver interface - */ -extern const gnrc_netdev_driver_t nrfmin_driver; - -/** - * @brief Initialize the NRF51822 radio - * - * The initialization uses static configuration values. - * - * @param[out] dev pointer to the netdev device descriptor - * - * @return 0 on success - * @return -ENODEV if @p dev is invalid - */ -int nrfmin_init(gnrc_netdev_t *dev); - -#ifdef __cplusplus -} -#endif - -#endif /* NRFMIN_H_ */ -/** @} */ diff --git a/cpu/nrf51/radio/nrfmin/Makefile b/cpu/nrf51/radio/nrfmin/Makefile deleted file mode 100644 index 0ea43c443..000000000 --- a/cpu/nrf51/radio/nrfmin/Makefile +++ /dev/null @@ -1,3 +0,0 @@ -MODULE = radio_nrfmin - -include $(RIOTBASE)/Makefile.base diff --git a/cpu/nrf51/radio/nrfmin/nrfmin.c b/cpu/nrf51/radio/nrfmin/nrfmin.c deleted file mode 100644 index 4866ed770..000000000 --- a/cpu/nrf51/radio/nrfmin/nrfmin.c +++ /dev/null @@ -1,723 +0,0 @@ -/* - * Copyright (C) 2015 Freie Universität Berlin - * - * 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_nrf51822_nrfmin - * @{ - * - * @file - * @brief Implementation of the nrfmin NRF51822 minimal radio driver - * - * @author Hauke Petersen - * - * @} - */ - -#include "cpu.h" -#include "mutex.h" -#include "periph_conf.h" -#include "periph/cpuid.h" -#include "nrfmin.h" -#include "net/gnrc.h" - -#define ENABLE_DEBUG (0) -#include "debug.h" - -/** - * @brief Driver specific device configuration - * @{ - */ -#define CONF_MODE RADIO_MODE_MODE_Nrf_2Mbit -#define CONF_PAYLOAD_LEN (250U) -#define CONF_LEN (8U) -#define CONF_S0 (0U) -#define CONF_S1 (0U) -#define CONF_STATLEN (0U) -#define CONF_BASE_ADDR_LEN (4U) -#define CONF_ENDIAN RADIO_PCNF1_ENDIAN_Big -#define CONF_WHITENING RADIO_PCNF1_WHITEEN_Disabled -#define CONF_CRC_LEN (2U) -#define CONF_CRC_POLY (0x11021) -#define CONF_CRC_INIT (0xf0f0f0) -/** @} */ - -/** - * @brief Driver specific address configuration - * @{ - */ -#define CONF_ADDR_PREFIX0 (0xE7E7E7E7) -#define CONF_ADDR_BCAST (0xffff) -/** @} */ - -/** - * @brief Driver specific (interrupt) events (not all of them used currently) - * @{ - */ -#define ISR_EVENT_RX_START (0x0001) -#define ISR_EVENT_RX_DONE (0x0002) -#define ISR_EVENT_TX_START (0x0004) -#define ISR_EVENT_TX_DONE (0x0008) -#define ISR_EVENT_WRONG_CHKSUM (0x0010) -/** @} */ - -/** - * @brief Payload types to use in driver specific framed format - * - * We expect the radio to carry either raw link layer data (UNDEF) or network - * layer data, so no need to map transport layer protocols etc... - * @{ - */ -#define NRFTYPE_UNDEF (0x01) -#define NRFTYPE_SIXLOWPAN (0x02) -#define NRFTYPE_IPV6 (0x03) -#define NRFTYPE_ICMPV6 (0x04) -/** - * @} - */ - -/** - * @brief Possible internal device states - */ -typedef enum { - STATE_OFF, /**< device is powered off */ - STATE_IDLE, /**< device is in idle mode */ - STATE_RX, /**< device is in receive mode */ - STATE_TX, /**< device is transmitting data */ -} state_t; - -/** - * @brief In-memory structure of a nrfmin radio packet - */ -typedef struct __attribute__((packed)) { - uint8_t length; /**< packet length */ - uint8_t src_addr[2]; /**< source address of the packet */ - uint8_t dst_addr[2]; /**< destination address */ - uint8_t proto; /**< protocol of payload */ - uint8_t payload[CONF_PAYLOAD_LEN]; /**< actual payload */ -} packet_t; - -/** - * @brief Pointer to the MAC layer event callback - */ -static gnrc_netdev_t *_netdev = NULL; - -/** - * @brief Current state of the device - */ -static volatile state_t _state = STATE_OFF; - -/** - * @brief Address of the device - */ -static uint16_t _addr; - -/** - * @brief Transmission buffer - */ -static packet_t _tx_buf; - -/** - * @brief Hold the state before sending to return to it afterwards - */ -static state_t _tx_prestate; - -/** - * @brief Double receive buffers - */ -static packet_t _rx_buf[2]; - -/** - * @brief Pointer to the free receive buffer - */ -static volatile int _rx_next = 0; - -/* - * Create an internal mapping between NETTYPE and NRFTYPE - */ -static inline gnrc_nettype_t _nrftype_to_nettype(uint8_t nrftype) -{ - switch (nrftype) { -#ifdef MODULE_GNRC_SIXLOWPAN - case NRFTYPE_SIXLOWPAN: - return GNRC_NETTYPE_SIXLOWPAN; -#endif -#ifdef MODULE_GNRC_IPV6 - case NRFTYPE_IPV6: - return GNRC_NETTYPE_IPV6; -#endif -#ifdef MODULE_GNRC_ICMPV6 - case NRFTYPE_ICMPV6: - return GNRC_NETTYPE_ICMPV6; -#endif - default: - return GNRC_NETTYPE_UNDEF; - } -} - -static inline uint8_t _nettype_to_nrftype(gnrc_nettype_t nettype) -{ - switch (nettype) { -#ifdef MODULE_GNRC_SIXLOWPAN - case GNRC_NETTYPE_SIXLOWPAN: - return NRFTYPE_SIXLOWPAN; -#endif -#ifdef MODULE_GNRC_IPV6 - case GNRC_NETTYPE_IPV6: - return NRFTYPE_IPV6; -#endif -#ifdef MODULE_GNRC_ICMPV6 - case GNRC_NETTYPE_ICMPV6: - return NRFTYPE_ICMPV6; -#endif - default: - return NRFTYPE_UNDEF; - } -} - -/* - * Functions for controlling the radios state - */ -static void _switch_to_idle(void) -{ - /* witch to idle state */ - NRF_RADIO->EVENTS_DISABLED = 0; - NRF_RADIO->TASKS_DISABLE = 1; - while (NRF_RADIO->EVENTS_DISABLED == 0) {} - _state = STATE_IDLE; -} - -static void _switch_to_rx(void) -{ - /* set pointer to receive buffer */ - NRF_RADIO->PACKETPTR = (uint32_t)&(_rx_buf[_rx_next]); - /* set address */ - NRF_RADIO->BASE0 &= ~(0xffff); - NRF_RADIO->BASE0 |= _addr; - /* switch int RX mode */ - NRF_RADIO->TASKS_RXEN = 1; - _state = STATE_RX; -} - -/* - * Getter and Setter functions - */ -int _get_state(uint8_t *val, size_t max_len) -{ - netopt_state_t state; - - if (max_len < sizeof(netopt_state_t)) { - return -EOVERFLOW; - } - switch (_state) { - case STATE_OFF: - state = NETOPT_STATE_OFF; - break; - case STATE_IDLE: - state = NETOPT_STATE_SLEEP; - break; - case STATE_RX: - state = NETOPT_STATE_IDLE; - break; - case STATE_TX: - state = NETOPT_STATE_TX; - break; - default: - return -ECANCELED; - } - memcpy(val, &state, sizeof(netopt_state_t)); - return sizeof(netopt_state_t); -} - -int _set_state(uint8_t *val, size_t len) -{ - netopt_state_t state; - - if (len != sizeof(netopt_state_t)) { - return -EINVAL; - } - /* get target state */ - memcpy(&state, val, len); - /* switch to target state */ - switch (state) { - case NETOPT_STATE_SLEEP: - _switch_to_idle(); - break; - case NETOPT_STATE_IDLE: - _switch_to_rx(); - break; - default: - return -ENOTSUP; - } - return sizeof(netopt_state_t); -} - -int _get_address(uint8_t *val, size_t max_len) -{ - /* check parameters */ - if (max_len < 2) { - return -EOVERFLOW; - } - /* get address */ - val[0] = (uint8_t)(_addr >> 8); - val[1] = (uint8_t)(_addr); - return 2; -} - -int _set_address(uint8_t *val, size_t len) -{ - int is_rx = 0; - - /* check parameters */ - if (len != 2) { - return -EINVAL; - } - /* keep track of state */ - while (_state == STATE_TX) {} - if (_state == STATE_RX) { - is_rx = 1; - _switch_to_idle(); - } - /* set address */ - _addr = (((uint16_t)val[0]) << 8) | val[1]; - NRF_RADIO->BASE0 &= ~(0xffff); - NRF_RADIO->BASE0 |= _addr; - /* restore old state */ - if (is_rx) { - _switch_to_rx(); - } - return 2; -} - -int _get_channel(uint8_t *val, size_t max_len) -{ - /* check parameters */ - if (max_len < 2) { - return -EOVERFLOW; - } - /* get channel */ - val[0] = (0x3f & NRF_RADIO->FREQUENCY); - val[1] = 0; - return 2; -} - -int _set_channel(uint8_t *val, size_t len) -{ - int is_rx = 0; - - /* check parameter */ - if (len != 2 || val[0] > 0x3f) { - return -EINVAL; - } - /* remember state */ - while (_state == STATE_TX) {} - if (_state == STATE_RX) { - is_rx = 1; - _switch_to_idle(); - } - /* set channel */ - NRF_RADIO->FREQUENCY = val[0]; - /* restore state */ - if (is_rx) { - _switch_to_rx(); - } - return 2; -} - -int _get_pan(uint8_t *val, size_t max_len) -{ - /* check parameters */ - if (max_len < 2) { - return -EOVERFLOW; - } - /* get PAN ID */ - val[0] = (uint8_t)((NRF_RADIO->BASE0 & 0x00ff0000) >> 16); - val[1] = (uint8_t)((NRF_RADIO->BASE0 & 0xff000000) >> 24); - return 2; -} - -int _set_pan(uint8_t *val, size_t len) -{ - int is_rx = 0; - uint32_t pan; - - /* check parameter */ - if (len != 2) { - return -EINVAL; - } - /* remember state */ - while (_state == STATE_TX) {} - if (_state == STATE_RX) { - is_rx = 1; - _switch_to_idle(); - } - /* set new PAN ID */ - pan = ((uint32_t)val[1] << 24) | ((uint32_t)val[0] << 16); - NRF_RADIO->BASE0 = pan | _addr; - NRF_RADIO->BASE1 = pan | CONF_ADDR_BCAST; - /* restore state */ - if (is_rx) { - _switch_to_rx(); - } - return 2; -} - -int _get_txpower(uint8_t *val, size_t len) -{ - /* check parameters */ - if (len < 2) { - return 0; - } - /* get value */ - val[0] = NRF_RADIO->TXPOWER; - if (val[0] & 0x80) { - val[1] = 0xff; - } - else { - val[1] = 0x00; - } - return 2; -} - -int _set_txpower(uint8_t *val, size_t len) -{ - int8_t power; - - /* check parameters */ - if (len < 2) { - return -EINVAL; - } - /* get TX power value */ - power = (int8_t)val[0]; - - if (power > 2) { - power = 4; - } - else if (power > -2) { - power = 0; - } - else if (power > -6) { - power = -4; - } - else if (power > -10) { - power = -8; - } - else if (power > -14) { - power = -12; - } - else if (power > -18) { - power = -16; - } - else { - power = -20; - } - NRF_RADIO->TXPOWER = power; - return 2; -} - -/* - * Radio interrupt routine - */ -void isr_radio(void) -{ - msg_t msg; - - if (NRF_RADIO->EVENTS_END == 1) { - NRF_RADIO->EVENTS_END = 0; - /* did we just send or receive something? */ - if (_state == STATE_RX) { - /* drop packet on invalid CRC */ - if (NRF_RADIO->CRCSTATUS != 1) { - return; - } - msg.type = GNRC_NETDEV_MSG_TYPE_EVENT; - msg.content.value = ISR_EVENT_RX_DONE; - msg_send_int(&msg, _netdev->mac_pid); - /* switch buffer */ - _rx_next = _rx_next ^ 1; - NRF_RADIO->PACKETPTR = (uint32_t)&(_rx_buf[_rx_next]); - /* go back into receive mode */ - NRF_RADIO->TASKS_START = 1; - } - else if (_state == STATE_TX) { - /* disable radio again */ - _switch_to_idle(); - /* if radio was receiving before, go back into RX state */ - if (_tx_prestate == STATE_RX) { - _switch_to_rx(); - } - } - } - cortexm_isr_end(); -} - -/* - * Event handlers - */ -static void _receive_data(void) -{ - packet_t *data; - gnrc_pktsnip_t *pkt_head; - gnrc_pktsnip_t *pkt; - gnrc_netif_hdr_t *hdr; - gnrc_nettype_t nettype; - - /* only read data if we have somewhere to send it to */ - if (_netdev->event_cb == NULL) { - return; - } - - /* get pointer to RX data buffer */ - data = &(_rx_buf[_rx_next ^ 1]); - - /* allocate and fill netif header */ - pkt_head = gnrc_pktbuf_add(NULL, NULL, sizeof(gnrc_netif_hdr_t) + 4, - GNRC_NETTYPE_UNDEF); - if (pkt_head == NULL) { - DEBUG("nrfmin: Error allocating netif header on RX\n"); - return; - } - hdr = (gnrc_netif_hdr_t *)pkt_head->data; - gnrc_netif_hdr_init(hdr, 2, 2); - hdr->if_pid = _netdev->mac_pid; - gnrc_netif_hdr_set_src_addr(hdr, data->src_addr, 2); - gnrc_netif_hdr_set_dst_addr(hdr, data->dst_addr, 2); - - /* allocate and fill payload */ - nettype = _nrftype_to_nettype(data->proto); - pkt = gnrc_pktbuf_add(pkt_head, data->payload, data->length - 6, nettype); - if (pkt == NULL) { - DEBUG("nrfmin: Error allocating packet payload on RX\n"); - gnrc_pktbuf_release(pkt_head); - return; - } - - /* pass on the received packet */ - _netdev->event_cb(NETDEV_EVENT_RX_COMPLETE, pkt); -} - -/* - * Public interface functions - */ -int nrfmin_init(gnrc_netdev_t *dev) -{ - uint8_t cpuid[CPUID_LEN]; - uint8_t tmp; - int i; - - /* check given device descriptor */ - if (dev == NULL) { - return -ENODEV; - } - /* set initial values */ - dev->driver = &nrfmin_driver; - dev->event_cb = NULL; - dev->mac_pid = KERNEL_PID_UNDEF; - /* keep a pointer for future reference */ - _netdev = dev; - - /* power on the NRFs radio */ - NRF_RADIO->POWER = 1; - /* load driver specific configuration */ - NRF_RADIO->MODE = CONF_MODE; - /* configure variable parameters to default values */ - NRF_RADIO->TXPOWER = NRFMIN_DEFAULT_TXPOWER; - NRF_RADIO->FREQUENCY = NRFMIN_DEFAULT_CHANNEL; - /* get default address from CPU ID */ - cpuid_get(cpuid); - tmp = 0; - for (i = 0; i < (CPUID_LEN / 2); i++) { - tmp ^= cpuid[i]; - } - _addr = ((uint16_t)tmp) << 8; - tmp = 0; - for (; i < CPUID_LEN; i++) { - tmp ^= cpuid[i]; - } - _addr |= tmp; - /* pre-configure radio addresses */ - NRF_RADIO->PREFIX0 = CONF_ADDR_PREFIX0; - NRF_RADIO->BASE0 = (NRFMIN_DEFAULT_PAN << 16) | _addr; - NRF_RADIO->BASE1 = (NRFMIN_DEFAULT_PAN << 16) | CONF_ADDR_BCAST; - NRF_RADIO->TXADDRESS = 0x00UL; /* always send from address 0 */ - NRF_RADIO->RXADDRESSES = 0x03UL; /* listen to addresses 0 and 1 */ - /* configure data fields and packet length whitening and endianess */ - NRF_RADIO->PCNF0 = (CONF_S1 << RADIO_PCNF0_S1LEN_Pos) | - (CONF_S0 << RADIO_PCNF0_S0LEN_Pos) | - (CONF_LEN << RADIO_PCNF0_LFLEN_Pos); - NRF_RADIO->PCNF1 = (CONF_WHITENING << RADIO_PCNF1_WHITEEN_Pos) | - (CONF_ENDIAN << RADIO_PCNF1_ENDIAN_Pos) | - (CONF_BASE_ADDR_LEN << RADIO_PCNF1_BALEN_Pos) | - (CONF_STATLEN << RADIO_PCNF1_STATLEN_Pos) | - (CONF_PAYLOAD_LEN << RADIO_PCNF1_MAXLEN_Pos); - /* configure CRC unit */ - NRF_RADIO->CRCCNF = CONF_CRC_LEN; - NRF_RADIO->CRCPOLY = CONF_CRC_POLY; - NRF_RADIO->CRCINIT = CONF_CRC_INIT; - /* set shortcuts for more efficient transfer */ - NRF_RADIO->SHORTS = (1 << RADIO_SHORTS_READY_START_Pos); - /* enable interrupts */ - NVIC_SetPriority(RADIO_IRQn, RADIO_IRQ_PRIO); - NVIC_EnableIRQ(RADIO_IRQn); - /* enable END interrupt */ - NRF_RADIO->EVENTS_END = 0; - NRF_RADIO->INTENSET = (1 << RADIO_INTENSET_END_Pos); - /* put device in receive mode */ - _switch_to_rx(); - return 0; -} - -int _send(gnrc_netdev_t *dev, gnrc_pktsnip_t *pkt) -{ - (void)dev; - size_t size; - size_t pos = 0; - uint8_t *dst_addr; - gnrc_netif_hdr_t *hdr; - gnrc_pktsnip_t *payload; - - /* check packet */ - if (pkt == NULL || pkt->next == NULL) { - DEBUG("nrfmin: Error sending packet: packet incomplete\n"); - return -ENOMSG; - } - - /* check if payload is withing length bounds */ - size = gnrc_pkt_len(pkt->next); - if (size > CONF_PAYLOAD_LEN) { - gnrc_pktbuf_release(pkt); - DEBUG("nrfmin: Error sending packet: payload to large\n"); - return -EOVERFLOW; - } - /* get netif header and check address length */ - hdr = (gnrc_netif_hdr_t *)pkt->data; - if (hdr->dst_l2addr_len != 2) { - DEBUG("nrfmin: Error sending packet: dest address has invalid size\n"); - gnrc_pktbuf_release(pkt); - return -ENOMSG; - } - dst_addr = gnrc_netif_hdr_get_dst_addr(hdr); - - DEBUG("nrfmin: Sending packet to %02x:%02x - size %u\n", - dst_addr[0], dst_addr[1], size); - - /* wait for any ongoing transmission to finish */ - while (_state == STATE_TX) {} - /* write data into TX buffer */ - payload = pkt->next; - _tx_buf.length = 6 + size; - _tx_buf.src_addr[0] = (uint8_t)(_addr >> 8); - _tx_buf.src_addr[1] = (uint8_t)(_addr); - _tx_buf.dst_addr[0] = dst_addr[0]; - _tx_buf.dst_addr[1] = dst_addr[1]; - _tx_buf.proto = _nettype_to_nrftype(payload->type); - while (payload) { - memcpy(&(_tx_buf.payload[pos]), payload->data, payload->size); - pos += payload->size; - payload = payload->next; - } - - /* save old state and switch to idle if applicable */ - _tx_prestate = _state; - if (_tx_prestate == STATE_RX) { - _switch_to_idle(); - } - /* set packet pointer to TX buffer and write destination address */ - NRF_RADIO->PACKETPTR = (uint32_t)(&_tx_buf); - NRF_RADIO->BASE0 &= ~(0xffff); - NRF_RADIO->BASE0 |= ((((uint16_t)dst_addr[0]) << 8) | dst_addr[1]); - /* start transmission */ - _state = STATE_TX; - NRF_RADIO->TASKS_TXEN = 1; - - /* release packet */ - gnrc_pktbuf_release(pkt); - return (int)size; -} - -int _add_event_cb(gnrc_netdev_t *dev, gnrc_netdev_event_cb_t cb) -{ - if (dev->event_cb != NULL) { - return -ENOBUFS; - } - dev->event_cb = cb; - return 0; -} - -int _rem_event_cb(gnrc_netdev_t *dev, gnrc_netdev_event_cb_t cb) -{ - if (dev->event_cb == cb) { - dev->event_cb = NULL; - return 0; - } - return -ENOENT; -} - -int _get(gnrc_netdev_t *dev, netopt_t opt, void *value, size_t max_len) -{ - (void)dev; - - switch (opt) { - case NETOPT_ADDRESS: - return _get_address(value, max_len); - case NETOPT_CHANNEL: - return _get_channel(value, max_len); - case NETOPT_NID: - return _get_pan(value, max_len); - case NETOPT_TX_POWER: - return _get_txpower(value, max_len); - case NETOPT_STATE: - return _get_state(value, max_len); - default: - return -ENOTSUP; - } -} - -int _set(gnrc_netdev_t *dev, netopt_t opt, void *value, size_t value_len) -{ - (void)dev; - - switch (opt) { - case NETOPT_ADDRESS: - return _set_address(value, value_len); - case NETOPT_CHANNEL: - return _set_channel(value, value_len); - case NETOPT_NID: - return _set_pan(value, value_len); - case NETOPT_TX_POWER: - return _set_txpower(value, value_len); - case NETOPT_STATE: - return _set_state(value, value_len); - default: - return -ENOTSUP; - } -} - -void _isr_event(gnrc_netdev_t *dev, uint32_t event_type) -{ - switch (event_type) { - case ISR_EVENT_RX_DONE: - _receive_data(); - break; - default: - /* do nothing */ - return; - } -} - -/* - * Mapping of netdev interface - */ -const gnrc_netdev_driver_t nrfmin_driver = { - .send_data = _send, - .add_event_callback = _add_event_cb, - .rem_event_callback = _rem_event_cb, - .get = _get, - .set = _set, - .isr_event = _isr_event, -}; -// diff --git a/cpu/nrf5x_common/Makefile b/cpu/nrf5x_common/Makefile index e09377cd1..3b0970ca1 100644 --- a/cpu/nrf5x_common/Makefile +++ b/cpu/nrf5x_common/Makefile @@ -1,3 +1,8 @@ DIRS = periph +# build one of the radio drivers, if enabled +ifneq (,$(filter nrfmin,$(USEMODULE))) + DIRS += radio/nrfmin +endif + include $(RIOTBASE)/Makefile.base diff --git a/cpu/nrf5x_common/include/nrfmin.h b/cpu/nrf5x_common/include/nrfmin.h new file mode 100644 index 000000000..aa8e39eae --- /dev/null +++ b/cpu/nrf5x_common/include/nrfmin.h @@ -0,0 +1,237 @@ +/* + * Copyright (C) 2015-2017 Freie Universität Berlin + * + * 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_nrf5x_nrfmin NRF Minimal Radio Driver + * @ingroup drivers_netdev2 + * @brief Minimal driver for the NRF51 radio + * + * This driver uses the nRF5x radio in a proprietary/custom way, defining our + * own custom link layer. This custom link layer resembles some characteristics + * of the IEEE802.15.4 link layer, but is not at all compatible to it. + * + * One key point is, that this custom link layer is only meant to operate + * between nRF5x devices, which let's us make some very nice assumptions: + * - all communicating hosts are little-endian + * -> we define host byte order := network byte order + * + * The driver is using a Nordic proprietary physical layer, configured to a + * bitrate of 2Mbit. The maximum payload length can be freely configured, but + * the maximal supported value is 250 byte (default is 200 byte). + * + * We define the nrfmin link layer to use 16-bit addresses. On the physical + * layer we encode these addresses by putting these addresses into the 2 least + * significant bytes of the supported 5-byte addresses, while setting the other + * 3 bytes to 0xe7. + * + * For out custom link layer, we define our own proprietary link layer format + * (all fields are in host byte order (little endian)): + * + * byte0 | byte1 - byte2 | byte3 - byte4 | byte5 | byte7 - byteN + * ------ | ------------- | ------------- | ----- | ------------- + * length | src_addr | dst_addr | proto | payload... + * + * With: + * - length: length of the packet, including the header -> payload len + 6 + * - src_addr: 16-bit source address + * - dst_addr: 16-bit destination address + * - proto: type of data transferred (similar to an Ethertype field) + * + * SUMMARY: + * This driver / link layer supports: + * - 16-bit addressing (16-bit) + * -> extract default address from CPU ID + * - broadcast (broadcast address is ff:ff) + * - channels from 0 to 31 [2400MHz to 2524MHz, 4MHz per channel] + * - setting of TX power [+4dBm to -20dBm, in ~4dBm steps] + * - 8-bit packet type/proto field (to be used as seen fit) + * - setting device state (RX, SLEEP) + * + * But so far no support for: + * - link layer ACKs + * - retransmissions + * + * @todo So far the driver uses only a single RX buffer that is locked + * until the data was read/discarded. This can potentially lead to + * a lot of packet loss -> using more than one buffer would help + * here... + * + * @{ + * + * @file + * @brief Interface definition for the nrfmin NRF51822 radio driver + * + * @author Hauke Petersen + */ + +#ifndef NRFMIN_H_ +#define NRFMIN_H_ + +#include "net/netdev2.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief nrfmin channel configuration + * @{ + */ +#define NRFMIN_CHAN_MIN (0U) +#define NRFMIN_CHAN_DEFAULT (0U) /* 2400MHz */ +#define NRFMIN_CHAN_MAX (32) +/** @} */ + +/** + * @brief Default transmission power used + */ +#define NRFMIN_TXPOWER_DEFAULT (0) /* 0dBm */ + +/** + * @brief Export the default nrfmin broadcast address + */ +#define NRFMIN_ADDR_BCAST (0xffff) + +/** + * @brief Default maximum payload length (must be <= 250) + */ +#ifndef NRFMIN_PAYLOAD_MAX +#define NRFMIN_PAYLOAD_MAX (200U) +#endif + +/** + * @brief Export some information on header and packet lengths + * @{ + */ +#define NRFMIN_HDR_LEN (sizeof(nrfmin_hdr_t)) +#define NRFMIN_PKT_MAX (NRFMIN_HDR_LEN + NRFMIN_PAYLOAD_MAX) +/** @} */ + +/** + * @brief Header format used for our custom nrfmin link layer + */ +typedef struct __attribute__((packed)) { + uint8_t len; /**< packet length, including this header */ + uint16_t src_addr; /**< source address of the packet */ + uint16_t dst_addr; /**< destination address */ + uint8_t proto; /**< protocol of payload */ +} nrfmin_hdr_t; + +/** + * @brief In-memory structure of a nrfmin radio packet + */ +typedef union { + struct __attribute__((packed)) { + nrfmin_hdr_t hdr; /**< the nrfmin header */ + uint8_t payload[NRFMIN_PAYLOAD_MAX]; /**< actual payload */ + } pkt; /**< typed packet access */ + uint8_t raw[NRFMIN_PKT_MAX]; /**< raw packet access */ +} nrfmin_pkt_t; + +/** + * @brief Export the netdev2 device descriptor + */ +extern netdev2_t nrfmin_dev; + +/** + * @brief Reference to the netdev driver interface + */ +extern const netdev2_driver_t nrfmin_netdev; + +/** + * @brief Setup the device driver's data structures + */ +void nrfmin_setup(void); + +/** + * @brief Get the currently active address + + * @return the 16-bit node address + */ +uint16_t nrfmin_get_addr(void); + +/** + * @brief Set the 16-bit radio address + * + * @param[in] addr address to set + */ +void nrfmin_set_addr(uint16_t addr); + +/** + * @brief Get a pseudo 64-bit long address (needed by IPv6 and 6LoWPAN) + * + * As we do not support 64-bit addresses, we just make one up, for this we + * simply return 4 times concatenated the 16-bit address. + * + * @param[out] addr 64-bit pseudo long address, as array of 4 * 16-bit + */ +void nrfmin_get_pseudo_long_addr(uint16_t *addr); + +/** + * @brief Get the IID build from the 16-bit node address + * + * @param[out] iid the 64-bit IID, as array of 4 * 16-bit + */ +void nrfmin_get_iid(uint16_t *iid); + +/** + * @brief Get the current channel + * + * @return currently active channel + */ +uint16_t nrfmin_get_channel(void); + +/** + * @brief Set the active channel + * + * @param[in] chan targeted channel [0-31] + * + * @return sizeof(uint16_t) on success + * @return -EOVERFLOW if channel is not applicable + */ +int nrfmin_set_channel(uint16_t chan); + +/** + * @brief Get the current radio state + * + * @return state the radio is currently in + */ +netopt_state_t nrfmin_get_state(void); + +/** + * @brief Put the device into the given state + * + * @param[in] val target state + * + * @return sizeof(netopt_state_t) on success + * @return -ENOTSUP if target state is not applicable + */ +int nrfmin_set_state(netopt_state_t val); + +/** + * @brief Get the current transmit power + * + * @return transmission power in [dBm] + */ +int16_t nrfmin_get_txpower(void); + +/** + * @brief Set the used transmission power + * + * @param[in] power targeted power, in [dBm] + */ +void nrfmin_set_txpower(int16_t power); + + + +#ifdef __cplusplus +} +#endif + +#endif /* NRFMIN_H_ */ +/** @} */ diff --git a/cpu/nrf5x_common/include/nrfmin_gnrc.h b/cpu/nrf5x_common/include/nrfmin_gnrc.h new file mode 100644 index 000000000..dc1a971a4 --- /dev/null +++ b/cpu/nrf5x_common/include/nrfmin_gnrc.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2016 Freie Universität Berlin + * + * 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_nrf5x_nrfmin_gnrc GNRC adapter for nrfmin + * @ingroup drivers_nrf5x_nrfmin + * @brief Minimal driver for the NRF51 radio + * + * @{ + * + * @file + * @brief GNRC adapter for nrfmin devices (e.g. nRF5x radios) + * + * @author Hauke Petersen + */ + +#ifndef NRFMIN_GNRC_H_ +#define NRFMIN_GNRC_H_ + +#include "nrfmin.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Initialize the nrfmin GNRC adapter, also takes care of the nrfmin + * driver setup + * + * As we have never more than 1 nrfmin device on a board, we can make some + * simplifications when it come to allocating device descriptors and adapter + * data structures -> we do this right in the driver/adapter code, so this + * function can be called from auto_init as is, without the need for external + * memory allocation. + */ +void gnrc_netdev2_nrfmin_init(void); + +#ifdef __cplusplus +} +#endif + +#endif /* NRFMIN_GNRC_H_ */ +/** @} */ diff --git a/cpu/nrf5x_common/radio/nrfmin/Makefile b/cpu/nrf5x_common/radio/nrfmin/Makefile new file mode 100644 index 000000000..5b85af78a --- /dev/null +++ b/cpu/nrf5x_common/radio/nrfmin/Makefile @@ -0,0 +1,9 @@ +MODULE = nrfmin + +SRC = nrfmin.c + +ifneq (,$(filter gnrc_netdev_default,$(USEMODULE))) + SRC += nrfmin_gnrc.c +endif + +include $(RIOTBASE)/Makefile.base diff --git a/cpu/nrf5x_common/radio/nrfmin/nrfmin.c b/cpu/nrf5x_common/radio/nrfmin/nrfmin.c new file mode 100644 index 000000000..c6cc5a390 --- /dev/null +++ b/cpu/nrf5x_common/radio/nrfmin/nrfmin.c @@ -0,0 +1,549 @@ +/* + * Copyright (C) 2015-2017 Freie Universität Berlin + * + * 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_nrf5x_nrfmin + * @{ + * + * @file + * @brief Implementation of the nrfmin radio driver for nRF51 radios + * + * @author Hauke Petersen + * + * @} + */ + +#include +#include + +#include "cpu.h" +#include "mutex.h" +#include "assert.h" + +#include "periph_conf.h" +#include "periph/cpuid.h" + +#include "nrfmin.h" +#include "net/netdev2.h" + +#define ENABLE_DEBUG (0) +#include "debug.h" + +/** + * @brief Driver specific device configuration + * @{ + */ +#define CONF_MODE RADIO_MODE_MODE_Nrf_1Mbit +#define CONF_LEN (8U) +#define CONF_S0 (0U) +#define CONF_S1 (0U) +#define CONF_STATLEN (0U) +#define CONF_BASE_ADDR_LEN (4U) +#define CONF_ENDIAN RADIO_PCNF1_ENDIAN_Big +#define CONF_WHITENING RADIO_PCNF1_WHITEEN_Disabled +#define CONF_CRC_LEN (2U) +#define CONF_CRC_POLY (0x11021) +#define CONF_CRC_INIT (0xf0f0f0) +/** @} */ + +/** + * @brief Driver specific address configuration + * @{ + */ +#define CONF_ADDR_PREFIX0 (0xe7e7e7e7) +#define CONF_ADDR_BASE (0xe7e70000) +#define CONF_ADDR_BCAST (CONF_ADDR_BASE | NRFMIN_ADDR_BCAST) +/** @} */ + +/** + * @brief We define a pseudo NID for compliance to 6LoWPAN + */ +#define CONF_PSEUDO_NID (0xaffe) + +/** + * @brief Driver specific (interrupt) events (not all of them used currently) + * @{ + */ +#define ISR_EVENT_RX_START (0x0001) +#define ISR_EVENT_RX_DONE (0x0002) +#define ISR_EVENT_TX_START (0x0004) +#define ISR_EVENT_TX_DONE (0x0008) +#define ISR_EVENT_WRONG_CHKSUM (0x0010) +/** @} */ + +/** + * @brief Possible internal device states + */ +typedef enum { + STATE_OFF, /**< device is powered off */ + STATE_IDLE, /**< device is in idle mode */ + STATE_RX, /**< device is in receive mode */ + STATE_TX, /**< device is transmitting data */ +} state_t; + + +/** + * @brief Since there can only be 1 nrfmin device, we allocate it right here + */ +netdev2_t nrfmin_dev; + +/** + * @brief For faster lookup we remember our own 16-bit address + */ +static uint16_t my_addr; + +/** + * @brief We need to keep track of the radio state in SW (-> PAN ID 20) + * + * See nRF51822 PAN ID 20: RADIO State Register is not functional. + */ +static volatile state_t state = STATE_OFF; + +/** + * @brief We also remember the 'long-term' state, so we can resume after TX + */ +static volatile state_t target_state = STATE_OFF; + +/** + * @brief When sending out data, the data needs to be in one continuous memory + * region. So we need to buffer outgoing data on the driver level. + */ +static nrfmin_pkt_t tx_buf; + +/** + * @brief As the device is memory mapped, we need some space to save incoming + * data to. + * + * @todo Improve the RX buffering to at least use double buffering + */ +static nrfmin_pkt_t rx_buf; + +/** + * @brief While we listen for incoming data, we lock the RX buffer + */ +static volatile uint8_t rx_lock = 0; + +/** + * @brief Set radio into idle (DISABLED) state + */ +static void go_idle(void) +{ + /* set device into basic disabled state */ + NRF_RADIO->EVENTS_DISABLED = 0; + NRF_RADIO->TASKS_DISABLE = 1; + while (NRF_RADIO->EVENTS_DISABLED == 0) {} + /* also release any existing lock on the RX buffer */ + rx_lock = 0; + state = STATE_IDLE; +} + +/** + * @brief Set radio into the target state as defined by `target_state` + * + * Trick here is, that the driver can go back to it's previous state after a + * send operation, so it can differentiate if the driver was in DISABLED or in + * RX mode before the send process had started. + */ +static void goto_target_state(void) +{ + go_idle(); + + if ((target_state == STATE_RX) && (rx_buf.pkt.hdr.len == 0)) { + /* set receive buffer and our own address */ + rx_lock = 1; + NRF_RADIO->PACKETPTR = (uint32_t)(&rx_buf); + NRF_RADIO->BASE0 = (CONF_ADDR_BASE | my_addr); + /* goto RX mode */ + NRF_RADIO->TASKS_RXEN = 1; + state = STATE_RX; + } + + if (target_state == STATE_OFF) { + NRF_RADIO->POWER = 0; + state = STATE_OFF; + } +} + +void nrfmin_setup(void) +{ + nrfmin_dev.driver = &nrfmin_netdev; + nrfmin_dev.event_callback = NULL; + nrfmin_dev.context = NULL; +#ifdef MODULE_NETSTATS_L2 + memset(&nrfmin_dev.stats, 0, sizeof(netstats_t));; +#endif +} + +uint16_t nrfmin_get_addr(void) +{ + return my_addr; +} + +void nrfmin_get_pseudo_long_addr(uint16_t *addr) +{ + for (int i = 0; i < 4; i++) { + addr[i] = my_addr; + } +} + +void nrfmin_get_iid(uint16_t *iid) +{ + iid[0] = 0; + iid[1] = 0xff00; + iid[2] = 0x00fe; + iid[3] = my_addr; +} + +uint16_t nrfmin_get_channel(void) +{ + return (uint16_t)(NRF_RADIO->FREQUENCY >> 2); +} + +netopt_state_t nrfmin_get_state(void) +{ + switch (state) { + case STATE_OFF: return NETOPT_STATE_OFF; + case STATE_IDLE: return NETOPT_STATE_SLEEP; + case STATE_RX: return NETOPT_STATE_IDLE; + case STATE_TX: return NETOPT_STATE_TX; + default: return NETOPT_STATE_RESET; /* should never show */ + } +} + +int16_t nrfmin_get_txpower(void) +{ + int8_t p = (int8_t)NRF_RADIO->TXPOWER; + if (p < 0) { + return (int16_t)(0xff00 | p); + } + return (int16_t)p; +} + +void nrfmin_set_addr(uint16_t addr) +{ + my_addr = addr; + goto_target_state(); +} + +int nrfmin_set_channel(uint16_t chan) +{ + if (chan > NRFMIN_CHAN_MAX) { + return -EOVERFLOW; + } + + NRF_RADIO->FREQUENCY = (chan << 2); + goto_target_state(); + + return sizeof(uint16_t); +} + +void nrfmin_set_txpower(int16_t power) +{ + if (power > 2) { + NRF_RADIO->TXPOWER = RADIO_TXPOWER_TXPOWER_Pos4dBm; + } + else if (power > -2) { + NRF_RADIO->TXPOWER = RADIO_TXPOWER_TXPOWER_0dBm; + } + else if (power > -6) { + NRF_RADIO->TXPOWER = RADIO_TXPOWER_TXPOWER_Neg4dBm; + } + else if (power > -10) { + NRF_RADIO->TXPOWER = RADIO_TXPOWER_TXPOWER_Neg8dBm; + } + else if (power > -14) { + NRF_RADIO->TXPOWER = RADIO_TXPOWER_TXPOWER_Neg12dBm; + } + else if (power > -18) { + NRF_RADIO->TXPOWER = RADIO_TXPOWER_TXPOWER_Neg16dBm; + } + else if (power > -25) { + NRF_RADIO->TXPOWER = RADIO_TXPOWER_TXPOWER_Neg20dBm; + } + else { + NRF_RADIO->TXPOWER = RADIO_TXPOWER_TXPOWER_Neg30dBm; + } +} + +int nrfmin_set_state(netopt_state_t val) +{ + /* make sure radio is turned on and no transmission is in progress */ + NRF_RADIO->POWER = 1; + + switch (val) { + case NETOPT_STATE_OFF: + target_state = STATE_OFF; + break; + case NETOPT_STATE_SLEEP: + target_state = STATE_IDLE; + break; + case NETOPT_STATE_IDLE: + target_state = STATE_RX; + break; + default: + return -ENOTSUP; + } + + goto_target_state(); + + return sizeof(netopt_state_t); +} + + +/** + * @brief Radio interrupt routine + */ +void isr_radio(void) +{ + if (NRF_RADIO->EVENTS_END == 1) { + NRF_RADIO->EVENTS_END = 0; + /* did we just send or receive something? */ + if (state == STATE_RX) { + /* drop packet on invalid CRC */ + if ((NRF_RADIO->CRCSTATUS != 1) || !(nrfmin_dev.event_callback)) { + rx_buf.pkt.hdr.len = 0; + NRF_RADIO->TASKS_START = 1; + return; + } + rx_lock = 0; + nrfmin_dev.event_callback(&nrfmin_dev, NETDEV2_EVENT_ISR); + } + else if (state == STATE_TX) { + goto_target_state(); + } + } + + cortexm_isr_end(); +} + +static int nrfmin_send(netdev2_t *dev, const struct iovec *vector, unsigned count) +{ + (void)dev; + + assert((vector != NULL) && (count > 0) && (state != STATE_OFF)); + + /* wait for any ongoing transmission to finish and go into idle state */ + while (state == STATE_TX) {} + go_idle(); + + /* copy packet data into the transmit buffer */ + int pos = 0; + for (unsigned i = 0; i < count; i++) { + if ((pos + vector[i].iov_len) > NRFMIN_PKT_MAX) { + DEBUG("[nrfmin] send: unable to do so, packet is too large!\n"); + return -EOVERFLOW; + } + memcpy(&tx_buf.raw[pos], vector[i].iov_base, vector[i].iov_len); + pos += vector[i].iov_len; + } + + /* set output buffer and destination address */ + nrfmin_hdr_t *hdr = (nrfmin_hdr_t *)vector[0].iov_base; + NRF_RADIO->PACKETPTR = (uint32_t)(&tx_buf); + NRF_RADIO->BASE0 = (CONF_ADDR_BASE | hdr->dst_addr); + + /* trigger the actual transmission */ + DEBUG("[nrfmin] send: putting %i byte into the ether\n", (int)hdr->len); + state = STATE_TX; + NRF_RADIO->TASKS_TXEN = 1; + + return (int)count; +} + +static int nrfmin_recv(netdev2_t *dev, void *buf, size_t len, void *info) +{ + (void)dev; + (void)info; + + assert(state != STATE_OFF); + + int pktlen = (int)rx_buf.pkt.hdr.len; + + /* check if packet data is readable */ + if (rx_lock || (pktlen == 0)) { + DEBUG("[nrfmin] recv: no packet data available\n"); + return 0; + } + + if (buf == NULL) { + if (len > 0) { + /* drop packet */ + DEBUG("[nrfmin] recv: dropping packet of length %i\n", pktlen); + rx_buf.pkt.hdr.len = 0; + goto_target_state(); + } + } + else { + DEBUG("[nrfmin] recv: reading packet of length %i\n", pktlen); + + pktlen = (len < pktlen) ? len : pktlen; + memcpy(buf, rx_buf.raw, pktlen); + rx_buf.pkt.hdr.len = 0; + goto_target_state(); + } + + return pktlen; +} + +static int nrfmin_init(netdev2_t *dev) +{ + uint8_t cpuid[CPUID_LEN]; + + /* check given device descriptor */ + assert(dev); + + /* initialize our own address from the CPU ID */ + my_addr = 0; + cpuid_get(cpuid); + for (int i = 0; i < CPUID_LEN; i++) { + my_addr ^= cpuid[i] << (8 * (i & 0x01)); + } + + /* power on the NRFs radio */ + NRF_RADIO->POWER = 1; + /* load driver specific configuration */ + NRF_RADIO->MODE = CONF_MODE; + /* configure variable parameters to default values */ + NRF_RADIO->TXPOWER = NRFMIN_TXPOWER_DEFAULT; + NRF_RADIO->FREQUENCY = NRFMIN_CHAN_DEFAULT; + /* pre-configure radio addresses */ + NRF_RADIO->PREFIX0 = CONF_ADDR_PREFIX0; + NRF_RADIO->BASE0 = (CONF_ADDR_BASE | my_addr); + NRF_RADIO->BASE1 = CONF_ADDR_BCAST; + /* always send from logical address 0 */ + NRF_RADIO->TXADDRESS = 0x00UL; + /* and listen to logical addresses 0 and 1 */ + NRF_RADIO->RXADDRESSES = 0x03UL; + /* configure data fields and packet length whitening and endianess */ + NRF_RADIO->PCNF0 = ((CONF_S1 << RADIO_PCNF0_S1LEN_Pos) | + (CONF_S0 << RADIO_PCNF0_S0LEN_Pos) | + (CONF_LEN << RADIO_PCNF0_LFLEN_Pos)); + NRF_RADIO->PCNF1 = ((CONF_WHITENING << RADIO_PCNF1_WHITEEN_Pos) | + (CONF_ENDIAN << RADIO_PCNF1_ENDIAN_Pos) | + (CONF_BASE_ADDR_LEN << RADIO_PCNF1_BALEN_Pos) | + (CONF_STATLEN << RADIO_PCNF1_STATLEN_Pos) | + (NRFMIN_PKT_MAX << RADIO_PCNF1_MAXLEN_Pos)); + /* configure the CRC unit, we skip the address field as this seems to lead + * to wrong checksum calculation on nRF52 devices in some cases */ + NRF_RADIO->CRCCNF = CONF_CRC_LEN | RADIO_CRCCNF_SKIPADDR_Msk; + NRF_RADIO->CRCPOLY = CONF_CRC_POLY; + NRF_RADIO->CRCINIT = CONF_CRC_INIT; + /* set shortcuts for more efficient transfer */ + NRF_RADIO->SHORTS = RADIO_SHORTS_READY_START_Msk; + /* enable interrupts */ + NVIC_EnableIRQ(RADIO_IRQn); + /* enable END interrupt */ + NRF_RADIO->EVENTS_END = 0; + NRF_RADIO->INTENSET = RADIO_INTENSET_END_Msk; + /* put device in receive mode */ + target_state = STATE_RX; + goto_target_state(); + + DEBUG("[nrfmin] initialization successful\n"); + + return 0; +} + +static void nrfmin_isr(netdev2_t *dev) +{ + if (nrfmin_dev.event_callback) { + nrfmin_dev.event_callback(dev, NETDEV2_EVENT_RX_COMPLETE); + } +} + +static int nrfmin_get(netdev2_t *dev, netopt_t opt, void *val, size_t max_len) +{ + (void)dev; + + switch (opt) { + case NETOPT_CHANNEL: + assert(max_len >= sizeof(uint16_t)); + *((uint16_t *)val) = nrfmin_get_channel(); + return sizeof(uint16_t); + case NETOPT_ADDRESS: + assert(max_len >= sizeof(uint16_t)); + *((uint16_t *)val) = nrfmin_get_addr(); + return sizeof(uint16_t); + case NETOPT_STATE: + assert(max_len >= sizeof(netopt_state_t)); + *((netopt_state_t *)val) = nrfmin_get_state(); + return sizeof(netopt_state_t); + case NETOPT_TX_POWER: + assert(max_len >= sizeof(int16_t)); + *((int16_t *)val) = nrfmin_get_txpower(); + return sizeof(int16_t); + case NETOPT_ADDRESS_LONG: + assert(max_len >= sizeof(uint64_t)); + nrfmin_get_pseudo_long_addr((uint16_t *)val); + return sizeof(uint64_t); + case NETOPT_ADDR_LEN: + assert(max_len >= sizeof(uint16_t)); + *((uint16_t *)val) = 2; + return sizeof(uint16_t); + case NETOPT_NID: + assert(max_len >= sizeof(uint16_t)); + *((uint16_t*)val) = CONF_PSEUDO_NID; + return sizeof(uint16_t); + case NETOPT_PROTO: + *((uint16_t *)val) = 809; /* TODO */ + return 2; + case NETOPT_DEVICE_TYPE: + assert(max_len >= sizeof(uint16_t)); + *((uint16_t *)val) = NETDEV2_TYPE_NRFMIN; + return sizeof(uint16_t); + case NETOPT_IPV6_IID: + assert(max_len >= sizeof(uint64_t)); + nrfmin_get_iid((uint16_t *)val); + return sizeof(uint64_t); + default: + return -ENOTSUP; + } +} + +static int nrfmin_set(netdev2_t *dev, netopt_t opt, void *val, size_t len) +{ + (void)dev; + + switch (opt) { + case NETOPT_CHANNEL: + assert(len == sizeof(uint16_t)); + return nrfmin_set_channel(*((uint16_t *)val)); + case NETOPT_ADDRESS: + assert(len == sizeof(uint16_t)); + nrfmin_set_addr(*((uint16_t *)val)); + return sizeof(uint16_t); + case NETOPT_ADDR_LEN: + case NETOPT_SRC_LEN: + assert(len == sizeof(uint16_t)); + if (*((uint16_t *)val) != 2) { + return -EAFNOSUPPORT; + } + return sizeof(uint16_t); + case NETOPT_STATE: + assert(len == sizeof(netopt_state_t)); + return nrfmin_set_state(*((netopt_state_t *)val)); + case NETOPT_TX_POWER: + assert(len == sizeof(int16_t)); + nrfmin_set_txpower(*((int16_t *)val)); + return sizeof(int16_t); + default: + return -ENOTSUP; + } +} + +/** + * @brief Export of the netdev2 interface + */ +const netdev2_driver_t nrfmin_netdev = { + .send = nrfmin_send, + .recv = nrfmin_recv, + .init = nrfmin_init, + .isr = nrfmin_isr, + .get = nrfmin_get, + .set = nrfmin_set +}; diff --git a/cpu/nrf5x_common/radio/nrfmin/nrfmin_gnrc.c b/cpu/nrf5x_common/radio/nrfmin/nrfmin_gnrc.c new file mode 100644 index 000000000..9253d490f --- /dev/null +++ b/cpu/nrf5x_common/radio/nrfmin/nrfmin_gnrc.c @@ -0,0 +1,201 @@ +/* + * Copyright (C) 2016 Freie Universität Berlin + * + * 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_nrf5x_nrfmin_gnrc + * @{ + * + * @file + * @brief GNRC adapter for the nrfmin radio driver + * + * @author Hauke Petersen + * + * @} + */ + +#include "thread.h" +#include "net/gnrc/netdev2.h" + +#include "nrfmin_gnrc.h" + +#define ENABLE_DEBUG (0) +#include "debug.h" + +/** + * @brief Definition of default thread priority and stacksize + * @{ + */ +#ifndef NRFMIN_GNRC_THREAD_PRIO +#define NRFMIN_GNRC_THREAD_PRIO GNRC_NETDEV2_MAC_PRIO +#endif + +#ifndef NRFMIN_GNRC_STACKSIZE +#define NRFMIN_GNRC_STACKSIZE THREAD_STACKSIZE_DEFAULT +#endif +/** @} */ + +/** + * @brief + */ +#define BCAST (GNRC_NETIF_HDR_FLAGS_BROADCAST | GNRC_NETIF_HDR_FLAGS_MULTICAST) + +/** + * @brief Allocate the stack for the GNRC netdev2 thread to run in + */ +static char stack[NRFMIN_GNRC_STACKSIZE]; + +/** + * @brief Allocate the GNRC netdev2 data structure. + */ +static gnrc_netdev2_t plug; + + +static int hdr_netif_to_nrfmin(nrfmin_hdr_t *nrfmin, gnrc_pktsnip_t *pkt) +{ + gnrc_netif_hdr_t *netif = (gnrc_netif_hdr_t *)pkt->data; + + if (!(netif->flags & BCAST) && (netif->dst_l2addr_len != 2)) { + return -EINVAL; + } + + nrfmin->len = gnrc_pkt_len(pkt->next) + NRFMIN_HDR_LEN; + if (netif->flags & BCAST) { + nrfmin->dst_addr = NRFMIN_ADDR_BCAST; + } + else { + memcpy(&nrfmin->dst_addr, gnrc_netif_hdr_get_dst_addr(netif), 2); + } + nrfmin->src_addr = nrfmin_get_addr(); + if (pkt->next) { + nrfmin->proto = (uint8_t)pkt->next->type; + } + else { + nrfmin->proto = 0; + } + + return 0; +} + +static int gnrc_nrfmin_send(gnrc_netdev2_t *dev, gnrc_pktsnip_t *pkt) +{ + int res; + struct iovec *vec; + size_t vec_len; + gnrc_pktsnip_t *vec_snip; + nrfmin_hdr_t nrfmin_hdr; + + assert(pkt); + + if (pkt->type != GNRC_NETTYPE_NETIF) { + DEBUG("[nrfmin_gnrc] send: first header is not generic netif header\n"); + return -EBADMSG; + } + + /* build the nrfmin header from the generic netif header */ + res = hdr_netif_to_nrfmin(&nrfmin_hdr, pkt); + if (res < 0) { + DEBUG("[nrfmin_gnrc] send: failed to build nrfmin header\n"); + gnrc_pktbuf_release(pkt); + return res; + } + + /* create iovec of data */ + vec_snip = gnrc_pktbuf_get_iovec(pkt, &vec_len); + if (vec_snip == NULL) { + DEBUG("[nrfmin_gnrc] send: failed to create IO vector\n"); + gnrc_pktbuf_release(pkt); + return -ENOBUFS; + } + + /* link first entry of the vector to the nrfmin header */ + vec = (struct iovec *)vec_snip->data; + vec[0].iov_base = &nrfmin_hdr; + vec[0].iov_len = NRFMIN_HDR_LEN; + + /* and finally send out the data and release the packet */ + res = dev->dev->driver->send(dev->dev, vec, vec_len); + gnrc_pktbuf_release(vec_snip); + + return res; +} + +static gnrc_pktsnip_t *gnrc_nrfmin_recv(gnrc_netdev2_t *dev) +{ + int pktsize; + nrfmin_hdr_t *nrfmin; + gnrc_netif_hdr_t *netif; + gnrc_pktsnip_t *pkt_snip; + gnrc_pktsnip_t *hdr_snip; + gnrc_pktsnip_t *netif_snip; + + /* get the size of the new packet */ + pktsize = nrfmin_dev.driver->recv(NULL, NULL, 0, NULL); + if (pktsize <= 0) { + DEBUG("[nrfmin_gnrc] recv: error: tried to read empty packet\n"); + return NULL; + } + + /* allocate space in the packet buffer */ + pkt_snip = gnrc_pktbuf_add(NULL, NULL, pktsize, GNRC_NETTYPE_UNDEF); + if (pkt_snip == NULL) { + DEBUG("[nrfmin_gnrc] recv: unable to allocate pktsnip\n"); + return NULL; + } + + /* read the incoming data into the packet buffer */ + nrfmin_dev.driver->recv(NULL, pkt_snip->data, pktsize, NULL); + + /* now we mark the nrfmin header */ + hdr_snip = gnrc_pktbuf_mark(pkt_snip, NRFMIN_HDR_LEN, GNRC_NETTYPE_UNDEF); + if (hdr_snip == NULL) { + DEBUG("[nrfmin_gnrc] recv: unable to mark the nrfmin header\n"); + gnrc_pktbuf_release(pkt_snip); + return NULL; + } + + /* allocate the generic netif header and populate it with data from the + nrfmin header */ + nrfmin = (nrfmin_hdr_t *)hdr_snip->data; + netif_snip = gnrc_netif_hdr_build((uint8_t *)&nrfmin->src_addr, 2, + (uint8_t *)&nrfmin->dst_addr, 2); + if (netif_snip == NULL) { + DEBUG("[nrfmin_gnrc] recv: unable to allocate netif header\n"); + gnrc_pktbuf_release(pkt_snip); + return NULL; + } + + netif = (gnrc_netif_hdr_t *)netif_snip->data; + if (nrfmin->dst_addr == NRFMIN_ADDR_BCAST) { + netif->flags |= GNRC_NETIF_HDR_FLAGS_BROADCAST; + } + netif->lqi = 0; + netif->rssi = 0; + netif->if_pid = plug.pid; + pkt_snip->type = nrfmin->proto; + + /* finally: remove the nrfmin header and append the netif header */ + gnrc_pktbuf_remove_snip(pkt_snip, hdr_snip); + LL_APPEND(pkt_snip, netif_snip); + + return pkt_snip; +} + +void gnrc_nrfmin_init(void) +{ + /* setup the NRFMIN driver */ + nrfmin_setup(); + + /* initialize the GNRC plug struct */ + plug.send = gnrc_nrfmin_send; + plug.recv = gnrc_nrfmin_recv; + plug.dev = &nrfmin_dev; + + gnrc_netdev2_init(stack, sizeof(stack), + NRFMIN_GNRC_THREAD_PRIO, + "nrfmin", &plug); +} diff --git a/drivers/Makefile.dep b/drivers/Makefile.dep index ce41876d3..7e8a3cb63 100644 --- a/drivers/Makefile.dep +++ b/drivers/Makefile.dep @@ -107,6 +107,12 @@ ifneq (,$(filter mpu9150,$(USEMODULE))) USEMODULE += xtimer endif +ifneq (,$(filter nrfmin,$(USEMODULE))) + FEATURES_REQUIRED += radio_nrfmin + FEATURES_REQUIRED += periph_cpuid + USEMODULE += netif +endif + ifneq (,$(filter nvram_spi,$(USEMODULE))) USEMODULE += xtimer endif diff --git a/drivers/include/net/netdev2.h b/drivers/include/net/netdev2.h index b859f2de6..b11e2e937 100644 --- a/drivers/include/net/netdev2.h +++ b/drivers/include/net/netdev2.h @@ -62,6 +62,7 @@ enum { NETDEV2_TYPE_ETHERNET, NETDEV2_TYPE_IEEE802154, NETDEV2_TYPE_CC110X, + NETDEV2_TYPE_NRFMIN }; /** diff --git a/examples/default/Makefile b/examples/default/Makefile index ad50732db..85e6687ed 100644 --- a/examples/default/Makefile +++ b/examples/default/Makefile @@ -37,9 +37,9 @@ USEMODULE += saul_default USEMODULE += auto_init_saul BOARD_PROVIDES_NETIF := airfy-beacon cc2538dk fox iotlab-m3 iotlab-a8-m3 mulle \ - native nrf51dongle nrf6310 openmote-cc2538 pba-d-01-kw2x pca10000 pca10005 \ - remote-pa remote-reva saml21-xpro samr21-xpro spark-core telosb \ - yunjia-nrf51822 z1 + microbit native nrf51dongle nrf52dk nrf6310 openmote-cc2538 pba-d-01-kw2x \ + pca10000 pca10005 remote-pa remote-reva saml21-xpro samr21-xpro \ + spark-core telosb yunjia-nrf51822 z1 ifneq (,$(filter $(BOARD),$(BOARD_PROVIDES_NETIF))) # Use modules for networking diff --git a/sys/auto_init/auto_init.c b/sys/auto_init/auto_init.c index b9b96bf3c..56d5d7294 100644 --- a/sys/auto_init/auto_init.c +++ b/sys/auto_init/auto_init.c @@ -229,6 +229,11 @@ void auto_init(void) gnrc_nordic_ble_6lowpan_init(); #endif +#ifdef MODULE_NRFMIN + extern void gnrc_nrfmin_init(void); + gnrc_nrfmin_init(); +#endif + #ifdef MODULE_W5100 extern void auto_init_w5100(void); auto_init_w5100(); diff --git a/tests/driver_nrfmin/Makefile b/tests/driver_nrfmin/Makefile deleted file mode 100644 index 11b3bba8e..000000000 --- a/tests/driver_nrfmin/Makefile +++ /dev/null @@ -1,14 +0,0 @@ -APPLICATION = driver_nrfmin -include ../Makefile.tests_common - -FEATURES_REQUIRED = radio_nrfmin - -USEMODULE += shell -USEMODULE += shell_commands -USEMODULE += ps -USEMODULE += radio_nrfmin -USEMODULE += gnrc -USEMODULE += gnrc_nomac -USEMODULE += gnrc_pktdump - -include $(RIOTBASE)/Makefile.include diff --git a/tests/driver_nrfmin/README.md b/tests/driver_nrfmin/README.md deleted file mode 100644 index 504f793fd..000000000 --- a/tests/driver_nrfmin/README.md +++ /dev/null @@ -1,9 +0,0 @@ -Expected result -=============== -The test will initialize all basic networking functionality including the -minimal NRF51822 radio driver and run the shell providing netif shell commands. - -Background -========== -Use the shell commands to test the link layer functionality of the minimal -NRF51822 radio driver (nrfmin). diff --git a/tests/driver_nrfmin/main.c b/tests/driver_nrfmin/main.c deleted file mode 100644 index c6b336b7b..000000000 --- a/tests/driver_nrfmin/main.c +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (C) 2015 Freie Universität Berlin - * - * 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 the NRF51822 minimal radio driver (nrfmin) - * - * @author Hauke Petersen - * - * @} - */ - -#include - -#include "shell.h" -#include "nrfmin.h" -#include "net/gnrc.h" -#include "net/gnrc/nomac.h" -#include "net/gnrc/pktdump.h" - -static char nomac_stack[THREAD_STACKSIZE_DEFAULT]; - -int main(void) -{ - gnrc_netdev_t dev; - gnrc_netreg_entry_t netobj = GNRC_NETREG_ENTRY_INIT_PID(GNRC_NETREG_DEMUX_CTX_ALL, - gnrc_pktdump_pid); - - puts("\nManual test for the minimal NRF51822 radio driver\n"); - puts("Use the 'ifconfig' and 'txtsnd' shell commands to verify the driver"); - - /* initialize network device */ - nrfmin_init(&dev); - gnrc_nomac_init(nomac_stack, sizeof(nomac_stack), 5, "nomac", &dev); - - /* initialize packet dumper */ - gnrc_netreg_register(GNRC_NETTYPE_UNDEF, &netobj); - - /* initialize and run the shell */ - char line_buf[SHELL_DEFAULT_BUFSIZE]; - shell_run(NULL, line_buf, SHELL_DEFAULT_BUFSIZE); - - return 0; -}