2002-10-19 12:59:30 +00:00
|
|
|
/*
|
2003-01-08 10:09:39 +00:00
|
|
|
* Copyright (c) 2001-2003 Swedish Institute of Computer Science.
|
2002-10-19 12:59:30 +00:00
|
|
|
* All rights reserved.
|
|
|
|
*
|
|
|
|
* Redistribution and use in source and binary forms, with or without modification,
|
|
|
|
* are permitted provided that the following conditions are met:
|
|
|
|
*
|
|
|
|
* 1. Redistributions of source code must retain the above copyright notice,
|
|
|
|
* this list of conditions and the following disclaimer.
|
|
|
|
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
|
|
|
* this list of conditions and the following disclaimer in the documentation
|
|
|
|
* and/or other materials provided with the distribution.
|
|
|
|
* 3. The name of the author may not be used to endorse or promote products
|
|
|
|
* derived from this software without specific prior written permission.
|
|
|
|
*
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
|
|
|
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
|
|
|
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
|
|
|
* SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
|
|
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
|
|
|
|
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
|
|
|
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
|
|
|
|
* OF SUCH DAMAGE.
|
|
|
|
*
|
|
|
|
* This file is part of the lwIP TCP/IP stack.
|
|
|
|
*
|
|
|
|
* Author: Adam Dunkels <adam@sics.se>
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*-----------------------------------------------------------------------------------*/
|
|
|
|
/* pbuf.c
|
|
|
|
*
|
|
|
|
* Functions for the manipulation of pbufs. The pbufs holds all packets in the
|
|
|
|
* system.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
/*-----------------------------------------------------------------------------------*/
|
2003-02-21 16:43:46 +00:00
|
|
|
#include "lwip/opt.h"
|
2002-10-19 12:59:30 +00:00
|
|
|
|
|
|
|
#include "lwip/stats.h"
|
|
|
|
|
|
|
|
#include "lwip/def.h"
|
|
|
|
#include "lwip/mem.h"
|
|
|
|
#include "lwip/memp.h"
|
|
|
|
#include "lwip/pbuf.h"
|
|
|
|
|
|
|
|
#include "lwip/sys.h"
|
|
|
|
|
|
|
|
#include "arch/perf.h"
|
|
|
|
|
|
|
|
static u8_t pbuf_pool_memory[(PBUF_POOL_SIZE * MEM_ALIGN_SIZE(PBUF_POOL_BUFSIZE + sizeof(struct pbuf)))];
|
2003-02-04 22:52:22 +00:00
|
|
|
|
2003-02-24 10:49:05 +00:00
|
|
|
#if !SYS_LIGHTWEIGHT_PROT
|
2002-10-19 12:59:30 +00:00
|
|
|
static volatile u8_t pbuf_pool_free_lock, pbuf_pool_alloc_lock;
|
|
|
|
static sys_sem_t pbuf_pool_free_sem;
|
2003-02-04 22:52:22 +00:00
|
|
|
#endif
|
|
|
|
|
2002-10-19 12:59:30 +00:00
|
|
|
static struct pbuf *pbuf_pool = NULL;
|
|
|
|
static struct pbuf *pbuf_pool_alloc_cache = NULL;
|
|
|
|
static struct pbuf *pbuf_pool_free_cache = NULL;
|
|
|
|
|
|
|
|
/*-----------------------------------------------------------------------------------*/
|
|
|
|
/* pbuf_init():
|
|
|
|
*
|
|
|
|
* Initializes the pbuf module. A large part of memory is allocated
|
|
|
|
* for holding the pool of pbufs. The size of the individual pbufs in
|
|
|
|
* the pool is given by the size parameter, and the number of pbufs in
|
|
|
|
* the pool by the num parameter.
|
|
|
|
*
|
|
|
|
* After the memory has been allocated, the pbufs are set up. The
|
|
|
|
* ->next pointer in each pbuf is set up to point to the next pbuf in
|
|
|
|
* the pool.
|
|
|
|
*/
|
|
|
|
/*-----------------------------------------------------------------------------------*/
|
|
|
|
void
|
|
|
|
pbuf_init(void)
|
|
|
|
{
|
2003-02-06 22:18:56 +00:00
|
|
|
struct pbuf *p, *q = 0;
|
2002-10-19 12:59:30 +00:00
|
|
|
u16_t i;
|
|
|
|
|
|
|
|
pbuf_pool = (struct pbuf *)&pbuf_pool_memory[0];
|
2003-02-06 22:18:56 +00:00
|
|
|
LWIP_ASSERT("pbuf_init: pool aligned", (long)pbuf_pool % MEM_ALIGNMENT == 0);
|
2002-10-19 12:59:30 +00:00
|
|
|
|
|
|
|
#ifdef PBUF_STATS
|
2002-12-18 12:49:01 +00:00
|
|
|
lwip_stats.pbuf.avail = PBUF_POOL_SIZE;
|
2002-10-19 12:59:30 +00:00
|
|
|
#endif /* PBUF_STATS */
|
|
|
|
|
|
|
|
/* Set up ->next pointers to link the pbufs of the pool together. */
|
|
|
|
p = pbuf_pool;
|
|
|
|
|
|
|
|
for(i = 0; i < PBUF_POOL_SIZE; ++i) {
|
|
|
|
p->next = (struct pbuf *)((u8_t *)p + PBUF_POOL_BUFSIZE + sizeof(struct pbuf));
|
|
|
|
p->len = p->tot_len = PBUF_POOL_BUFSIZE;
|
|
|
|
p->payload = MEM_ALIGN((void *)((u8_t *)p + sizeof(struct pbuf)));
|
|
|
|
q = p;
|
|
|
|
p = p->next;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* The ->next pointer of last pbuf is NULL to indicate that there
|
|
|
|
are no more pbufs in the pool. */
|
|
|
|
q->next = NULL;
|
|
|
|
|
2003-02-24 10:49:05 +00:00
|
|
|
#if !SYS_LIGHTWEIGHT_PROT
|
2002-10-19 12:59:30 +00:00
|
|
|
pbuf_pool_alloc_lock = 0;
|
|
|
|
pbuf_pool_free_lock = 0;
|
|
|
|
pbuf_pool_free_sem = sys_sem_new(1);
|
2003-02-04 22:52:22 +00:00
|
|
|
#endif
|
2002-10-19 12:59:30 +00:00
|
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------------------*/
|
|
|
|
/* The following two functions are only called from pbuf_alloc(). */
|
|
|
|
/*-----------------------------------------------------------------------------------*/
|
|
|
|
static struct pbuf *
|
|
|
|
pbuf_pool_alloc(void)
|
|
|
|
{
|
|
|
|
struct pbuf *p = NULL;
|
|
|
|
|
2003-02-12 22:00:18 +00:00
|
|
|
SYS_ARCH_DECL_PROTECT(old_level);
|
|
|
|
SYS_ARCH_PROTECT(old_level);
|
2002-10-19 12:59:30 +00:00
|
|
|
/* First, see if there are pbufs in the cache. */
|
|
|
|
if(pbuf_pool_alloc_cache) {
|
|
|
|
p = pbuf_pool_alloc_cache;
|
|
|
|
if(p) {
|
|
|
|
pbuf_pool_alloc_cache = p->next;
|
|
|
|
}
|
|
|
|
} else {
|
2003-02-24 10:49:05 +00:00
|
|
|
#if !SYS_LIGHTWEIGHT_PROT
|
2002-10-19 12:59:30 +00:00
|
|
|
/* Next, check the actual pbuf pool, but if the pool is locked, we
|
|
|
|
pretend to be out of buffers and return NULL. */
|
|
|
|
if(pbuf_pool_free_lock) {
|
|
|
|
#ifdef PBUF_STATS
|
2002-12-18 12:49:01 +00:00
|
|
|
++lwip_stats.pbuf.alloc_locked;
|
2002-10-19 12:59:30 +00:00
|
|
|
#endif /* PBUF_STATS */
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
pbuf_pool_alloc_lock = 1;
|
|
|
|
if(!pbuf_pool_free_lock) {
|
2003-02-04 22:52:22 +00:00
|
|
|
#endif /* SYS_LIGHTWEIGHT_PROT */
|
2002-10-19 12:59:30 +00:00
|
|
|
p = pbuf_pool;
|
|
|
|
if(p) {
|
|
|
|
pbuf_pool = p->next;
|
|
|
|
}
|
2003-02-24 10:49:05 +00:00
|
|
|
#if !SYS_LIGHTWEIGHT_PROT
|
2002-10-19 12:59:30 +00:00
|
|
|
#ifdef PBUF_STATS
|
|
|
|
} else {
|
2002-12-18 12:49:01 +00:00
|
|
|
++lwip_stats.pbuf.alloc_locked;
|
2002-10-19 12:59:30 +00:00
|
|
|
#endif /* PBUF_STATS */
|
|
|
|
}
|
|
|
|
pbuf_pool_alloc_lock = 0;
|
2003-02-04 22:52:22 +00:00
|
|
|
#endif /* SYS_LIGHTWEIGHT_PROT */
|
2002-10-19 12:59:30 +00:00
|
|
|
}
|
2003-02-04 22:52:22 +00:00
|
|
|
|
2002-10-19 12:59:30 +00:00
|
|
|
#ifdef PBUF_STATS
|
|
|
|
if(p != NULL) {
|
2002-12-18 12:49:01 +00:00
|
|
|
++lwip_stats.pbuf.used;
|
|
|
|
if(lwip_stats.pbuf.used > lwip_stats.pbuf.max) {
|
|
|
|
lwip_stats.pbuf.max = lwip_stats.pbuf.used;
|
2002-10-19 12:59:30 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif /* PBUF_STATS */
|
|
|
|
|
2003-02-12 22:00:18 +00:00
|
|
|
SYS_ARCH_UNPROTECT(old_level);
|
2002-10-19 12:59:30 +00:00
|
|
|
return p;
|
|
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------------------*/
|
|
|
|
static void
|
|
|
|
pbuf_pool_free(struct pbuf *p)
|
|
|
|
{
|
|
|
|
struct pbuf *q;
|
2003-02-12 22:00:18 +00:00
|
|
|
SYS_ARCH_DECL_PROTECT(old_level);
|
|
|
|
SYS_ARCH_PROTECT(old_level);
|
2002-10-19 12:59:30 +00:00
|
|
|
|
|
|
|
#ifdef PBUF_STATS
|
|
|
|
for(q = p; q != NULL; q = q->next) {
|
2002-12-18 12:49:01 +00:00
|
|
|
--lwip_stats.pbuf.used;
|
2002-10-19 12:59:30 +00:00
|
|
|
}
|
|
|
|
#endif /* PBUF_STATS */
|
2003-02-04 22:52:22 +00:00
|
|
|
|
2002-10-19 12:59:30 +00:00
|
|
|
if(pbuf_pool_alloc_cache == NULL) {
|
|
|
|
pbuf_pool_alloc_cache = p;
|
|
|
|
} else {
|
|
|
|
for(q = pbuf_pool_alloc_cache; q->next != NULL; q = q->next);
|
|
|
|
q->next = p;
|
|
|
|
}
|
2003-02-12 22:00:18 +00:00
|
|
|
SYS_ARCH_UNPROTECT(old_level);
|
2002-10-19 12:59:30 +00:00
|
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------------------*/
|
|
|
|
/* pbuf_alloc():
|
|
|
|
*
|
|
|
|
* Allocates a pbuf at protocol layer l. The actual memory allocated
|
|
|
|
* for the pbuf is determined by the layer at which the pbuf is
|
|
|
|
* allocated and the requested size (from the size parameter). The
|
|
|
|
* flag parameter decides how and where the pbuf should be allocated
|
|
|
|
* as follows:
|
|
|
|
*
|
|
|
|
* * PBUF_RAM: buffer memory for pbuf is allocated as one large
|
|
|
|
* chunk. This includes protocol headers as well.
|
2002-11-18 07:36:29 +00:00
|
|
|
* * PBUF_ROM: no buffer memory is allocated for the pbuf, even for
|
2002-10-19 12:59:30 +00:00
|
|
|
* protocol headers. Additional headers must be prepended
|
|
|
|
* by allocating another pbuf and chain in to the front of
|
2003-03-19 22:14:49 +00:00
|
|
|
* the ROM pbuf. It is assumed that the memory used is really
|
|
|
|
* similar to ROM in that it is immutable and will not be
|
|
|
|
* changed. Memory which is dynamic should generally not
|
|
|
|
* be attached to PBUF_ROM pbufs. Use PBUF_REF instead.
|
|
|
|
* * PBUF_REF: no buffer memory is allocated for the pbuf, even for
|
|
|
|
* protocol headers. It is assumed that the pbuf is only
|
|
|
|
* being used in a single thread. If the pbuf gets queued,
|
|
|
|
* then pbuf_unref should be called to copy the buffer.
|
2002-11-18 07:36:29 +00:00
|
|
|
* * PBUF_POOL: the pbuf is allocated as a pbuf chain, with pbufs from
|
2002-10-19 12:59:30 +00:00
|
|
|
* the pbuf pool that is allocated during pbuf_init().
|
|
|
|
*/
|
|
|
|
/*-----------------------------------------------------------------------------------*/
|
|
|
|
struct pbuf *
|
|
|
|
pbuf_alloc(pbuf_layer l, u16_t size, pbuf_flag flag)
|
|
|
|
{
|
|
|
|
struct pbuf *p, *q, *r;
|
|
|
|
u16_t offset;
|
|
|
|
s32_t rsize;
|
|
|
|
|
|
|
|
offset = 0;
|
|
|
|
switch(l) {
|
|
|
|
case PBUF_TRANSPORT:
|
|
|
|
offset += PBUF_TRANSPORT_HLEN;
|
|
|
|
/* FALLTHROUGH */
|
|
|
|
case PBUF_IP:
|
|
|
|
offset += PBUF_IP_HLEN;
|
|
|
|
/* FALLTHROUGH */
|
|
|
|
case PBUF_LINK:
|
2002-11-22 08:32:31 +00:00
|
|
|
offset += PBUF_LINK_HLEN;
|
2002-10-19 12:59:30 +00:00
|
|
|
break;
|
|
|
|
case PBUF_RAW:
|
|
|
|
break;
|
|
|
|
default:
|
2003-02-06 22:18:56 +00:00
|
|
|
LWIP_ASSERT("pbuf_alloc: bad pbuf layer", 0);
|
2002-10-19 12:59:30 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch(flag) {
|
|
|
|
case PBUF_POOL:
|
|
|
|
/* Allocate head of pbuf chain into p. */
|
|
|
|
p = pbuf_pool_alloc();
|
|
|
|
if(p == NULL) {
|
|
|
|
#ifdef PBUF_STATS
|
2002-12-18 12:49:01 +00:00
|
|
|
++lwip_stats.pbuf.err;
|
2002-10-19 12:59:30 +00:00
|
|
|
#endif /* PBUF_STATS */
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
p->next = NULL;
|
|
|
|
|
|
|
|
/* Set the payload pointer so that it points offset bytes into
|
|
|
|
pbuf data memory. */
|
|
|
|
p->payload = MEM_ALIGN((void *)((u8_t *)p + (sizeof(struct pbuf) + offset)));
|
|
|
|
|
|
|
|
/* The total length of the pbuf is the requested size. */
|
|
|
|
p->tot_len = size;
|
|
|
|
|
|
|
|
/* Set the length of the first pbuf is the chain. */
|
|
|
|
p->len = size > PBUF_POOL_BUFSIZE - offset? PBUF_POOL_BUFSIZE - offset: size;
|
|
|
|
|
|
|
|
p->flags = PBUF_FLAG_POOL;
|
|
|
|
|
|
|
|
/* Allocate the tail of the pbuf chain. */
|
|
|
|
r = p;
|
|
|
|
rsize = size - p->len;
|
|
|
|
while(rsize > 0) {
|
|
|
|
q = pbuf_pool_alloc();
|
2003-03-24 15:15:18 +00:00
|
|
|
if (q == NULL) {
|
2003-03-24 10:29:03 +00:00
|
|
|
DEBUGF(PBUF_DEBUG | 2, ("pbuf_alloc: Out of pbufs in pool.\n"));
|
2002-10-19 12:59:30 +00:00
|
|
|
#ifdef PBUF_STATS
|
2002-12-18 12:49:01 +00:00
|
|
|
++lwip_stats.pbuf.err;
|
2002-10-19 12:59:30 +00:00
|
|
|
#endif /* PBUF_STATS */
|
|
|
|
pbuf_pool_free(p);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
q->next = NULL;
|
|
|
|
r->next = q;
|
|
|
|
q->len = rsize > PBUF_POOL_BUFSIZE? PBUF_POOL_BUFSIZE: rsize;
|
|
|
|
q->flags = PBUF_FLAG_POOL;
|
|
|
|
q->payload = (void *)((u8_t *)q + sizeof(struct pbuf));
|
|
|
|
r = q;
|
|
|
|
q->ref = 1;
|
|
|
|
rsize -= PBUF_POOL_BUFSIZE;
|
|
|
|
}
|
2003-03-24 15:15:18 +00:00
|
|
|
/* end of chain */
|
2002-10-19 12:59:30 +00:00
|
|
|
r->next = NULL;
|
|
|
|
|
2003-02-06 22:18:56 +00:00
|
|
|
LWIP_ASSERT("pbuf_alloc: pbuf->payload properly aligned",
|
2002-10-19 12:59:30 +00:00
|
|
|
((u32_t)p->payload % MEM_ALIGNMENT) == 0);
|
|
|
|
break;
|
|
|
|
case PBUF_RAM:
|
|
|
|
/* If pbuf is to be allocated in RAM, allocate memory for it. */
|
|
|
|
p = mem_malloc(MEM_ALIGN_SIZE(sizeof(struct pbuf) + size + offset));
|
|
|
|
if(p == NULL) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
/* Set up internal structure of the pbuf. */
|
|
|
|
p->payload = MEM_ALIGN((void *)((u8_t *)p + sizeof(struct pbuf) + offset));
|
|
|
|
p->len = p->tot_len = size;
|
|
|
|
p->next = NULL;
|
|
|
|
p->flags = PBUF_FLAG_RAM;
|
|
|
|
|
2003-02-06 22:18:56 +00:00
|
|
|
LWIP_ASSERT("pbuf_alloc: pbuf->payload properly aligned",
|
2002-10-19 12:59:30 +00:00
|
|
|
((u32_t)p->payload % MEM_ALIGNMENT) == 0);
|
|
|
|
break;
|
2003-03-24 15:15:18 +00:00
|
|
|
/* pbuf references existing ROM payload? */
|
|
|
|
case PBUF_ROM:
|
|
|
|
/* pbuf references existing (externally allocated) RAM payload? */
|
|
|
|
case PBUF_REF:
|
|
|
|
/* only allocate memory for the pbuf structure */
|
2002-10-19 12:59:30 +00:00
|
|
|
p = memp_mallocp(MEMP_PBUF);
|
|
|
|
if(p == NULL) {
|
2003-03-24 15:15:18 +00:00
|
|
|
DEBUGF(PBUF_DEBUG | DBG_TRACE | 2, ("pbuf_alloc: Could not allocate MEMP_PBUF for PBUF_REF.\n"));
|
2002-10-19 12:59:30 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
2003-03-24 15:15:18 +00:00
|
|
|
/* caller must set this field properly, afterwards */
|
2002-10-19 12:59:30 +00:00
|
|
|
p->payload = NULL;
|
|
|
|
p->len = p->tot_len = size;
|
|
|
|
p->next = NULL;
|
2003-03-19 22:14:49 +00:00
|
|
|
if (flag == PBUF_ROM)
|
|
|
|
p->flags = PBUF_FLAG_ROM;
|
|
|
|
else
|
|
|
|
p->flags = PBUF_FLAG_REF;
|
2002-10-19 12:59:30 +00:00
|
|
|
break;
|
|
|
|
default:
|
2003-02-06 22:18:56 +00:00
|
|
|
LWIP_ASSERT("pbuf_alloc: erroneous flag", 0);
|
2002-10-19 12:59:30 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
p->ref = 1;
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------------------*/
|
|
|
|
/* pbuf_refresh():
|
|
|
|
*
|
|
|
|
* Moves free buffers from the pbuf_pool_free_cache to the pbuf_pool
|
|
|
|
* list (if possible).
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
/*-----------------------------------------------------------------------------------*/
|
|
|
|
void
|
|
|
|
pbuf_refresh(void)
|
|
|
|
{
|
|
|
|
struct pbuf *p;
|
2003-02-12 22:00:18 +00:00
|
|
|
SYS_ARCH_DECL_PROTECT(old_level);
|
|
|
|
SYS_ARCH_PROTECT(old_level);
|
|
|
|
|
2003-02-24 10:49:05 +00:00
|
|
|
#if !SYS_LIGHTWEIGHT_PROT
|
2002-10-19 12:59:30 +00:00
|
|
|
sys_sem_wait(pbuf_pool_free_sem);
|
2003-02-04 22:52:22 +00:00
|
|
|
#endif /* else SYS_LIGHTWEIGHT_PROT */
|
2002-10-19 12:59:30 +00:00
|
|
|
|
|
|
|
if(pbuf_pool_free_cache != NULL) {
|
2003-02-24 10:49:05 +00:00
|
|
|
#if !SYS_LIGHTWEIGHT_PROT
|
2002-10-19 12:59:30 +00:00
|
|
|
pbuf_pool_free_lock = 1;
|
|
|
|
if(!pbuf_pool_alloc_lock) {
|
2003-02-04 22:52:22 +00:00
|
|
|
#endif /* SYS_LIGHTWEIGHT_PROT */
|
2002-10-19 12:59:30 +00:00
|
|
|
if(pbuf_pool == NULL) {
|
|
|
|
pbuf_pool = pbuf_pool_free_cache;
|
|
|
|
} else {
|
|
|
|
for(p = pbuf_pool; p->next != NULL; p = p->next);
|
|
|
|
p->next = pbuf_pool_free_cache;
|
|
|
|
}
|
|
|
|
pbuf_pool_free_cache = NULL;
|
2003-02-24 10:49:05 +00:00
|
|
|
#if !SYS_LIGHTWEIGHT_PROT
|
2002-10-19 12:59:30 +00:00
|
|
|
#ifdef PBUF_STATS
|
|
|
|
} else {
|
2002-12-18 12:49:01 +00:00
|
|
|
++lwip_stats.pbuf.refresh_locked;
|
2002-10-19 12:59:30 +00:00
|
|
|
#endif /* PBUF_STATS */
|
|
|
|
}
|
|
|
|
|
2003-02-04 22:52:22 +00:00
|
|
|
pbuf_pool_free_lock = 0;
|
|
|
|
#endif /* SYS_LIGHTWEIGHT_PROT */
|
2002-10-19 12:59:30 +00:00
|
|
|
}
|
2003-02-12 22:00:18 +00:00
|
|
|
SYS_ARCH_UNPROTECT(old_level);
|
2003-02-24 10:49:05 +00:00
|
|
|
#if !SYS_LIGHTWEIGHT_PROT
|
2002-10-19 12:59:30 +00:00
|
|
|
sys_sem_signal(pbuf_pool_free_sem);
|
2003-02-04 22:52:22 +00:00
|
|
|
#endif /* SYS_LIGHTWEIGHT_PROT */
|
2002-10-19 12:59:30 +00:00
|
|
|
}
|
2003-02-04 22:52:22 +00:00
|
|
|
|
|
|
|
#ifdef PBUF_STATS
|
|
|
|
#define DEC_PBUF_STATS do { --lwip_stats.pbuf.used; } while (0)
|
|
|
|
#else /* PBUF_STATS */
|
|
|
|
#define DEC_PBUF_STATS
|
|
|
|
#endif /* PBUF_STATS */
|
|
|
|
|
|
|
|
#define PBUF_POOL_FAST_FREE(p) do { \
|
|
|
|
p->next = pbuf_pool_free_cache; \
|
|
|
|
pbuf_pool_free_cache = p; \
|
|
|
|
DEC_PBUF_STATS; \
|
|
|
|
} while (0)
|
|
|
|
|
2003-02-24 10:49:05 +00:00
|
|
|
#if SYS_LIGHTWEIGHT_PROT
|
2003-02-12 22:00:18 +00:00
|
|
|
#define PBUF_POOL_FREE(p) do { \
|
|
|
|
SYS_ARCH_DECL_PROTECT(old_level); \
|
|
|
|
SYS_ARCH_PROTECT(old_level); \
|
|
|
|
PBUF_POOL_FAST_FREE(p); \
|
|
|
|
SYS_ARCH_UNPROTECT(old_level); \
|
|
|
|
} while(0)
|
2003-02-04 22:52:22 +00:00
|
|
|
#else /* SYS_LIGHTWEIGHT_PROT */
|
|
|
|
#define PBUF_POOL_FREE(p) do { \
|
|
|
|
sys_sem_wait(pbuf_pool_free_sem); \
|
|
|
|
PBUF_POOL_FAST_FREE(p); \
|
|
|
|
sys_sem_signal(pbuf_pool_free_sem); \
|
2002-10-19 12:59:30 +00:00
|
|
|
} while(0)
|
2003-02-04 22:52:22 +00:00
|
|
|
#endif /* SYS_LIGHTWEIGHT_PROT */
|
2002-10-19 12:59:30 +00:00
|
|
|
/*-----------------------------------------------------------------------------------*/
|
|
|
|
/* pbuf_realloc:
|
|
|
|
*
|
|
|
|
* Reallocates the memory for a pbuf. If the pbuf is in ROM, this as
|
|
|
|
* simple as to adjust the ->tot_len and ->len fields. If the pbuf is
|
|
|
|
* a pbuf chain, as it might be with both pbufs in dynamically
|
|
|
|
* allocated RAM and for pbufs from the pbuf pool, we have to step
|
|
|
|
* through the chain until we find the new endpoint in the pbuf chain.
|
|
|
|
* Then the pbuf that is right on the endpoint is resized and any
|
|
|
|
* further pbufs on the chain are deallocated.
|
|
|
|
*/
|
|
|
|
/*-----------------------------------------------------------------------------------*/
|
|
|
|
void
|
|
|
|
pbuf_realloc(struct pbuf *p, u16_t size)
|
|
|
|
{
|
|
|
|
struct pbuf *q, *r;
|
|
|
|
u16_t rsize;
|
|
|
|
|
2003-02-06 22:18:56 +00:00
|
|
|
LWIP_ASSERT("pbuf_realloc: sane p->flags", p->flags == PBUF_FLAG_POOL ||
|
2003-03-19 22:14:49 +00:00
|
|
|
p->flags == PBUF_FLAG_ROM ||
|
|
|
|
p->flags == PBUF_FLAG_RAM ||
|
|
|
|
p->flags == PBUF_FLAG_REF);
|
2002-10-19 12:59:30 +00:00
|
|
|
|
|
|
|
|
|
|
|
if(p->tot_len <= size) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch(p->flags) {
|
|
|
|
case PBUF_FLAG_POOL:
|
|
|
|
/* First, step over any pbufs that should still be in the chain. */
|
|
|
|
rsize = size;
|
|
|
|
q = p;
|
|
|
|
while(rsize > q->len) {
|
|
|
|
rsize -= q->len;
|
|
|
|
q = q->next;
|
|
|
|
}
|
|
|
|
/* Adjust the length of the pbuf that will be halved. */
|
|
|
|
q->len = rsize;
|
|
|
|
|
|
|
|
/* And deallocate any left over pbufs. */
|
|
|
|
r = q->next;
|
|
|
|
q->next = NULL;
|
|
|
|
q = r;
|
|
|
|
while(q != NULL) {
|
|
|
|
r = q->next;
|
|
|
|
PBUF_POOL_FREE(q);
|
|
|
|
q = r;
|
|
|
|
}
|
|
|
|
break;
|
2003-03-19 22:14:49 +00:00
|
|
|
case PBUF_FLAG_ROM:
|
|
|
|
case PBUF_FLAG_REF:
|
2002-10-19 12:59:30 +00:00
|
|
|
p->len = size;
|
|
|
|
break;
|
|
|
|
case PBUF_FLAG_RAM:
|
|
|
|
/* First, step over the pbufs that should still be in the chain. */
|
|
|
|
rsize = size;
|
|
|
|
q = p;
|
|
|
|
while(rsize > q->len) {
|
|
|
|
rsize -= q->len;
|
|
|
|
q = q->next;
|
|
|
|
}
|
|
|
|
if(q->flags == PBUF_FLAG_RAM) {
|
|
|
|
/* Reallocate and adjust the length of the pbuf that will be halved. */
|
|
|
|
mem_realloc(q, (u8_t *)q->payload - (u8_t *)q + rsize);
|
|
|
|
}
|
|
|
|
|
|
|
|
q->len = rsize;
|
|
|
|
|
|
|
|
/* And deallocate any left over pbufs. */
|
|
|
|
r = q->next;
|
|
|
|
q->next = NULL;
|
|
|
|
q = r;
|
|
|
|
while(q != NULL) {
|
|
|
|
r = q->next;
|
|
|
|
pbuf_free(q);
|
|
|
|
q = r;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
p->tot_len = size;
|
|
|
|
|
|
|
|
pbuf_refresh();
|
|
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------------------*/
|
|
|
|
/* pbuf_header():
|
|
|
|
*
|
|
|
|
* Adjusts the ->payload pointer so that space for a header appears in
|
|
|
|
* the pbuf. Also, the ->tot_len and ->len fields are adjusted.
|
2003-01-30 12:38:03 +00:00
|
|
|
*
|
|
|
|
* Decreases the header size by the given amount.
|
|
|
|
* Using a negative value increases the header size.
|
2002-10-19 12:59:30 +00:00
|
|
|
*/
|
|
|
|
/*-----------------------------------------------------------------------------------*/
|
|
|
|
u8_t
|
|
|
|
pbuf_header(struct pbuf *p, s16_t header_size)
|
|
|
|
{
|
|
|
|
void *payload;
|
|
|
|
|
2003-03-19 22:14:49 +00:00
|
|
|
if(p->flags == PBUF_FLAG_ROM ||
|
|
|
|
p->flags == PBUF_FLAG_REF) {
|
2002-10-19 12:59:30 +00:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
payload = p->payload;
|
2002-12-19 09:04:58 +00:00
|
|
|
p->payload = (u8_t *)p->payload - header_size;
|
2002-10-19 12:59:30 +00:00
|
|
|
|
|
|
|
DEBUGF(PBUF_DEBUG, ("pbuf_header: old %p new %p (%d)\n", payload, p->payload, header_size));
|
|
|
|
|
|
|
|
if((u8_t *)p->payload < (u8_t *)p + sizeof(struct pbuf)) {
|
2003-03-24 10:29:03 +00:00
|
|
|
DEBUGF(PBUF_DEBUG | 2, ("pbuf_header: failed %p %p\n",
|
2002-10-19 12:59:30 +00:00
|
|
|
(u8_t *)p->payload,
|
|
|
|
(u8_t *)p + sizeof(struct pbuf)));
|
|
|
|
p->payload = payload;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
p->len += header_size;
|
|
|
|
p->tot_len += header_size;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------------------*/
|
|
|
|
/* pbuf_free():
|
|
|
|
*
|
|
|
|
* Decrements the reference count and deallocates the pbuf if the
|
|
|
|
* reference count is zero. If the pbuf is a chain all pbufs in the
|
|
|
|
* chain are deallocated.
|
|
|
|
*/
|
|
|
|
/*-----------------------------------------------------------------------------------*/
|
|
|
|
u8_t
|
|
|
|
pbuf_free(struct pbuf *p)
|
|
|
|
{
|
|
|
|
struct pbuf *q;
|
|
|
|
u8_t count = 0;
|
2003-02-12 22:00:18 +00:00
|
|
|
SYS_ARCH_DECL_PROTECT(old_level);
|
2003-03-24 15:15:18 +00:00
|
|
|
|
2002-10-19 12:59:30 +00:00
|
|
|
if(p == NULL) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
PERF_START;
|
2003-03-24 15:15:18 +00:00
|
|
|
|
2003-02-06 22:18:56 +00:00
|
|
|
LWIP_ASSERT("pbuf_free: sane flags", p->flags == PBUF_FLAG_POOL ||
|
2003-03-24 15:15:18 +00:00
|
|
|
p->flags == PBUF_FLAG_ROM ||
|
|
|
|
p->flags == PBUF_FLAG_RAM ||
|
|
|
|
p->flags == PBUF_FLAG_REF );
|
2003-02-04 22:52:22 +00:00
|
|
|
|
|
|
|
/* Since decrementing ref cannot be guarranteed to be a single machine operation
|
2003-03-24 15:15:18 +00:00
|
|
|
we must protect it. Also, the later test of ref must be protected.
|
2003-02-04 22:52:22 +00:00
|
|
|
*/
|
2003-02-12 22:00:18 +00:00
|
|
|
SYS_ARCH_PROTECT(old_level);
|
2003-03-24 15:15:18 +00:00
|
|
|
/* Decrement reference count. */
|
2003-03-21 23:01:20 +00:00
|
|
|
for (q = p; q != NULL; q = q->next) {
|
2003-03-24 15:15:18 +00:00
|
|
|
LWIP_ASSERT("pbuf_free: q->ref > 0", q->ref > 0);
|
2003-03-21 23:01:20 +00:00
|
|
|
q->ref--;
|
2003-03-24 15:15:18 +00:00
|
|
|
}
|
2002-10-19 12:59:30 +00:00
|
|
|
|
|
|
|
/* If reference count == 0, actually deallocate pbuf. */
|
|
|
|
if(p->ref == 0) {
|
2003-02-12 22:00:18 +00:00
|
|
|
SYS_ARCH_UNPROTECT(old_level);
|
2003-03-24 15:15:18 +00:00
|
|
|
|
|
|
|
while(p != NULL) {
|
|
|
|
/* this is a pbuf from the pool? */
|
|
|
|
if(p->flags == PBUF_FLAG_POOL) {
|
|
|
|
p->len = p->tot_len = PBUF_POOL_BUFSIZE;
|
|
|
|
p->payload = (void *)((u8_t *)p + sizeof(struct pbuf));
|
|
|
|
q = p->next;
|
|
|
|
PBUF_POOL_FREE(p);
|
|
|
|
/* not a pbuf from the pool */
|
2002-10-19 12:59:30 +00:00
|
|
|
}
|
2003-03-24 15:15:18 +00:00
|
|
|
else {
|
|
|
|
if(p->flags == PBUF_FLAG_ROM || p->flags == PBUF_FLAG_REF) {
|
|
|
|
q = p->next;
|
|
|
|
memp_freep(MEMP_PBUF, p);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
q = p->next;
|
|
|
|
mem_free(p);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
p = q;
|
|
|
|
/* Only free the next one in a chain if it's reference count is 0.
|
|
|
|
This allows buffer chains to have multiple headers pointing to them. */
|
|
|
|
if (p)
|
|
|
|
{
|
|
|
|
p->ref--;
|
|
|
|
if (p->ref > 0)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
++count;
|
|
|
|
}
|
|
|
|
pbuf_refresh();
|
|
|
|
}
|
2003-02-04 22:52:22 +00:00
|
|
|
else
|
2003-02-12 22:00:18 +00:00
|
|
|
SYS_ARCH_UNPROTECT(old_level);
|
2002-10-19 12:59:30 +00:00
|
|
|
|
|
|
|
PERF_STOP("pbuf_free");
|
2003-03-24 15:15:18 +00:00
|
|
|
|
2002-10-19 12:59:30 +00:00
|
|
|
return count;
|
|
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------------------*/
|
|
|
|
/* pbuf_clen():
|
|
|
|
*
|
|
|
|
* Returns the length of the pbuf chain.
|
|
|
|
*/
|
|
|
|
/*-----------------------------------------------------------------------------------*/
|
|
|
|
u8_t
|
|
|
|
pbuf_clen(struct pbuf *p)
|
|
|
|
{
|
|
|
|
u8_t len;
|
|
|
|
|
|
|
|
if(p == NULL) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
for(len = 0; p != NULL; p = p->next) {
|
|
|
|
++len;
|
|
|
|
}
|
|
|
|
return len;
|
|
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------------------*/
|
|
|
|
/* pbuf_ref():
|
|
|
|
*
|
|
|
|
* Increments the reference count of the pbuf.
|
|
|
|
*/
|
|
|
|
/*-----------------------------------------------------------------------------------*/
|
|
|
|
void
|
|
|
|
pbuf_ref(struct pbuf *p)
|
|
|
|
{
|
2003-02-12 22:00:18 +00:00
|
|
|
SYS_ARCH_DECL_PROTECT(old_level);
|
2003-02-04 22:52:22 +00:00
|
|
|
|
2002-10-19 12:59:30 +00:00
|
|
|
if(p == NULL) {
|
|
|
|
return;
|
|
|
|
}
|
2003-02-04 22:52:22 +00:00
|
|
|
|
2003-02-12 22:00:18 +00:00
|
|
|
SYS_ARCH_PROTECT(old_level);
|
2002-10-19 12:59:30 +00:00
|
|
|
++(p->ref);
|
2003-02-12 22:00:18 +00:00
|
|
|
SYS_ARCH_UNPROTECT(old_level);
|
2002-10-19 12:59:30 +00:00
|
|
|
}
|
2002-11-11 11:22:49 +00:00
|
|
|
|
2002-11-18 07:36:29 +00:00
|
|
|
/*------------------------------------------------------------------------------*/
|
2002-11-11 11:22:49 +00:00
|
|
|
/* pbuf_ref_chain():
|
|
|
|
*
|
|
|
|
* Increments the reference count of all pbufs in a chain.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
pbuf_ref_chain(struct pbuf *p)
|
|
|
|
{
|
2003-02-12 22:00:18 +00:00
|
|
|
SYS_ARCH_DECL_PROTECT(old_level);
|
|
|
|
SYS_ARCH_PROTECT(old_level);
|
2003-02-04 22:52:22 +00:00
|
|
|
|
2003-02-12 22:00:18 +00:00
|
|
|
while (p != NULL) {
|
|
|
|
p->ref++;
|
|
|
|
p=p->next;
|
|
|
|
}
|
2003-02-04 22:52:22 +00:00
|
|
|
|
2003-02-12 22:00:18 +00:00
|
|
|
SYS_ARCH_UNPROTECT(old_level);
|
2002-11-11 11:22:49 +00:00
|
|
|
}
|
2002-10-19 12:59:30 +00:00
|
|
|
/*-----------------------------------------------------------------------------------*/
|
|
|
|
/* pbuf_chain():
|
|
|
|
*
|
|
|
|
* Chains the two pbufs h and t together. The ->tot_len field of the
|
|
|
|
* first pbuf (h) is adjusted.
|
|
|
|
*/
|
|
|
|
/*-----------------------------------------------------------------------------------*/
|
|
|
|
void
|
|
|
|
pbuf_chain(struct pbuf *h, struct pbuf *t)
|
|
|
|
{
|
|
|
|
struct pbuf *p;
|
|
|
|
|
|
|
|
if(t == NULL) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
for(p = h; p->next != NULL; p = p->next);
|
|
|
|
p->next = t;
|
|
|
|
h->tot_len += t->tot_len;
|
|
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------------------*/
|
|
|
|
/* pbuf_dechain():
|
|
|
|
*
|
|
|
|
* Adjusts the ->tot_len field of the pbuf and returns the tail (if
|
|
|
|
* any) of the pbuf chain.
|
|
|
|
*/
|
|
|
|
/*-----------------------------------------------------------------------------------*/
|
|
|
|
struct pbuf *
|
|
|
|
pbuf_dechain(struct pbuf *p)
|
|
|
|
{
|
|
|
|
struct pbuf *q;
|
|
|
|
|
|
|
|
q = p->next;
|
|
|
|
if (q != NULL) {
|
|
|
|
q->tot_len = p->tot_len - p->len;
|
|
|
|
}
|
|
|
|
p->tot_len = p->len;
|
|
|
|
p->next = NULL;
|
|
|
|
return q;
|
|
|
|
}
|
2003-03-19 22:14:49 +00:00
|
|
|
|
2003-03-24 15:15:18 +00:00
|
|
|
/**
|
|
|
|
*
|
|
|
|
* Replace any PBUF_REF pbufs of a chain into PBUF_POOL/RAM buffers.
|
|
|
|
*
|
|
|
|
* Go through pbuf chain and replace any PBUF_REF buffers with PBUF_POOL
|
|
|
|
* (or PBUF_RAM) buffers.
|
|
|
|
*
|
|
|
|
* Used to queue packets on behalf of the lwIP stack, such as ARP based
|
|
|
|
* queueing.
|
|
|
|
*
|
|
|
|
* @param f Head of pbuf chain to process
|
|
|
|
*
|
|
|
|
* @return Pointer to new head of pbuf chain.
|
|
|
|
*/
|
2002-11-18 09:51:48 +00:00
|
|
|
struct pbuf *
|
|
|
|
pbuf_unref(struct pbuf *f)
|
|
|
|
{
|
2003-03-24 13:27:12 +00:00
|
|
|
struct pbuf *p, *prev, *top;
|
2003-03-24 15:15:18 +00:00
|
|
|
LWIP_ASSERT("pbuf_unref: f != NULL", f != NULL);
|
|
|
|
DEBUGF(PBUF_DEBUG | DBG_TRACE | 3, ("pbuf_unref(%p)\n", (void*)f));
|
2003-03-19 22:14:49 +00:00
|
|
|
|
2003-03-24 13:27:12 +00:00
|
|
|
prev = NULL;
|
2003-03-19 22:14:49 +00:00
|
|
|
p = f;
|
|
|
|
top = f;
|
2003-03-24 15:15:18 +00:00
|
|
|
/* iterate through pbuf chain */
|
2003-03-19 22:14:49 +00:00
|
|
|
do
|
2002-11-18 09:51:48 +00:00
|
|
|
{
|
2003-03-19 22:14:49 +00:00
|
|
|
/* pbuf is of type PBUF_REF? */
|
|
|
|
if (p->flags == PBUF_FLAG_REF)
|
|
|
|
{
|
2003-03-24 15:15:18 +00:00
|
|
|
/* the replacement pbuf */
|
2003-03-24 13:27:12 +00:00
|
|
|
struct pbuf *q;
|
|
|
|
q = NULL;
|
2003-03-24 15:15:18 +00:00
|
|
|
DEBUGF(PBUF_DEBUG | DBG_TRACE, ("pbuf_unref: encountered PBUF_REF %p\n", (void *)p));
|
2003-03-19 22:14:49 +00:00
|
|
|
/* allocate a pbuf (w/ payload) fully in RAM */
|
|
|
|
/* PBUF_POOL buffers are faster if we can use them */
|
2003-03-24 13:27:12 +00:00
|
|
|
if (p->len <= PBUF_POOL_BUFSIZE) {
|
2003-03-19 22:14:49 +00:00
|
|
|
q = pbuf_alloc(PBUF_RAW, p->len, PBUF_POOL);
|
2003-03-24 15:15:18 +00:00
|
|
|
if (q == NULL) DEBUGF(PBUF_DEBUG | DBG_TRACE | 2, ("pbuf_unref: Could not allocate PBUF_RAW\n"));
|
2003-03-24 13:27:12 +00:00
|
|
|
}
|
|
|
|
/* no (large enough) PBUF_POOL was available? retry with PBUF_RAM */
|
|
|
|
if (q == NULL) {
|
2003-03-19 22:14:49 +00:00
|
|
|
q = pbuf_alloc(PBUF_RAW, p->len, PBUF_RAM);
|
2003-03-24 15:15:18 +00:00
|
|
|
if (q == NULL) DEBUGF(PBUF_DEBUG | DBG_TRACE | 2, ("pbuf_unref: Could not allocate PBUF_POOL\n"));
|
2003-03-24 13:27:12 +00:00
|
|
|
}
|
|
|
|
if (q != NULL)
|
2003-03-19 22:14:49 +00:00
|
|
|
{
|
2003-03-24 15:15:18 +00:00
|
|
|
/* copy successor */
|
2003-03-19 22:14:49 +00:00
|
|
|
q->next = p->next;
|
2003-03-24 13:27:12 +00:00
|
|
|
if (prev != NULL)
|
2003-03-19 22:14:49 +00:00
|
|
|
/* Break chain and insert new pbuf instead */
|
|
|
|
prev->next = q;
|
|
|
|
else
|
|
|
|
top = q;
|
|
|
|
p->next = NULL;
|
2003-03-24 13:27:12 +00:00
|
|
|
/* copy pbuf payload */
|
2003-03-19 22:14:49 +00:00
|
|
|
memcpy(q->payload, p->payload, p->len);
|
|
|
|
q->tot_len = p->tot_len;
|
|
|
|
q->len = p->len;
|
2003-03-24 15:15:18 +00:00
|
|
|
/* do not copy ref, since someone else might be using the old buffer */
|
|
|
|
/* pbuf is not freed, as this is the responsibility of the application */
|
|
|
|
DEBUGF(PBUF_DEBUG, ("pbuf_unref: replaced PBUF_REF %p with %q\n", (void *)p, (void *)q));
|
2003-03-19 22:14:49 +00:00
|
|
|
p = q;
|
|
|
|
}
|
|
|
|
else
|
2002-11-18 09:51:48 +00:00
|
|
|
{
|
2003-03-19 22:14:49 +00:00
|
|
|
/* deallocate chain */
|
|
|
|
pbuf_free(top);
|
2003-03-24 15:15:18 +00:00
|
|
|
DEBUGF(PBUF_DEBUG | 2, ("pbuf_unref: failed to allocate replacement pbuf for %p\n", (void *)p));
|
2003-03-19 22:14:49 +00:00
|
|
|
return NULL;
|
2002-11-18 09:51:48 +00:00
|
|
|
}
|
|
|
|
}
|
2003-03-24 15:15:18 +00:00
|
|
|
else {
|
|
|
|
DEBUGF(PBUF_DEBUG | DBG_TRACE | 1, ("pbuf_unref: not PBUF_REF"));
|
|
|
|
}
|
|
|
|
|
2003-03-19 22:14:49 +00:00
|
|
|
prev = p;
|
|
|
|
p = p->next;
|
|
|
|
} while (p);
|
2003-03-24 15:15:18 +00:00
|
|
|
DEBUGF(PBUF_DEBUG | DBG_TRACE | 1, ("pbuf_unref: end of chain reached."));
|
2003-03-19 22:14:49 +00:00
|
|
|
|
|
|
|
return top;
|
2002-11-22 08:32:31 +00:00
|
|
|
}
|