cpu/nrf51: ported nrmin radio driver to netdev2

pr/spi.typo
Hauke Petersen 6 years ago
parent e224cf8de8
commit 36317c56f3

@ -5,7 +5,7 @@ MODULE = cpu
DIRS = periph $(RIOTCPU)/cortexm_common $(RIOTCPU)/nrf5x_common
# build one of the radio drivers, if enabled
ifneq (,$(filter radio_nrfmin,$(USEMODULE)))
ifneq (,$(filter nrfmin,$(USEMODULE)))
DIRS += radio/nrfmin
endif

@ -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,5 +1,5 @@
/*
* Copyright (C) 2015 Freie Universität Berlin
* 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
@ -7,91 +7,227 @@
*/
/**
* @defgroup drivers_nrf51822_nrfmin NRF Minimal Radio Driver
* @ingroup drivers_netdev
* @brief Minimal driver for the NRF51822 radio
* @defgroup drivers_nrf51822_nrfmin NRF Minimal Radio Driver
* @ingroup drivers_netdev
* @brief Minimal driver for the NRF51 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.
* 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.
*
* 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:
* 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
*
* byte0 | byte1 - byte2 | byte3 - byte4 | byte5 byte6 | byte7 - byteN
* ------ | ------------- | ------------- | ------------- | -------------
* length | src_addr | dst_addr | proto | payload...
* 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).
*
* 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.
* 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.
*
* 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.
* For out custom link layer, we define our own proprietary link layer format
* (all fields are in host byte order (little endian)):
*
* 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
* 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)
*
* SUMMERY:
* 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
* @brief Interface definition for the nrfmin NRF51822 radio driver
*
* @author Hauke Petersen <hauke.petersen@fu-berlin.de>
* @author Hauke Petersen <hauke.petersen@fu-berlin.de>
*/
#ifndef NRFMIN_H_
#define NRFMIN_H_
#include "net/gnrc/netdev.h"
#include "net/netdev2.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Default PAN ID used after initialization
* @brief nrfmin channel configuration
* @{
*/
#define NRFMIN_DEFAULT_PAN (0x0550)
#define NRFMIN_CHAN_MIN (0U)
#define NRFMIN_CHAN_DEFAULT (0U) /* 2400MHz */
#define NRFMIN_CHAN_MAX (32)
/** @} */
/**
* @brief Default channel set after initialization
* @brief Default transmission power used
*/
#define NRFMIN_DEFAULT_CHANNEL (1U) /* 2401MHz */
#define NRFMIN_TXPOWER_DEFAULT (0) /* 0dBm */
/**
* @brief Default transmission power used
* @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
*/
#define NRFMIN_DEFAULT_TXPOWER (0) /* 0dBm */
extern netdev2_t nrfmin_dev;
/**
* @brief Reference to the netdev driver interface
*/
extern const gnrc_netdev_driver_t nrfmin_driver;
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 Initialize the NRF51822 radio
* @brief Get the current radio state
*
* The initialization uses static configuration values.
* @return state the radio is currently in
*/
netopt_state_t nrfmin_get_state(void);
/**
* @brief Put the device into the given state
*
* @param[out] dev pointer to the netdev device descriptor
* @param[in] val target state
*
* @return 0 on success
* @return -ENODEV if @p dev is invalid
* @return sizeof(netopt_state_t) on success
* @return -ENOTSUP if target state is not applicable
*/
int nrfmin_init(gnrc_netdev_t *dev);
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
}

@ -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_nrf51822_nrfmin_gnrc GNRC adapter for nrfmin
* @ingroup drivers_nrf51822_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_ */
/** @} */

@ -1,3 +1,3 @@
MODULE = radio_nrfmin
MODULE = nrfmin
include $(RIOTBASE)/Makefile.base

File diff suppressed because it is too large Load Diff

@ -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_nrf51_nrfmin
* @{
*
* @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 nrfmin_gnrc_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 *nrfmin_gnrc_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 nrfmin_gnrc_init(void)
{
/* setup the NRFMIN driver */
nrfmin_setup();
/* initialize the GNRC plug struct */
plug.send = nrfmin_gnrc_send;
plug.recv = nrfmin_gnrc_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
};
/**

Loading…
Cancel
Save