mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
Merge pull request #6108 from haukepetersen/opt_nrf_nrfmin
cpu/nrf5x: ported nrfmin driver to netdev2
This commit is contained in:
commit
a8297595e6
3
boards/airfy-beacon/Makefile.dep
Normal file
3
boards/airfy-beacon/Makefile.dep
Normal file
@ -0,0 +1,3 @@
|
||||
ifneq (,$(filter gnrc_netdev_default netdev_default,$(USEMODULE)))
|
||||
USEMODULE += nrfmin
|
||||
endif
|
@ -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
|
||||
|
@ -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 */
|
||||
/** @} */
|
||||
|
3
boards/nrf51dongle/Makefile.dep
Normal file
3
boards/nrf51dongle/Makefile.dep
Normal file
@ -0,0 +1,3 @@
|
||||
ifneq (,$(filter gnrc_netdev_default netdev_default,$(USEMODULE)))
|
||||
USEMODULE += nrfmin
|
||||
endif
|
@ -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
|
||||
|
@ -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
|
||||
|
3
boards/nrf6310/Makefile.dep
Normal file
3
boards/nrf6310/Makefile.dep
Normal file
@ -0,0 +1,3 @@
|
||||
ifneq (,$(filter gnrc_netdev_default netdev_default,$(USEMODULE)))
|
||||
USEMODULE += nrfmin
|
||||
endif
|
3
boards/pca10000/Makefile.dep
Normal file
3
boards/pca10000/Makefile.dep
Normal file
@ -0,0 +1,3 @@
|
||||
ifneq (,$(filter gnrc_netdev_default netdev_default,$(USEMODULE)))
|
||||
USEMODULE += nrfmin
|
||||
endif
|
3
boards/pca10005/Makefile.dep
Normal file
3
boards/pca10005/Makefile.dep
Normal file
@ -0,0 +1,3 @@
|
||||
ifneq (,$(filter gnrc_netdev_default netdev_default,$(USEMODULE)))
|
||||
USEMODULE += nrfmin
|
||||
endif
|
3
boards/yunjia-nrf51822/Makefile.dep
Normal file
3
boards/yunjia-nrf51822/Makefile.dep
Normal file
@ -0,0 +1,3 @@
|
||||
ifneq (,$(filter gnrc_netdev_default netdev_default,$(USEMODULE)))
|
||||
USEMODULE += nrfmin
|
||||
endif
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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 <hauke.petersen@fu-berlin.de>
|
||||
*/
|
||||
|
||||
#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_ */
|
||||
/** @} */
|
@ -1,3 +0,0 @@
|
||||
MODULE = radio_nrfmin
|
||||
|
||||
include $(RIOTBASE)/Makefile.base
|
@ -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 <hauke.petersen@fu-berlin.de>
|
||||
*
|
||||
* @}
|
||||
*/
|
||||
|
||||
#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,
|
||||
};
|
||||
//
|
@ -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
|
||||
|
237
cpu/nrf5x_common/include/nrfmin.h
Normal file
237
cpu/nrf5x_common/include/nrfmin.h
Normal file
@ -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 <hauke.petersen@fu-berlin.de>
|
||||
*/
|
||||
|
||||
#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_ */
|
||||
/** @} */
|
48
cpu/nrf5x_common/include/nrfmin_gnrc.h
Normal file
48
cpu/nrf5x_common/include/nrfmin_gnrc.h
Normal file
@ -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 <hauke.petersen@fu-berlin.de>
|
||||
*/
|
||||
|
||||
#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_ */
|
||||
/** @} */
|
9
cpu/nrf5x_common/radio/nrfmin/Makefile
Normal file
9
cpu/nrf5x_common/radio/nrfmin/Makefile
Normal file
@ -0,0 +1,9 @@
|
||||
MODULE = nrfmin
|
||||
|
||||
SRC = nrfmin.c
|
||||
|
||||
ifneq (,$(filter gnrc_netdev_default,$(USEMODULE)))
|
||||
SRC += nrfmin_gnrc.c
|
||||
endif
|
||||
|
||||
include $(RIOTBASE)/Makefile.base
|
549
cpu/nrf5x_common/radio/nrfmin/nrfmin.c
Normal file
549
cpu/nrf5x_common/radio/nrfmin/nrfmin.c
Normal file
@ -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 <hauke.petersen@fu-berlin.de>
|
||||
*
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
#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
|
||||
};
|
201
cpu/nrf5x_common/radio/nrfmin/nrfmin_gnrc.c
Normal file
201
cpu/nrf5x_common/radio/nrfmin/nrfmin_gnrc.c
Normal file
@ -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 <hauke.petersen@fu-berlin.de>
|
||||
*
|
||||
* @}
|
||||
*/
|
||||
|
||||
#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);
|
||||
}
|
@ -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
|
||||
|
@ -62,6 +62,7 @@ enum {
|
||||
NETDEV2_TYPE_ETHERNET,
|
||||
NETDEV2_TYPE_IEEE802154,
|
||||
NETDEV2_TYPE_CC110X,
|
||||
NETDEV2_TYPE_NRFMIN
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
|
@ -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
|
@ -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).
|
@ -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 <hauke.petersen@fu-berlin.de>
|
||||
*
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#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;
|
||||
}
|
Loading…
Reference in New Issue
Block a user