netdev_test.h 8.86 KB
/*
 * 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_netdev_test    netdev dummy test driver
 * @ingroup     drivers_netdev
 * @brief       Provides a test dummy for the netdev 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 <stdint.h>
 *
 * #include "mutex.h"
 * #include "net/af.h"
 * #include "net/conn/udp.h"
 * #include "net/ipv6/addr.h"
 * #include "net/netdev_test.h"
 * #include "xtimer.h"
 *
 * #define PKT_NUMBER   (1000)
 *
 * static netdev_test_t dev;
 * static uint32_t last_start;
 * static uint32_t sum = 0;
 * static mutex_t wait = MUTEX_INIT;
 *
 * int _send_timer(netdev_t *dev, const struct iovec *vector, int count)
 * {
 *     (void)dev;
 *     (void)vector;
 *     (void)count;
 *
 *     sum += (xtimer_now_usec() - last_start);
 *     mutex_unlock(&wait);
 * }
 *
 * int main(void) {
 *     ipv6_addr_t dst = IPV6_ADDR_UNSPECIFIED;
 *
 *     netdev_test_setup(&dev, NULL);
 *     dev->driver->init((netdev_t *)&dev)
 *     // initialize stack and connect `dev` to it
 *     // ...
 *     mutex_lock(&wait);
 *     for (int i = 0; i < PKT_NUMBER; i++) {
 *         last_start = xtimer_now_usec();
 *         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 netdev_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_netdev_test definitions
 *
 * @author  Martine Lenders <mlenders@inf.fu-berlin.de>
 */
#ifndef NET_NETDEV_TEST_H
#define NET_NETDEV_TEST_H

#include "mutex.h"

#ifdef MODULE_NETDEV_IEEE802154
#include "net/netdev/ieee802154.h"
#endif

#include "net/netdev.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 (*netdev_test_send_cb_t)(netdev_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 netdev 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 (*netdev_test_recv_cb_t)(netdev_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 (*netdev_test_init_cb_t)(netdev_t *dev);

/**
 * @brief   Callback type to handle user-space ISR events
 *
 * @param[in] dev       network device descriptor
 */
typedef void (*netdev_test_isr_cb_t)(netdev_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 (*netdev_test_get_cb_t)(netdev_t *dev, void *value,
                                    size_t max_len);

/**
 * @brief   Callback type to handle set commands
 *
 * @param[in] dev       network device descriptor
 * @param[in] 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 (*netdev_test_set_cb_t)(netdev_t *dev, const void *value,
                                    size_t value_len);

/**
 * @brief   Device descriptor for @ref sys_netdev_test devices
 *
 * @extends netdev_t
 */
typedef struct {
    /**
     * @brief   netdev fields
     * @{
     */
#ifdef  MODULE_NETDEV_IEEE802154
    netdev_ieee802154_t netdev;     /**< superclass */
#else                               /* MODULE_NETDEV_IEEE802154 */
    netdev_t netdev;                /**< superclass */
#endif  /* MODULE_NETDEV_IEEE802154 */
    /** @} */

    /**
     * @brief   device specific fields
     * @{
     */
    netdev_test_send_cb_t send_cb;                  /**< callback to handle send command */
    netdev_test_recv_cb_t recv_cb;                  /**< callback to handle receive command */
    netdev_test_init_cb_t init_cb;                  /**< callback to handle initialization events */
    netdev_test_isr_cb_t isr_cb;                    /**< callback to handle ISR events */
    netdev_test_get_cb_t get_cbs[NETOPT_NUMOF];     /**< callback to handle get command */
    netdev_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 */
    /** @} */
} netdev_test_t;

/**
 * @brief   override send callback
 *
 * @param[in] dev       a @ref sys_netdev_test device
 * @param[in] send_cb   a send callback
 */
static inline void netdev_test_set_send_cb(netdev_test_t *dev,
                                           netdev_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_netdev_test device
 * @param[in] recv_cb   a receive callback
 */
static inline void netdev_test_set_recv_cb(netdev_test_t *dev,
                                           netdev_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_netdev_test device
 * @param[in] init_cb   an initialization callback
 */
static inline void netdev_test_set_init_cb(netdev_test_t *dev,
                                           netdev_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_netdev_test device
 * @param[in] isr_cb    an ISR event handler callback
 */
static inline void netdev_test_set_isr_cb(netdev_test_t *dev,
                                          netdev_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_netdev_test device
 * @param[in] opt       an option type
 * @param[in] get_cb    a get callback for @p opt
 */
static inline void netdev_test_set_get_cb(netdev_test_t *dev, netopt_t opt,
                                          netdev_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_netdev_test device
 * @param[in] opt       an option type
 * @param[in] set_cb    a set callback for @p opt
 */
static inline void netdev_test_set_set_cb(netdev_test_t *dev, netopt_t opt,
                                          netdev_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_netdev_test device
 *
 * @param[in] dev   a @ref sys_netdev_test device to initialize
 * @param[in] state external state for the device
 */
void netdev_test_setup(netdev_test_t *dev, void *state);

/**
 * @brief   Resets all callbacks for the device to NULL
 *
 * @param[in] dev   a @ref sys_netdev_test device to initialize
 */
void netdev_test_reset(netdev_test_t *dev);

#ifdef __cplusplus
}
#endif

#endif /* NET_NETDEV_TEST_H */
/** @} */