1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00
RIOT/tests/unittests/netdev_dummy/netdev_dummy.c
2015-02-08 18:52:16 +01:00

700 lines
19 KiB
C

/*
* Copyright (C) 2014 Martine Lenders <mlenders@inf.fu-berlin.de>
*
* 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
* @{
*
* @file netdev_dummy.c
*
* @author Martine Lenders <mlenders@inf.fu-berlin.de>
*/
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include "netdev/base.h"
#include "netdev_dummy.h"
#define _NETDEV_MORE(dev) ((_ut_dev_internal *)(dev->more))
typedef struct {
char dst[UNITTESTS_NETDEV_DUMMY_MAX_LONG_ADDR_LEN];
size_t dst_len;
char src[UNITTESTS_NETDEV_DUMMY_MAX_LONG_ADDR_LEN];
size_t src_len;
char data[UNITTESTS_NETDEV_DUMMY_MAX_PACKET];
size_t data_len;
} _unittest_test_buffer;
typedef struct {
unsigned int channel;
char short_address[UNITTESTS_NETDEV_DUMMY_MAX_ADDR_LEN];
unsigned int nid;
char long_address[UNITTESTS_NETDEV_DUMMY_MAX_LONG_ADDR_LEN];
int tx_power;
netdev_state_t state;
netdev_rcv_data_cb_t callbacks[UNITTESTS_NETDEV_DUMMY_MAX_CB];
_unittest_test_buffer rx_buffer;
_unittest_test_buffer tx_buffer;
uint32_t last_event;
} _ut_dev_internal;
netdev_t unittest_netdev_dummy_devs[UNITTESTS_NETDEV_DUMMY_MAX_DEV];
static _ut_dev_internal _netdevs_internal[UNITTESTS_NETDEV_DUMMY_MAX_DEV];
static int _find_dev(netdev_t *dev)
{
if (dev == NULL || dev < (&(unittest_netdev_dummy_devs[0])) ||
dev > (&(unittest_netdev_dummy_devs[UNITTESTS_NETDEV_DUMMY_MAX_DEV - 1]))) {
return -1;
}
for (int i = 0; i < UNITTESTS_NETDEV_DUMMY_MAX_DEV; i++) {
if (&(unittest_netdev_dummy_devs[i]) == dev) {
return i;
}
}
return -1;
}
static int _init(netdev_t *dev)
{
if (_find_dev(dev) < 0) {
return -ENODEV;
}
_NETDEV_MORE(dev)->channel = 0;
_NETDEV_MORE(dev)->nid = 0;
_NETDEV_MORE(dev)->state = 0;
memset(_NETDEV_MORE(dev)->short_address, 0,
UNITTESTS_NETDEV_DUMMY_MAX_ADDR_LEN);
memset(_NETDEV_MORE(dev)->long_address, 0,
UNITTESTS_NETDEV_DUMMY_MAX_LONG_ADDR_LEN);
memset(&(_NETDEV_MORE(dev)->rx_buffer), 0, sizeof(_unittest_test_buffer));
memset(&(_NETDEV_MORE(dev)->tx_buffer), 0, sizeof(_unittest_test_buffer));
_NETDEV_MORE(dev)->last_event = 0;
for (int j = 0; j < UNITTESTS_NETDEV_DUMMY_MAX_CB; j++) {
_NETDEV_MORE(dev)->callbacks[j] = NULL;
}
return 0;
}
static int _send_data(netdev_t *dev, void *dest, size_t dest_len,
netdev_hlist_t *upper_layer_hdrs, void *data,
size_t data_len)
{
netdev_hlist_t *ptr = upper_layer_hdrs;
size_t tx_ptr = 0;
if (dest == NULL || data == NULL) {
return -EFAULT;
}
if (_find_dev(dev) < 0) {
return -ENODEV;
}
if ((data_len + netdev_get_hlist_len(upper_layer_hdrs)) >
UNITTESTS_NETDEV_DUMMY_MAX_PACKET) {
return -EMSGSIZE;
}
if (dest_len > UNITTESTS_NETDEV_DUMMY_MAX_ADDR_LEN) {
return -EAFNOSUPPORT;
}
memcpy(_NETDEV_MORE(dev)->tx_buffer.dst, dest, dest_len);
_NETDEV_MORE(dev)->tx_buffer.dst_len = dest_len;
_NETDEV_MORE(dev)->tx_buffer.data_len = 0;
if (upper_layer_hdrs) {
do {
memcpy(&(_NETDEV_MORE(dev)->tx_buffer.data[tx_ptr]),
ptr->header, ptr->header_len);
_NETDEV_MORE(dev)->tx_buffer.data_len += ptr->header_len;
tx_ptr += ptr->header_len;
netdev_hlist_advance(&ptr);
} while (ptr != upper_layer_hdrs);
}
memcpy(&(_NETDEV_MORE(dev)->tx_buffer.data[tx_ptr]), data,
data_len);
_NETDEV_MORE(dev)->tx_buffer.data_len += data_len;
return _NETDEV_MORE(dev)->tx_buffer.data_len;
}
static int _add_receive_data_callback(netdev_t *dev, netdev_rcv_data_cb_t cb)
{
if (_find_dev(dev) < 0) {
return -ENODEV;
}
for (int j = 0; j < UNITTESTS_NETDEV_DUMMY_MAX_CB; j++) {
if (_NETDEV_MORE(dev)->callbacks[j] == cb) {
return 0;
}
if (_NETDEV_MORE(dev)->callbacks[j] == NULL) {
_NETDEV_MORE(dev)->callbacks[j] = cb;
return 0;
}
}
return -ENOBUFS;
}
static int _rem_receive_data_callback(netdev_t *dev, netdev_rcv_data_cb_t cb)
{
if (_find_dev(dev) < 0) {
return -ENODEV;
}
for (int j = 0; j < UNITTESTS_NETDEV_DUMMY_MAX_CB; j++) {
if (_NETDEV_MORE(dev)->callbacks[j] == cb) {
_NETDEV_MORE(dev)->callbacks[j] = NULL;
return 0;
}
}
return 0;
}
static int _get_option(netdev_t *dev, netdev_opt_t opt, void *value,
size_t *value_len)
{
if (_find_dev(dev) < 0) {
return -ENODEV;
}
if (value == NULL || value_len == NULL) {
return -EFAULT;
}
switch (opt) {
case NETDEV_OPT_CHANNEL:
if (*value_len < sizeof(unsigned int)) {
return -EOVERFLOW;
}
if (*value_len > sizeof(unsigned int)) {
*value_len = sizeof(unsigned int);
}
*((unsigned int *)value) = _NETDEV_MORE(dev)->channel;
break;
case NETDEV_OPT_ADDRESS:
if (*value_len < UNITTESTS_NETDEV_DUMMY_MAX_ADDR_LEN) {
return -EOVERFLOW;
}
if (*value_len > UNITTESTS_NETDEV_DUMMY_MAX_ADDR_LEN) {
*value_len = UNITTESTS_NETDEV_DUMMY_MAX_ADDR_LEN;
}
memcpy(value, _NETDEV_MORE(dev)->short_address, *value_len);
break;
case NETDEV_OPT_NID:
if (*value_len < sizeof(unsigned int)) {
return -EOVERFLOW;
}
if (*value_len > sizeof(unsigned int)) {
*value_len = sizeof(unsigned int);
}
*((unsigned int *)value) = _NETDEV_MORE(dev)->nid;
break;
case NETDEV_OPT_ADDRESS_LONG:
if (*value_len < UNITTESTS_NETDEV_DUMMY_MAX_LONG_ADDR_LEN) {
return -EOVERFLOW;
}
if (*value_len > UNITTESTS_NETDEV_DUMMY_MAX_LONG_ADDR_LEN) {
*value_len = UNITTESTS_NETDEV_DUMMY_MAX_LONG_ADDR_LEN;
}
memcpy(value, _NETDEV_MORE(dev)->long_address, *value_len);
break;
case NETDEV_OPT_TX_POWER:
if (*value_len < sizeof(int)) {
return -EOVERFLOW;
}
if (*value_len > sizeof(int)) {
*value_len = sizeof(int);
}
*((unsigned int *)value) = _NETDEV_MORE(dev)->tx_power;
break;
case NETDEV_OPT_MAX_PACKET_SIZE:
#if UNITTESTS_NETDEV_DUMMY_MAX_PACKET < 256
if (*value_len == 0) {
return -EOVERFLOW;
}
*value_len = sizeof(uint8_t);
*((uint8_t *)value) = (uint8_t)UNITTESTS_NETDEV_DUMMY_MAX_PACKET;
#elif UNITTESTS_NETDEV_DUMMY_MAX_PACKET < (1 << 16)
if (*value_len < sizeof(uint16_t)) {
return -EOVERFLOW;
}
*value_len = sizeof(uint16_t);
*((uint16_t *)value) = (uint16_t)UNITTESTS_NETDEV_DUMMY_MAX_PACKET;
#elif UNITTESTS_NETDEV_DUMMY_MAX_PACKET < (1 << 32)
if (*value_len < sizeof(uint32_t)) {
return -EOVERFLOW;
}
*value_len = sizeof(uint32_t);
*((uint32_t *)value) = (uint32_t)UNITTESTS_NETDEV_DUMMY_MAX_PACKET;
#else
if (*value_len < sizeof(uint64_t)) {
return -EOVERFLOW;
}
*value_len = sizeof(uint64_t);
*((uint64_t *)value) = (uint64_t)UNITTESTS_NETDEV_DUMMY_MAX_PACKET;
#endif
break;
case NETDEV_OPT_PROTO:
if (*value_len < sizeof(netdev_proto_t)) {
return -EOVERFLOW;
}
if (*value_len > sizeof(netdev_proto_t)) {
*value_len = sizeof(netdev_proto_t);
}
*((netdev_proto_t *)value) = NETDEV_PROTO_UNKNOWN;
break;
default:
return -ENOTSUP;
}
return 0;
}
static int _type_pun_up_unsigned(void *value_out, size_t desired_len,
void *value_in, size_t given_len)
{
if (given_len > desired_len) {
return -EOVERFLOW;
}
/* XXX this is ugly, but bear with me */
switch (given_len) {
case 8:
switch (desired_len) {
case 8:
*((uint64_t *)value_out) = (*((uint64_t *)value_in));
return 0;
default:
return -EINVAL;
}
case 4:
switch (desired_len) {
case 8:
*((uint64_t *)value_out) = (uint64_t)(*((uint32_t *)value_in));
return 0;
case 4:
*((uint32_t *)value_out) = (*((uint32_t *)value_in));
return 0;
default:
return -EINVAL;
}
case 2:
switch (desired_len) {
case 8:
*((uint64_t *)value_out) = (uint64_t)(*((uint16_t *)value_in));
return 0;
case 4:
*((uint32_t *)value_out) = (uint32_t)(*((uint16_t *)value_in));
return 0;
case 2:
*((uint16_t *)value_out) = (*((uint16_t *)value_in));
return 0;
default:
return -EINVAL;
}
case 1:
switch (desired_len) {
case 8:
*((uint64_t *)value_out) = (uint64_t)(*((uint8_t *)value_in));
return 0;
case 4:
*((uint32_t *)value_out) = (uint32_t)(*((uint8_t *)value_in));
return 0;
case 2:
*((uint16_t *)value_out) = (uint16_t)(*((uint8_t *)value_in));
return 0;
case 1:
*((uint8_t *)value_out) = (*((uint8_t *)value_in));
return 0;
default:
return -EINVAL;
}
default:
return -EINVAL;
}
}
static int _type_pun_up_signed(void *value_out, size_t desired_len,
void *value_in, size_t given_len)
{
if (given_len > desired_len) {
return -EOVERFLOW;
}
/* XXX this is ugly, but bear with me */
switch (given_len) {
case 8:
switch (desired_len) {
case 8:
*((int64_t *)value_out) = (*((int64_t *)value_in));
return 0;
default:
return -EINVAL;
}
case 4:
switch (desired_len) {
case 8:
*((int64_t *)value_out) = (int64_t)(*((int32_t *)value_in));
return 0;
case 4:
*((int32_t *)value_out) = (*((int32_t *)value_in));
return 0;
default:
return -EINVAL;
}
case 2:
switch (desired_len) {
case 8:
*((int64_t *)value_out) = (int64_t)(*((int16_t *)value_in));
return 0;
case 4:
*((int32_t *)value_out) = (int32_t)(*((int16_t *)value_in));
return 0;
case 2:
*((int16_t *)value_out) = (*((int16_t *)value_in));
return 0;
default:
return -EINVAL;
}
case 1:
switch (desired_len) {
case 8:
*((int64_t *)value_out) = (int64_t)(*((int8_t *)value_in));
return 0;
case 4:
*((int32_t *)value_out) = (int32_t)(*((int8_t *)value_in));
return 0;
case 2:
*((int16_t *)value_out) = (int16_t)(*((int8_t *)value_in));
return 0;
case 1:
*((int8_t *)value_out) = (*((int8_t *)value_in));
return 0;
default:
return -EINVAL;
}
default:
return -EINVAL;
}
}
static int _set_option(netdev_t *dev, netdev_opt_t opt, void *value,
size_t value_len)
{
uint8_t set_value[UNITTESTS_NETDEV_DUMMY_MAX_LONG_ADDR_LEN];
int res = 0;
if (_find_dev(dev) < 0) {
return -ENODEV;
}
if (value == NULL) {
return -EFAULT;
}
switch (opt) {
case NETDEV_OPT_CHANNEL:
if ((res = _type_pun_up_unsigned(set_value, sizeof(unsigned int),
value, value_len)) == 0) {
_NETDEV_MORE(dev)->channel = *((unsigned int *)value);
}
break;
case NETDEV_OPT_ADDRESS:
if (value_len > UNITTESTS_NETDEV_DUMMY_MAX_ADDR_LEN) {
return -EOVERFLOW;
}
else {
int start = UNITTESTS_NETDEV_DUMMY_MAX_ADDR_LEN - value_len;
if (value_len == 0) {
return -EINVAL;
}
if (value_len < UNITTESTS_NETDEV_DUMMY_MAX_ADDR_LEN) {
memcpy(_NETDEV_MORE(dev)->short_address, value, start);
}
memcpy(&(_NETDEV_MORE(dev)->short_address[start]), value,
value_len);
}
break;
case NETDEV_OPT_NID:
if ((res = _type_pun_up_unsigned(set_value, sizeof(unsigned int),
value, value_len)) == 0) {
_NETDEV_MORE(dev)->nid = *((unsigned int *)value);
}
break;
case NETDEV_OPT_ADDRESS_LONG:
if (value_len > UNITTESTS_NETDEV_DUMMY_MAX_LONG_ADDR_LEN) {
return -EOVERFLOW;
}
else {
int start = UNITTESTS_NETDEV_DUMMY_MAX_LONG_ADDR_LEN - value_len;
if (value_len == 0) {
return -EINVAL;
}
if (value_len < UNITTESTS_NETDEV_DUMMY_MAX_LONG_ADDR_LEN) {
memcpy(_NETDEV_MORE(dev)->long_address, value, start);
}
memcpy(&(_NETDEV_MORE(dev)->long_address[start]), value,
value_len);
}
break;
case NETDEV_OPT_TX_POWER:
if ((res = _type_pun_up_signed(set_value, sizeof(int),
value, value_len)) == 0) {
_NETDEV_MORE(dev)->tx_power = *((int *)value);
}
break;
default:
return -ENOTSUP;
}
return res;
}
static int _get_state(netdev_t *dev, netdev_state_t *state)
{
if (_find_dev(dev) < 0) {
return -ENODEV;
}
if (state == NULL) {
return -EFAULT;
}
*state = _NETDEV_MORE(dev)->state;
return 0;
}
static int _set_state(netdev_t *dev, netdev_state_t state)
{
if (_find_dev(dev) < 0) {
return -ENODEV;
}
switch (state) {
case NETDEV_STATE_POWER_OFF:
case NETDEV_STATE_POWER_SLEEP:
case NETDEV_STATE_POWER_IDLE:
case NETDEV_STATE_RX_MODE:
case NETDEV_STATE_PROMISCUOUS_MODE:
_NETDEV_MORE(dev)->state = state;
return 0;
default:
return -ENOTSUP;
}
}
static void _event(netdev_t *dev, uint32_t event_type)
{
if (_find_dev(dev) < 0) {
return;
}
_NETDEV_MORE(dev)->last_event = event_type;
}
const netdev_driver_t unittest_netdev_dummy_driver = {
_init,
_send_data,
_add_receive_data_callback,
_rem_receive_data_callback,
_get_option,
_set_option,
_get_state,
_set_state,
_event,
};
int unittest_netdev_dummy_fire_rcv_event(netdev_t *dev, void *src,
size_t src_len, void *dest, size_t dest_len, void *data,
size_t data_len)
{
if (_find_dev(dev) < 0) {
return -ENODEV;
}
if ((src_len != UNITTESTS_NETDEV_DUMMY_MAX_LONG_ADDR_LEN &&
src_len != UNITTESTS_NETDEV_DUMMY_MAX_ADDR_LEN) ||
(dest_len != UNITTESTS_NETDEV_DUMMY_MAX_LONG_ADDR_LEN &&
dest_len != UNITTESTS_NETDEV_DUMMY_MAX_ADDR_LEN)) {
return -EAFNOSUPPORT;
}
if (data_len > UNITTESTS_NETDEV_DUMMY_MAX_PACKET) {
return -EMSGSIZE;
}
if (src == NULL || dest == NULL || (data == NULL && data_len != 0)) {
return -EINVAL;
}
_NETDEV_MORE(dev)->rx_buffer.src_len = src_len;
_NETDEV_MORE(dev)->rx_buffer.dst_len = dest_len;
_NETDEV_MORE(dev)->rx_buffer.data_len = data_len;
memcpy(_NETDEV_MORE(dev)->rx_buffer.dst, dest, dest_len);
memcpy(_NETDEV_MORE(dev)->rx_buffer.src, src, src_len);
memcpy(_NETDEV_MORE(dev)->rx_buffer.data, data, data_len);
for (int j = 0; j < UNITTESTS_NETDEV_DUMMY_MAX_CB; j++) {
if (_NETDEV_MORE(dev)->callbacks[j] != 0) {
int res = _NETDEV_MORE(dev)->callbacks[j](dev,
_NETDEV_MORE(dev)->rx_buffer.src,
_NETDEV_MORE(dev)->rx_buffer.src_len,
_NETDEV_MORE(dev)->rx_buffer.dst,
_NETDEV_MORE(dev)->rx_buffer.dst_len,
_NETDEV_MORE(dev)->rx_buffer.data,
_NETDEV_MORE(dev)->rx_buffer.data_len);
if (res < 0) {
return -ECANCELED;
}
}
}
return 0;
}
int unittest_netdev_dummy_check_transmitted(netdev_t *dev,
void *expected_dest,
size_t expected_dest_len,
void *expected_data,
size_t expected_data_len)
{
if (_find_dev(dev) < 0) {
return -ENODEV;
}
if ((expected_dest == NULL && expected_dest_len != 0) ||
(expected_data == NULL && expected_data_len != 0)) {
return -EINVAL;
}
if (!(expected_dest_len == UNITTESTS_NETDEV_DUMMY_MAX_LONG_ADDR_LEN ||
expected_dest_len == UNITTESTS_NETDEV_DUMMY_MAX_ADDR_LEN) ||
expected_dest_len != _NETDEV_MORE(dev)->tx_buffer.dst_len ||
expected_data_len != _NETDEV_MORE(dev)->tx_buffer.data_len ||
(memcmp(expected_dest, _NETDEV_MORE(dev)->tx_buffer.dst, expected_dest_len) != 0) ||
(memcmp(expected_data, _NETDEV_MORE(dev)->tx_buffer.data, expected_data_len) != 0)) {
return 1;
}
return 0;
}
uint32_t unittest_netdev_dummy_get_last_event(netdev_t *dev)
{
if (_find_dev(dev) < 0) {
return ENODEV;
}
return _NETDEV_MORE(dev)->last_event;
}
void unittest_netdev_dummy_init(void)
{
for (int i = 0; i < UNITTESTS_NETDEV_DUMMY_MAX_DEV; i++) {
netdev_t *dev = &(unittest_netdev_dummy_devs[i]);
dev->type = NETDEV_TYPE_BASE;
dev->driver = &unittest_netdev_dummy_driver;
dev->more = &(_netdevs_internal[i]);
}
}
/**
* @}
*/