diff --git a/cpu/msp430-common/include/cpu-conf.h b/cpu/msp430-common/include/cpu-conf.h index 24be279586..8ca97d1113 100644 --- a/cpu/msp430-common/include/cpu-conf.h +++ b/cpu/msp430-common/include/cpu-conf.h @@ -29,6 +29,11 @@ #ifndef UART0_BUFSIZE #define UART0_BUFSIZE (32) #endif + +#ifndef PKTBUF_SIZE +#define PKTBUF_SIZE (2560) /* TODO: Make this value + overall MTU dependent */ +#endif /** @} */ #endif /* CPUCONF_H_ */ diff --git a/sys/Makefile b/sys/Makefile index ee4a219984..ebbb3bd0c1 100644 --- a/sys/Makefile +++ b/sys/Makefile @@ -13,6 +13,9 @@ endif ifneq (,$(filter ping,$(USEMODULE))) DIRS += ping endif +ifneq (,$(filter pktbuf,$(USEMODULE))) + DIRS += net/crosslayer/pktbuf +endif ifneq (,$(filter ps,$(USEMODULE))) DIRS += ps endif diff --git a/sys/Makefile.include b/sys/Makefile.include index 827ec61aa4..15ae37aed1 100644 --- a/sys/Makefile.include +++ b/sys/Makefile.include @@ -18,6 +18,9 @@ endif ifneq (,$(filter net_if,$(USEMODULE))) USEMODULE_INCLUDES += $(RIOTBASE)/sys/net/include endif +ifneq (,$(filter pktbuf,$(USEMODULE))) + USEMODULE_INCLUDES += $(RIOTBASE)/sys/net/include +endif ifneq (,$(filter pktqueue,$(USEMODULE))) USEMODULE_INCLUDES += $(RIOTBASE)/sys/net/include endif diff --git a/sys/net/crosslayer/pktbuf/Makefile b/sys/net/crosslayer/pktbuf/Makefile new file mode 100644 index 0000000000..48422e909a --- /dev/null +++ b/sys/net/crosslayer/pktbuf/Makefile @@ -0,0 +1 @@ +include $(RIOTBASE)/Makefile.base diff --git a/sys/net/crosslayer/pktbuf/pktbuf.c b/sys/net/crosslayer/pktbuf/pktbuf.c new file mode 100644 index 0000000000..905644a60f --- /dev/null +++ b/sys/net/crosslayer/pktbuf/pktbuf.c @@ -0,0 +1,509 @@ +/* + * Copyright (C) 2014 Martin Lenders + * + * 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 pktbuf.c + * + * @author Martine Lenders + */ + +#include +#include +#include +#include + +#include "mutex.h" +#include "pktbuf.h" + +typedef struct __attribute__((packed)) _packet_t { + volatile struct _packet_t *next; + uint8_t processing; + size_t size; +} _packet_t; + +static uint8_t _pktbuf[PKTBUF_SIZE]; +static mutex_t _pktbuf_mutex = MUTEX_INIT; + +/** + * @brief Get first element in packet buffer. + */ +static inline _packet_t *_pktbuf_head(void) +{ + return (_packet_t *)(&(_pktbuf[0])); +} + +/** + * @brief Get data part (memory behind `_packet_t` descriptive header) of a packet + * + * @param[in] pkt A packet + * + * @return Data part of the packet. + */ +static inline void *_pkt_data(_packet_t *pkt) +{ + return (void *)(((_packet_t *)pkt) + 1); +} + +/** + * @brief Calculates total size (*size* + size of `_packet_t` descriptive header) of a + * packet in memory + * + * @param[in] size A given packet size + * + * @return Total size of a packet in memory. + */ +static inline size_t _pkt_total_sz(size_t size) +{ + return sizeof(_packet_t) + size; +} + +/** + * @brief Get pointer to the byte after the last byte of a packet. + * + * @param[in] pkt A packet + * + * @return Pointer to the last byte of a packet + */ +static inline void *_pkt_end(_packet_t *pkt) +{ + return (void *)((uint8_t *)pkt + _pkt_total_sz(pkt->size)); +} + +/** + * @brief Get index in packet buffer of the first byte of a packet's data part + * + * @param[in] pkt A packet + * + * @return Index in packet buffer of the first byte of *pkt*'s data part. + */ +static inline size_t _pktbuf_start_idx(_packet_t *pkt) +{ + return (size_t)(((uint8_t *)pkt) - (&(_pktbuf[0]))); +} + +/** + * @brief Get index in packet buffer of the last byte of a packet's data part + * + * @param[in] pkt A packet + * + * @return Index in packet buffer of the last byte of *pkt*'s data part. + */ +static inline size_t _pktbuf_end_idx(_packet_t *pkt) +{ + return _pktbuf_start_idx(pkt) + _pkt_total_sz(pkt->size) - 1; +} + +static _packet_t *_pktbuf_find_with_prev(_packet_t **prev_ptr, + _packet_t **packet_ptr, const void *pkt) +{ + _packet_t *packet = _pktbuf_head(), *prev = NULL; + + while (packet != NULL) { + + if (_pkt_data(packet) == pkt) { + *prev_ptr = prev; + *packet_ptr = packet; + return packet; + } + + prev = packet; + packet = (_packet_t *)packet->next; + } + + *prev_ptr = NULL; + *packet_ptr = NULL; + + return NULL; +} + +static _packet_t *_pktbuf_find(const void *pkt) +{ + _packet_t *packet = _pktbuf_head(); + +#ifdef DEVELHELP + + if (pkt == NULL) { + return NULL; + } + +#endif /* DEVELHELP */ + + while (packet != NULL) { + if ((_pkt_data(packet) <= pkt) && (pkt < _pkt_end(packet))) { + mutex_unlock(&_pktbuf_mutex); + return packet; + } + + packet = (_packet_t *)packet->next; + } + + return NULL; +} + +static _packet_t *_pktbuf_alloc(size_t size) +{ + _packet_t *packet = _pktbuf_head(), *old_next; + + if ((size == 0) || (size > PKTBUF_SIZE)) { + return NULL; + } + + if ((packet->size == 0) /* if first packet is currently not initialized + * but next packet and no space between first + * and next in its slot */ + && (packet->processing == 0) + && (packet->next != NULL) + && ((_pktbuf_start_idx((_packet_t *)(packet->next)) - _pkt_total_sz(packet->size)) < size)) { + packet = (_packet_t *)packet->next; + } + + /* while packet is not initialized */ + while ((packet->processing > 0) || (packet->size > size)) { + old_next = (_packet_t *)packet->next; + + /* if current packet is the last in buffer, but buffer space is exceeded */ + if ((old_next == NULL) + && ((_pktbuf_end_idx(packet) + _pkt_total_sz(size)) > PKTBUF_SIZE)) { + return NULL; + } + + /* if current packet is the last in the buffer or if space between + * current packet and next packet is big enough */ + if ((old_next == NULL) + || ((_pktbuf_start_idx((_packet_t *)(packet->next)) - _pktbuf_end_idx(packet)) >= _pkt_total_sz( + size))) { + + _packet_t *new_next = (_packet_t *)(((uint8_t *)packet) + _pkt_total_sz(packet->size)); + /* jump ahead size of current packet. */ + packet->next = new_next; + packet->next->next = old_next; + + packet = new_next; + + break; + } + + packet = old_next; + } + + packet->size = size; + packet->processing = 1; + + return packet; +} + +static void _pktbuf_free(_packet_t *prev, _packet_t *packet) +{ + if ((packet->processing)-- > 1) { /* `> 1` because packet->processing may already + * be 0 in which case --(packet->processing) + * would wrap to 255. */ + return; + } + + if (prev == NULL) { /* packet is _pktbuf_head() */ + packet->size = 0; + } + else { + prev->next = packet->next; + } +} + +int pktbuf_contains(const void *pkt) +{ + return ((&(_pktbuf[0]) < ((uint8_t *)pkt)) && (((uint8_t *)pkt) <= &(_pktbuf[PKTBUF_SIZE - 1]))); +} + +void *pktbuf_alloc(size_t size) +{ + _packet_t *packet; + + mutex_lock(&_pktbuf_mutex); + + packet = _pktbuf_alloc(size); + + if (packet == NULL) { + mutex_unlock(&_pktbuf_mutex); + return NULL; + } + + mutex_unlock(&_pktbuf_mutex); + + return _pkt_data(packet); +} + +void *pktbuf_realloc(const void *pkt, size_t size) +{ + _packet_t *new, *prev, *orig; + + mutex_lock(&_pktbuf_mutex); + + if ((size == 0) || (size > PKTBUF_SIZE)) { + mutex_unlock(&_pktbuf_mutex); + return NULL; + } + + _pktbuf_find_with_prev(&prev, &orig, pkt); + + if ((orig != NULL) && + ((orig->size >= size) /* and *orig* is last packet, and space in + * _pktbuf is sufficient */ + || ((orig->next == NULL) + && ((_pktbuf_start_idx(orig) + _pkt_total_sz(size)) < PKTBUF_SIZE)) + || ((orig->next != NULL) /* or space between pointer and the next is big enough: */ + && ((_pktbuf_start_idx((_packet_t *)(orig->next)) - _pktbuf_start_idx(orig)) + >= _pkt_total_sz(size))))) { + orig->size = size; + + mutex_unlock(&_pktbuf_mutex); + return (void *)pkt; + } + + new = _pktbuf_alloc(size); + + if (new == NULL) { + mutex_unlock(&_pktbuf_mutex); + return NULL; + } + + if (orig != NULL) { + memcpy(_pkt_data(new), _pkt_data(orig), orig->size); + + _pktbuf_free(prev, orig); + } + else { + memcpy(_pkt_data(new), pkt, size); + } + + + mutex_unlock(&_pktbuf_mutex); + + return _pkt_data(new); +} + +void *pktbuf_insert(const void *data, size_t size) +{ + _packet_t *packet; + + if ((data == NULL) || (size == 0)) { + return NULL; + } + + mutex_lock(&_pktbuf_mutex); + packet = _pktbuf_alloc(size); + + if (packet == NULL) { + mutex_unlock(&_pktbuf_mutex); + return NULL; + } + + memcpy(_pkt_data(packet), data, size); + + mutex_unlock(&_pktbuf_mutex); + return _pkt_data(packet); +} + +int pktbuf_copy(void *pkt, const void *data, size_t data_len) +{ + _packet_t *packet; + + mutex_lock(&_pktbuf_mutex); + +#ifdef DEVELHELP + + if (data == NULL) { + mutex_unlock(&_pktbuf_mutex); + return -EFAULT; + } + + if (pkt == NULL) { + mutex_unlock(&_pktbuf_mutex); + return -EINVAL; + } + +#endif /* DEVELHELP */ + + packet = _pktbuf_find(pkt); + + + if ((packet != NULL) && (packet->size > 0) && (packet->processing > 0)) { + /* packet space not engough? */ + if (data_len > (size_t)(((uint8_t *)_pkt_end(packet)) - ((uint8_t *)pkt))) { + mutex_unlock(&_pktbuf_mutex); + return -ENOMEM; + } + } + + + memcpy(pkt, data, data_len); + + mutex_unlock(&_pktbuf_mutex); + return data_len; +} + +void pktbuf_hold(const void *pkt) +{ + _packet_t *packet; + + mutex_lock(&_pktbuf_mutex); + + packet = _pktbuf_find(pkt); + + if (packet != NULL) { + packet->processing++; + } + + mutex_unlock(&_pktbuf_mutex); +} + +void pktbuf_release(const void *pkt) +{ + _packet_t *packet = _pktbuf_head(), *prev = NULL; + + mutex_lock(&_pktbuf_mutex); + + while (packet != NULL) { + + if ((_pkt_data(packet) <= pkt) && (pkt < _pkt_end(packet))) { + _pktbuf_free(prev, packet); + + mutex_unlock(&_pktbuf_mutex); + return; + } + + prev = packet; + packet = (_packet_t *)packet->next; + } + + mutex_unlock(&_pktbuf_mutex); +} + +#ifdef DEVELHELP +#include + +void pktbuf_print(void) +{ + _packet_t *packet = _pktbuf_head(); + int i = 0; + + mutex_lock(&_pktbuf_mutex); + + printf("current pktbuf allocations:\n"); + printf("===================================================\n"); + + if (packet->next == NULL && packet->size == 0) { + printf("empty\n"); + printf("===================================================\n"); + printf("\n"); + mutex_unlock(&_pktbuf_mutex); + + return; + } + else if (packet->next != NULL && packet->size == 0) { + packet = (_packet_t *)packet->next; + } + + while (packet != NULL) { + uint8_t *data = (uint8_t *)_pkt_data(packet); + + printf("packet %d (%p):\n", i, (void *)packet); + printf(" next: %p\n", (void *)(packet->next)); + printf(" size: %" PRIu32 "\n", (uint32_t)packet->size); + printf(" processing: %" PRIu8 "\n", packet->processing); + + if (packet->next != NULL) { + printf(" free data after: %" PRIu32 "\n", + (uint32_t)(_pktbuf_start_idx((_packet_t *)(packet->next)) - _pktbuf_end_idx(packet) - 1)); + } + else { + printf(" free data after: %" PRIu32 "\n", (uint32_t)(PKTBUF_SIZE - _pktbuf_end_idx(packet) - 1)); + + } + + printf(" data: (start address: %p)\n ", data); + + if (packet->size > PKTBUF_SIZE) { + printf(" We have a problem: packet->size (%" PRIu32 ") > PKTBUF_SIZE (%" PRIu32 ")\n", + (uint32_t)(packet->size), (uint32_t)PKTBUF_SIZE); + } + else { + for (size_t j = 0; j < packet->size; j++) { + printf(" %02x", data[j]); + + if (((j + 1) % 16) == 0) { + printf("\n "); + } + } + + printf("\n\n"); + } + + packet = (_packet_t *)packet->next; + i++; + } + + printf("===================================================\n"); + printf("\n"); + + mutex_unlock(&_pktbuf_mutex); + +} +#endif + +#ifdef TEST_SUITES +size_t pktbuf_bytes_allocated(void) +{ + _packet_t *packet = _pktbuf_head(); + size_t bytes = 0; + + mutex_lock(&_pktbuf_mutex); + + while (packet != NULL) { + bytes += packet->size; + packet = (_packet_t *)(packet->next); + } + + mutex_unlock(&_pktbuf_mutex); + + return bytes; +} + +unsigned int pktbuf_packets_allocated(void) +{ + _packet_t *packet = _pktbuf_head(); + unsigned int packets = 0; + + mutex_lock(&_pktbuf_mutex); + + while (packet != NULL) { + if ((packet != _pktbuf_head()) || (packet->size > 0)) { /* ignore head if head->size == 0 */ + packets++; + } + + packet = (_packet_t *)(packet->next); + } + + mutex_unlock(&_pktbuf_mutex); + + return packets; +} + +int pktbuf_is_empty(void) +{ + return ((_pktbuf_head()->next == NULL) && (_pktbuf_head()->size == 0)); +} + +void pktbuf_reset(void) +{ + memset(_pktbuf, 0, PKTBUF_SIZE); + mutex_init(&_pktbuf_mutex); +} +#endif + +/** @} */ diff --git a/sys/net/include/pktbuf.h b/sys/net/include/pktbuf.h new file mode 100644 index 0000000000..d1ccb8348c --- /dev/null +++ b/sys/net/include/pktbuf.h @@ -0,0 +1,187 @@ +/* + * Copyright (C) 2014 Martin Lenders + * + * 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 pktbuf Packet buffer + * @ingroup net + * @brief A global network packet buffer. + * @{ + * + * @file pktbuf.h + * @brief Interface definition for the global network buffer. Network devices + * and layers can allocate space for packets here. + * + * @note **WARNING!!** Do not store data structures that are not packed + * (defined with `__attribute__((packed))`) or enforce alignment in + * in any way in here. On some RISC architectures this *will* lead to + * alignment problems and can potentially result in segmentation/hard + * faults and other unexpected behaviour. + * + * @author Martine Lenders + */ +#ifndef __PKTBUF_H_ +#define __PKTBUF_H_ + +#include + +#include "cpu-conf.h" + +#ifndef PKTBUF_SIZE +/** + * @brief Maximum size of the packet buffer. + * + * @detail The rational here is to have at least space for 4 full-MTU IPv6 + * packages (2 incoming, 2 outgoing; 2 * 2 * 1280 B = 5 KiB) + + * Meta-Data (roughly estimated to 1 KiB; might be smaller) + */ +#define PKTBUF_SIZE (6144) +#endif /* PKTBUF_SIZE */ + +/** + * @brief Allocates new packet data in the packet buffer. This also marks the + * allocated data as processed. + * + * @see @ref pktbuf_hold() + * + * @param[in] size The length of the packet you want to allocate + * + * @return Pointer to the start of the data in the packet buffer, on success. + * @return NULL, if no space is left in the packet buffer or size was 0. + */ +void *pktbuf_alloc(size_t size); + +/** + * @brief Reallocates new space in the packet buffer, without changing the + * content. + * + * @datail If enough memory is available behind it or *size* is smaller than + * the original size the packet will not be moved. Otherwise, it will + * be moved. If no space is available nothing happens. + * + * @param[in] pkt Old position of the packet in the packet buffer + * @param[in] size New amount of data you want to allocate + * + * @return Pointer to the (maybe new) position of the packet in the packet buffer, + * on success. + * @return NULL, if no space is left in the packet buffer or size was 0. + * The packet will remain at *ptr*. + */ +void *pktbuf_realloc(const void *pkt, size_t size); + +/** + * @brief Allocates and copies new packet data into the packet buffer. + * This also marks the allocated data as processed for the current + * thread. + * + * @see @ref pktbuf_hold() + * + * @param[in] data Data you want to copy into the new packet. + * @param[in] size The length of the packet you want to allocate + * + * @return Pointer to the start of the data in the packet buffer, on success. + * @return NULL, if no space is left in the packet buffer. + */ +void *pktbuf_insert(const void *data, size_t size); + +/** + * @brief Copies packet data into the packet buffer, safely. + * + * @detail Use this instead of memcpy, since it is thread-safe and checks if + * *pkt* is + * + * -# in the buffer at all + * -# its *size* is smaller or equal to the data allocated at *pkt* + * + * If the *pkt* is not in the buffer the data is just copied as + * memcpy would do. + * + * @param[in,out] pkt The packet you want to set the data for. + * @param[in] data The data you want to copy into the packet. + * @param[in] data_len The length of the data you want to copy. + * + * @return *data_len*, on success. + * @return -EFAULT, if *data* is NULL and DEVELHELP is defined. + * @return -EINVAL, if *pkt* is NULL and DEVELHELP is defined. + * @return -ENOBUFS, if *data_len* was greater than the packet size of *pkt*. + */ +int pktbuf_copy(void *pkt, const void *data, size_t data_len); + +/** + * @brief Marks the data as being processed. + * + * @detail Internally this increments just a counter on the data. + * @ref pktbuf_release() decrements it. If the counter is <=0 the + * reserved data block in the buffer will be made available again. + * + * @param[in] pkt The packet you want mark as being processed. + */ +void pktbuf_hold(const void *pkt); + +/** + * @brief Marks the data as not being processed. + * + * @param[in] pkt The packet you want mark as not being processed anymore. + * + * @detail Internally this decrements just a counter on the data. + * @ref pktbuf_hold() increments and any allocation + * operation initializes it. If the counter is <=0 the reserved data + * block in the buffer will be made available again. + */ +void pktbuf_release(const void *pkt); + +/** + * @brief Prints current packet buffer to stdout if DEVELHELP is defined. + */ +#ifdef DEVELHELP +void pktbuf_print(void); +#else +#define pktbuf_print() ; +#endif + +/* for testing */ +#ifdef TEST_SUITES +/** + * @brief Counts the number of allocated bytes + * + * @return Number of allocated bytes + */ +size_t pktbuf_bytes_allocated(void); + +/** + * @brief Counts the number of allocated packets + * + * @return Number of allocated packets + */ +size_t pktbuf_packets_allocated(void); + +/** + * @brief Checks if packet buffer is empty + * + * @return 1, if packet buffer is empty + * @return 0, if packet buffer is not empty + */ +int pktbuf_is_empty(void); + +/** + * @brief Checks if a given pointer is stored in the packet buffer + * + * @param[in] pkt Pointer to be checked + * + * @return 1, if *pkt* is in packet buffer + * @return 0, otherwise + */ +int pktbuf_contains(const void *pkt); + +/** + * @brief Sets the whole packet buffer to 0 + */ +void pktbuf_reset(void); +#endif + +#endif /* __PKTBUF_H_ */ +/** @} */ diff --git a/tests/unittests/Makefile b/tests/unittests/Makefile index b3c97fc25d..deacb7d7b4 100644 --- a/tests/unittests/Makefile +++ b/tests/unittests/Makefile @@ -1,7 +1,7 @@ APPLICATION = unittests include ../Makefile.tests_common -BOARD_INSUFFICIENT_RAM := chronos redbee-econotag stm32f0discovery +BOARD_INSUFFICIENT_RAM := chronos msb-430 msb-430h redbee-econotag stm32f0discovery USEMODULE += embunit diff --git a/tests/unittests/tests-pktbuf/Makefile b/tests/unittests/tests-pktbuf/Makefile new file mode 100644 index 0000000000..74a5e77d89 --- /dev/null +++ b/tests/unittests/tests-pktbuf/Makefile @@ -0,0 +1,3 @@ +MODULE = tests-pktbuf + +include $(RIOTBASE)/Makefile.base diff --git a/tests/unittests/tests-pktbuf/Makefile.include b/tests/unittests/tests-pktbuf/Makefile.include new file mode 100644 index 0000000000..48eec3ae44 --- /dev/null +++ b/tests/unittests/tests-pktbuf/Makefile.include @@ -0,0 +1 @@ +USEMODULE += pktbuf diff --git a/tests/unittests/tests-pktbuf/tests-pktbuf.c b/tests/unittests/tests-pktbuf/tests-pktbuf.c new file mode 100644 index 0000000000..7117220774 --- /dev/null +++ b/tests/unittests/tests-pktbuf/tests-pktbuf.c @@ -0,0 +1,538 @@ +/* + * Copyright (C) 2014 Martine Lenders + * + * 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 tests-pktbuf.c + */ +#include +#include + +#include "embUnit/embUnit.h" + +#include "pktbuf.h" + +#include "tests-pktbuf.h" + +typedef struct __attribute__((packed)) { + uint8_t u8; + uint16_t u16; + uint32_t u32; + uint64_t u64; + int8_t s8; + int16_t s16; + int32_t s32; + int64_t s64; +} test_pktbuf_struct_t; + +static void tear_down(void) +{ + pktbuf_reset(); +} + +static void test_pktbuf_alloc_0(void) +{ + TEST_ASSERT_NULL(pktbuf_alloc(0)); +} + +static void test_pktbuf_alloc_memfull(void) +{ + for (int i = 0; i < 9; i++) { + TEST_ASSERT_NOT_NULL(pktbuf_alloc((PKTBUF_SIZE / 10) + 4)); + /* Why 4? Because: http://xkcd.com/221/, thats why ;-) */ + } + + TEST_ASSERT_NULL(pktbuf_alloc((PKTBUF_SIZE / 10) + 4)); +} + +static void test_pktbuf_alloc_success(void) +{ + void *data, *data_prev = NULL; + + for (int i = 0; i < 9; i++) { + data = pktbuf_alloc((PKTBUF_SIZE / 10) + 4); + + TEST_ASSERT(data_prev < data); + + data_prev = data; + } +} + +static void test_pktbuf_realloc_0(void) +{ + void *data = pktbuf_alloc(512); + + TEST_ASSERT_NULL(pktbuf_realloc(data, 0)); +} + +static void test_pktbuf_realloc_memfull(void) +{ + void *data = pktbuf_alloc(512); + + TEST_ASSERT_NULL(pktbuf_realloc(data, PKTBUF_SIZE + 1)); +} + +static void test_pktbuf_realloc_memfull2(void) +{ + void *data = pktbuf_alloc(512); + + TEST_ASSERT_NOT_NULL(data); + TEST_ASSERT_NOT_NULL(pktbuf_alloc(512)); + TEST_ASSERT_NULL(pktbuf_realloc(data, PKTBUF_SIZE - 512)); +} + +static void test_pktbuf_realloc_memfull3(void) +{ + void *data; + + TEST_ASSERT_NOT_NULL(pktbuf_alloc(25)); + + data = pktbuf_alloc(512); + + TEST_ASSERT_NOT_NULL(data); + TEST_ASSERT_NOT_NULL(pktbuf_alloc(73)); + + TEST_ASSERT_NULL(pktbuf_realloc(data, PKTBUF_SIZE - 512)); +} + +static void test_pktbuf_realloc_smaller(void) +{ + void *data; + + data = pktbuf_alloc(512); + + TEST_ASSERT_NOT_NULL(data); + TEST_ASSERT_NOT_NULL(pktbuf_alloc(25)); + + TEST_ASSERT(data == pktbuf_realloc(data, 128)); +} + +static void test_pktbuf_realloc_memenough(void) +{ + void *data; + + data = pktbuf_alloc(128); + + TEST_ASSERT_NOT_NULL(data); + + TEST_ASSERT(data == pktbuf_realloc(data, 200)); +} + +static void test_pktbuf_realloc_memenough2(void) +{ + void *data, *data2; + + data = pktbuf_alloc(128); + + TEST_ASSERT_NOT_NULL(data); + + data2 = pktbuf_alloc(128); + + TEST_ASSERT_NOT_NULL(data); + TEST_ASSERT_NOT_NULL(pktbuf_alloc(25)); + + pktbuf_release(data2); + + TEST_ASSERT(data == pktbuf_realloc(data, 200)); +} + +static void test_pktbuf_realloc_nomemenough(void) +{ + void *data, *data2; + + data = pktbuf_alloc(128); + + TEST_ASSERT_NOT_NULL(data); + + data2 = pktbuf_alloc(128); + + TEST_ASSERT_NOT_NULL(data); + TEST_ASSERT_NOT_NULL(pktbuf_alloc(25)); + + pktbuf_release(data2); + + TEST_ASSERT(data != pktbuf_realloc(data, 512)); +} + +static void test_pktbuf_realloc_unknown_ptr(void) +{ + char *data = "abcd", *new_data = pktbuf_realloc(data, 5); + + TEST_ASSERT_NOT_NULL(new_data); + TEST_ASSERT(data != new_data); + TEST_ASSERT_EQUAL_STRING(data, new_data); +} + +static void test_pktbuf_insert_size_0(void) +{ + TEST_ASSERT_NULL(pktbuf_insert("", 0)); +} + +static void test_pktbuf_insert_data_NULL(void) +{ + TEST_ASSERT_NULL(pktbuf_insert(NULL, 4)); +} + +static void test_pktbuf_insert_memfull(void) +{ + while (pktbuf_insert("abc", 4)); + + TEST_ASSERT_NULL(pktbuf_insert("abc", 4)); +} + +static void test_pktbuf_insert_success(void) +{ + char *data, *data_prev = NULL; + + for (int i = 0; i < 10; i++) { + data = (char *)pktbuf_insert("abc", 4); + + TEST_ASSERT(data_prev < data); + TEST_ASSERT_EQUAL_STRING("abc", data); + + data_prev = data; + } +} + +#ifdef DEVELHELP +static void test_pktbuf_copy_efault(void) +{ + char *data = (char *)pktbuf_insert("abcd", 5); + + TEST_ASSERT_NOT_NULL(data); + TEST_ASSERT_EQUAL_INT(-EFAULT, pktbuf_copy(data, NULL, 3)); + TEST_ASSERT_EQUAL_STRING("abcd", data); +} +#endif + +static void test_pktbuf_copy_data_len_too_long(void) +{ + char *data = (char *)pktbuf_insert("ab", 3); + + TEST_ASSERT_NOT_NULL(data); + TEST_ASSERT_EQUAL_INT(-ENOMEM, pktbuf_copy(data, "cdef", 5)); + TEST_ASSERT_EQUAL_STRING("ab", data); +} + +static void test_pktbuf_copy_data_len_too_long2(void) +{ + char *data = (char *)pktbuf_insert("abcd", 5); + + TEST_ASSERT_NOT_NULL(data); + TEST_ASSERT_EQUAL_INT(-ENOMEM, pktbuf_copy(data + 2, "efgh", 5)); + TEST_ASSERT_EQUAL_STRING("abcd", data); +} + +static void test_pktbuf_copy_data_len_0(void) +{ + char *data = (char *)pktbuf_insert("abcd", 5); + + TEST_ASSERT_NOT_NULL(data); + TEST_ASSERT_EQUAL_INT(0, pktbuf_copy(data, "ef", 0)); + TEST_ASSERT_EQUAL_STRING("abcd", data); +} + +static void test_pktbuf_copy_success(void) +{ + char *data = (char *)pktbuf_insert("abcd", 5); + + TEST_ASSERT_NOT_NULL(data); + TEST_ASSERT_EQUAL_INT(3, pktbuf_copy(data, "ef", 3)); + TEST_ASSERT_EQUAL_STRING("ef", data); +} + +static void test_pktbuf_copy_success2(void) +{ + char *data = (char *)pktbuf_insert("abcdef", 7); + + TEST_ASSERT_NOT_NULL(data); + TEST_ASSERT_EQUAL_INT(2, pktbuf_copy(data + 3, "gh", 2)); + TEST_ASSERT_EQUAL_STRING("abcghf", data); +} + +static void test_pktbuf_hold_ptr_null(void) +{ + char *data; + + TEST_ASSERT_NOT_NULL(pktbuf_alloc(25)); + data = (char *)pktbuf_insert("abcd", 5); + TEST_ASSERT_NOT_NULL(data); + TEST_ASSERT_NOT_NULL(pktbuf_alloc(16)); + + TEST_ASSERT_EQUAL_INT(3, pktbuf_packets_allocated()); + + pktbuf_hold(NULL); + pktbuf_release(data); + + TEST_ASSERT_EQUAL_INT(2, pktbuf_packets_allocated()); + TEST_ASSERT_EQUAL_STRING("abcd", data); +} + +static void test_pktbuf_hold_wrong_ptr(void) +{ + char *data, wrong; + + TEST_ASSERT_NOT_NULL(pktbuf_alloc(25)); + data = (char *)pktbuf_insert("abcd", 5); + TEST_ASSERT_NOT_NULL(data); + TEST_ASSERT_NOT_NULL(pktbuf_alloc(16)); + + TEST_ASSERT_EQUAL_INT(3, pktbuf_packets_allocated()); + + pktbuf_hold(&wrong); + pktbuf_release(data); + + TEST_ASSERT_EQUAL_INT(2, pktbuf_packets_allocated()); + TEST_ASSERT_EQUAL_STRING("abcd", data); +} + +static void test_pktbuf_hold_success(void) +{ + char *data; + + TEST_ASSERT_NOT_NULL(pktbuf_alloc(25)); + data = (char *)pktbuf_insert("abcd", 5); + TEST_ASSERT_NOT_NULL(data); + TEST_ASSERT_NOT_NULL(pktbuf_alloc(16)); + + pktbuf_hold(data); + pktbuf_release(data); + + TEST_ASSERT_EQUAL_INT(3, pktbuf_copy(data, "ef", 3)); + TEST_ASSERT_EQUAL_STRING("ef", data); +} + +static void test_pktbuf_hold_success2(void) +{ + char *data; + + TEST_ASSERT_NOT_NULL(pktbuf_alloc(25)); + data = (char *)pktbuf_insert("abcd", 5); + TEST_ASSERT_NOT_NULL(data); + TEST_ASSERT_NOT_NULL(pktbuf_alloc(16)); + + pktbuf_hold(data + 4); + pktbuf_release(data + 4); + + TEST_ASSERT_EQUAL_INT(3, pktbuf_copy(data, "ef", 3)); + TEST_ASSERT_EQUAL_STRING("ef", data); +} + +static void test_pktbuf_release_ptr_null(void) +{ + char *data; + + TEST_ASSERT_NOT_NULL(pktbuf_alloc(25)); + data = (char *)pktbuf_insert("abcd", 5); + TEST_ASSERT_NOT_NULL(data); + TEST_ASSERT_NOT_NULL(pktbuf_alloc(16)); + + pktbuf_release(NULL); + + TEST_ASSERT_EQUAL_INT(3, pktbuf_copy(data, "ef", 3)); + TEST_ASSERT_EQUAL_STRING("ef", data); +} + +static void test_pktbuf_release_wrong_ptr(void) +{ + char *data, wrong; + + TEST_ASSERT_NOT_NULL(pktbuf_alloc(25)); + data = (char *)pktbuf_insert("abcd", 5); + TEST_ASSERT_NOT_NULL(data); + TEST_ASSERT_NOT_NULL(pktbuf_alloc(16)); + + pktbuf_release(&wrong); + + TEST_ASSERT_EQUAL_INT(3, pktbuf_copy(data, "ef", 3)); + TEST_ASSERT_EQUAL_STRING("ef", data); +} + +static void test_pktbuf_release_success(void) +{ + char *data; + + TEST_ASSERT_NOT_NULL(pktbuf_alloc(25)); + data = (char *)pktbuf_insert("abcd", 5); + TEST_ASSERT_NOT_NULL(data); + TEST_ASSERT_NOT_NULL(pktbuf_alloc(16)); + + TEST_ASSERT_EQUAL_INT(3, pktbuf_packets_allocated()); + + pktbuf_hold(data); + pktbuf_hold(data); + pktbuf_release(data + 3); + pktbuf_release(data + 4); + pktbuf_release(data + 2); + + TEST_ASSERT_EQUAL_INT(2, pktbuf_packets_allocated()); +} + +static void test_pktbuf_release_success2(void) +{ + char *data1, *data2, *data3; + + data1 = (char *)pktbuf_insert("abcd", 5); + TEST_ASSERT_NOT_NULL(data1); + data2 = (char *)pktbuf_insert("ef", 3); + TEST_ASSERT_NOT_NULL(data2); + data3 = (char *)pktbuf_insert("ghijkl", 7); + TEST_ASSERT_NOT_NULL(data3); + + TEST_ASSERT_EQUAL_INT(3, pktbuf_packets_allocated()); + + pktbuf_release(data2); + + TEST_ASSERT_EQUAL_INT(2, pktbuf_packets_allocated()); + TEST_ASSERT_EQUAL_INT(2, pktbuf_copy(data1, "m", 2)); + TEST_ASSERT_EQUAL_STRING("m", data1); + TEST_ASSERT_EQUAL_INT(4, pktbuf_copy(data3, "nop", 4)); + TEST_ASSERT_EQUAL_STRING("nop", data3); +} + +static void test_pktbuf_release_success3(void) +{ + char *data1, *data2, *data3; + + data1 = (char *)pktbuf_insert("abcd", 5); + TEST_ASSERT_NOT_NULL(data1); + data2 = (char *)pktbuf_insert("ef", 3); + TEST_ASSERT_NOT_NULL(data2); + data3 = (char *)pktbuf_insert("ghijkl", 7); + TEST_ASSERT_NOT_NULL(data3); + + TEST_ASSERT_EQUAL_INT(3, pktbuf_packets_allocated()); + + pktbuf_release(data1); + + TEST_ASSERT_EQUAL_INT(2, pktbuf_packets_allocated()); + TEST_ASSERT_EQUAL_INT(2, pktbuf_copy(data2, "m", 2)); + TEST_ASSERT_EQUAL_STRING("m", data2); + TEST_ASSERT_EQUAL_INT(4, pktbuf_copy(data3, "nop", 4)); + TEST_ASSERT_EQUAL_STRING("nop", data3); +} + +static void test_pktbuf_release_success4(void) +{ + char *data1, *data2, *data3; + + data1 = (char *)pktbuf_insert("abcd", 5); + TEST_ASSERT_NOT_NULL(data1); + data2 = (char *)pktbuf_insert("ef", 3); + TEST_ASSERT_NOT_NULL(data2); + data3 = (char *)pktbuf_insert("ghijkl", 7); + TEST_ASSERT_NOT_NULL(data3); + + TEST_ASSERT_EQUAL_INT(3, pktbuf_packets_allocated()); + + pktbuf_release(data3); + + TEST_ASSERT_EQUAL_INT(2, pktbuf_packets_allocated()); + TEST_ASSERT_EQUAL_INT(2, pktbuf_copy(data1, "m", 2)); + TEST_ASSERT_EQUAL_STRING("m", data1); + TEST_ASSERT_EQUAL_INT(1, pktbuf_copy(data2, "", 1)); + TEST_ASSERT_EQUAL_STRING("", data2); +} + +static void test_pktbuf_insert_packed_struct(void) +{ + test_pktbuf_struct_t data = { 0x4d, 0xef43, 0xacdef574, 0x43644305695afde5, + 34, -4469, 149699748, -46590430597 + }; + test_pktbuf_struct_t *data_cpy; + + data_cpy = (test_pktbuf_struct_t *)pktbuf_insert(&data, sizeof(test_pktbuf_struct_t)); + + TEST_ASSERT_EQUAL_INT(data.u8, data_cpy->u8); + TEST_ASSERT_EQUAL_INT(data.u16, data_cpy->u16); + TEST_ASSERT_EQUAL_INT(data.u32, data_cpy->u32); + TEST_ASSERT_EQUAL_INT(data.u64, data_cpy->u64); + TEST_ASSERT_EQUAL_INT(data.s8, data_cpy->s8); + TEST_ASSERT_EQUAL_INT(data.s16, data_cpy->s16); + TEST_ASSERT_EQUAL_INT(data.s32, data_cpy->s32); + TEST_ASSERT_EQUAL_INT(data.s64, data_cpy->s64); +} + +static void test_pktbuf_alloc_off_by_one1(void) +{ + char *data1, *data2, *data3, *data4; + + data1 = (char *)pktbuf_insert("1234567890a", 12); + TEST_ASSERT_NOT_NULL(data1); + data2 = (char *)pktbuf_alloc(44); + TEST_ASSERT_NOT_NULL(data2); + data4 = (char *)pktbuf_alloc(4); + TEST_ASSERT_NOT_NULL(data4); + TEST_ASSERT_EQUAL_INT(3, pktbuf_packets_allocated()); + TEST_ASSERT_EQUAL_INT(12 + 44 + 4, pktbuf_bytes_allocated()); + + pktbuf_release(data1); + + TEST_ASSERT_EQUAL_INT(2, pktbuf_packets_allocated()); + TEST_ASSERT_EQUAL_INT(44 + 4, pktbuf_bytes_allocated()); + + data3 = (char *)pktbuf_insert("bcdefghijklm", 13); + TEST_ASSERT_NOT_NULL(data3); + TEST_ASSERT(data1 != data3); + + TEST_ASSERT_EQUAL_INT(3, pktbuf_packets_allocated()); + TEST_ASSERT_EQUAL_INT(44 + 4 + 13, pktbuf_bytes_allocated()); +} + +Test *tests_pktbuf_tests(void) +{ + EMB_UNIT_TESTFIXTURES(fixtures) { + new_TestFixture(test_pktbuf_alloc_0), + new_TestFixture(test_pktbuf_alloc_memfull), + new_TestFixture(test_pktbuf_alloc_success), + new_TestFixture(test_pktbuf_realloc_0), + new_TestFixture(test_pktbuf_realloc_memfull), + new_TestFixture(test_pktbuf_realloc_memfull2), + new_TestFixture(test_pktbuf_realloc_memfull3), + new_TestFixture(test_pktbuf_realloc_smaller), + new_TestFixture(test_pktbuf_realloc_memenough), + new_TestFixture(test_pktbuf_realloc_memenough2), + new_TestFixture(test_pktbuf_realloc_nomemenough), + new_TestFixture(test_pktbuf_realloc_unknown_ptr), + new_TestFixture(test_pktbuf_insert_size_0), + new_TestFixture(test_pktbuf_insert_data_NULL), + new_TestFixture(test_pktbuf_insert_memfull), + new_TestFixture(test_pktbuf_insert_success), +#ifdef DEVELHELP + new_TestFixture(test_pktbuf_copy_efault), +#endif + new_TestFixture(test_pktbuf_copy_data_len_too_long), + new_TestFixture(test_pktbuf_copy_data_len_too_long2), + new_TestFixture(test_pktbuf_copy_data_len_0), + new_TestFixture(test_pktbuf_copy_success), + new_TestFixture(test_pktbuf_copy_success2), + new_TestFixture(test_pktbuf_hold_ptr_null), + new_TestFixture(test_pktbuf_hold_wrong_ptr), + new_TestFixture(test_pktbuf_hold_success), + new_TestFixture(test_pktbuf_hold_success2), + new_TestFixture(test_pktbuf_release_ptr_null), + new_TestFixture(test_pktbuf_release_wrong_ptr), + new_TestFixture(test_pktbuf_release_success), + new_TestFixture(test_pktbuf_release_success2), + new_TestFixture(test_pktbuf_release_success3), + new_TestFixture(test_pktbuf_release_success4), + new_TestFixture(test_pktbuf_insert_packed_struct), + new_TestFixture(test_pktbuf_alloc_off_by_one1), + }; + + EMB_UNIT_TESTCALLER(pktbuf_tests, NULL, tear_down, fixtures); + + return (Test *)&pktbuf_tests; +} + +void tests_pktbuf(void) +{ + TESTS_RUN(tests_pktbuf_tests()); +} +/** @} */ diff --git a/tests/unittests/tests-pktbuf/tests-pktbuf.h b/tests/unittests/tests-pktbuf/tests-pktbuf.h new file mode 100644 index 0000000000..6248328c3b --- /dev/null +++ b/tests/unittests/tests-pktbuf/tests-pktbuf.h @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2014 Martin Lenders + * + * 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 tests-pktbuf.h + * @brief Unittests for the ``pktbuf`` module + * + * @author Martine Lenders + */ +#ifndef __TESTS_PKTBUF_H_ +#define __TESTS_PKTBUF_H_ + +#include "../unittests.h" + +/** + * @brief The entry point of this test suite. + */ +void tests_pktbuf(void); + +#endif /* __TESTS_PKTBUF_H_ */ +/** @} */