mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
248 lines
8.0 KiB
C
248 lines
8.0 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 "luid.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 = 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(container_of(netdev, 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);
|
|
}
|
|
|
|
void __attribute__((weak)) nrf24l01p_ng_eui_get(const netdev_t *netdev, uint8_t *eui)
|
|
{
|
|
(void)netdev;
|
|
do {
|
|
luid_get_lb(eui, NRF24L01P_NG_ADDR_WIDTH);
|
|
}
|
|
while (eui[NRF24L01P_NG_ADDR_WIDTH - 1] ==
|
|
((uint8_t[])NRF24L01P_NG_BROADCAST_ADDR)[NRF24L01P_NG_ADDR_WIDTH - 1]);
|
|
}
|