/* * 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 * @} */ #include #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]); }