From 6a0ac75d2c2f39e2c7c3c9a9ca4fb660911b5d1e Mon Sep 17 00:00:00 2001 From: zhuoshuguo Date: Mon, 7 Nov 2016 18:18:19 +0100 Subject: [PATCH] gnrc_mac: add mac tx and rx internal types and API. --- sys/include/net/gnrc/mac/internal.h | 80 +++++ sys/include/net/gnrc/mac/mac.h | 64 ++++ sys/include/net/gnrc/mac/types.h | 162 +++++++++- sys/include/net/gnrc/netdev2.h | 19 +- sys/net/gnrc/Makefile | 3 + sys/net/gnrc/link_layer/gnrc_mac/Makefile | 3 + sys/net/gnrc/link_layer/gnrc_mac/internal.c | 248 +++++++++++++++ tests/unittests/Makefile | 2 +- .../tests-gnrc_mac_internal/Makefile | 1 + .../tests-gnrc_mac_internal/Makefile.include | 3 + .../tests-gnrc_mac_internal.c | 283 ++++++++++++++++++ .../tests-gnrc_mac_internal.h | 37 +++ 12 files changed, 890 insertions(+), 15 deletions(-) create mode 100644 sys/include/net/gnrc/mac/internal.h create mode 100644 sys/include/net/gnrc/mac/mac.h create mode 100644 sys/net/gnrc/link_layer/gnrc_mac/Makefile create mode 100644 sys/net/gnrc/link_layer/gnrc_mac/internal.c create mode 100644 tests/unittests/tests-gnrc_mac_internal/Makefile create mode 100644 tests/unittests/tests-gnrc_mac_internal/Makefile.include create mode 100644 tests/unittests/tests-gnrc_mac_internal/tests-gnrc_mac_internal.c create mode 100644 tests/unittests/tests-gnrc_mac_internal/tests-gnrc_mac_internal.h diff --git a/sys/include/net/gnrc/mac/internal.h b/sys/include/net/gnrc/mac/internal.h new file mode 100644 index 0000000000..0c09e333da --- /dev/null +++ b/sys/include/net/gnrc/mac/internal.h @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2015 Daniel Krebs + * 2016 INRIA + * + * 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 net_gnrc_mac + * @{ + * + * @file + * @brief Definitions of internal functions of GNRC_MAC module + * @internal + * @author Daniel Krebs + * @author Shuguo Zhuo + */ + +#ifndef GNRC_MAC_INTERNAL_H_ +#define GNRC_MAC_INTERNAL_H_ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#if (GNRC_MAC_TX_QUEUE_SIZE != 0) || defined(DOXYGEN) +/** + * @brief Queues the packet into the related transmission packet queue in netdev2_t::tx. + * Note that, in case the `gnrc_mac_tx_neighbor_t` structure is in used (indicated + * by `GNRC_MAC_NEIGHBOR_COUNT != 0`), this function queues the packet to + * the queue associated to the pkt's destination neighbor, including a + * `broadcast-neighbor` (neighbor id is `0` in netdev2_t::tx::neighbors) which + * specifically stores broadcasting packets. + * On the other hand, if `gnrc_mac_tx_neighbor_t` structure is not in used (indicated + * by `GNRC_MAC_NEIGHBOR_COUNT == 0`), this function queues the packet into the single + * priority TX queue defined in in netdev2_t::tx. + * + * @param[in,out] tx gnrc_mac transmission management object + * @param[in] priority the priority of @p pkt + * @param[in] pkt gnrc packet that will be queued + * + * @return true if queued successfully, otherwise false. + */ +bool gnrc_mac_queue_tx_packet(gnrc_mac_tx_t* tx, uint32_t priority, gnrc_pktsnip_t* pkt); +#endif /* (GNRC_MAC_TX_QUEUE_SIZE != 0) || defined(DOXYGEN) */ + +#if (GNRC_MAC_RX_QUEUE_SIZE != 0) || defined(DOXYGEN) +/** + * @brief Queues the packet into the reception packet queue in netdev2_t::rx. + * + * @param[in,out] rx gnrc_mac reception management object + * @param[in] priority the priority of @p pkt + * @param[in] pkt gnrc packet that will be queued + * + * @return true if queued successfully, otherwise false. + */ +bool gnrc_mac_queue_rx_packet(gnrc_mac_rx_t* rx, uint32_t priority, gnrc_pktsnip_t* pkt); +#endif /* (GNRC_MAC_RX_QUEUE_SIZE != 0) || defined(DOXYGEN) */ + +#if (GNRC_MAC_DISPATCH_BUFFER_SIZE != 0) || defined(DOXYGEN) +/** + * @brief Dispatch all the packets stored in netdev2_t::rx:dispatch_buffer to upper layer. + * + * @param[in,out] rx gnrc_mac reception management object + */ +void gnrc_mac_dispatch(gnrc_mac_rx_t* rx); +#endif /* (GNRC_MAC_DISPATCH_BUFFER_SIZE != 0) || defined(DOXYGEN) */ + +#ifdef __cplusplus +} +#endif + +#endif /* GNRC_MAC_INTERNAL_H_ */ +/** @} */ diff --git a/sys/include/net/gnrc/mac/mac.h b/sys/include/net/gnrc/mac/mac.h new file mode 100644 index 0000000000..2299919844 --- /dev/null +++ b/sys/include/net/gnrc/mac/mac.h @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2015 Daniel Krebs + * 2016 INRIA + * + * 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 net_gnrc_mac Common MAC module + * @ingroup net_gnrc + * @brief A MAC module for providing common MAC parameters and helper functions. + * + * @{ + * + * @file + * @brief Definitions of GNRC_MAC + * + * @author Daniel Krebs + * @author Shuguo Zhuo + */ + +#ifndef GNRC_MAC_H +#define GNRC_MAC_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief The default rx queue size for incoming packets + */ +#ifndef GNRC_MAC_RX_QUEUE_SIZE +#define GNRC_MAC_RX_QUEUE_SIZE (8U) +#endif + +/** + * @brief The default buffer size for storing dispatching packets + */ +#ifndef GNRC_MAC_DISPATCH_BUFFER_SIZE +#define GNRC_MAC_DISPATCH_BUFFER_SIZE (8U) +#endif + +/** + * @brief Count of neighbor nodes in one-hop distance + */ +#ifndef GNRC_MAC_NEIGHBOR_COUNT +#define GNRC_MAC_NEIGHBOR_COUNT (8U) +#endif + +/** + * @brief The default queue size for transmission packets coming from higher layers + */ +#ifndef GNRC_MAC_TX_QUEUE_SIZE +#define GNRC_MAC_TX_QUEUE_SIZE (8U) +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* GNRC_MAC_H */ +/** @} */ diff --git a/sys/include/net/gnrc/mac/types.h b/sys/include/net/gnrc/mac/types.h index dffcadb143..47b5c29070 100644 --- a/sys/include/net/gnrc/mac/types.h +++ b/sys/include/net/gnrc/mac/types.h @@ -1,6 +1,6 @@ /* * Copyright (C) 2015 Daniel Krebs - * + * 2016 INRIA * * 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 @@ -8,13 +8,11 @@ */ /** - * @defgroup net_gnrc_mac A common MAC type for providing key MAC parameters and helper functions - * @ingroup net - * @brief A common MAC type for providing key MAC parameters and helper functions. + * @ingroup net_gnrc_mac * @{ * * @file - * @brief Internal types used by the GNRC_MAC entities + * @brief Internal data types used by GNRC_MAC * * @author Daniel Krebs * @author Shuguo Zhuo @@ -26,11 +24,10 @@ #include #include #include -#include -#include -#include #include - +#include +#include +#include #ifdef __cplusplus extern "C" { @@ -40,12 +37,151 @@ extern "C" { * @brief definition for device transmission feedback types */ typedef enum { - TX_FEEDBACK_UNDEF = 0, /* Transmission just start, no Tx feedback yet */ - TX_FEEDBACK_SUCCESS, /* Transmission succeeded */ - TX_FEEDBACK_NOACK, /* No ACK for the transmitted packet */ - TX_FEEDBACK_BUSY /* found medium busy when doing transmission */ + TX_FEEDBACK_UNDEF = 0, /**< Transmission just start, no Tx feedback yet */ + TX_FEEDBACK_SUCCESS, /**< Transmission succeeded */ + TX_FEEDBACK_NOACK, /**< No ACK for the transmitted packet */ + TX_FEEDBACK_BUSY /**< found medium busy when doing transmission */ } gnrc_mac_tx_feedback_t; +/** + * @brief Static initializer for gnrc_mac_tx_feedback_t. + */ +#define GNRC_MAC_TX_FEEDBACK_INIT { TX_FEEDBACK_UNDEF } + +#if ((GNRC_MAC_RX_QUEUE_SIZE != 0) || (GNRC_MAC_DISPATCH_BUFFER_SIZE != 0)) || defined(DOXYGEN) +/** + * @brief MAC internal type for storing reception state parameters and + * state machines. + * This structure can be extended to contain more needed + * states and parameters. Please guard them by appropriate + * #ifdef directives when applicable. + */ +typedef struct { +#if (GNRC_MAC_RX_QUEUE_SIZE != 0) || defined(DOXYGEN) + gnrc_priority_pktqueue_t queue; /**< RX packet queue */ + gnrc_priority_pktqueue_node_t _queue_nodes[GNRC_MAC_RX_QUEUE_SIZE]; /**< RX queue nodes */ +#endif /* (GNRC_MAC_RX_QUEUE_SIZE != 0) || defined(DOXYGEN) */ + +#if (GNRC_MAC_DISPATCH_BUFFER_SIZE != 0) || defined(DOXYGEN) + gnrc_pktsnip_t* dispatch_buffer[GNRC_MAC_DISPATCH_BUFFER_SIZE]; /**< dispatch packet buffer */ +#endif /* (GNRC_MAC_DISPATCH_BUFFER_SIZE != 0) || defined(DOXYGEN) */ +} gnrc_mac_rx_t; + +/** + * @brief Static initializer for gnrc_mac_rx_t. + */ +#if ((GNRC_MAC_RX_QUEUE_SIZE != 0) && (GNRC_MAC_DISPATCH_BUFFER_SIZE != 0)) || defined(DOXYGEN) +#define GNRC_MAC_RX_INIT { \ + PRIORITY_PKTQUEUE_INIT, \ + { PRIORITY_PKTQUEUE_NODE_INIT(0, NULL) }, \ + { NULL }, \ +} +#elif (GNRC_MAC_RX_QUEUE_SIZE != 0) && (GNRC_MAC_DISPATCH_BUFFER_SIZE == 0) || defined(DOXYGEN) +#define GNRC_MAC_RX_INIT { \ + PRIORITY_PKTQUEUE_INIT, \ + { PRIORITY_PKTQUEUE_NODE_INIT(0, NULL) }, \ +} +#elif (GNRC_MAC_RX_QUEUE_SIZE == 0) && (GNRC_MAC_DISPATCH_BUFFER_SIZE != 0) || defined(DOXYGEN) +#define GNRC_MAC_RX_INIT { \ + { NULL }, \ +} +#endif /* ((GNRC_MAC_RX_QUEUE_SIZE != 0) && (GNRC_MAC_DISPATCH_BUFFER_SIZE != 0)) || defined(DOXYGEN) */ +#endif /* ((GNRC_MAC_RX_QUEUE_SIZE != 0) || (GNRC_MAC_DISPATCH_BUFFER_SIZE != 0)) || defined(DOXYGEN) */ + +#if (GNRC_MAC_NEIGHBOR_COUNT != 0) || defined(DOXYGEN) +/** + * @brief type for storing states of TX neighbor node. + */ +typedef struct { + uint8_t l2_addr[IEEE802154_LONG_ADDRESS_LEN]; /**< Address of neighbor node */ + uint8_t l2_addr_len; /**< Neighbor address length */ + uint32_t phase; /**< Neighbor's wake-up Phase */ + +#if (GNRC_MAC_TX_QUEUE_SIZE != 0) || defined(DOXYGEN) + gnrc_priority_pktqueue_t queue; /**< TX queue for this particular Neighbor */ +#endif /* (GNRC_MAC_TX_QUEUE_SIZE != 0) || defined(DOXYGEN) */ +} gnrc_mac_tx_neighbor_t; + +/** + * @brief Uninitialized phase value. + */ +#define GNRC_MAC_PHASE_UNINITIALIZED (0) + +/** + * @brief Maximum phase value. + */ +#define GNRC_MAC_PHASE_MAX (-1) + +/** + * @brief Static initializer for gnrc_mac_tx_neighbor_t. + */ +#if (GNRC_MAC_TX_QUEUE_SIZE != 0) || defined(DOXYGEN) +#define GNRC_MAC_TX_NEIGHBOR_INIT { \ + { 0 }, \ + 0, \ + GNRC_MAC_PHASE_UNINITIALIZED, \ + PRIORITY_PKTQUEUE_INIT, \ +} +#else +#define GNRC_MAC_TX_NEIGHBOR_INIT { \ + { 0 }, \ + 0, \ + GNRC_MAC_PHASE_UNINITIALIZED, \ +} +#endif /* (GNRC_MAC_TX_QUEUE_SIZE != 0) || defined(DOXYGEN) */ +#endif /* (GNRC_MAC_NEIGHBOR_COUNT != 0) || defined(DOXYGEN) */ + +#if ((GNRC_MAC_TX_QUEUE_SIZE != 0) || (GNRC_MAC_NEIGHBOR_COUNT != 0)) || defined(DOXYGEN) +/** + * @brief MAC internal type for storing transmission state parameters and + * state machines. + * This structure can be extended to contain more needed + * states and parameters. Please guard them by appropriate + * #ifdef directives when applicable. + */ +typedef struct { +#if (GNRC_MAC_NEIGHBOR_COUNT != 0) || defined(DOXYGEN) + gnrc_mac_tx_neighbor_t neighbors[GNRC_MAC_NEIGHBOR_COUNT + 1]; /**< Neighbor information units for one-hop neighbors. + First unit is for broadcast (+1) */ + gnrc_mac_tx_neighbor_t* current_neighbor; /**< Neighbor information unit of destination node to which + the current packet will be sent */ +#endif /* (GNRC_MAC_NEIGHBOR_COUNT != 0) || defined(DOXYGEN) */ + +#if (GNRC_MAC_TX_QUEUE_SIZE != 0) || defined(DOXYGEN) +#if (GNRC_MAC_NEIGHBOR_COUNT == 0) || defined(DOXYGEN) + gnrc_priority_pktqueue_t queue; /**< If neighbor queues is not used, define + a single queue for managing TX packets. */ +#endif /* (GNRC_MAC_NEIGHBOR_COUNT == 0) || defined(DOXYGEN) */ + + gnrc_priority_pktqueue_node_t _queue_nodes[GNRC_MAC_TX_QUEUE_SIZE]; /**< Shared buffer for TX queue nodes */ + gnrc_pktsnip_t* packet; /**< currently scheduled packet for sending */ +#endif /* (GNRC_MAC_TX_QUEUE_SIZE != 0) || defined(DOXYGEN) */ +} gnrc_mac_tx_t; + +/** + * @brief Static initializer for gnrc_mac_tx_t. + */ +#if ((GNRC_MAC_TX_QUEUE_SIZE != 0) && (GNRC_MAC_NEIGHBOR_COUNT != 0)) || defined(DOXYGEN) +#define GNRC_MAC_TX_INIT { \ + { GNRC_MAC_TX_NEIGHBOR_INIT }, \ + NULL, \ + { PRIORITY_PKTQUEUE_NODE_INIT(0, NULL) }, \ + NULL, \ +} +#elif ((GNRC_MAC_TX_QUEUE_SIZE != 0) && (GNRC_MAC_NEIGHBOR_COUNT == 0)) || defined(DOXYGEN) +#define GNRC_MAC_TX_INIT { \ + PRIORITY_PKTQUEUE_INIT, \ + { PRIORITY_PKTQUEUE_NODE_INIT(0, NULL) }, \ + NULL, \ +} +#elif ((GNRC_MAC_TX_QUEUE_SIZE == 0) && (GNRC_MAC_NEIGHBOR_COUNT != 0)) || defined(DOXYGEN) +#define GNRC_MAC_TX_INIT { \ + { GNRC_MAC_TX_NEIGHBOR_INIT }, \ + NULL, \ +} +#endif /* ((GNRC_MAC_TX_QUEUE_SIZE != 0) && (GNRC_MAC_NEIGHBOR_COUNT != 0)) || defined(DOXYGEN) */ +#endif /* ((GNRC_MAC_TX_QUEUE_SIZE != 0) || (GNRC_MAC_NEIGHBOR_COUNT != 0)) || defined(DOXYGEN) */ + #ifdef __cplusplus } #endif diff --git a/sys/include/net/gnrc/netdev2.h b/sys/include/net/gnrc/netdev2.h index 9221a83a17..d4cf3bbffe 100644 --- a/sys/include/net/gnrc/netdev2.h +++ b/sys/include/net/gnrc/netdev2.h @@ -37,6 +37,7 @@ #include "net/gnrc.h" #include "net/gnrc/mac/types.h" #include "net/ieee802154.h" +#include "net/gnrc/mac/mac.h" #ifdef __cplusplus extern "C" { @@ -113,7 +114,23 @@ typedef struct gnrc_netdev2 { * @brief device's l2 address length */ uint8_t l2_addr_len; -#endif + +#if ((GNRC_MAC_RX_QUEUE_SIZE != 0) || (GNRC_MAC_DISPATCH_BUFFER_SIZE != 0)) || defined(DOXYGEN) + /** + * @brief MAC internal object which stores reception parameters, queues, and + * state machines. + */ + gnrc_mac_rx_t rx; +#endif /* ((GNRC_MAC_RX_QUEUE_SIZE != 0) || (GNRC_MAC_DISPATCH_BUFFER_SIZE != 0)) || defined(DOXYGEN) */ + +#if ((GNRC_MAC_TX_QUEUE_SIZE != 0) || (GNRC_MAC_NEIGHBOR_COUNT != 0)) || defined(DOXYGEN) + /** + * @brief MAC internal object which stores transmission parameters, queues, and + * state machines. + */ + gnrc_mac_tx_t tx; +#endif /* ((GNRC_MAC_TX_QUEUE_SIZE != 0) || (GNRC_MAC_NEIGHBOR_COUNT == 0)) || defined(DOXYGEN) */ +#endif /* MODULE_GNRC_MAC */ } gnrc_netdev2_t; #ifdef MODULE_GNRC_MAC diff --git a/sys/net/gnrc/Makefile b/sys/net/gnrc/Makefile index b3718a0579..a974618c5e 100644 --- a/sys/net/gnrc/Makefile +++ b/sys/net/gnrc/Makefile @@ -70,6 +70,9 @@ endif ifneq (,$(filter gnrc_nomac,$(USEMODULE))) DIRS += link_layer/nomac endif +ifneq (,$(filter gnrc_mac,$(USEMODULE))) + DIRS += link_layer/gnrc_mac +endif ifneq (,$(filter gnrc_pkt,$(USEMODULE))) DIRS += pkt endif diff --git a/sys/net/gnrc/link_layer/gnrc_mac/Makefile b/sys/net/gnrc/link_layer/gnrc_mac/Makefile new file mode 100644 index 0000000000..b6a53cb27a --- /dev/null +++ b/sys/net/gnrc/link_layer/gnrc_mac/Makefile @@ -0,0 +1,3 @@ +MODULE = gnrc_mac + +include $(RIOTBASE)/Makefile.base diff --git a/sys/net/gnrc/link_layer/gnrc_mac/internal.c b/sys/net/gnrc/link_layer/gnrc_mac/internal.c new file mode 100644 index 0000000000..5bb3da3263 --- /dev/null +++ b/sys/net/gnrc/link_layer/gnrc_mac/internal.c @@ -0,0 +1,248 @@ +/* + * Copyright (C) 2015 Daniel Krebs + * 2016 INRIA + * + * 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 net_gnrc_mac + * @{ + * + * @file + * @brief Implementation of internal functions of GNRC_MAC + * + * @author Daniel Krebs + * @author Shuguo Zhuo + * @} + */ + +#include +#include +#include + +#define ENABLE_DEBUG (0) +#include "debug.h" + +#if ((GNRC_MAC_TX_QUEUE_SIZE != 0) || (GNRC_MAC_RX_QUEUE_SIZE != 0)) +gnrc_priority_pktqueue_node_t* _alloc_pktqueue_node(gnrc_priority_pktqueue_node_t* nodes, uint32_t size) +{ + assert(nodes != NULL); + assert(size > 0); + + /* search for free packet_queue_node */ + for (size_t i = 0; i < size; i++) { + if ((nodes[i].pkt == NULL) && + (nodes[i].next == NULL)) { + return &nodes[i]; + } + } + + return NULL; +} +#endif /* ((GNRC_MAC_TX_QUEUE_SIZE != 0) || (GNRC_MAC_RX_QUEUE_SIZE != 0)) */ + +#if GNRC_MAC_TX_QUEUE_SIZE != 0 +#if GNRC_MAC_NEIGHBOR_COUNT != 0 +/* Find the neighbor's id based on the given address */ +int _gnrc_mac_find_neighbor(gnrc_mac_tx_t* tx, const uint8_t* dst_addr, int addr_len) +{ + assert(tx != NULL); + assert(dst_addr != NULL); + assert(addr_len > 0); + + gnrc_mac_tx_neighbor_t* neighbors; + neighbors = tx->neighbors; + + /* Don't attempt to find broadcast neighbor, so start at index 1 */ + for (int i = 1; i <= (signed)GNRC_MAC_NEIGHBOR_COUNT; i++) { + if (neighbors[i].l2_addr_len == addr_len) { + if (memcmp(&(neighbors[i].l2_addr), dst_addr, addr_len) == 0) { + return i; + } + } + } + return -ENOENT; +} + +/* Free first empty queue (neighbor) that is not active */ +int _gnrc_mac_free_neighbor(gnrc_mac_tx_t* tx) +{ + assert(tx != NULL); + + gnrc_mac_tx_neighbor_t* neighbors; + neighbors = tx->neighbors; + + /* Don't attempt to free broadcast neighbor, so start at index 1 */ + for (int i = 1; i <= (signed)GNRC_MAC_NEIGHBOR_COUNT; i++) { + if ((gnrc_priority_pktqueue_length(&(neighbors[i].queue)) == 0) && + (&neighbors[i] != tx->current_neighbor)) { + /* Mark as free */ + neighbors[i].l2_addr_len = 0; + return i; + } + } + return -ENOSPC; +} + +/* Allocate first unused queue (neighbor) */ +int _gnrc_mac_alloc_neighbor(gnrc_mac_tx_t* tx) +{ + assert(tx != NULL); + + gnrc_mac_tx_neighbor_t* neighbors; + neighbors = tx->neighbors; + + /* Don't attempt to allocate broadcast neighbor, so start at index 1 */ + for (int i = 1; i <= (signed)GNRC_MAC_NEIGHBOR_COUNT; i++) { + if (neighbors[i].l2_addr_len == 0) { + gnrc_priority_pktqueue_init(&(neighbors[i].queue)); + return i; + } + } + return -ENOSPC; +} + +/* Initialize the neighbor */ +void _gnrc_mac_init_neighbor(gnrc_mac_tx_neighbor_t* neighbor, const uint8_t* addr, int len) +{ + assert(neighbor != NULL); + assert(addr != NULL); + assert(len > 0); + + neighbor->l2_addr_len = len; + neighbor->phase = GNRC_MAC_PHASE_MAX; + memcpy(&(neighbor->l2_addr), addr, len); +} +#endif /* GNRC_MAC_NEIGHBOR_COUNT != 0 */ + +bool gnrc_mac_queue_tx_packet(gnrc_mac_tx_t* tx, uint32_t priority, gnrc_pktsnip_t* pkt) +{ + assert(tx != NULL); + assert(pkt != NULL); + +#if GNRC_MAC_NEIGHBOR_COUNT == 0 + + gnrc_priority_pktqueue_node_t* node; + node = _alloc_pktqueue_node(tx->_queue_nodes, GNRC_MAC_TX_QUEUE_SIZE); + + if (node) { + gnrc_priority_pktqueue_node_init(node, priority, pkt); + gnrc_priority_pktqueue_push(&tx->queue, node); + return true; + } + + DEBUG("[gnrc_mac-int] Can't push to TX queue, no entries left\n"); + return false; + +#else + + gnrc_mac_tx_neighbor_t* neighbor; + int neighbor_id; + + /* Check whether the packet it for broadcast */ + if (gnrc_netif_hdr_get_flag(pkt)&GNRC_NETIF_HDR_FLAGS_BROADCAST) { + /* Broadcast queue is neighbor 0 by definition */ + neighbor_id = 0; + neighbor = &tx->neighbors[neighbor_id]; + + } else { + uint8_t* addr; + int addr_len; + bool neighbor_known = true; + + /* Get destination address of packet */ + addr_len = gnrc_netif_hdr_get_dstaddr(pkt, &addr); + if (addr_len <= 0) { + DEBUG("[gnrc_mac-int] Packet has no destination address\n"); + return false; + } + + /* Search for existing queue for destination */ + neighbor_id = _gnrc_mac_find_neighbor(tx, addr, addr_len); + + /* neighbor node doesn't have a queue yet */ + if (neighbor_id < 0) { + neighbor_known = false; + + /* Try to allocate neighbor entry */ + neighbor_id = _gnrc_mac_alloc_neighbor(tx); + + /* No neighbor entries left */ + if (neighbor_id < 0) { + DEBUG("[gnrc_mac-int] No neighbor entries left, maybe increase " + "GNRC_MAC_NEIGHBOR_COUNT for better performance\n"); + + /* Try to free an unused queue */ + neighbor_id = _gnrc_mac_free_neighbor(tx); + + /* All queues are in use, so reject */ + if (neighbor_id < 0) { + DEBUG("[gnrc_mac-int] Couldn't allocate tx queue for packet\n"); + return false; + } + } + } + + neighbor = &tx->neighbors[neighbor_id]; + + if (!neighbor_known) { + _gnrc_mac_init_neighbor(neighbor, addr, addr_len); + } + } + + gnrc_priority_pktqueue_node_t* node; + node = _alloc_pktqueue_node(tx->_queue_nodes, GNRC_MAC_TX_QUEUE_SIZE); + if (node) { + gnrc_priority_pktqueue_node_init(node, priority, pkt); + gnrc_priority_pktqueue_push(&neighbor->queue, node); + DEBUG("[gnrc_mac-int] Queuing pkt to neighbor #%d\n", neighbor_id); + return true; + } + + DEBUG("[gnrc_mac-int] Can't push to neighbor #%d's queue, no entries left\n", + neighbor_id); + return false; + +#endif /* GNRC_MAC_NEIGHBOR_COUNT == 0 */ +} +#endif /* GNRC_MAC_TX_QUEUE_SIZE != 0 */ + +#if GNRC_MAC_RX_QUEUE_SIZE != 0 +bool gnrc_mac_queue_rx_packet(gnrc_mac_rx_t* rx, uint32_t priority, gnrc_pktsnip_t* pkt) +{ + assert(rx != NULL); + assert(pkt != NULL); + + gnrc_priority_pktqueue_node_t* node; + node = _alloc_pktqueue_node(rx->_queue_nodes, GNRC_MAC_RX_QUEUE_SIZE); + + if (node) { + gnrc_priority_pktqueue_node_init(node, priority, pkt); + gnrc_priority_pktqueue_push(&rx->queue, node); + return true; + } + + DEBUG("[gnrc_mac] Can't push RX packet @ %p, no entries left\n", pkt); + return false; +} +#endif /* GNRC_MAC_RX_QUEUE_SIZE != 0 */ + +#if GNRC_MAC_DISPATCH_BUFFER_SIZE != 0 +void gnrc_mac_dispatch(gnrc_mac_rx_t* rx) +{ + assert(rx != NULL); + + for (unsigned i = 0; i < GNRC_MAC_DISPATCH_BUFFER_SIZE; i++) { + if (rx->dispatch_buffer[i]) { + if (!gnrc_netapi_dispatch_receive(rx->dispatch_buffer[i]->type, GNRC_NETREG_DEMUX_CTX_ALL, rx->dispatch_buffer[i])) { + DEBUG("Unable to forward packet of type %i\n", buffer[i]->type); + gnrc_pktbuf_release(rx->dispatch_buffer[i]); + } + rx->dispatch_buffer[i] = NULL; + } + } +} +#endif /* GNRC_MAC_DISPATCH_BUFFER_SIZE != 0 */ diff --git a/tests/unittests/Makefile b/tests/unittests/Makefile index 268ab2dae1..045b531ec8 100644 --- a/tests/unittests/Makefile +++ b/tests/unittests/Makefile @@ -11,7 +11,7 @@ BOARD_INSUFFICIENT_MEMORY := airfy-beacon cc2650stk chronos ek-lm4f120xl \ arduino-duemilanove sodaq-autonomo arduino-zero \ nucleo-f030 nucleo-f070 nucleo-f091 pba-d-01-kw2x \ saml21-xpro microbit calliope-mini limifrog-v1 \ - slwstk6220a + slwstk6220a ek-lm4f120xl stm32f3discovery USEMODULE += embunit diff --git a/tests/unittests/tests-gnrc_mac_internal/Makefile b/tests/unittests/tests-gnrc_mac_internal/Makefile new file mode 100644 index 0000000000..48422e909a --- /dev/null +++ b/tests/unittests/tests-gnrc_mac_internal/Makefile @@ -0,0 +1 @@ +include $(RIOTBASE)/Makefile.base diff --git a/tests/unittests/tests-gnrc_mac_internal/Makefile.include b/tests/unittests/tests-gnrc_mac_internal/Makefile.include new file mode 100644 index 0000000000..29394f3096 --- /dev/null +++ b/tests/unittests/tests-gnrc_mac_internal/Makefile.include @@ -0,0 +1,3 @@ +USEMODULE += gnrc_priority_pktqueue +USEMODULE += gnrc_mac +CFLAGS += -DGNRC_MAC_TX_QUEUE_SIZE=4 -DGNRC_MAC_NEIGHBOR_COUNT=4 diff --git a/tests/unittests/tests-gnrc_mac_internal/tests-gnrc_mac_internal.c b/tests/unittests/tests-gnrc_mac_internal/tests-gnrc_mac_internal.c new file mode 100644 index 0000000000..31cc5870f0 --- /dev/null +++ b/tests/unittests/tests-gnrc_mac_internal/tests-gnrc_mac_internal.c @@ -0,0 +1,283 @@ +/* + * Copyright (C) 2016, 2016 Shuguo Zhuo + * + * 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. + */ + +/** + * @{ + * + * @file + */ +#include + +#include "embUnit.h" + +#include "net/gnrc/pkt.h" +#include "net/gnrc/mac/internal.h" + +#include "unittests-constants.h" +#include "tests-gnrc_mac_internal.h" + +static void set_up(void) +{ + gnrc_pktbuf_init(); +} + +#if GNRC_MAC_TX_QUEUE_SIZE != 0 +/** + * @brief This function test the `gnrc_mac_queue_tx_packet()`, to see whether it can + * correctly queue the packet to the corresponded priority packet queue. + * + * In case when the `gnrc_mac_tx_neighbor_t` structure is in used (indicated by + * by `GNRC_MAC_NEIGHBOR_COUNT != 0`), `test_gnrc_mac_queue_tx_packet()` successively + * queues 4 packets, which are pkt1, pkt2, pkt3 and pkt_bcast, into a defined `tx` + * (type of `gnrc_mac_tx_t`). Pkt1, pkt2 have the same destination address of "0x76b6", + * , pkt3 is heading for "0x447e", while pkt_bcast is for broadcasting. + * Expected results: pkt1 and pkt2 should be queued to `tx::neighbors[1]::queue`, + * pkt3 should be queued to `tx::neighbors[2]::queue`, while pkt_bcast should be + * queued to `tx::neighbors[0]::queue`. + * + * In case when the `gnrc_mac_tx_neighbor_t` structure is not in used (indicated by + * by `GNRC_MAC_NEIGHBOR_COUNT == 0`), `test_gnrc_mac_queue_tx_packet()` successively + * queues 4 packets, which are pkt1, pkt2, pkt3 and pkt_bcast, into a defined `tx` + * (type of `gnrc_mac_tx_t`). Pkt1, pkt2 have the same destination address of "0x76b6", + * , pkt3 is heading for "0x447e", while pkt_bcast is for broadcasting. + * Expected results: all packets should be queued to `tx::queue`, and ranking in + * `tx::queue` according to their priorities. + * + */ +static void test_gnrc_mac_queue_tx_packet(void) +{ + gnrc_mac_tx_t tx = GNRC_MAC_TX_INIT; + gnrc_pktsnip_t *hdr; + gnrc_netif_hdr_t* netif_hdr; + uint8_t dst_addr[2]; + + dst_addr[0] = 0x76; + dst_addr[1] = 0xb6; + + hdr = gnrc_netif_hdr_build(NULL, 0, NULL, 0); + gnrc_pktsnip_t *pkt_bcast = gnrc_pktbuf_add(NULL, TEST_STRING12, sizeof(TEST_STRING12), + GNRC_NETTYPE_UNDEF); + LL_APPEND(hdr, pkt_bcast); + pkt_bcast = hdr; + + netif_hdr = hdr->data; + netif_hdr->flags |= GNRC_NETIF_HDR_FLAGS_BROADCAST; + + hdr = gnrc_netif_hdr_build(NULL, 0, dst_addr, 2); + gnrc_pktsnip_t *pkt1 = gnrc_pktbuf_add(NULL, TEST_STRING4, sizeof(TEST_STRING4), + GNRC_NETTYPE_UNDEF); + LL_APPEND(hdr, pkt1); + pkt1 = hdr; + + hdr = gnrc_netif_hdr_build(NULL, 0, dst_addr, 2); + gnrc_pktsnip_t *pkt2 = gnrc_pktbuf_add(NULL, TEST_STRING8, sizeof(TEST_STRING8), + GNRC_NETTYPE_UNDEF); + LL_APPEND(hdr, pkt2); + pkt2 = hdr; + + dst_addr[0] = 0x44; + dst_addr[1] = 0x7e; + + hdr = gnrc_netif_hdr_build(NULL, 0, dst_addr, 2); + gnrc_pktsnip_t *pkt3 = gnrc_pktbuf_add(NULL, TEST_STRING16, sizeof(TEST_STRING16), + GNRC_NETTYPE_UNDEF); + LL_APPEND(hdr, pkt3); + pkt3 = hdr; + +#if GNRC_MAC_NEIGHBOR_COUNT != 0 + + gnrc_pktsnip_t *pkt_head; + TEST_ASSERT(gnrc_mac_queue_tx_packet(&tx,1,pkt1)); + pkt_head = gnrc_priority_pktqueue_head(&tx.neighbors[1].queue); + TEST_ASSERT(pkt_head == pkt1); + TEST_ASSERT(1 == gnrc_priority_pktqueue_length(&tx.neighbors[1].queue)); + TEST_ASSERT_EQUAL_STRING(TEST_STRING4, pkt_head->next->data); + + TEST_ASSERT(gnrc_mac_queue_tx_packet(&tx,0,pkt2)); + pkt_head = gnrc_priority_pktqueue_head(&tx.neighbors[1].queue); + TEST_ASSERT(pkt_head == pkt2); + TEST_ASSERT(2 == gnrc_priority_pktqueue_length(&tx.neighbors[1].queue)); + TEST_ASSERT_EQUAL_STRING(TEST_STRING8, pkt_head->next->data); + + pkt_head = gnrc_priority_pktqueue_pop(&tx.neighbors[1].queue); + TEST_ASSERT(pkt_head == pkt2); + TEST_ASSERT(1 == gnrc_priority_pktqueue_length(&tx.neighbors[1].queue)); + TEST_ASSERT_EQUAL_STRING(TEST_STRING8, pkt_head->next->data); + + pkt_head = gnrc_priority_pktqueue_head(&tx.neighbors[1].queue); + TEST_ASSERT(pkt_head == pkt1); + TEST_ASSERT_EQUAL_STRING(TEST_STRING4, pkt_head->next->data); + + TEST_ASSERT(gnrc_mac_queue_tx_packet(&tx,0,pkt3)); + pkt_head = gnrc_priority_pktqueue_head(&tx.neighbors[2].queue); + TEST_ASSERT(pkt_head == pkt3); + TEST_ASSERT(1 == gnrc_priority_pktqueue_length(&tx.neighbors[2].queue)); + TEST_ASSERT_EQUAL_STRING(TEST_STRING16, pkt_head->next->data); + + TEST_ASSERT(gnrc_mac_queue_tx_packet(&tx,0,pkt_bcast)); + pkt_head = gnrc_priority_pktqueue_head(&tx.neighbors[0].queue); + TEST_ASSERT(pkt_head == pkt_bcast); + TEST_ASSERT(1 == gnrc_priority_pktqueue_length(&tx.neighbors[0].queue)); + TEST_ASSERT_EQUAL_STRING(TEST_STRING12, pkt_head->next->data); + +#else + + TEST_ASSERT(gnrc_mac_queue_tx_packet(&tx,1,pkt1)); + TEST_ASSERT(1 == gnrc_priority_pktqueue_length(&tx.queue)); + gnrc_pktsnip_t *pkt_head; + pkt_head = gnrc_priority_pktqueue_head(&tx.queue); + TEST_ASSERT(pkt_head == pkt1); + TEST_ASSERT_EQUAL_STRING(TEST_STRING4, pkt_head->next->data); + + TEST_ASSERT(gnrc_mac_queue_tx_packet(&tx,1,pkt2)); + TEST_ASSERT(2 == gnrc_priority_pktqueue_length(&tx.queue)); + pkt_head = gnrc_priority_pktqueue_head(&tx.queue); + TEST_ASSERT(pkt_head == pkt1); + TEST_ASSERT_EQUAL_STRING(TEST_STRING4, pkt_head->next->data); + + TEST_ASSERT(gnrc_mac_queue_tx_packet(&tx,0,pkt3)); + TEST_ASSERT(3 == gnrc_priority_pktqueue_length(&tx.queue)); + pkt_head = gnrc_priority_pktqueue_head(&tx.queue); + TEST_ASSERT(pkt_head == pkt3); + TEST_ASSERT_EQUAL_STRING(TEST_STRING16, pkt_head->next->data); + + TEST_ASSERT(gnrc_mac_queue_tx_packet(&tx,0,pkt_bcast)); + TEST_ASSERT(4 == gnrc_priority_pktqueue_length(&tx.queue)); + pkt_head = gnrc_priority_pktqueue_head(&tx.queue); + TEST_ASSERT(pkt_head == pkt3); + + pkt_head = gnrc_priority_pktqueue_pop(&tx.queue); + TEST_ASSERT(pkt_head == pkt3); + TEST_ASSERT(3 == gnrc_priority_pktqueue_length(&tx.queue)); + TEST_ASSERT_EQUAL_STRING(TEST_STRING16, pkt_head->next->data); + + pkt_head = gnrc_priority_pktqueue_pop(&tx.queue); + TEST_ASSERT(pkt_head == pkt_bcast); + TEST_ASSERT(2 == gnrc_priority_pktqueue_length(&tx.queue)); + TEST_ASSERT_EQUAL_STRING(TEST_STRING12, pkt_head->next->data); + + pkt_head = gnrc_priority_pktqueue_pop(&tx.queue); + TEST_ASSERT(pkt_head == pkt1); + TEST_ASSERT(1 == gnrc_priority_pktqueue_length(&tx.queue)); + TEST_ASSERT_EQUAL_STRING(TEST_STRING4, pkt_head->next->data); + + pkt_head = gnrc_priority_pktqueue_pop(&tx.queue); + TEST_ASSERT(pkt_head == pkt2); + TEST_ASSERT(0 == gnrc_priority_pktqueue_length(&tx.queue)); + TEST_ASSERT_EQUAL_STRING(TEST_STRING8, pkt_head->next->data); + +#endif /* GNRC_MAC_NEIGHBOR_COUNT != 0 */ +} +#endif /* GNRC_MAC_TX_QUEUE_SIZE != 0 */ + +#if GNRC_MAC_RX_QUEUE_SIZE != 0 +/** + * @brief This function test the `gnrc_mac_queue_rx_packet()`, to see whether it can + * correctly queue the packets to `rx::queue` according to their priorities. + * + * `test_gnrc_mac_queue_tx_packet()` successively queues 3 packets, which are + * pkt1, pkt2, pkt3, into a defined `rx` (type of `gnrc_mac_rx_t`). + * Pkt1, pkt2 have the same priority of "1", while pkt3 has the priority of "0". + * Expected results: after all the packets are queued, in `rx::queue`, them should + * be ranked as (from high priority to low): pkt3, pkt1 and pkt2. + * + */ +static void test_gnrc_mac_queue_rx_packet(void) +{ + gnrc_mac_rx_t rx = GNRC_MAC_RX_INIT; + gnrc_pktsnip_t *pkt1 = gnrc_pktbuf_add(NULL, TEST_STRING4, sizeof(TEST_STRING4), + GNRC_NETTYPE_UNDEF); + gnrc_pktsnip_t *pkt2 = gnrc_pktbuf_add(NULL, TEST_STRING8, sizeof(TEST_STRING8), + GNRC_NETTYPE_UNDEF); + gnrc_pktsnip_t *pkt3 = gnrc_pktbuf_add(NULL, TEST_STRING16, sizeof(TEST_STRING16), + GNRC_NETTYPE_UNDEF); + + TEST_ASSERT(gnrc_mac_queue_rx_packet(&rx,1,pkt1)); + TEST_ASSERT(1 == gnrc_priority_pktqueue_length(&rx.queue)); + + gnrc_pktsnip_t *pkt_head; + pkt_head = gnrc_priority_pktqueue_head(&rx.queue); + + TEST_ASSERT(pkt_head == pkt1); + TEST_ASSERT_EQUAL_STRING(TEST_STRING4, pkt_head->data); + + TEST_ASSERT(gnrc_mac_queue_rx_packet(&rx,1,pkt2)); + TEST_ASSERT(2 == gnrc_priority_pktqueue_length(&rx.queue)); + + pkt_head = gnrc_priority_pktqueue_head(&rx.queue); + + TEST_ASSERT(pkt_head == pkt1); + TEST_ASSERT_EQUAL_STRING(TEST_STRING4, pkt_head->data); + + TEST_ASSERT(gnrc_mac_queue_rx_packet(&rx,0,pkt3)); + TEST_ASSERT(3 == gnrc_priority_pktqueue_length(&rx.queue)); + + pkt_head = gnrc_priority_pktqueue_head(&rx.queue); + + TEST_ASSERT(pkt_head == pkt3); + TEST_ASSERT_EQUAL_STRING(TEST_STRING16, pkt_head->data); + + pkt_head = gnrc_priority_pktqueue_pop(&rx.queue); + TEST_ASSERT(pkt_head == pkt3); + TEST_ASSERT(2 == gnrc_priority_pktqueue_length(&rx.queue)); + TEST_ASSERT_EQUAL_STRING(TEST_STRING16, pkt_head->data); + + pkt_head = gnrc_priority_pktqueue_pop(&rx.queue); + TEST_ASSERT(pkt_head == pkt1); + TEST_ASSERT(1 == gnrc_priority_pktqueue_length(&rx.queue)); + TEST_ASSERT_EQUAL_STRING(TEST_STRING4, pkt_head->data); + + pkt_head = gnrc_priority_pktqueue_pop(&rx.queue); + TEST_ASSERT(pkt_head == pkt2); + TEST_ASSERT(0 == gnrc_priority_pktqueue_length(&rx.queue)); + TEST_ASSERT_EQUAL_STRING(TEST_STRING8, pkt_head->data); +} +#endif /* GNRC_MAC_RX_QUEUE_SIZE != 0 */ + +#if GNRC_MAC_DISPATCH_BUFFER_SIZE != 0 +static void test_gnrc_mac_dispatch(void) +{ + gnrc_mac_rx_t rx = GNRC_MAC_RX_INIT; + + for (size_t i = 0; i < GNRC_MAC_DISPATCH_BUFFER_SIZE; i++) { + rx.dispatch_buffer[i] = gnrc_pktbuf_add(NULL, TEST_STRING4, sizeof(TEST_STRING4), + GNRC_NETTYPE_UNDEF); + } + + gnrc_mac_dispatch(&rx); + + for (size_t i = 0; i < GNRC_MAC_DISPATCH_BUFFER_SIZE; i++) { + TEST_ASSERT_NULL(rx.dispatch_buffer[i]); + } +} +#endif /* GNRC_MAC_DISPATCH_BUFFER_SIZE != 0 */ + +Test *tests_gnrc_mac_internal_tests(void) +{ + EMB_UNIT_TESTFIXTURES(fixtures) { +#if GNRC_MAC_TX_QUEUE_SIZE != 0 + new_TestFixture(test_gnrc_mac_queue_tx_packet), +#endif /* GNRC_MAC_TX_QUEUE_SIZE != 0 */ +#if GNRC_MAC_RX_QUEUE_SIZE != 0 + new_TestFixture(test_gnrc_mac_queue_rx_packet), +#endif /* GNRC_MAC_RX_QUEUE_SIZE != 0 */ +#if GNRC_MAC_DISPATCH_BUFFER_SIZE != 0 + new_TestFixture(test_gnrc_mac_dispatch), +#endif /* GNRC_MAC_DISPATCH_BUFFER_SIZE != 0 */ + }; + + EMB_UNIT_TESTCALLER(gnrc_mac_internal_tests, set_up, NULL, fixtures); + + return (Test *)&gnrc_mac_internal_tests; +} + +void tests_gnrc_mac_internal(void) +{ + TESTS_RUN(tests_gnrc_mac_internal_tests()); +} +/** @} */ diff --git a/tests/unittests/tests-gnrc_mac_internal/tests-gnrc_mac_internal.h b/tests/unittests/tests-gnrc_mac_internal/tests-gnrc_mac_internal.h new file mode 100644 index 0000000000..00a55775af --- /dev/null +++ b/tests/unittests/tests-gnrc_mac_internal/tests-gnrc_mac_internal.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2016 Shuguo Zhuo + * + * 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. + */ + +/** + * @addtogroup unittests + * @{ + * + * @file + * @brief Unittests for the ``gnrc_mac`` module + * + * @author Shuguo Zhuo + */ +#ifndef TESTS_PRIORITY_PKTQUEUE_H_ +#define TESTS_PRIORITY_PKTQUEUE_H_ + +#include "embUnit.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief The entry point of this test suite. + */ +void tests_gnrc_mac_internal(void); + +#ifdef __cplusplus +} +#endif + +#endif /* TESTS_PRIORITY_PKTQUEUE_H_ */ +/** @} */