From 97de8aa52b4626fd06b5354cfa200b84520fb251 Mon Sep 17 00:00:00 2001 From: Martine Lenders Date: Tue, 16 Feb 2016 17:17:58 +0100 Subject: [PATCH] netdev2_test: initial import Imports a generic framework to test and experiment with netdev2-based modules. --- sys/Makefile | 3 + sys/include/net/netdev2_test.h | 306 ++++++++++++++++++++++++++++ sys/net/netdev2_test/Makefile | 1 + sys/net/netdev2_test/netdev2_test.c | 144 +++++++++++++ 4 files changed, 454 insertions(+) create mode 100644 sys/include/net/netdev2_test.h create mode 100644 sys/net/netdev2_test/Makefile create mode 100644 sys/net/netdev2_test/netdev2_test.c diff --git a/sys/Makefile b/sys/Makefile index 01986396d3..9fce303df9 100644 --- a/sys/Makefile +++ b/sys/Makefile @@ -22,6 +22,9 @@ endif ifneq (,$(filter ieee802154,$(USEMODULE))) DIRS += net/link_layer/ieee802154 endif +ifneq (,$(filter netdev2_test,$(USEMODULE))) + DIRS += net/netdev2_test +endif ifneq (,$(filter ipv4_addr,$(USEMODULE))) DIRS += net/network_layer/ipv4/addr endif diff --git a/sys/include/net/netdev2_test.h b/sys/include/net/netdev2_test.h new file mode 100644 index 0000000000..8d4c448b4d --- /dev/null +++ b/sys/include/net/netdev2_test.h @@ -0,0 +1,306 @@ +/* + * 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 sys_netdev2_test Netdev2 dummy test driver + * @ingroup drivers_netdev_netdev2 + * @brief Provides a test dummy for the netdev2 interface. + * + * See the following simple packet traversal timer for an example. Note that + * this example assumes that the stack doesn't require any option values from + * the device and that the stack doesn't create packets on its own or looses + * packets (neither of those can be assumed for in-production stacks). + * + * ~~~~~~~~~~~ {.c} + * #include + * + * #include "mutex.h" + * #include "net/af.h" + * #include "net/conn/udp.h" + * #include "net/ipv6/addr.h" + * #include "net/netdev2_test.h" + * #include "xtimer.h" + * + * #define PKT_NUMBER (1000) + * + * static netdev2_test_t dev; + * static uint32_t last_start; + * static uint32_t sum = 0; + * static mutex_t wait = MUTEX_INIT; + * + * int _send_timer(netdev2_t *dev, const struct iovec *vector, int count) + * { + * (void)dev; + * (void)vector; + * (void)count; + * + * sum += (xtimer_now() - last_start); + * mutex_unlock(&wait); + * } + * + * int main(void) { + * ipv6_addr_t dst = IPV6_ADDR_UNSPECIFIED; + * + * netdev2_test_setup(&dev, NULL); + * dev->driver->init((netdev2_t *)&dev) + * // initialize stack and connect `dev` to it + * // ... + * mutex_lock(&wait); + * for (int i = 0; i < PKT_NUMBER; i++) { + * last_start = xtimer_now(); + * conn_udp_sendto("abcd", sizeof("abcd"), NULL, 0, &dst, sizeof(dst), + * AF_INET6, 0xcafe, 0xcafe); + * mutex_lock(&wait); + * } + * printf("Average send packet traversal time: %u\n", sum / PKT_NUMBER); + * mutex_unlock(&wait); + * return 0; + * } + * ~~~~~~~~~~~ + * + * To provide options to the stack, the + * @ref netdev2_test_t::get_cbs "get callbacks" for the specific options need + * to be set. + * To catch lost packets and additional sent by the stack the send handler needs + * to be adapted accordingly. + * + * @{ + * + * @file + * @brief @ref sys_netdev2_test definitions + * + * @author Martine Lenders + */ +#ifndef NETDEV2_TEST_H_ +#define NETDEV2_TEST_H_ + +#include "mutex.h" + +#ifdef MODULE_NETDEV2_IEEE802154 +#include "net/netdev2/ieee802154.h" +#endif + +#include "net/netdev2.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Callback type to handle send command + * + * @param[in] dev network device descriptor + * @param[in] vector io vector array to send + * @param[in] count number of entries in vector + * + * @return number of bytes sent + * @return <= 0 on error + */ +typedef int (*netdev2_test_send_cb_t)(netdev2_t *dev, + const struct iovec *vector, + int count); + +/** + * @brief Callback type to handle receive command + * + * @param[in] dev network device descriptor + * @param[out] buf buffer to write into or `NULL` + * @param[in] len maximum number of bytes to read + * @param[out] info status information for the received packet. Might + * be of different type for different netdev2 devices. + * May be NULL if not needed or applicable + * + * @return <=0 on error + * @return number of bytes read if buf != NULL + * @return currently received packet size if buf == NULL + */ +typedef int (*netdev2_test_recv_cb_t)(netdev2_t *dev, char *buf, int len, + void *info); + +/** + * @brief Callback type to handle device initialization + * + * @param[in] dev network device descriptor + * + * @return <= on error + * @return 0 on success + */ +typedef int (*netdev2_test_init_cb_t)(netdev2_t *dev); + +/** + * @brief Callback type to handle user-space ISR events + * + * @param[in] dev network device descriptor + */ +typedef void (*netdev2_test_isr_cb_t)(netdev2_t *dev); + +/** + * @brief Callback type to handle get commands + * + * @param[in] dev network device descriptor + * @param[out] value pointer to store the option's value in + * @param[in] max_len maximal amount of bytes that fit into @p value + * + * @return number of bytes written to @p value + * @return <0 on error + */ +typedef int (*netdev2_test_get_cb_t)(netdev2_t *dev, void *value, + size_t max_len); + +/** + * @brief Callback type to handle set commands + * + * @param[in] dev network device descriptor + * @param[out] value value to set + * @param[in] value_len the length of @p value + * + * @return number of bytes used from @p value + * @return <0 on error + */ +typedef int (*netdev2_test_set_cb_t)(netdev2_t *dev, void *value, + size_t value_len); + +/** + * @brief Device descriptor for @ref sys_netdev2_test devices + * + * @extends netdev2_t + */ +typedef struct { + /** + * @brief netdev2 fields + * @{ + */ +#ifdef MODULE_NETDEV2_IEEE802154 + netdev2_ieee802154_t netdev; /**< superclass */ +#else /* MODULE_NETDEV2_IEEE802154 */ + netdev2_t netdev; /**< superclass */ +#endif /* MODULE_NETDEV2_IEEE802154 */ + /** @} */ + + /** + * @brief device specific fields + * @{ + */ + netdev2_test_send_cb_t send_cb; /**< callback to handle send command */ + netdev2_test_recv_cb_t recv_cb; /**< callback to handle receive command */ + netdev2_test_init_cb_t init_cb; /**< callback to handle initialization events */ + netdev2_test_isr_cb_t isr_cb; /**< callback to handle ISR events */ + netdev2_test_get_cb_t get_cbs[NETOPT_NUMOF]; /**< callback to handle get command */ + netdev2_test_set_cb_t set_cbs[NETOPT_NUMOF]; /**< callback to handle set command */ + void *state; /**< external state for the device */ + mutex_t mutex; /**< mutex for the device */ + /** @} */ +} netdev2_test_t; + +/** + * @brief override send callback + * + * @param[in] dev a @ref sys_netdev2_test device + * @param[in] send_cb a send callback + */ +static inline void netdev2_test_set_send_cb(netdev2_test_t *dev, + netdev2_test_send_cb_t send_cb) +{ + mutex_lock(&dev->mutex); + dev->send_cb = send_cb; + mutex_unlock(&dev->mutex); +} + +/** + * @brief override receive callback + * + * @param[in] dev a @ref sys_netdev2_test device + * @param[in] recv_cb a receive callback + */ +static inline void netdev2_test_set_recv_cb(netdev2_test_t *dev, + netdev2_test_recv_cb_t recv_cb) +{ + mutex_lock(&dev->mutex); + dev->recv_cb = recv_cb; + mutex_unlock(&dev->mutex); +} + +/** + * @brief override initialization callback + * + * @param[in] dev a @ref sys_netdev2_test device + * @param[in] init_cb an initialization callback + */ +static inline void netdev2_test_set_init_cb(netdev2_test_t *dev, + netdev2_test_init_cb_t init_cb) +{ + mutex_lock(&dev->mutex); + dev->init_cb = init_cb; + mutex_unlock(&dev->mutex); +} + +/** + * @brief override ISR event handler callback + * + * @param[in] dev a @ref sys_netdev2_test device + * @param[in] isr_cb an ISR event handler callback + */ +static inline void netdev2_test_set_isr_cb(netdev2_test_t *dev, + netdev2_test_isr_cb_t isr_cb) +{ + mutex_lock(&dev->mutex); + dev->isr_cb = isr_cb; + mutex_unlock(&dev->mutex); +} + +/** + * @brief override get callback for a certain option type + * + * @param[in] dev a @ref sys_netdev2_test device + * @param[in] opt an option type + * @param[in] get_cb a get callback for @p opt + */ +static inline void netdev2_test_set_get_cb(netdev2_test_t *dev, netopt_t opt, + netdev2_test_get_cb_t get_cb) +{ + mutex_lock(&dev->mutex); + dev->get_cbs[opt] = get_cb; + mutex_unlock(&dev->mutex); +} + +/** + * @brief override get callback for a certain option type + * + * @param[in] dev a @ref sys_netdev2_test device + * @param[in] opt an option type + * @param[in] set_cb a set callback for @p opt + */ +static inline void netdev2_test_set_set_cb(netdev2_test_t *dev, netopt_t opt, + netdev2_test_set_cb_t set_cb) +{ + mutex_lock(&dev->mutex); + dev->set_cbs[opt] = set_cb; + mutex_unlock(&dev->mutex); +} + +/** + * @brief Setup a given @ref sys_netdev2_test device + * + * @param[in] dev a @ref sys_netdev2_test device to initialize + * @param[in] state external state for the device + */ +void netdev2_test_setup(netdev2_test_t *dev, void *state); + +/** + * @brief Resets all callbacks for the device to NULL + * + * @param[in] dev a @ref sys_netdev2_test device to initialize + */ +void netdev2_test_reset(netdev2_test_t *dev); + +#ifdef __cplusplus +} +#endif + +#endif /* NETDEV2_TEST_H_ */ +/** @} */ diff --git a/sys/net/netdev2_test/Makefile b/sys/net/netdev2_test/Makefile new file mode 100644 index 0000000000..48422e909a --- /dev/null +++ b/sys/net/netdev2_test/Makefile @@ -0,0 +1 @@ +include $(RIOTBASE)/Makefile.base diff --git a/sys/net/netdev2_test/netdev2_test.c b/sys/net/netdev2_test/netdev2_test.c new file mode 100644 index 0000000000..7859cae5c8 --- /dev/null +++ b/sys/net/netdev2_test/netdev2_test.c @@ -0,0 +1,144 @@ +/* + * 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. + */ + +/** + * @{ + * + * @file + * @author Martine Lenders + */ + +#include +#include +#include + +#include "net/netdev2_test.h" + +static int _send(netdev2_t *netdev, const struct iovec *vector, int count); +static int _recv(netdev2_t *netdev, char *buf, int len, void *info); +static int _init(netdev2_t *dev); +static void _isr(netdev2_t *dev); +static int _get(netdev2_t *dev, netopt_t opt, void *value, size_t max_len); +static int _set(netdev2_t *dev, netopt_t opt, void *value, size_t value_len); + +static const netdev2_driver_t _driver = { + .send = _send, + .recv = _recv, + .init = _init, + .isr = _isr, + .get = _get, + .set = _set, +}; + +void netdev2_test_setup(netdev2_test_t *dev, void *state) +{ + netdev2_t *netdev = (netdev2_t *)dev; + + netdev->driver = &_driver; + dev->state = state; + mutex_init(&dev->mutex); + netdev2_test_reset(dev); +} + +void netdev2_test_reset(netdev2_test_t *dev) +{ + mutex_lock(&dev->mutex); + dev->send_cb = NULL; + dev->recv_cb = NULL; + dev->init_cb = NULL; + dev->isr_cb = NULL; + memset(dev->get_cbs, 0, sizeof(dev->get_cbs)); + memset(dev->set_cbs, 0, sizeof(dev->set_cbs)); + mutex_unlock(&dev->mutex); +} + +static int _send(netdev2_t *netdev, const struct iovec *vector, int count) +{ + netdev2_test_t *dev = (netdev2_test_t *)netdev; + int res = count; /* assume everything would be fine */ + + mutex_lock(&dev->mutex); + if (dev->send_cb != NULL) { + res = dev->send_cb(netdev, vector, count); + } + mutex_unlock(&dev->mutex); + return res; +} + +static int _recv(netdev2_t *netdev, char *buf, int len, void *info) +{ + netdev2_test_t *dev = (netdev2_test_t *)netdev; + int res = (buf == NULL) ? 0 : len; /* assume everything would be fine */ + + mutex_lock(&dev->mutex); + if (dev->recv_cb != NULL) { + /* could fire context change and call _recv so we need to unlock */ + mutex_unlock(&dev->mutex); + res = dev->recv_cb(netdev, buf, len, info); + } + else { + mutex_unlock(&dev->mutex); + } + return res; +} + +static int _init(netdev2_t *netdev) +{ + netdev2_test_t *dev = (netdev2_test_t *)netdev; + int res = 0; /* assume everything would be fine */ + + mutex_lock(&dev->mutex); + if (dev->init_cb != NULL) { + res = dev->init_cb(netdev); + } + mutex_unlock(&dev->mutex); + return res; +} + +static void _isr(netdev2_t *netdev) +{ + netdev2_test_t *dev = (netdev2_test_t *)netdev; + + mutex_lock(&dev->mutex); + if (dev->isr_cb != NULL) { + mutex_unlock(&dev->mutex); + dev->isr_cb(netdev); + } + else { + mutex_unlock(&dev->mutex); + } +} + +static int _get(netdev2_t *netdev, netopt_t opt, void *value, size_t max_len) +{ + netdev2_test_t *dev = (netdev2_test_t *)netdev; + int res = -ENOTSUP; /* option assumed to be not supported */ + + mutex_lock(&dev->mutex); + if (dev->get_cbs[opt] != NULL) { + res = dev->get_cbs[opt](netdev, value, max_len); + } + mutex_unlock(&dev->mutex); + return res; +} + +static int _set(netdev2_t *netdev, netopt_t opt, void *value, size_t value_len) +{ + netdev2_test_t *dev = (netdev2_test_t *)netdev; + int res = -ENOTSUP; /* option assumed to be not supported */ + + mutex_lock(&dev->mutex); + if (dev->set_cbs[opt] != NULL) { + res = dev->set_cbs[opt](netdev, value, value_len); + } + mutex_unlock(&dev->mutex); + return res; +} + + +/** @} */