diff --git a/include/net/buf.h b/include/net/buf.h new file mode 100644 index 00000000000..8bc8cdd4682 --- /dev/null +++ b/include/net/buf.h @@ -0,0 +1,249 @@ +/** @file + * @brief Buffer management. + */ + +/* + * Copyright (c) 2015 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef __NET_BUF_H +#define __NET_BUF_H + +#include +#include +#include +#include +#include + +/* Alignment needed for various parts of the buffer definition */ +#define __net_buf_align __aligned(sizeof(int)) + +struct net_buf { + /** FIFO uses first 4 bytes itself, reserve space */ + int __unused; + + /** Size of the user data associated with this buffer. */ + const uint16_t user_data_size; + + /** Reference count. */ + uint8_t ref; + + /** Pointer to the start of data in the buffer. */ + uint8_t *data; + + /** Length of the data behind the data pointer. */ + uint16_t len; + + /** Amount of data that this buffer can store. */ + const uint16_t size; + + /** Where the buffer should go when freed up. */ + struct nano_fifo * const free; + + /** Function to be called when the buffer is freed. */ + void (*const destroy)(struct net_buf *buf); + + /** Start of the data storage. Not to be accessed directly + * (the data pointer should be used instead). + */ + uint8_t __buf[0] __net_buf_align; +}; + +/** @brief Define a pool of buffers of a certain amount and size. + * + * Defines the necessary memory space (array of structs) for the needed + * amount of buffers. After this the net_buf_pool_init() API still + * needs to be used (at runtime), after which the buffers can be + * accessed using the fifo given as one of the parameters. + * + * @param _name Name of buffer pool. + * @param _count Number of buffers in the pool. + * @param _size Maximum data size for each buffer. + * @param _fifo FIFO for the buffers when they are unused. + * @param _destroy Optional destroy callback when buffer is freed. + * @param _ud_size Amount of user data space to reserve. + */ +#define NET_BUF_POOL(_name, _count, _size, _fifo, _destroy, _ud_size) \ + struct { \ + struct net_buf buf; \ + uint8_t data[ROUND_UP(_size, 4)] __net_buf_align; \ + uint8_t ud[ROUND_UP(_ud_size, 4)] __net_buf_align; \ + } _name[_count] = { \ + [0 ... (_count - 1)] = { .buf = { \ + .user_data_size = ROUND_UP(_ud_size, 4), \ + .free = _fifo, \ + .destroy = _destroy, \ + .size = ROUND_UP(_size, 4) } }, \ + } + +/** @brief Initialize an available buffers FIFO based on a pool. + * + * Initializes a buffer pool created using NET_BUF_POOL(). After + * calling this API the buffers can ge accessed through the FIFO that + * was given to NET_BUF_POOL(), i.e. after this call there should be no + * need to access the buffer pool (struct array) directly anymore. + * + * @param pool Buffer pool to initialize. + */ +#define net_buf_pool_init(pool) \ + do { \ + int i; \ + \ + nano_fifo_init(pool[0].buf.free); \ + \ + for (i = 0; i < ARRAY_SIZE(pool); i++) { \ + nano_fifo_put(pool[i].buf.free, &pool[i]); \ + } \ + } while (0) + +/** @brief Get a new buffer from the pool. + * + * Get buffer from the available buffers pool with specified type and + * reserved headroom. + * + * @param fifo Which FIFO to take the buffer from. + * @param reserve_head How much headroom to reserve. + * + * @return New buffer or NULL if out of buffers. + * + * @warning If there are no available buffers and the function is + * called from a task or fiber the call will block until a buffer + * becomes available in the pool. + */ +struct net_buf *net_buf_get(struct nano_fifo *fifo, size_t reserve_head); + +/** @brief Decrements the reference count of a buffer. + * + * Decrements the reference count of a buffer and puts it back into the + * pool if the count reaches zero. + * + * @param buf Buffer. + */ +void net_buf_unref(struct net_buf *buf); + +/** Increment the reference count of a buffer. + * + * Increment the reference count of a buffer. + * + * @param buf Buffer. + */ +struct net_buf *net_buf_ref(struct net_buf *buf); + +/** @brief Duplicate buffer + * + * Duplicate given buffer including any data and headers currently stored. + * + * @param buf Buffer. + * + * @return Duplicated buffer or NULL if out of buffers. + */ +struct net_buf *net_buf_clone(struct net_buf *buf); + +/** Get a pointer to the user data of a buffer. + * + * @param buf The buffer in question. + * + * @return Pointer to the user data of the buffer. + */ +#define net_buf_user_data(buf) \ + ((void *)(ROUND_UP(((buf)->__buf + (buf)->size), sizeof(int)))) + +/** @brief Prepare data to be added at the end of the buffer + * + * Increments the data length of a buffer to account for more data + * at the end. + * + * @param buf Buffer to update. + * @param len Number of bytes to increment the length with. + * + * @return The original tail of the buffer. + */ +void *net_buf_add(struct net_buf *buf, size_t len); + +/** @brief Add 16-bit value at the end of the buffer + * + * Adds 16-bit value in little endian format at the end of buffer. + * Increments the data length of a buffer to account for more data + * at the end. + * + * @param buf Buffer to update. + * @param value 16-bit value to be added. + * + * @return void + */ +void net_buf_add_le16(struct net_buf *buf, uint16_t value); + +/** @brief Push data to the beginning of the buffer. + * + * Modifies the data pointer and buffer length to account for more data + * in the beginning of the buffer. + * + * @param buf Buffer to update. + * @param len Number of bytes to add to the beginning. + * + * @return The new beginning of the buffer data. + */ +void *net_buf_push(struct net_buf *buf, size_t len); + +/** @brief Remove data from the beginning of the buffer. + * + * Removes data from the beginnig of the buffer by modifying the data + * pointer and buffer length. + * + * @param buf Buffer to update. + * @param len Number of bytes to remove. + * + * @return New beginning of the buffer data. + */ +void *net_buf_pull(struct net_buf *buf, size_t len); + +/** @brief Remove and convert 16 bits from the beginning of the buffer. + * + * Same idea as with bt_buf_pull(), but a helper for operating on + * 16-bit little endian data. + * + * @param buf Buffer. + * + * @return 16-bit value converted from little endian to host endian. + */ +uint16_t net_buf_pull_le16(struct net_buf *buf); + +/** @brief Check buffer tailroom. + * + * Check how much free space there is at the end of the buffer. + * + * @return Number of bytes available at the end of the buffer. + */ +size_t net_buf_tailroom(struct net_buf *buf); + +/** @brief Check buffer headroom. + * + * Check how much free space there is in the beginning of the buffer. + * + * @return Number of bytes available in the beginning of the buffer. + */ +size_t net_buf_headroom(struct net_buf *buf); + +/** @def net_buf_tail + * @brief Get the tail pointer for a buffer. + * + * Get a pointer to the end of the data in a buffer. + * + * @param buf Buffer. + * + * @return Tail pointer for the buffer. + */ +#define net_buf_tail(buf) ((buf)->data + (buf)->len) + +#endif /* __NET_BUF_H */ diff --git a/net/Kconfig b/net/Kconfig index 1eb84cff66e..1061f25b96c 100644 --- a/net/Kconfig +++ b/net/Kconfig @@ -22,4 +22,18 @@ source "net/bluetooth/Kconfig" source "net/ip/Kconfig" +config NET_BUF + bool "Network buffer support" + default n + help + This option enebles support for generic network protocol + buffers. + +config NET_BUF_DEBUG + bool "Network buffer debugging" + depends on NET_BUF + default n + help + Enable debug logs and checks for the generic network buffers. + endmenu diff --git a/net/Makefile b/net/Makefile index b021af7c0ad..72292eddb22 100644 --- a/net/Makefile +++ b/net/Makefile @@ -1,2 +1,3 @@ obj-$(CONFIG_BLUETOOTH) += bluetooth/ obj-$(CONFIG_NETWORKING) += ip/ +obj-$(CONFIG_NET_BUF) += buf.o diff --git a/net/bluetooth/Kconfig b/net/bluetooth/Kconfig index bf7ba7b456c..a643c8e6875 100644 --- a/net/bluetooth/Kconfig +++ b/net/bluetooth/Kconfig @@ -21,6 +21,7 @@ menuconfig BLUETOOTH prompt "Bluetooth LE support" default n select NANO_TIMEOUTS + select NET_BUF help This option enables Bluetooth Low Energy support. diff --git a/net/buf.c b/net/buf.c new file mode 100644 index 00000000000..f2d8bc56d8b --- /dev/null +++ b/net/buf.c @@ -0,0 +1,169 @@ +/* buf.c - Buffer management */ + +/* + * Copyright (c) 2015 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#if defined(CONFIG_NET_BUF_DEBUG) +#define NET_BUF_DBG(fmt, ...) printf("buf: %s (%p): " fmt, __func__, \ + sys_thread_self_get(), ##__VA_ARGS__) +#define NET_BUF_ERR(fmt, ...) printf("buf: %s: " fmt, __func__, ##__VA_ARGS__) +#define NET_BUF_WARN(fmt, ...) printf("buf: %s: " fmt, __func__, ##__VA_ARGS__) +#define NET_BUF_INFO(fmt, ...) printf("buf: " fmt, ##__VA_ARGS__) +#define NET_BUF_ASSERT(cond) (if (!(cond)) { \ + NET_BUF_ERR("buf: assert: '" #cond "' failed\n"); \ + }) +#else +#define NET_BUF_DBG(fmt, ...) +#define NET_BUF_ERR(fmt, ...) +#define NET_BUF_WARN(fmt, ...) +#define NET_BUF_INFO(fmt, ...) +#define NET_BUF_ASSERT(cond) +#endif /* CONFIG_net_buf_DEBUG */ + +struct net_buf *net_buf_get(struct nano_fifo *fifo, size_t reserve_head) +{ + struct net_buf *buf; + + NET_BUF_DBG("fifo %p reserve %u\n", fifo, reserve_head); + + buf = nano_fifo_get(fifo); + if (!buf) { + if (sys_execution_context_type_get() == NANO_CTX_ISR) { + NET_BUF_ERR("Failed to get free buffer\n"); + return NULL; + } + + NET_BUF_WARN("Low on buffers. Waiting (fifo %p)\n", fifo); + buf = nano_fifo_get_wait(fifo); + } + + buf->ref = 1; + buf->data = buf->__buf + reserve_head; + buf->len = 0; + + NET_BUF_DBG("buf %p fifo %p reserve %u\n", buf, fifo, reserve_head); + + return buf; +} + +void net_buf_unref(struct net_buf *buf) +{ + NET_BUF_DBG("buf %p ref %u fifo %p\n", buf, buf->ref, buf->free); + NET_BUF_ASSERT(buf->ref > 0); + + if (--buf->ref) { + return; + } + + if (buf->destroy) { + buf->destroy(buf); + } + + nano_fifo_put(buf->free, buf); +} + +struct net_buf *net_buf_ref(struct net_buf *buf) +{ + NET_BUF_DBG("buf %p (old) ref %u fifo %p\n", buf, buf->ref, buf->free); + buf->ref++; + return buf; +} + +struct net_buf *net_buf_clone(struct net_buf *buf) +{ + struct net_buf *clone; + + clone = net_buf_get(buf->free, net_buf_headroom(buf)); + if (!clone) { + return NULL; + } + + /* TODO: Add reference to the original buffer instead of copying it. */ + memcpy(net_buf_add(clone, buf->len), buf->data, buf->len); + + return clone; +} + +void *net_buf_add(struct net_buf *buf, size_t len) +{ + uint8_t *tail = net_buf_tail(buf); + + NET_BUF_DBG("buf %p len %u\n", buf, len); + + NET_BUF_ASSERT(net_buf_tailroom(buf) >= len); + + buf->len += len; + return tail; +} + +void net_buf_add_le16(struct net_buf *buf, uint16_t value) +{ + NET_BUF_DBG("buf %p value %u\n", buf, value); + + value = sys_cpu_to_le16(value); + memcpy(net_buf_add(buf, sizeof(value)), &value, sizeof(value)); +} + +void *net_buf_push(struct net_buf *buf, size_t len) +{ + NET_BUF_DBG("buf %p len %u\n", buf, len); + + NET_BUF_ASSERT(net_buf_headroom(buf) >= len); + + buf->data -= len; + buf->len += len; + return buf->data; +} + +void *net_buf_pull(struct net_buf *buf, size_t len) +{ + NET_BUF_DBG("buf %p len %u\n", buf, len); + + NET_BUF_ASSERT(buf->len >= len); + + buf->len -= len; + return buf->data += len; +} + +uint16_t net_buf_pull_le16(struct net_buf *buf) +{ + uint16_t value; + + value = UNALIGNED_GET((uint16_t *)buf->data); + net_buf_pull(buf, sizeof(value)); + + return sys_le16_to_cpu(value); +} + +size_t net_buf_headroom(struct net_buf *buf) +{ + return buf->data - buf->__buf; +} + +size_t net_buf_tailroom(struct net_buf *buf) +{ + return buf->size - net_buf_headroom(buf) - buf->len; +} diff --git a/samples/network/buf/Makefile b/samples/network/buf/Makefile new file mode 100644 index 00000000000..376a4267a36 --- /dev/null +++ b/samples/network/buf/Makefile @@ -0,0 +1,6 @@ +PLATFORM_CONFIG ?= basic_atom +MDEF_FILE = prj.mdef +KERNEL_TYPE ?= nano +CONF_FILE = prj_$(ARCH).conf + +include $(ZEPHYR_BASE)/Makefile.inc diff --git a/samples/network/buf/prj.mdef b/samples/network/buf/prj.mdef new file mode 100644 index 00000000000..5aaf534cb0a --- /dev/null +++ b/samples/network/buf/prj.mdef @@ -0,0 +1,5 @@ +% Application : Buffer test + +% TASK NAME PRIO ENTRY STACK GROUPS +% =================================================== + TASK MAIN 7 mainloop 2048 [EXE] diff --git a/samples/network/buf/prj_x86.conf b/samples/network/buf/prj_x86.conf new file mode 100644 index 00000000000..a872f078be2 --- /dev/null +++ b/samples/network/buf/prj_x86.conf @@ -0,0 +1,2 @@ +CONFIG_NET_BUF=y +CONFIG_BUF_DEBUG=y diff --git a/samples/network/buf/src/Makefile b/samples/network/buf/src/Makefile new file mode 100644 index 00000000000..cd4df1268b9 --- /dev/null +++ b/samples/network/buf/src/Makefile @@ -0,0 +1,3 @@ +ccflags-y += ${PROJECTINCLUDE} + +obj-y = main.o diff --git a/samples/network/buf/src/main.c b/samples/network/buf/src/main.c new file mode 100644 index 00000000000..979b22e4d75 --- /dev/null +++ b/samples/network/buf/src/main.c @@ -0,0 +1,89 @@ +/* main.c - Application main entry point */ + +/* + * Copyright (c) 2015 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include + +#include + +struct bt_data { + void *hci_sync; + + union { + uint16_t hci_opcode; + uint16_t acl_handle; + }; + + uint8_t type; +}; + +static int destroy_called; + +static struct nano_fifo bufs_fifo; + +static void buf_destroy(struct net_buf *buf) +{ + destroy_called++; + + if (buf->free != &bufs_fifo) { + printk("Invalid free pointer in buffer!\n"); + } +} + +static NET_BUF_POOL(bufs_pool, 22, 74, &bufs_fifo, buf_destroy, + sizeof(struct bt_data)); + +#ifdef CONFIG_MICROKERNEL +void mainloop(void) +#else +void main(void) +#endif +{ + struct net_buf *bufs[ARRAY_SIZE(bufs_pool)]; + int i; + + printk("sizeof(struct net_buf) = %u\n", sizeof(struct net_buf)); + printk("sizeof(bufs_pool) = %u\n", sizeof(bufs_pool)); + + net_buf_pool_init(bufs_pool); + + for (i = 0; i < ARRAY_SIZE(bufs_pool); i++) { + struct net_buf *buf; + + buf = net_buf_get(&bufs_fifo, 0); + if (!buf) { + printk("Failed to get buffer!\n"); + return; + } + + bufs[i] = buf; + } + + for (i = 0; i < ARRAY_SIZE(bufs_pool); i++) { + net_buf_unref(bufs[i]); + } + + if (destroy_called != ARRAY_SIZE(bufs_pool)) { + printk("Incorrect destroy callback count: %d\n", + destroy_called); + return; + } + + printk("Buffer tests passed\n"); +} diff --git a/samples/network/buf/testcase.ini b/samples/network/buf/testcase.ini new file mode 100644 index 00000000000..af4deaab9b0 --- /dev/null +++ b/samples/network/buf/testcase.ini @@ -0,0 +1,6 @@ +[test] +tags = buf +build_only = true +arch_whitelist = x86 +# Doesn't work for ia32_pci +config_whitelist = CONFIG_PLATFORM="ia32"