2016-05-09 14:12:14 +03:00
|
|
|
/** @file
|
|
|
|
@brief Network buffers for IP stack
|
|
|
|
|
|
|
|
Network data is passed between components using nbuf.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Copyright (c) 2016 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.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#if defined(CONFIG_NETWORK_IP_STACK_DEBUG_NET_BUF)
|
|
|
|
#define SYS_LOG_DOMAIN "net/nbuf"
|
|
|
|
#define SYS_LOG_LEVEL SYS_LOG_LEVEL_DEBUG
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include <nanokernel.h>
|
|
|
|
#include <toolchain.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <stdint.h>
|
|
|
|
|
|
|
|
#include <net/net_ip.h>
|
|
|
|
#include <net/buf.h>
|
|
|
|
#include <net/nbuf.h>
|
|
|
|
|
|
|
|
#include <net/net_core.h>
|
|
|
|
|
|
|
|
#if !defined(NET_DEBUG_NBUFS)
|
|
|
|
#undef NET_DBG
|
|
|
|
#define NET_DBG(...)
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include "net_private.h"
|
|
|
|
|
|
|
|
/* Available (free) buffers queue */
|
|
|
|
#if !defined(NBUF_RX_COUNT)
|
|
|
|
#if CONFIG_NET_NBUF_RX_COUNT > 0
|
|
|
|
#define NBUF_RX_COUNT CONFIG_NET_NBUF_RX_COUNT
|
|
|
|
#else
|
|
|
|
#define NBUF_RX_COUNT 1
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if !defined(NBUF_TX_COUNT)
|
|
|
|
#if CONFIG_NET_NBUF_TX_COUNT > 0
|
|
|
|
#define NBUF_TX_COUNT CONFIG_NET_NBUF_TX_COUNT
|
|
|
|
#else
|
|
|
|
#define NBUF_TX_COUNT 1
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if !defined(NBUF_DATA_COUNT)
|
|
|
|
#if CONFIG_NET_NBUF_DATA_COUNT > 0
|
|
|
|
#define NBUF_DATA_COUNT CONFIG_NET_NBUF_DATA_COUNT
|
|
|
|
#else
|
|
|
|
#define NBUF_DATA_COUNT 13
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if !defined(NBUF_DATA_LEN)
|
|
|
|
#if CONFIG_NET_NBUF_DATA_SIZE > 0
|
|
|
|
#define NBUF_DATA_LEN CONFIG_NET_NBUF_DATA_SIZE
|
|
|
|
#else
|
|
|
|
#define NBUF_DATA_LEN 128
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if defined(CONFIG_NET_TCP)
|
|
|
|
#define APP_PROTO_LEN NET_TCPH_LEN
|
|
|
|
#else
|
|
|
|
#if defined(CONFIG_NET_UDP)
|
|
|
|
#define APP_PROTO_LEN NET_UDPH_LEN
|
|
|
|
#else
|
|
|
|
#define APP_PROTO_LEN 0
|
|
|
|
#endif /* UDP */
|
|
|
|
#endif /* TCP */
|
|
|
|
|
|
|
|
#if defined(CONFIG_NET_IPV6)
|
|
|
|
#define IP_PROTO_LEN NET_IPV6H_LEN
|
|
|
|
#else
|
|
|
|
#if defined(CONFIG_NET_IPV4)
|
|
|
|
#define IP_PROTO_LEN NET_IPV4H_LEN
|
2016-06-07 12:32:53 +03:00
|
|
|
#else
|
|
|
|
#error "Either IPv6 or IPv4 needs to be selected."
|
2016-05-09 14:12:14 +03:00
|
|
|
#endif /* IPv4 */
|
|
|
|
#endif /* IPv6 */
|
|
|
|
|
|
|
|
#define EXTRA_PROTO_LEN NET_ICMPH_LEN
|
|
|
|
|
|
|
|
/* Make sure that IP + TCP/UDP header fit into one
|
|
|
|
* fragment. This makes possible to cast a protocol header
|
|
|
|
* struct into memory area.
|
|
|
|
*/
|
|
|
|
#if NBUF_DATA_LEN < (IP_PROTO_LEN + APP_PROTO_LEN)
|
|
|
|
#if defined(STRING2)
|
|
|
|
#undef STRING2
|
|
|
|
#endif
|
|
|
|
#if defined(STRING)
|
|
|
|
#undef STRING
|
|
|
|
#endif
|
|
|
|
#define STRING2(x) #x
|
|
|
|
#define STRING(x) STRING2(x)
|
|
|
|
#pragma message "Data len " STRING(NBUF_DATA_LEN)
|
|
|
|
#pragma message "Minimum len " STRING(IP_PROTO_LEN + APP_PROTO_LEN)
|
|
|
|
#error "Too small net_buf fragment size"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if defined(NET_DEBUG_NBUFS)
|
|
|
|
static int num_free_rx_bufs = NBUF_RX_COUNT;
|
|
|
|
static int num_free_tx_bufs = NBUF_TX_COUNT;
|
|
|
|
static int num_free_data_bufs = NBUF_DATA_COUNT;
|
|
|
|
|
|
|
|
static inline void dec_free_rx_bufs(struct net_buf *buf)
|
|
|
|
{
|
|
|
|
if (!buf) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
num_free_rx_bufs--;
|
|
|
|
if (num_free_rx_bufs < 0) {
|
|
|
|
NET_DBG("*** ERROR *** Invalid RX buffer count.");
|
|
|
|
num_free_rx_bufs = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void inc_free_rx_bufs(struct net_buf *buf)
|
|
|
|
{
|
|
|
|
if (!buf) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (num_free_rx_bufs > NBUF_RX_COUNT) {
|
|
|
|
num_free_rx_bufs = NBUF_RX_COUNT;
|
|
|
|
} else {
|
|
|
|
num_free_rx_bufs++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void dec_free_tx_bufs(struct net_buf *buf)
|
|
|
|
{
|
|
|
|
if (!buf) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
num_free_tx_bufs--;
|
|
|
|
if (num_free_tx_bufs < 0) {
|
|
|
|
NET_DBG("*** ERROR *** Invalid TX buffer count.");
|
|
|
|
num_free_tx_bufs = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void inc_free_tx_bufs(struct net_buf *buf)
|
|
|
|
{
|
|
|
|
if (!buf) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (num_free_tx_bufs > NBUF_TX_COUNT) {
|
|
|
|
num_free_tx_bufs = NBUF_TX_COUNT;
|
|
|
|
} else {
|
|
|
|
num_free_tx_bufs++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void dec_free_data_bufs(struct net_buf *buf)
|
|
|
|
{
|
|
|
|
if (!buf) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
num_free_data_bufs--;
|
|
|
|
if (num_free_data_bufs < 0) {
|
|
|
|
NET_DBG("*** ERROR *** Invalid data buffer count.");
|
|
|
|
num_free_data_bufs = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void inc_free_data_bufs(struct net_buf *buf)
|
|
|
|
{
|
|
|
|
if (!buf) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (num_free_data_bufs > NBUF_DATA_COUNT) {
|
|
|
|
num_free_data_bufs = NBUF_DATA_COUNT;
|
|
|
|
} else {
|
|
|
|
num_free_data_bufs++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline int get_frees(enum net_nbuf_type type)
|
|
|
|
{
|
|
|
|
switch (type) {
|
|
|
|
case NET_NBUF_RX:
|
|
|
|
return num_free_rx_bufs;
|
|
|
|
case NET_NBUF_TX:
|
|
|
|
return num_free_tx_bufs;
|
|
|
|
case NET_NBUF_DATA:
|
|
|
|
return num_free_data_bufs;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0xffffffff;
|
|
|
|
}
|
|
|
|
|
|
|
|
#define inc_free_rx_bufs_func inc_free_rx_bufs
|
|
|
|
#define inc_free_tx_bufs_func inc_free_tx_bufs
|
|
|
|
#define inc_free_data_bufs_func inc_free_data_bufs
|
|
|
|
|
|
|
|
#else /* NET_DEBUG_NBUFS */
|
|
|
|
#define dec_free_rx_bufs(...)
|
|
|
|
#define inc_free_rx_bufs(...)
|
|
|
|
#define dec_free_tx_bufs(...)
|
|
|
|
#define inc_free_tx_bufs(...)
|
|
|
|
#define dec_free_data_bufs(...)
|
|
|
|
#define inc_free_data_bufs(...)
|
|
|
|
#define inc_free_rx_bufs_func(...)
|
|
|
|
#define inc_free_tx_bufs_func(...)
|
|
|
|
#define inc_free_data_bufs_func(...)
|
|
|
|
#endif /* NET_DEBUG_NBUFS */
|
|
|
|
|
|
|
|
static struct nano_fifo free_rx_bufs;
|
|
|
|
static struct nano_fifo free_tx_bufs;
|
|
|
|
static struct nano_fifo free_data_bufs;
|
|
|
|
|
|
|
|
static inline void free_rx_bufs_func(struct net_buf *buf)
|
|
|
|
{
|
|
|
|
inc_free_rx_bufs_func(buf);
|
|
|
|
|
|
|
|
nano_fifo_put(buf->free, buf);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void free_tx_bufs_func(struct net_buf *buf)
|
|
|
|
{
|
|
|
|
inc_free_tx_bufs_func(buf);
|
|
|
|
|
|
|
|
nano_fifo_put(buf->free, buf);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void free_data_bufs_func(struct net_buf *buf)
|
|
|
|
{
|
|
|
|
inc_free_data_bufs_func(buf);
|
|
|
|
|
|
|
|
nano_fifo_put(buf->free, buf);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* The RX and TX pools do not store any data. Only bearer / protocol
|
|
|
|
* related data is stored here.
|
|
|
|
*/
|
|
|
|
static NET_BUF_POOL(rx_buffers, NBUF_RX_COUNT, 0, \
|
|
|
|
&free_rx_bufs, free_rx_bufs_func, \
|
|
|
|
sizeof(struct net_nbuf));
|
|
|
|
static NET_BUF_POOL(tx_buffers, NBUF_TX_COUNT, 0, \
|
|
|
|
&free_tx_bufs, free_tx_bufs_func, \
|
|
|
|
sizeof(struct net_nbuf));
|
|
|
|
|
|
|
|
/* The data fragment pool is for storing network data.
|
|
|
|
* This pool does not need any user data because the rx/tx pool already
|
|
|
|
* contains all the protocol/bearer specific information.
|
|
|
|
*/
|
|
|
|
static NET_BUF_POOL(data_buffers, NBUF_DATA_COUNT, \
|
|
|
|
NBUF_DATA_LEN, &free_data_bufs, \
|
|
|
|
free_data_bufs_func, 0);
|
|
|
|
|
|
|
|
static inline const char *type2str(enum net_nbuf_type type)
|
|
|
|
{
|
|
|
|
switch (type) {
|
|
|
|
case NET_NBUF_RX:
|
|
|
|
return "RX";
|
|
|
|
case NET_NBUF_TX:
|
|
|
|
return "TX";
|
|
|
|
case NET_NBUF_DATA:
|
|
|
|
return "DATA";
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef NET_DEBUG_NBUFS
|
|
|
|
static struct net_buf *net_nbuf_get_reserve_debug(enum net_nbuf_type type,
|
|
|
|
uint16_t reserve_head,
|
|
|
|
const char *caller,
|
|
|
|
int line)
|
|
|
|
#else
|
|
|
|
static struct net_buf *net_nbuf_get_reserve(enum net_nbuf_type type,
|
|
|
|
uint16_t reserve_head)
|
|
|
|
#endif
|
|
|
|
{
|
|
|
|
struct net_buf *buf = NULL;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The reserve_head variable in the function will tell
|
|
|
|
* the size of the link layer headers if there are any.
|
|
|
|
*/
|
|
|
|
switch (type) {
|
|
|
|
case NET_NBUF_RX:
|
|
|
|
buf = net_buf_get(&free_rx_bufs, 0);
|
2016-06-09 08:40:37 +03:00
|
|
|
if (!buf) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
NET_ASSERT(buf->ref);
|
|
|
|
|
2016-05-09 14:12:14 +03:00
|
|
|
dec_free_rx_bufs(buf);
|
|
|
|
net_nbuf_type(buf) = type;
|
|
|
|
break;
|
|
|
|
case NET_NBUF_TX:
|
|
|
|
buf = net_buf_get(&free_tx_bufs, 0);
|
2016-06-09 08:40:37 +03:00
|
|
|
if (!buf) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
NET_ASSERT(buf->ref);
|
|
|
|
|
2016-05-09 14:12:14 +03:00
|
|
|
dec_free_tx_bufs(buf);
|
|
|
|
net_nbuf_type(buf) = type;
|
|
|
|
break;
|
|
|
|
case NET_NBUF_DATA:
|
2016-06-07 13:06:35 +03:00
|
|
|
buf = net_buf_get(&free_data_bufs, 0);
|
2016-06-09 08:40:37 +03:00
|
|
|
if (!buf) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
NET_ASSERT(buf->ref);
|
2016-06-07 13:06:35 +03:00
|
|
|
|
|
|
|
/* The buf->data will point to the start of the L3
|
|
|
|
* header (like IPv4 or IPv6 packet header) after the
|
|
|
|
* add() and pull().
|
|
|
|
*/
|
|
|
|
net_buf_add(buf, reserve_head);
|
|
|
|
net_buf_pull(buf, reserve_head);
|
|
|
|
|
2016-05-09 14:12:14 +03:00
|
|
|
dec_free_data_bufs(buf);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
NET_ERR("Invalid type %d for net_buf", type);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!buf) {
|
|
|
|
#if defined(CONFIG_NETWORK_IP_STACK_DEBUG_NET_BUF)
|
|
|
|
#define PRINT_CYCLE (30 * sys_clock_ticks_per_sec)
|
|
|
|
static uint32_t next_print;
|
|
|
|
uint32_t curr = sys_tick_get_32();
|
|
|
|
|
|
|
|
if (!next_print || (next_print < curr &&
|
|
|
|
(!((curr - next_print) > PRINT_CYCLE)))) {
|
|
|
|
uint32_t new_print;
|
|
|
|
|
|
|
|
#ifdef NET_DEBUG_NBUFS
|
|
|
|
NET_ERR("Failed to get free %s buffer (%s():%d)",
|
|
|
|
type2str(type), caller, line);
|
|
|
|
#else
|
|
|
|
NET_ERR("Failed to get free %s buffer",
|
|
|
|
type2str(type));
|
|
|
|
#endif /* NET_DEBUG_NBUFS */
|
|
|
|
|
|
|
|
new_print = curr + PRINT_CYCLE;
|
|
|
|
if (new_print > curr) {
|
|
|
|
next_print = new_print;
|
|
|
|
} else {
|
|
|
|
/* Overflow */
|
|
|
|
next_print = PRINT_CYCLE -
|
|
|
|
(0xffffffff - curr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
NET_BUF_CHECK_IF_NOT_IN_USE(buf, buf->ref + 1);
|
|
|
|
|
|
|
|
#ifdef NET_DEBUG_NBUFS
|
|
|
|
NET_DBG("%s [%d] buf %p reserve %u ref %d (%s():%d)",
|
|
|
|
type2str(type), get_frees(type),
|
|
|
|
buf, reserve_head, buf->ref, caller, line);
|
|
|
|
#else
|
|
|
|
NET_DBG("%s buf %p reserve %u ref %d",
|
|
|
|
type2str(type), buf, reserve_head, buf->ref);
|
|
|
|
#endif
|
|
|
|
return buf;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef NET_DEBUG_NBUFS
|
|
|
|
struct net_buf *net_nbuf_get_reserve_rx_debug(uint16_t reserve_head,
|
|
|
|
const char *caller, int line)
|
|
|
|
#else
|
|
|
|
struct net_buf *net_nbuf_get_reserve_rx(uint16_t reserve_head)
|
|
|
|
#endif
|
|
|
|
{
|
|
|
|
#ifdef NET_DEBUG_NBUFS
|
|
|
|
return net_nbuf_get_reserve_debug(NET_NBUF_RX, reserve_head,
|
|
|
|
caller, line);
|
|
|
|
#else
|
|
|
|
return net_nbuf_get_reserve(NET_NBUF_RX, reserve_head);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef NET_DEBUG_NBUFS
|
|
|
|
struct net_buf *net_nbuf_get_reserve_tx_debug(uint16_t reserve_head,
|
|
|
|
const char *caller, int line)
|
|
|
|
#else
|
|
|
|
struct net_buf *net_nbuf_get_reserve_tx(uint16_t reserve_head)
|
|
|
|
#endif
|
|
|
|
{
|
|
|
|
#ifdef NET_DEBUG_NBUFS
|
|
|
|
return net_nbuf_get_reserve_debug(NET_NBUF_TX, reserve_head,
|
|
|
|
caller, line);
|
|
|
|
#else
|
|
|
|
return net_nbuf_get_reserve(NET_NBUF_TX, reserve_head);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef NET_DEBUG_NBUFS
|
|
|
|
struct net_buf *net_nbuf_get_reserve_data_debug(uint16_t reserve_head,
|
|
|
|
const char *caller, int line)
|
|
|
|
#else
|
|
|
|
struct net_buf *net_nbuf_get_reserve_data(uint16_t reserve_head)
|
|
|
|
#endif
|
|
|
|
{
|
|
|
|
#ifdef NET_DEBUG_NBUFS
|
|
|
|
return net_nbuf_get_reserve_debug(NET_NBUF_DATA, reserve_head,
|
|
|
|
caller, line);
|
|
|
|
#else
|
|
|
|
return net_nbuf_get_reserve(NET_NBUF_DATA, reserve_head);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef NET_DEBUG_NBUFS
|
|
|
|
static struct net_buf *net_nbuf_get_debug(enum net_nbuf_type type,
|
|
|
|
struct net_context *context,
|
|
|
|
const char *caller, int line)
|
|
|
|
#else
|
|
|
|
static struct net_buf *net_nbuf_get(enum net_nbuf_type type,
|
|
|
|
struct net_context *context)
|
|
|
|
#endif
|
|
|
|
{
|
|
|
|
struct net_buf *buf;
|
|
|
|
int16_t reserve = 0;
|
|
|
|
|
|
|
|
if (type == NET_NBUF_DATA) {
|
|
|
|
reserve = NBUF_DATA_LEN -
|
|
|
|
net_if_get_mtu(net_context_get_iface(context));
|
|
|
|
if (reserve < 0) {
|
|
|
|
NET_ERR("MTU %d bigger than fragment size %d",
|
|
|
|
net_if_get_mtu(net_context_get_iface(context)),
|
|
|
|
NBUF_DATA_LEN);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef NET_DEBUG_NBUFS
|
|
|
|
buf = net_nbuf_get_reserve_debug(type, (uint16_t)reserve, caller, line);
|
|
|
|
#else
|
|
|
|
buf = net_nbuf_get_reserve(type, (uint16_t)reserve);
|
|
|
|
#endif
|
|
|
|
if (!buf) {
|
|
|
|
return buf;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (type != NET_NBUF_DATA) {
|
|
|
|
net_nbuf_context(buf) = context;
|
|
|
|
net_nbuf_ll_reserve(buf) = (uint16_t)reserve;
|
|
|
|
}
|
|
|
|
|
|
|
|
return buf;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef NET_DEBUG_NBUFS
|
|
|
|
struct net_buf *net_nbuf_get_rx_debug(struct net_context *context,
|
|
|
|
const char *caller, int line)
|
|
|
|
#else
|
|
|
|
struct net_buf *net_nbuf_get_rx(struct net_context *context)
|
|
|
|
#endif
|
|
|
|
{
|
|
|
|
#ifdef NET_DEBUG_NBUFS
|
|
|
|
return net_nbuf_get_debug(NET_NBUF_RX, context, caller, line);
|
|
|
|
#else
|
|
|
|
return net_nbuf_get(NET_NBUF_RX, context);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef NET_DEBUG_NBUFS
|
|
|
|
struct net_buf *net_nbuf_get_tx_debug(struct net_context *context,
|
|
|
|
const char *caller, int line)
|
|
|
|
#else
|
|
|
|
struct net_buf *net_nbuf_get_tx(struct net_context *context)
|
|
|
|
#endif
|
|
|
|
{
|
|
|
|
#ifdef NET_DEBUG_NBUFS
|
|
|
|
return net_nbuf_get_debug(NET_NBUF_TX, context, caller, line);
|
|
|
|
#else
|
|
|
|
return net_nbuf_get(NET_NBUF_TX, context);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef NET_DEBUG_NBUFS
|
|
|
|
struct net_buf *net_nbuf_get_data_debug(struct net_context *context,
|
|
|
|
const char *caller, int line)
|
|
|
|
#else
|
|
|
|
struct net_buf *net_nbuf_get_data(struct net_context *context)
|
|
|
|
#endif
|
|
|
|
{
|
|
|
|
#ifdef NET_DEBUG_NBUFS
|
|
|
|
return net_nbuf_get_debug(NET_NBUF_DATA, context, caller, line);
|
|
|
|
#else
|
|
|
|
return net_nbuf_get(NET_NBUF_DATA, context);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef NET_DEBUG_NBUFS
|
|
|
|
void net_nbuf_unref_debug(struct net_buf *buf, const char *caller, int line)
|
|
|
|
#else
|
|
|
|
void net_nbuf_unref(struct net_buf *buf)
|
|
|
|
#endif
|
|
|
|
{
|
|
|
|
struct net_buf *frag;
|
|
|
|
|
|
|
|
if (!buf) {
|
|
|
|
#ifdef NET_DEBUG_NBUFS
|
|
|
|
NET_DBG("*** ERROR *** buf %p (%s():%d)", buf, caller, line);
|
|
|
|
#else
|
|
|
|
NET_DBG("*** ERROR *** buf %p", buf);
|
|
|
|
#endif
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!buf->ref) {
|
|
|
|
#ifdef NET_DEBUG_NBUFS
|
|
|
|
NET_DBG("*** ERROR *** buf %p is freed already (%s():%d)",
|
|
|
|
buf, caller, line);
|
|
|
|
#else
|
|
|
|
NET_DBG("*** ERROR *** buf %p is freed already", buf);
|
|
|
|
#endif
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (buf->user_data_size) {
|
|
|
|
#ifdef NET_DEBUG_NBUFS
|
|
|
|
NET_DBG("%s [%d] buf %p ref %d frags %p (%s():%d)",
|
|
|
|
type2str(net_nbuf_type(buf)),
|
|
|
|
get_frees(net_nbuf_type(buf)),
|
|
|
|
buf, buf->ref - 1, buf->frags, caller, line);
|
|
|
|
#else
|
|
|
|
NET_DBG("%s buf %p ref %d frags %p",
|
|
|
|
type2str(net_nbuf_type(buf)), buf, buf->ref - 1,
|
|
|
|
buf->frags);
|
|
|
|
#endif
|
|
|
|
} else {
|
|
|
|
#ifdef NET_DEBUG_NBUFS
|
2016-06-01 10:40:28 +03:00
|
|
|
NET_DBG("%s [%d] buf %p ref %d frags %p (%s():%d)",
|
2016-05-09 14:12:14 +03:00
|
|
|
type2str(NET_NBUF_DATA),
|
2016-06-01 10:40:28 +03:00
|
|
|
get_frees(NET_NBUF_DATA),
|
2016-05-09 14:12:14 +03:00
|
|
|
buf, buf->ref - 1, buf->frags, caller, line);
|
|
|
|
#else
|
|
|
|
NET_DBG("%s buf %p ref %d frags %p",
|
|
|
|
type2str(NET_NBUF_DATA), buf, buf->ref - 1,
|
|
|
|
buf->frags);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Remove the fragment list elements first, otherwise we
|
2016-05-23 17:33:14 +03:00
|
|
|
* have a memory leak. But only if we are to be remove the
|
|
|
|
* buffer.
|
2016-05-09 14:12:14 +03:00
|
|
|
*/
|
|
|
|
frag = buf->frags;
|
2016-05-23 17:33:14 +03:00
|
|
|
while (!(buf->ref - 1) && frag) {
|
2016-05-09 14:12:14 +03:00
|
|
|
struct net_buf *next = frag->frags;
|
|
|
|
|
|
|
|
net_buf_frag_del(buf, frag);
|
|
|
|
|
|
|
|
#ifdef NET_DEBUG_NBUFS
|
2016-06-01 10:40:28 +03:00
|
|
|
NET_DBG("%s [%d] buf %p ref %d frags %p (%s():%d)",
|
2016-05-09 14:12:14 +03:00
|
|
|
type2str(NET_NBUF_DATA),
|
2016-06-01 10:40:28 +03:00
|
|
|
get_frees(NET_NBUF_DATA),
|
2016-05-09 14:12:14 +03:00
|
|
|
frag, frag->ref - 1, frag->frags, caller, line);
|
|
|
|
#else
|
|
|
|
NET_DBG("%s buf %p ref %d frags %p",
|
|
|
|
type2str(NET_NBUF_DATA), frag, frag->ref - 1,
|
|
|
|
frag->frags);
|
|
|
|
#endif
|
|
|
|
net_buf_unref(frag);
|
|
|
|
|
|
|
|
frag = next;
|
|
|
|
}
|
|
|
|
|
|
|
|
net_buf_unref(buf);
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef NET_DEBUG_NBUFS
|
|
|
|
struct net_buf *net_nbuf_ref_debug(struct net_buf *buf, const char *caller,
|
|
|
|
int line)
|
|
|
|
#else
|
|
|
|
struct net_buf *net_nbuf_ref(struct net_buf *buf)
|
|
|
|
#endif
|
|
|
|
{
|
|
|
|
if (!buf) {
|
|
|
|
#ifdef NET_DEBUG_NBUFS
|
|
|
|
NET_DBG("*** ERROR *** buf %p (%s():%d)", buf, caller, line);
|
|
|
|
#else
|
|
|
|
NET_DBG("*** ERROR *** buf %p", buf);
|
|
|
|
#endif
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (buf->user_data_size) {
|
|
|
|
#ifdef NET_DEBUG_NBUFS
|
|
|
|
NET_DBG("%s [%d] buf %p ref %d (%s():%d)",
|
|
|
|
type2str(net_nbuf_type(buf)),
|
|
|
|
get_frees(net_nbuf_type(buf)),
|
|
|
|
buf, buf->ref + 1, caller, line);
|
|
|
|
#else
|
|
|
|
NET_DBG("%s buf %p ref %d",
|
|
|
|
type2str(net_nbuf_type(buf)), buf, buf->ref + 1);
|
|
|
|
#endif
|
|
|
|
} else {
|
|
|
|
#ifdef NET_DEBUG_NBUFS
|
|
|
|
NET_DBG("%s buf %p ref %d (%s():%d)",
|
|
|
|
type2str(NET_NBUF_DATA),
|
|
|
|
buf, buf->ref + 1, caller, line);
|
|
|
|
#else
|
|
|
|
NET_DBG("%s buf %p ref %d",
|
|
|
|
type2str(NET_NBUF_DATA), buf, buf->ref + 1);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
return net_buf_ref(buf);
|
|
|
|
}
|
|
|
|
|
2016-06-14 09:25:52 +03:00
|
|
|
struct net_buf *net_nbuf_copy(struct net_buf *orig, size_t amount,
|
|
|
|
size_t reserve)
|
|
|
|
{
|
|
|
|
uint16_t ll_reserve = net_buf_headroom(orig);
|
|
|
|
struct net_buf *frag, *first;
|
|
|
|
|
|
|
|
if (orig->user_data_size) {
|
|
|
|
NET_ERR("Buffer %p is not a data fragment", orig);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
frag = net_nbuf_get_reserve_data(ll_reserve);
|
|
|
|
|
|
|
|
if (reserve > net_buf_tailroom(frag)) {
|
|
|
|
NET_ERR("Reserve %d is too long, max is %d",
|
|
|
|
reserve, net_buf_tailroom(frag));
|
|
|
|
net_nbuf_unref(frag);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
net_buf_add(frag, reserve);
|
|
|
|
|
|
|
|
first = frag;
|
|
|
|
|
|
|
|
NET_DBG("Copying frag %p with %d bytes and reserving %d bytes",
|
|
|
|
first, amount, reserve);
|
|
|
|
|
|
|
|
if (!orig->len) {
|
|
|
|
/* No data in the first fragment in the original message */
|
|
|
|
NET_DBG("Original buffer empty!");
|
|
|
|
return frag;
|
|
|
|
}
|
|
|
|
|
|
|
|
while (orig && amount) {
|
|
|
|
int left_len = net_buf_tailroom(frag);
|
|
|
|
int copy_len;
|
|
|
|
|
|
|
|
if (amount > orig->len) {
|
|
|
|
copy_len = orig->len;
|
|
|
|
} else {
|
|
|
|
copy_len = amount;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((copy_len - left_len) >= 0) {
|
|
|
|
/* Just copy the data from original fragment
|
|
|
|
* to new fragment. The old data will fit the
|
|
|
|
* new fragment and there could be some space
|
|
|
|
* left in the new fragment.
|
|
|
|
*/
|
|
|
|
amount -= left_len;
|
|
|
|
|
|
|
|
memcpy(net_buf_add(frag, left_len), orig->data,
|
|
|
|
left_len);
|
|
|
|
|
|
|
|
if (!net_buf_tailroom(frag)) {
|
|
|
|
/* There is no space left in copy fragment.
|
|
|
|
* We must allocate a new one.
|
|
|
|
*/
|
|
|
|
struct net_buf *new_frag =
|
|
|
|
net_nbuf_get_reserve_data(ll_reserve);
|
|
|
|
|
|
|
|
net_buf_frag_add(frag, new_frag);
|
|
|
|
|
|
|
|
frag = new_frag;
|
|
|
|
}
|
|
|
|
|
|
|
|
net_buf_pull(orig, left_len);
|
|
|
|
|
|
|
|
continue;
|
|
|
|
} else {
|
|
|
|
/* We should be at the end of the original buf
|
|
|
|
* fragment list.
|
|
|
|
*/
|
|
|
|
amount -= copy_len;
|
|
|
|
|
|
|
|
memcpy(net_buf_add(frag, copy_len), orig->data,
|
|
|
|
copy_len);
|
|
|
|
net_buf_pull(orig, copy_len);
|
|
|
|
}
|
|
|
|
|
|
|
|
orig = orig->frags;
|
|
|
|
}
|
|
|
|
|
|
|
|
return first;
|
|
|
|
}
|
|
|
|
|
2016-06-15 14:31:41 +03:00
|
|
|
bool net_nbuf_is_compact(struct net_buf *buf)
|
|
|
|
{
|
|
|
|
struct net_buf *last;
|
|
|
|
size_t total = 0, calc;
|
|
|
|
int count = 0;
|
|
|
|
|
|
|
|
last = NULL;
|
|
|
|
|
|
|
|
if (buf->user_data_size) {
|
|
|
|
/* Skip the first element that does not contain any data.
|
|
|
|
*/
|
|
|
|
buf = buf->frags;
|
|
|
|
}
|
|
|
|
|
|
|
|
while (buf) {
|
|
|
|
total += buf->len;
|
|
|
|
count++;
|
|
|
|
|
|
|
|
last = buf;
|
|
|
|
buf = buf->frags;
|
|
|
|
}
|
|
|
|
|
|
|
|
NET_ASSERT(last);
|
|
|
|
|
|
|
|
if (!last) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
calc = count * last->size - net_buf_tailroom(last) -
|
|
|
|
count * net_buf_headroom(last);
|
|
|
|
|
|
|
|
if (total == calc) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
NET_DBG("Not compacted total %u real %u", total, calc);
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct net_buf *net_nbuf_compact(struct net_buf *buf)
|
|
|
|
{
|
|
|
|
struct net_buf *first, *prev;
|
|
|
|
|
|
|
|
first = buf;
|
|
|
|
|
|
|
|
if (buf->user_data_size) {
|
|
|
|
NET_DBG("Buffer %p is not a data fragment", buf);
|
|
|
|
buf = buf->frags;
|
|
|
|
}
|
|
|
|
|
|
|
|
prev = NULL;
|
|
|
|
|
|
|
|
NET_DBG("Compacting data to buf %p", first);
|
|
|
|
|
|
|
|
while (buf) {
|
|
|
|
if (buf->frags) {
|
|
|
|
/* Copy amount of data from next fragment to this
|
|
|
|
* fragment.
|
|
|
|
*/
|
|
|
|
size_t copy_len;
|
|
|
|
|
|
|
|
copy_len = buf->frags->len;
|
|
|
|
if (copy_len > net_buf_tailroom(buf)) {
|
|
|
|
copy_len = net_buf_tailroom(buf);
|
|
|
|
}
|
|
|
|
|
|
|
|
memcpy(net_buf_tail(buf), buf->frags->data, copy_len);
|
|
|
|
net_buf_add(buf, copy_len);
|
|
|
|
|
|
|
|
memmove(buf->frags->data,
|
|
|
|
buf->frags->data + copy_len,
|
|
|
|
buf->frags->len - copy_len);
|
|
|
|
|
|
|
|
buf->frags->len -= copy_len;
|
|
|
|
|
|
|
|
/* Is there any more space in this fragment */
|
|
|
|
if (net_buf_tailroom(buf)) {
|
|
|
|
struct net_buf *frag;
|
|
|
|
|
|
|
|
/* There is. This also means that the next
|
|
|
|
* fragment is empty as otherwise we could
|
|
|
|
* not have copied all data.
|
|
|
|
*/
|
|
|
|
frag = buf->frags;
|
|
|
|
|
|
|
|
/* Remove next fragment as there is no
|
|
|
|
* data in it any more.
|
|
|
|
*/
|
|
|
|
net_buf_frag_del(buf, buf->frags);
|
|
|
|
|
|
|
|
net_nbuf_unref(frag);
|
|
|
|
|
|
|
|
/* Then check next fragment */
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (!buf->len) {
|
|
|
|
/* Remove the last fragment because there is no
|
|
|
|
* data in it.
|
|
|
|
*/
|
|
|
|
NET_ASSERT_INFO(prev,
|
|
|
|
"First element cannot be deleted!");
|
|
|
|
|
|
|
|
net_buf_frag_del(prev, buf);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
prev = buf;
|
|
|
|
buf = buf->frags;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* If the buf exists, then it is the last fragment and can be removed.
|
|
|
|
*/
|
|
|
|
if (buf) {
|
|
|
|
net_nbuf_unref(buf);
|
|
|
|
|
|
|
|
if (prev) {
|
|
|
|
prev->frags = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return first;
|
|
|
|
}
|
|
|
|
|
2016-06-15 14:35:17 +03:00
|
|
|
struct net_buf *net_nbuf_push(struct net_buf *parent,
|
|
|
|
struct net_buf *buf,
|
|
|
|
size_t amount)
|
|
|
|
{
|
|
|
|
struct net_buf *frag;
|
|
|
|
|
|
|
|
NET_ASSERT_INFO(amount > 3,
|
|
|
|
"Amount %d very small and not recommended", amount);
|
|
|
|
|
|
|
|
if (amount > buf->len) {
|
|
|
|
NET_DBG("Cannot move amount %d because the buf "
|
|
|
|
"length is only %d bytes", amount, buf->len);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
frag = net_nbuf_get_reserve_data(net_buf_headroom(buf));
|
|
|
|
|
|
|
|
net_buf_add(frag, amount);
|
|
|
|
|
|
|
|
if (parent) {
|
|
|
|
net_buf_frag_insert(parent, frag);
|
|
|
|
} else {
|
|
|
|
net_buf_frag_insert(frag, buf);
|
|
|
|
parent = frag;
|
|
|
|
}
|
|
|
|
|
|
|
|
return net_nbuf_compact(parent);
|
|
|
|
}
|
|
|
|
|
2016-05-09 14:12:14 +03:00
|
|
|
void net_nbuf_init(void)
|
|
|
|
{
|
|
|
|
NET_DBG("Allocating %d RX (%d bytes), %d TX (%d bytes) "
|
|
|
|
"and %d data (%d bytes) buffers",
|
|
|
|
NBUF_RX_COUNT, sizeof(rx_buffers),
|
|
|
|
NBUF_TX_COUNT, sizeof(tx_buffers),
|
|
|
|
NBUF_DATA_COUNT, sizeof(data_buffers));
|
|
|
|
|
|
|
|
net_buf_pool_init(rx_buffers);
|
|
|
|
net_buf_pool_init(tx_buffers);
|
|
|
|
net_buf_pool_init(data_buffers);
|
|
|
|
}
|