1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00
RIOT/drivers/nrf24l01p_ng/gnrc_netif_nrf24l01p_ng.c
fabian18 f7a77ebb04 drivers/nrf24l01p_ng: netdev driver for nrf24l01+
The driver uses the netdev interface. Due to the limited
capabilities of the transceiver (32 byte FIFO and no source address in the layer2 frame),
it relies on 6LowPAN compression and adds the source address to the frame for that.
2021-01-24 20:49:39 +01:00

237 lines
7.7 KiB
C

/*
* Copyright (C) 2019 Otto-von-Guericke-Universität Magdeburg
*
* 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_nrf24l01p_ng
* @{
*
* @file
* @brief Implementation of gnrc_netif Rx/Tx adaptation functions
* for the NRF24L01+ (NG) transceiver
*
* @author Fabian Hüßler <fabian.huessler@ovgu.de>
* @}
*/
#include <assert.h>
#define ENABLE_DEBUG (0)
#include "debug.h"
#include "net/gnrc.h"
#include "gnrc_netif_nrf24l01p_ng.h"
#include "nrf24l01p_ng.h"
/**
* @brief Broadcast/Multicast flag
*/
#define BCAST (GNRC_NETIF_HDR_FLAGS_BROADCAST | GNRC_NETIF_HDR_FLAGS_MULTICAST)
static gnrc_pktsnip_t *_nrf24l01p_ng_pkt_recv(gnrc_netif_t *netif)
{
/* get frame size */
int frame_len = netif->dev->driver->recv(netif->dev, NULL, 1, NULL);
/* allocate space for the packet in the pktbuf */
gnrc_pktsnip_t *frame = gnrc_pktbuf_add(NULL, NULL, frame_len,
NRF24L01P_NG_UPPER_LAYER_PROTOCOL);
if (!frame) {
DEBUG_PUTS("[nrf24l01p_ng] _pkt_recv: unable to allocate space");
return NULL;
}
/* copy the payload into the packet buffer */
frame_len = netif->dev->driver->recv(netif->dev, frame->data,
frame_len, NULL);
if (frame_len <= 0) {
DEBUG_PUTS("[nrf24l01p_ng] _pkt_recv: driver error");
gnrc_pktbuf_release(frame);
return NULL;
}
return frame;
}
/**
* @brief Receives a @ref net_gnrc_pkt "packet" from the network interface
*
* @pre `netif != NULL`
*
* @note The function takes the bytes received via netdev_driver_t::recv()
* from gnrc_netif_t::dev and re-formats it to a
* @ref net_gnrc_pkt "packet" containing a @ref net_gnrc_netif_hdr
* and a payload header in receive order.
*
* @param[in] netif The network interface.
*
* @return The packet received. Contains the payload (with the type marked
* accordingly) and a @ref net_gnrc_netif_hdr in receive order.
* @return NULL, if @ref net_gnrc_pktbuf was full.
*/
static gnrc_pktsnip_t *_nrf24l01p_ng_adpt_recv(gnrc_netif_t *netif)
{
assert(netif);
assert(netif->dev);
gnrc_pktsnip_t *frame;
gnrc_pktsnip_t *snip;
gnrc_netif_hdr_t *netif_hdr;
uint8_t dst_addr[NRF24L01P_NG_ADDR_WIDTH];
uint8_t src_addr[NRF24L01P_NG_ADDR_WIDTH];
uint8_t src_addr_len;
if (!(frame = _nrf24l01p_ng_pkt_recv(netif))) {
DEBUG_PUTS("[nrf24l01p_ng] _adpt_recv: no frame");
return NULL;
}
if (!(snip = gnrc_pktbuf_mark(frame, sizeof(dst_addr), GNRC_NETTYPE_UNDEF))) {
DEBUG_PUTS("[nrf24l01p_ng] _adpt_recv: unable to mark header snip");
gnrc_pktbuf_release(frame);
return NULL;
}
memcpy(dst_addr, snip->data, sizeof(dst_addr));
gnrc_pktbuf_remove_snip(frame, snip);
src_addr_len = ((uint8_t *)frame->data)[0];
if (src_addr_len < NRF24L01P_NG_MIN_ADDR_WIDTH ||
src_addr_len > NRF24L01P_NG_MAX_ADDR_WIDTH) {
DEBUG_PUTS("[nrf24l01p_ng] _adpt_recv: Invalid source address length");
gnrc_pktbuf_release(frame);
return NULL;
}
if (!(snip = gnrc_pktbuf_mark(frame, 1 + src_addr_len, GNRC_NETTYPE_UNDEF))) {
DEBUG_PUTS("[nrf24l01p_ng] _adpt_recv: unable to mark src header snip");
gnrc_pktbuf_release(frame);
return NULL;
}
memcpy(src_addr, ((uint8_t *)snip->data) + 1, src_addr_len);
gnrc_pktbuf_remove_snip(frame, snip);
if (!(snip = gnrc_netif_hdr_build(src_addr, src_addr_len,
dst_addr, sizeof(dst_addr)))) {
DEBUG_PUTS("[nrf24l01p_ng] _adpt_recv: unable to allocate netif header");
gnrc_pktbuf_release(frame);
return NULL;
}
netif_hdr = (gnrc_netif_hdr_t *)snip->data;
const uint8_t bcast_addr[] = NRF24L01P_NG_BROADCAST_ADDR;
if (!memcmp(dst_addr, bcast_addr, sizeof(dst_addr))) {
netif_hdr->flags |= GNRC_NETIF_HDR_FLAGS_BROADCAST;
}
gnrc_netif_hdr_set_netif(netif_hdr, netif);
LL_APPEND(frame, snip);
#if IS_USED(MODULE_NETSTATS_L2)
netif->stats.rx_count++;
netif->stats.rx_bytes += frame->size;
#endif
return frame;
}
/**
* @brief Send a @ref net_gnrc_pkt "packet" over the network interface
*
* @pre `netif != NULL && pkt != NULL`
*
* @note The function re-formats the content of @p pkt to a format expected
* by the netdev_driver_t::send() method of gnrc_netif_t::dev and
* releases the packet before returning (so no additional release
* should be required after calling this method).
*
* @param[in] netif The network interface.
* @param[in] pkt A packet to send.
*
* @return The number of bytes actually sent on success
* @return -EBADMSG, if the @ref net_gnrc_netif_hdr in @p pkt is missing
* or is in an unexpected format.
* @return -ENOTSUP, if sending @p pkt in the given format isn't supported
* (e.g. empty payload with Ethernet).
* @return Any negative error code reported by gnrc_netif_t::dev.
*/
static int _nrf24l01p_ng_adpt_send(gnrc_netif_t *netif, gnrc_pktsnip_t *pkt)
{
assert(netif);
assert(pkt);
assert(netif->dev);
netdev_t *netdev = (netdev_t *)netif->dev;
gnrc_netif_hdr_t *netif_hdr = (gnrc_netif_hdr_t *)pkt->data;
if (!netif_hdr) {
return -EBADMSG;
}
const uint8_t bcast_addr[] = NRF24L01P_NG_BROADCAST_ADDR;
const uint8_t *dst_addr = gnrc_netif_hdr_get_dst_addr(netif_hdr);
uint8_t dst_addr_len = netif_hdr->dst_l2addr_len;
if (netif_hdr->flags & BCAST) {
DEBUG_PUTS("[nrf24l01p_ng] _adpt_send: preparing to send broadcast");
dst_addr = bcast_addr;
dst_addr_len = NRF24L01P_NG_ADDR_WIDTH;
#if IS_USED(MODULE_NETSTATS_L2)
netif->stats.tx_mcast_count++;
#endif
}
else {
if (!dst_addr ||
dst_addr_len > NRF24L01P_NG_MAX_ADDR_WIDTH ||
dst_addr_len < NRF24L01P_NG_MIN_ADDR_WIDTH) {
return -EBADMSG;
}
if (!memcmp(dst_addr, bcast_addr, dst_addr_len)) {
DEBUG_PUTS("[nrf24l01p_ng] _adpt_send: preparing to send broadcast");
#if IS_USED(MODULE_NETSTATS_L2)
netif->stats.tx_mcast_count++;
#endif
}
}
uint8_t src[1 + NRF24L01P_NG_ADDR_WIDTH];
src[0] = NRF24L01P_NG_ADDR_WIDTH;
memcpy(src + 1,
NRF24L01P_NG_ADDR_P1((nrf24l01p_ng_t *)netdev),
NRF24L01P_NG_ADDR_WIDTH);
iolist_t iolist_src_addr = {
.iol_next = ((iolist_t *)pkt->next),
.iol_base = src,
.iol_len = sizeof(src)
};
iolist_t iolist = {
.iol_next = &iolist_src_addr,
.iol_base = (uint8_t *)dst_addr,
.iol_len = dst_addr_len
};
int res;
while ((res = netdev->driver->send(netdev, &iolist)) < 0) {
if (res == -EAGAIN) {
/* pending interrupts? */
continue;
}
else if (res == -EBUSY) {
if (!IS_ACTIVE(MODULE_GNRC_NETIF_PKTQ)) {
/* busy send */
continue;
}
}
break;
}
gnrc_pktbuf_release(pkt);
return res;
}
static const gnrc_netif_ops_t nrf24l01p_ng_netif_ops = {
.init = gnrc_netif_default_init,
.send = _nrf24l01p_ng_adpt_send,
.recv = _nrf24l01p_ng_adpt_recv,
.get = gnrc_netif_get_from_netdev,
.set = gnrc_netif_set_from_netdev,
};
int gnrc_netif_nrf24l01p_ng_create(gnrc_netif_t *netif, char *stack,
int stacksize, char priority, char *name,
netdev_t *dev)
{
return gnrc_netif_create(netif, stack, stacksize, priority, name,
dev, &nrf24l01p_ng_netif_ops);
}