1195972f6Sopenharmony_ci/** 2195972f6Sopenharmony_ci * @file 3195972f6Sopenharmony_ci * Packet buffer management 4195972f6Sopenharmony_ci */ 5195972f6Sopenharmony_ci 6195972f6Sopenharmony_ci/** 7195972f6Sopenharmony_ci * @defgroup pbuf Packet buffers (PBUF) 8195972f6Sopenharmony_ci * @ingroup infrastructure 9195972f6Sopenharmony_ci * 10195972f6Sopenharmony_ci * Packets are built from the pbuf data structure. It supports dynamic 11195972f6Sopenharmony_ci * memory allocation for packet contents or can reference externally 12195972f6Sopenharmony_ci * managed packet contents both in RAM and ROM. Quick allocation for 13195972f6Sopenharmony_ci * incoming packets is provided through pools with fixed sized pbufs. 14195972f6Sopenharmony_ci * 15195972f6Sopenharmony_ci * A packet may span over multiple pbufs, chained as a singly linked 16195972f6Sopenharmony_ci * list. This is called a "pbuf chain". 17195972f6Sopenharmony_ci * 18195972f6Sopenharmony_ci * Multiple packets may be queued, also using this singly linked list. 19195972f6Sopenharmony_ci * This is called a "packet queue". 20195972f6Sopenharmony_ci * 21195972f6Sopenharmony_ci * So, a packet queue consists of one or more pbuf chains, each of 22195972f6Sopenharmony_ci * which consist of one or more pbufs. CURRENTLY, PACKET QUEUES ARE 23195972f6Sopenharmony_ci * NOT SUPPORTED!!! Use helper structs to queue multiple packets. 24195972f6Sopenharmony_ci * 25195972f6Sopenharmony_ci * The differences between a pbuf chain and a packet queue are very 26195972f6Sopenharmony_ci * precise but subtle. 27195972f6Sopenharmony_ci * 28195972f6Sopenharmony_ci * The last pbuf of a packet has a ->tot_len field that equals the 29195972f6Sopenharmony_ci * ->len field. It can be found by traversing the list. If the last 30195972f6Sopenharmony_ci * pbuf of a packet has a ->next field other than NULL, more packets 31195972f6Sopenharmony_ci * are on the queue. 32195972f6Sopenharmony_ci * 33195972f6Sopenharmony_ci * Therefore, looping through a pbuf of a single packet, has an 34195972f6Sopenharmony_ci * loop end condition (tot_len == p->len), NOT (next == NULL). 35195972f6Sopenharmony_ci * 36195972f6Sopenharmony_ci * Example of custom pbuf usage: @ref zerocopyrx 37195972f6Sopenharmony_ci */ 38195972f6Sopenharmony_ci 39195972f6Sopenharmony_ci/* 40195972f6Sopenharmony_ci * Copyright (c) 2001-2004 Swedish Institute of Computer Science. 41195972f6Sopenharmony_ci * All rights reserved. 42195972f6Sopenharmony_ci * 43195972f6Sopenharmony_ci * Redistribution and use in source and binary forms, with or without modification, 44195972f6Sopenharmony_ci * are permitted provided that the following conditions are met: 45195972f6Sopenharmony_ci * 46195972f6Sopenharmony_ci * 1. Redistributions of source code must retain the above copyright notice, 47195972f6Sopenharmony_ci * this list of conditions and the following disclaimer. 48195972f6Sopenharmony_ci * 2. Redistributions in binary form must reproduce the above copyright notice, 49195972f6Sopenharmony_ci * this list of conditions and the following disclaimer in the documentation 50195972f6Sopenharmony_ci * and/or other materials provided with the distribution. 51195972f6Sopenharmony_ci * 3. The name of the author may not be used to endorse or promote products 52195972f6Sopenharmony_ci * derived from this software without specific prior written permission. 53195972f6Sopenharmony_ci * 54195972f6Sopenharmony_ci * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 55195972f6Sopenharmony_ci * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 56195972f6Sopenharmony_ci * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 57195972f6Sopenharmony_ci * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 58195972f6Sopenharmony_ci * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 59195972f6Sopenharmony_ci * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 60195972f6Sopenharmony_ci * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 61195972f6Sopenharmony_ci * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 62195972f6Sopenharmony_ci * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 63195972f6Sopenharmony_ci * OF SUCH DAMAGE. 64195972f6Sopenharmony_ci * 65195972f6Sopenharmony_ci * This file is part of the lwIP TCP/IP stack. 66195972f6Sopenharmony_ci * 67195972f6Sopenharmony_ci * Author: Adam Dunkels <adam@sics.se> 68195972f6Sopenharmony_ci * 69195972f6Sopenharmony_ci */ 70195972f6Sopenharmony_ci 71195972f6Sopenharmony_ci#include "lwip/opt.h" 72195972f6Sopenharmony_ci 73195972f6Sopenharmony_ci#include "lwip/pbuf.h" 74195972f6Sopenharmony_ci#include "lwip/stats.h" 75195972f6Sopenharmony_ci#include "lwip/def.h" 76195972f6Sopenharmony_ci#include "lwip/mem.h" 77195972f6Sopenharmony_ci#include "lwip/memp.h" 78195972f6Sopenharmony_ci#include "lwip/sys.h" 79195972f6Sopenharmony_ci#include "lwip/netif.h" 80195972f6Sopenharmony_ci#if LWIP_TCP && TCP_QUEUE_OOSEQ 81195972f6Sopenharmony_ci#include "lwip/priv/tcp_priv.h" 82195972f6Sopenharmony_ci#endif 83195972f6Sopenharmony_ci#if LWIP_CHECKSUM_ON_COPY 84195972f6Sopenharmony_ci#include "lwip/inet_chksum.h" 85195972f6Sopenharmony_ci#endif 86195972f6Sopenharmony_ci 87195972f6Sopenharmony_ci#include <string.h> 88195972f6Sopenharmony_ci 89195972f6Sopenharmony_ci#define SIZEOF_STRUCT_PBUF LWIP_MEM_ALIGN_SIZE(sizeof(struct pbuf)) 90195972f6Sopenharmony_ci/* Since the pool is created in memp, PBUF_POOL_BUFSIZE will be automatically 91195972f6Sopenharmony_ci aligned there. Therefore, PBUF_POOL_BUFSIZE_ALIGNED can be used here. */ 92195972f6Sopenharmony_ci#define PBUF_POOL_BUFSIZE_ALIGNED LWIP_MEM_ALIGN_SIZE(PBUF_POOL_BUFSIZE) 93195972f6Sopenharmony_ci 94195972f6Sopenharmony_cistatic const struct pbuf * 95195972f6Sopenharmony_cipbuf_skip_const(const struct pbuf *in, u16_t in_offset, u16_t *out_offset); 96195972f6Sopenharmony_ci 97195972f6Sopenharmony_ci#if !LWIP_TCP || !TCP_QUEUE_OOSEQ || !PBUF_POOL_FREE_OOSEQ 98195972f6Sopenharmony_ci#define PBUF_POOL_IS_EMPTY() 99195972f6Sopenharmony_ci#else /* !LWIP_TCP || !TCP_QUEUE_OOSEQ || !PBUF_POOL_FREE_OOSEQ */ 100195972f6Sopenharmony_ci 101195972f6Sopenharmony_ci#if !NO_SYS 102195972f6Sopenharmony_ci#ifndef PBUF_POOL_FREE_OOSEQ_QUEUE_CALL 103195972f6Sopenharmony_ci#include "lwip/tcpip.h" 104195972f6Sopenharmony_ci#define PBUF_POOL_FREE_OOSEQ_QUEUE_CALL() do { \ 105195972f6Sopenharmony_ci if (tcpip_try_callback(pbuf_free_ooseq_callback, NULL) != ERR_OK) { \ 106195972f6Sopenharmony_ci SYS_ARCH_PROTECT(old_level); \ 107195972f6Sopenharmony_ci pbuf_free_ooseq_pending = 0; \ 108195972f6Sopenharmony_ci SYS_ARCH_UNPROTECT(old_level); \ 109195972f6Sopenharmony_ci } } while(0) 110195972f6Sopenharmony_ci#endif /* PBUF_POOL_FREE_OOSEQ_QUEUE_CALL */ 111195972f6Sopenharmony_ci#endif /* !NO_SYS */ 112195972f6Sopenharmony_ci 113195972f6Sopenharmony_civolatile u8_t pbuf_free_ooseq_pending; 114195972f6Sopenharmony_ci#define PBUF_POOL_IS_EMPTY() pbuf_pool_is_empty() 115195972f6Sopenharmony_ci 116195972f6Sopenharmony_ci/** 117195972f6Sopenharmony_ci * Attempt to reclaim some memory from queued out-of-sequence TCP segments 118195972f6Sopenharmony_ci * if we run out of pool pbufs. It's better to give priority to new packets 119195972f6Sopenharmony_ci * if we're running out. 120195972f6Sopenharmony_ci * 121195972f6Sopenharmony_ci * This must be done in the correct thread context therefore this function 122195972f6Sopenharmony_ci * can only be used with NO_SYS=0 and through tcpip_callback. 123195972f6Sopenharmony_ci */ 124195972f6Sopenharmony_ci#if !NO_SYS 125195972f6Sopenharmony_cistatic 126195972f6Sopenharmony_ci#endif /* !NO_SYS */ 127195972f6Sopenharmony_civoid 128195972f6Sopenharmony_cipbuf_free_ooseq(void) 129195972f6Sopenharmony_ci{ 130195972f6Sopenharmony_ci struct tcp_pcb *pcb; 131195972f6Sopenharmony_ci SYS_ARCH_SET(pbuf_free_ooseq_pending, 0); 132195972f6Sopenharmony_ci 133195972f6Sopenharmony_ci for (pcb = tcp_active_pcbs; NULL != pcb; pcb = pcb->next) { 134195972f6Sopenharmony_ci if (pcb->ooseq != NULL) { 135195972f6Sopenharmony_ci /** Free the ooseq pbufs of one PCB only */ 136195972f6Sopenharmony_ci LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_free_ooseq: freeing out-of-sequence pbufs\n")); 137195972f6Sopenharmony_ci tcp_free_ooseq(pcb); 138195972f6Sopenharmony_ci return; 139195972f6Sopenharmony_ci } 140195972f6Sopenharmony_ci } 141195972f6Sopenharmony_ci} 142195972f6Sopenharmony_ci 143195972f6Sopenharmony_ci#if !NO_SYS 144195972f6Sopenharmony_ci/** 145195972f6Sopenharmony_ci * Just a callback function for tcpip_callback() that calls pbuf_free_ooseq(). 146195972f6Sopenharmony_ci */ 147195972f6Sopenharmony_cistatic void 148195972f6Sopenharmony_cipbuf_free_ooseq_callback(void *arg) 149195972f6Sopenharmony_ci{ 150195972f6Sopenharmony_ci LWIP_UNUSED_ARG(arg); 151195972f6Sopenharmony_ci pbuf_free_ooseq(); 152195972f6Sopenharmony_ci} 153195972f6Sopenharmony_ci#endif /* !NO_SYS */ 154195972f6Sopenharmony_ci 155195972f6Sopenharmony_ci/** Queue a call to pbuf_free_ooseq if not already queued. */ 156195972f6Sopenharmony_cistatic void 157195972f6Sopenharmony_cipbuf_pool_is_empty(void) 158195972f6Sopenharmony_ci{ 159195972f6Sopenharmony_ci#ifndef PBUF_POOL_FREE_OOSEQ_QUEUE_CALL 160195972f6Sopenharmony_ci SYS_ARCH_SET(pbuf_free_ooseq_pending, 1); 161195972f6Sopenharmony_ci#else /* PBUF_POOL_FREE_OOSEQ_QUEUE_CALL */ 162195972f6Sopenharmony_ci u8_t queued; 163195972f6Sopenharmony_ci SYS_ARCH_DECL_PROTECT(old_level); 164195972f6Sopenharmony_ci SYS_ARCH_PROTECT(old_level); 165195972f6Sopenharmony_ci queued = pbuf_free_ooseq_pending; 166195972f6Sopenharmony_ci pbuf_free_ooseq_pending = 1; 167195972f6Sopenharmony_ci SYS_ARCH_UNPROTECT(old_level); 168195972f6Sopenharmony_ci 169195972f6Sopenharmony_ci if (!queued) { 170195972f6Sopenharmony_ci /* queue a call to pbuf_free_ooseq if not already queued */ 171195972f6Sopenharmony_ci PBUF_POOL_FREE_OOSEQ_QUEUE_CALL(); 172195972f6Sopenharmony_ci } 173195972f6Sopenharmony_ci#endif /* PBUF_POOL_FREE_OOSEQ_QUEUE_CALL */ 174195972f6Sopenharmony_ci} 175195972f6Sopenharmony_ci#endif /* !LWIP_TCP || !TCP_QUEUE_OOSEQ || !PBUF_POOL_FREE_OOSEQ */ 176195972f6Sopenharmony_ci 177195972f6Sopenharmony_ci/* Initialize members of struct pbuf after allocation */ 178195972f6Sopenharmony_cistatic void 179195972f6Sopenharmony_cipbuf_init_alloced_pbuf(struct pbuf *p, void *payload, u16_t tot_len, u16_t len, pbuf_type type, u8_t flags) 180195972f6Sopenharmony_ci{ 181195972f6Sopenharmony_ci p->next = NULL; 182195972f6Sopenharmony_ci p->payload = payload; 183195972f6Sopenharmony_ci p->tot_len = tot_len; 184195972f6Sopenharmony_ci p->len = len; 185195972f6Sopenharmony_ci p->type_internal = (u8_t)type; 186195972f6Sopenharmony_ci p->flags = flags; 187195972f6Sopenharmony_ci p->ref = 1; 188195972f6Sopenharmony_ci p->if_idx = NETIF_NO_INDEX; 189195972f6Sopenharmony_ci} 190195972f6Sopenharmony_ci 191195972f6Sopenharmony_ci/** 192195972f6Sopenharmony_ci * @ingroup pbuf 193195972f6Sopenharmony_ci * Allocates a pbuf of the given type (possibly a chain for PBUF_POOL type). 194195972f6Sopenharmony_ci * 195195972f6Sopenharmony_ci * The actual memory allocated for the pbuf is determined by the 196195972f6Sopenharmony_ci * layer at which the pbuf is allocated and the requested size 197195972f6Sopenharmony_ci * (from the size parameter). 198195972f6Sopenharmony_ci * 199195972f6Sopenharmony_ci * @param layer header size 200195972f6Sopenharmony_ci * @param length size of the pbuf's payload 201195972f6Sopenharmony_ci * @param type this parameter decides how and where the pbuf 202195972f6Sopenharmony_ci * should be allocated as follows: 203195972f6Sopenharmony_ci * 204195972f6Sopenharmony_ci * - PBUF_RAM: buffer memory for pbuf is allocated as one large 205195972f6Sopenharmony_ci * chunk. This includes protocol headers as well. 206195972f6Sopenharmony_ci * - PBUF_ROM: no buffer memory is allocated for the pbuf, even for 207195972f6Sopenharmony_ci * protocol headers. Additional headers must be prepended 208195972f6Sopenharmony_ci * by allocating another pbuf and chain in to the front of 209195972f6Sopenharmony_ci * the ROM pbuf. It is assumed that the memory used is really 210195972f6Sopenharmony_ci * similar to ROM in that it is immutable and will not be 211195972f6Sopenharmony_ci * changed. Memory which is dynamic should generally not 212195972f6Sopenharmony_ci * be attached to PBUF_ROM pbufs. Use PBUF_REF instead. 213195972f6Sopenharmony_ci * - PBUF_REF: no buffer memory is allocated for the pbuf, even for 214195972f6Sopenharmony_ci * protocol headers. It is assumed that the pbuf is only 215195972f6Sopenharmony_ci * being used in a single thread. If the pbuf gets queued, 216195972f6Sopenharmony_ci * then pbuf_take should be called to copy the buffer. 217195972f6Sopenharmony_ci * - PBUF_POOL: the pbuf is allocated as a pbuf chain, with pbufs from 218195972f6Sopenharmony_ci * the pbuf pool that is allocated during pbuf_init(). 219195972f6Sopenharmony_ci * 220195972f6Sopenharmony_ci * @return the allocated pbuf. If multiple pbufs where allocated, this 221195972f6Sopenharmony_ci * is the first pbuf of a pbuf chain. 222195972f6Sopenharmony_ci */ 223195972f6Sopenharmony_cistruct pbuf * 224195972f6Sopenharmony_cipbuf_alloc(pbuf_layer layer, u16_t length, pbuf_type type) 225195972f6Sopenharmony_ci{ 226195972f6Sopenharmony_ci struct pbuf *p; 227195972f6Sopenharmony_ci u16_t offset = (u16_t)layer; 228195972f6Sopenharmony_ci LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_alloc(length=%"U16_F")\n", length)); 229195972f6Sopenharmony_ci 230195972f6Sopenharmony_ci switch (type) { 231195972f6Sopenharmony_ci case PBUF_REF: /* fall through */ 232195972f6Sopenharmony_ci case PBUF_ROM: 233195972f6Sopenharmony_ci p = pbuf_alloc_reference(NULL, length, type); 234195972f6Sopenharmony_ci break; 235195972f6Sopenharmony_ci case PBUF_POOL: { 236195972f6Sopenharmony_ci struct pbuf *q, *last; 237195972f6Sopenharmony_ci u16_t rem_len; /* remaining length */ 238195972f6Sopenharmony_ci p = NULL; 239195972f6Sopenharmony_ci last = NULL; 240195972f6Sopenharmony_ci rem_len = length; 241195972f6Sopenharmony_ci do { 242195972f6Sopenharmony_ci u16_t qlen; 243195972f6Sopenharmony_ci q = (struct pbuf *)memp_malloc(MEMP_PBUF_POOL); 244195972f6Sopenharmony_ci if (q == NULL) { 245195972f6Sopenharmony_ci PBUF_POOL_IS_EMPTY(); 246195972f6Sopenharmony_ci /* free chain so far allocated */ 247195972f6Sopenharmony_ci if (p) { 248195972f6Sopenharmony_ci pbuf_free(p); 249195972f6Sopenharmony_ci } 250195972f6Sopenharmony_ci /* bail out unsuccessfully */ 251195972f6Sopenharmony_ci return NULL; 252195972f6Sopenharmony_ci } 253195972f6Sopenharmony_ci qlen = LWIP_MIN(rem_len, (u16_t)(PBUF_POOL_BUFSIZE_ALIGNED - LWIP_MEM_ALIGN_SIZE(offset))); 254195972f6Sopenharmony_ci pbuf_init_alloced_pbuf(q, LWIP_MEM_ALIGN((void *)((u8_t *)q + SIZEOF_STRUCT_PBUF + offset)), 255195972f6Sopenharmony_ci rem_len, qlen, type, 0); 256195972f6Sopenharmony_ci LWIP_ASSERT("pbuf_alloc: pbuf q->payload properly aligned", 257195972f6Sopenharmony_ci ((mem_ptr_t)q->payload % MEM_ALIGNMENT) == 0); 258195972f6Sopenharmony_ci LWIP_ASSERT("PBUF_POOL_BUFSIZE must be bigger than MEM_ALIGNMENT", 259195972f6Sopenharmony_ci (PBUF_POOL_BUFSIZE_ALIGNED - LWIP_MEM_ALIGN_SIZE(offset)) > 0 ); 260195972f6Sopenharmony_ci if (p == NULL) { 261195972f6Sopenharmony_ci /* allocated head of pbuf chain (into p) */ 262195972f6Sopenharmony_ci p = q; 263195972f6Sopenharmony_ci } else { 264195972f6Sopenharmony_ci /* make previous pbuf point to this pbuf */ 265195972f6Sopenharmony_ci last->next = q; 266195972f6Sopenharmony_ci } 267195972f6Sopenharmony_ci last = q; 268195972f6Sopenharmony_ci rem_len = (u16_t)(rem_len - qlen); 269195972f6Sopenharmony_ci offset = 0; 270195972f6Sopenharmony_ci } while (rem_len > 0); 271195972f6Sopenharmony_ci break; 272195972f6Sopenharmony_ci } 273195972f6Sopenharmony_ci case PBUF_RAM: { 274195972f6Sopenharmony_ci mem_size_t payload_len = (mem_size_t)(LWIP_MEM_ALIGN_SIZE(offset) + LWIP_MEM_ALIGN_SIZE(length)); 275195972f6Sopenharmony_ci mem_size_t alloc_len = (mem_size_t)(LWIP_MEM_ALIGN_SIZE(SIZEOF_STRUCT_PBUF) + payload_len); 276195972f6Sopenharmony_ci 277195972f6Sopenharmony_ci /* bug #50040: Check for integer overflow when calculating alloc_len */ 278195972f6Sopenharmony_ci if ((payload_len < LWIP_MEM_ALIGN_SIZE(length)) || 279195972f6Sopenharmony_ci (alloc_len < LWIP_MEM_ALIGN_SIZE(length))) { 280195972f6Sopenharmony_ci return NULL; 281195972f6Sopenharmony_ci } 282195972f6Sopenharmony_ci 283195972f6Sopenharmony_ci /* If pbuf is to be allocated in RAM, allocate memory for it. */ 284195972f6Sopenharmony_ci p = (struct pbuf *)mem_malloc(alloc_len); 285195972f6Sopenharmony_ci if (p == NULL) { 286195972f6Sopenharmony_ci return NULL; 287195972f6Sopenharmony_ci } 288195972f6Sopenharmony_ci pbuf_init_alloced_pbuf(p, LWIP_MEM_ALIGN((void *)((u8_t *)p + SIZEOF_STRUCT_PBUF + offset)), 289195972f6Sopenharmony_ci length, length, type, 0); 290195972f6Sopenharmony_ci LWIP_ASSERT("pbuf_alloc: pbuf->payload properly aligned", 291195972f6Sopenharmony_ci ((mem_ptr_t)p->payload % MEM_ALIGNMENT) == 0); 292195972f6Sopenharmony_ci break; 293195972f6Sopenharmony_ci } 294195972f6Sopenharmony_ci default: 295195972f6Sopenharmony_ci LWIP_ASSERT("pbuf_alloc: erroneous type", 0); 296195972f6Sopenharmony_ci return NULL; 297195972f6Sopenharmony_ci } 298195972f6Sopenharmony_ci LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_alloc(length=%"U16_F") == %p\n", length, (void *)p)); 299195972f6Sopenharmony_ci return p; 300195972f6Sopenharmony_ci} 301195972f6Sopenharmony_ci 302195972f6Sopenharmony_ci/** 303195972f6Sopenharmony_ci * @ingroup pbuf 304195972f6Sopenharmony_ci * Allocates a pbuf for referenced data. 305195972f6Sopenharmony_ci * Referenced data can be volatile (PBUF_REF) or long-lived (PBUF_ROM). 306195972f6Sopenharmony_ci * 307195972f6Sopenharmony_ci * The actual memory allocated for the pbuf is determined by the 308195972f6Sopenharmony_ci * layer at which the pbuf is allocated and the requested size 309195972f6Sopenharmony_ci * (from the size parameter). 310195972f6Sopenharmony_ci * 311195972f6Sopenharmony_ci * @param payload referenced payload 312195972f6Sopenharmony_ci * @param length size of the pbuf's payload 313195972f6Sopenharmony_ci * @param type this parameter decides how and where the pbuf 314195972f6Sopenharmony_ci * should be allocated as follows: 315195972f6Sopenharmony_ci * 316195972f6Sopenharmony_ci * - PBUF_ROM: It is assumed that the memory used is really 317195972f6Sopenharmony_ci * similar to ROM in that it is immutable and will not be 318195972f6Sopenharmony_ci * changed. Memory which is dynamic should generally not 319195972f6Sopenharmony_ci * be attached to PBUF_ROM pbufs. Use PBUF_REF instead. 320195972f6Sopenharmony_ci * - PBUF_REF: It is assumed that the pbuf is only 321195972f6Sopenharmony_ci * being used in a single thread. If the pbuf gets queued, 322195972f6Sopenharmony_ci * then pbuf_take should be called to copy the buffer. 323195972f6Sopenharmony_ci * 324195972f6Sopenharmony_ci * @return the allocated pbuf. 325195972f6Sopenharmony_ci */ 326195972f6Sopenharmony_cistruct pbuf * 327195972f6Sopenharmony_cipbuf_alloc_reference(void *payload, u16_t length, pbuf_type type) 328195972f6Sopenharmony_ci{ 329195972f6Sopenharmony_ci struct pbuf *p; 330195972f6Sopenharmony_ci LWIP_ASSERT("invalid pbuf_type", (type == PBUF_REF) || (type == PBUF_ROM)); 331195972f6Sopenharmony_ci /* only allocate memory for the pbuf structure */ 332195972f6Sopenharmony_ci p = (struct pbuf *)memp_malloc(MEMP_PBUF); 333195972f6Sopenharmony_ci if (p == NULL) { 334195972f6Sopenharmony_ci LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_LEVEL_SERIOUS, 335195972f6Sopenharmony_ci ("pbuf_alloc_reference: Could not allocate MEMP_PBUF for PBUF_%s.\n", 336195972f6Sopenharmony_ci (type == PBUF_ROM) ? "ROM" : "REF")); 337195972f6Sopenharmony_ci return NULL; 338195972f6Sopenharmony_ci } 339195972f6Sopenharmony_ci pbuf_init_alloced_pbuf(p, payload, length, length, type, 0); 340195972f6Sopenharmony_ci return p; 341195972f6Sopenharmony_ci} 342195972f6Sopenharmony_ci 343195972f6Sopenharmony_ci 344195972f6Sopenharmony_ci#if LWIP_SUPPORT_CUSTOM_PBUF 345195972f6Sopenharmony_ci/** 346195972f6Sopenharmony_ci * @ingroup pbuf 347195972f6Sopenharmony_ci * Initialize a custom pbuf (already allocated). 348195972f6Sopenharmony_ci * Example of custom pbuf usage: @ref zerocopyrx 349195972f6Sopenharmony_ci * 350195972f6Sopenharmony_ci * @param l header size 351195972f6Sopenharmony_ci * @param length size of the pbuf's payload 352195972f6Sopenharmony_ci * @param type type of the pbuf (only used to treat the pbuf accordingly, as 353195972f6Sopenharmony_ci * this function allocates no memory) 354195972f6Sopenharmony_ci * @param p pointer to the custom pbuf to initialize (already allocated) 355195972f6Sopenharmony_ci * @param payload_mem pointer to the buffer that is used for payload and headers, 356195972f6Sopenharmony_ci * must be at least big enough to hold 'length' plus the header size, 357195972f6Sopenharmony_ci * may be NULL if set later. 358195972f6Sopenharmony_ci * ATTENTION: The caller is responsible for correct alignment of this buffer!! 359195972f6Sopenharmony_ci * @param payload_mem_len the size of the 'payload_mem' buffer, must be at least 360195972f6Sopenharmony_ci * big enough to hold 'length' plus the header size 361195972f6Sopenharmony_ci */ 362195972f6Sopenharmony_cistruct pbuf * 363195972f6Sopenharmony_cipbuf_alloced_custom(pbuf_layer l, u16_t length, pbuf_type type, struct pbuf_custom *p, 364195972f6Sopenharmony_ci void *payload_mem, u16_t payload_mem_len) 365195972f6Sopenharmony_ci{ 366195972f6Sopenharmony_ci u16_t offset = (u16_t)l; 367195972f6Sopenharmony_ci void *payload; 368195972f6Sopenharmony_ci LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_alloced_custom(length=%"U16_F")\n", length)); 369195972f6Sopenharmony_ci 370195972f6Sopenharmony_ci if (LWIP_MEM_ALIGN_SIZE(offset) + length > payload_mem_len) { 371195972f6Sopenharmony_ci LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_LEVEL_WARNING, ("pbuf_alloced_custom(length=%"U16_F") buffer too short\n", length)); 372195972f6Sopenharmony_ci return NULL; 373195972f6Sopenharmony_ci } 374195972f6Sopenharmony_ci 375195972f6Sopenharmony_ci if (payload_mem != NULL) { 376195972f6Sopenharmony_ci payload = (u8_t *)payload_mem + LWIP_MEM_ALIGN_SIZE(offset); 377195972f6Sopenharmony_ci } else { 378195972f6Sopenharmony_ci payload = NULL; 379195972f6Sopenharmony_ci } 380195972f6Sopenharmony_ci pbuf_init_alloced_pbuf(&p->pbuf, payload, length, length, type, PBUF_FLAG_IS_CUSTOM); 381195972f6Sopenharmony_ci return &p->pbuf; 382195972f6Sopenharmony_ci} 383195972f6Sopenharmony_ci#endif /* LWIP_SUPPORT_CUSTOM_PBUF */ 384195972f6Sopenharmony_ci 385195972f6Sopenharmony_ci/** 386195972f6Sopenharmony_ci * @ingroup pbuf 387195972f6Sopenharmony_ci * Shrink a pbuf chain to a desired length. 388195972f6Sopenharmony_ci * 389195972f6Sopenharmony_ci * @param p pbuf to shrink. 390195972f6Sopenharmony_ci * @param new_len desired new length of pbuf chain 391195972f6Sopenharmony_ci * 392195972f6Sopenharmony_ci * Depending on the desired length, the first few pbufs in a chain might 393195972f6Sopenharmony_ci * be skipped and left unchanged. The new last pbuf in the chain will be 394195972f6Sopenharmony_ci * resized, and any remaining pbufs will be freed. 395195972f6Sopenharmony_ci * 396195972f6Sopenharmony_ci * @note If the pbuf is ROM/REF, only the ->tot_len and ->len fields are adjusted. 397195972f6Sopenharmony_ci * @note May not be called on a packet queue. 398195972f6Sopenharmony_ci * 399195972f6Sopenharmony_ci * @note Despite its name, pbuf_realloc cannot grow the size of a pbuf (chain). 400195972f6Sopenharmony_ci */ 401195972f6Sopenharmony_civoid 402195972f6Sopenharmony_cipbuf_realloc(struct pbuf *p, u16_t new_len) 403195972f6Sopenharmony_ci{ 404195972f6Sopenharmony_ci struct pbuf *q; 405195972f6Sopenharmony_ci u16_t rem_len; /* remaining length */ 406195972f6Sopenharmony_ci u16_t shrink; 407195972f6Sopenharmony_ci 408195972f6Sopenharmony_ci LWIP_ASSERT("pbuf_realloc: p != NULL", p != NULL); 409195972f6Sopenharmony_ci 410195972f6Sopenharmony_ci /* desired length larger than current length? */ 411195972f6Sopenharmony_ci if (new_len >= p->tot_len) { 412195972f6Sopenharmony_ci /* enlarging not yet supported */ 413195972f6Sopenharmony_ci return; 414195972f6Sopenharmony_ci } 415195972f6Sopenharmony_ci 416195972f6Sopenharmony_ci /* the pbuf chain grows by (new_len - p->tot_len) bytes 417195972f6Sopenharmony_ci * (which may be negative in case of shrinking) */ 418195972f6Sopenharmony_ci shrink = (u16_t)(p->tot_len - new_len); 419195972f6Sopenharmony_ci 420195972f6Sopenharmony_ci /* first, step over any pbufs that should remain in the chain */ 421195972f6Sopenharmony_ci rem_len = new_len; 422195972f6Sopenharmony_ci q = p; 423195972f6Sopenharmony_ci /* should this pbuf be kept? */ 424195972f6Sopenharmony_ci while (rem_len > q->len) { 425195972f6Sopenharmony_ci /* decrease remaining length by pbuf length */ 426195972f6Sopenharmony_ci rem_len = (u16_t)(rem_len - q->len); 427195972f6Sopenharmony_ci /* decrease total length indicator */ 428195972f6Sopenharmony_ci q->tot_len = (u16_t)(q->tot_len - shrink); 429195972f6Sopenharmony_ci /* proceed to next pbuf in chain */ 430195972f6Sopenharmony_ci q = q->next; 431195972f6Sopenharmony_ci LWIP_ASSERT("pbuf_realloc: q != NULL", q != NULL); 432195972f6Sopenharmony_ci } 433195972f6Sopenharmony_ci /* we have now reached the new last pbuf (in q) */ 434195972f6Sopenharmony_ci /* rem_len == desired length for pbuf q */ 435195972f6Sopenharmony_ci 436195972f6Sopenharmony_ci /* shrink allocated memory for PBUF_RAM */ 437195972f6Sopenharmony_ci /* (other types merely adjust their length fields */ 438195972f6Sopenharmony_ci if (pbuf_match_allocsrc(q, PBUF_TYPE_ALLOC_SRC_MASK_STD_HEAP) && (rem_len != q->len) 439195972f6Sopenharmony_ci#if LWIP_SUPPORT_CUSTOM_PBUF 440195972f6Sopenharmony_ci && ((q->flags & PBUF_FLAG_IS_CUSTOM) == 0) 441195972f6Sopenharmony_ci#endif /* LWIP_SUPPORT_CUSTOM_PBUF */ 442195972f6Sopenharmony_ci ) { 443195972f6Sopenharmony_ci /* reallocate and adjust the length of the pbuf that will be split */ 444195972f6Sopenharmony_ci q = (struct pbuf *)mem_trim(q, (mem_size_t)(((u8_t *)q->payload - (u8_t *)q) + rem_len)); 445195972f6Sopenharmony_ci LWIP_ASSERT("mem_trim returned q == NULL", q != NULL); 446195972f6Sopenharmony_ci } 447195972f6Sopenharmony_ci /* adjust length fields for new last pbuf */ 448195972f6Sopenharmony_ci q->len = rem_len; 449195972f6Sopenharmony_ci q->tot_len = q->len; 450195972f6Sopenharmony_ci 451195972f6Sopenharmony_ci /* any remaining pbufs in chain? */ 452195972f6Sopenharmony_ci if (q->next != NULL) { 453195972f6Sopenharmony_ci /* free remaining pbufs in chain */ 454195972f6Sopenharmony_ci pbuf_free(q->next); 455195972f6Sopenharmony_ci } 456195972f6Sopenharmony_ci /* q is last packet in chain */ 457195972f6Sopenharmony_ci q->next = NULL; 458195972f6Sopenharmony_ci 459195972f6Sopenharmony_ci} 460195972f6Sopenharmony_ci 461195972f6Sopenharmony_ci/** 462195972f6Sopenharmony_ci * Adjusts the payload pointer to reveal headers in the payload. 463195972f6Sopenharmony_ci * @see pbuf_add_header. 464195972f6Sopenharmony_ci * 465195972f6Sopenharmony_ci * @param p pbuf to change the header size. 466195972f6Sopenharmony_ci * @param header_size_increment Number of bytes to increment header size. 467195972f6Sopenharmony_ci * @param force Allow 'header_size_increment > 0' for PBUF_REF/PBUF_ROM types 468195972f6Sopenharmony_ci * 469195972f6Sopenharmony_ci * @return non-zero on failure, zero on success. 470195972f6Sopenharmony_ci * 471195972f6Sopenharmony_ci */ 472195972f6Sopenharmony_cistatic u8_t 473195972f6Sopenharmony_cipbuf_add_header_impl(struct pbuf *p, size_t header_size_increment, u8_t force) 474195972f6Sopenharmony_ci{ 475195972f6Sopenharmony_ci u16_t type_internal; 476195972f6Sopenharmony_ci void *payload; 477195972f6Sopenharmony_ci u16_t increment_magnitude; 478195972f6Sopenharmony_ci 479195972f6Sopenharmony_ci LWIP_ASSERT("p != NULL", p != NULL); 480195972f6Sopenharmony_ci if ((p == NULL) || (header_size_increment > 0xFFFF)) { 481195972f6Sopenharmony_ci return 1; 482195972f6Sopenharmony_ci } 483195972f6Sopenharmony_ci if (header_size_increment == 0) { 484195972f6Sopenharmony_ci return 0; 485195972f6Sopenharmony_ci } 486195972f6Sopenharmony_ci 487195972f6Sopenharmony_ci increment_magnitude = (u16_t)header_size_increment; 488195972f6Sopenharmony_ci /* Do not allow tot_len to wrap as a result. */ 489195972f6Sopenharmony_ci if ((u16_t)(increment_magnitude + p->tot_len) < increment_magnitude) { 490195972f6Sopenharmony_ci return 1; 491195972f6Sopenharmony_ci } 492195972f6Sopenharmony_ci 493195972f6Sopenharmony_ci type_internal = p->type_internal; 494195972f6Sopenharmony_ci 495195972f6Sopenharmony_ci /* pbuf types containing payloads? */ 496195972f6Sopenharmony_ci if (type_internal & PBUF_TYPE_FLAG_STRUCT_DATA_CONTIGUOUS) { 497195972f6Sopenharmony_ci /* set new payload pointer */ 498195972f6Sopenharmony_ci payload = (u8_t *)p->payload - header_size_increment; 499195972f6Sopenharmony_ci /* boundary check fails? */ 500195972f6Sopenharmony_ci if ((u8_t *)payload < (u8_t *)p + SIZEOF_STRUCT_PBUF) { 501195972f6Sopenharmony_ci LWIP_DEBUGF( PBUF_DEBUG | LWIP_DBG_TRACE, 502195972f6Sopenharmony_ci ("pbuf_add_header: failed as %p < %p (not enough space for new header size)\n", 503195972f6Sopenharmony_ci (void *)payload, (void *)((u8_t *)p + SIZEOF_STRUCT_PBUF))); 504195972f6Sopenharmony_ci /* bail out unsuccessfully */ 505195972f6Sopenharmony_ci return 1; 506195972f6Sopenharmony_ci } 507195972f6Sopenharmony_ci /* pbuf types referring to external payloads? */ 508195972f6Sopenharmony_ci } else { 509195972f6Sopenharmony_ci /* hide a header in the payload? */ 510195972f6Sopenharmony_ci if (force) { 511195972f6Sopenharmony_ci payload = (u8_t *)p->payload - header_size_increment; 512195972f6Sopenharmony_ci } else { 513195972f6Sopenharmony_ci /* cannot expand payload to front (yet!) 514195972f6Sopenharmony_ci * bail out unsuccessfully */ 515195972f6Sopenharmony_ci return 1; 516195972f6Sopenharmony_ci } 517195972f6Sopenharmony_ci } 518195972f6Sopenharmony_ci LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_add_header: old %p new %p (%"U16_F")\n", 519195972f6Sopenharmony_ci (void *)p->payload, (void *)payload, increment_magnitude)); 520195972f6Sopenharmony_ci 521195972f6Sopenharmony_ci /* modify pbuf fields */ 522195972f6Sopenharmony_ci p->payload = payload; 523195972f6Sopenharmony_ci p->len = (u16_t)(p->len + increment_magnitude); 524195972f6Sopenharmony_ci p->tot_len = (u16_t)(p->tot_len + increment_magnitude); 525195972f6Sopenharmony_ci 526195972f6Sopenharmony_ci 527195972f6Sopenharmony_ci return 0; 528195972f6Sopenharmony_ci} 529195972f6Sopenharmony_ci 530195972f6Sopenharmony_ci/** 531195972f6Sopenharmony_ci * Adjusts the payload pointer to reveal headers in the payload. 532195972f6Sopenharmony_ci * 533195972f6Sopenharmony_ci * Adjusts the ->payload pointer so that space for a header 534195972f6Sopenharmony_ci * appears in the pbuf payload. 535195972f6Sopenharmony_ci * 536195972f6Sopenharmony_ci * The ->payload, ->tot_len and ->len fields are adjusted. 537195972f6Sopenharmony_ci * 538195972f6Sopenharmony_ci * @param p pbuf to change the header size. 539195972f6Sopenharmony_ci * @param header_size_increment Number of bytes to increment header size which 540195972f6Sopenharmony_ci * increases the size of the pbuf. New space is on the front. 541195972f6Sopenharmony_ci * If header_size_increment is 0, this function does nothing and returns successful. 542195972f6Sopenharmony_ci * 543195972f6Sopenharmony_ci * PBUF_ROM and PBUF_REF type buffers cannot have their sizes increased, so 544195972f6Sopenharmony_ci * the call will fail. A check is made that the increase in header size does 545195972f6Sopenharmony_ci * not move the payload pointer in front of the start of the buffer. 546195972f6Sopenharmony_ci * 547195972f6Sopenharmony_ci * @return non-zero on failure, zero on success. 548195972f6Sopenharmony_ci * 549195972f6Sopenharmony_ci */ 550195972f6Sopenharmony_ciu8_t 551195972f6Sopenharmony_cipbuf_add_header(struct pbuf *p, size_t header_size_increment) 552195972f6Sopenharmony_ci{ 553195972f6Sopenharmony_ci return pbuf_add_header_impl(p, header_size_increment, 0); 554195972f6Sopenharmony_ci} 555195972f6Sopenharmony_ci 556195972f6Sopenharmony_ci/** 557195972f6Sopenharmony_ci * Same as @ref pbuf_add_header but does not check if 'header_size > 0' is allowed. 558195972f6Sopenharmony_ci * This is used internally only, to allow PBUF_REF for RX. 559195972f6Sopenharmony_ci */ 560195972f6Sopenharmony_ciu8_t 561195972f6Sopenharmony_cipbuf_add_header_force(struct pbuf *p, size_t header_size_increment) 562195972f6Sopenharmony_ci{ 563195972f6Sopenharmony_ci return pbuf_add_header_impl(p, header_size_increment, 1); 564195972f6Sopenharmony_ci} 565195972f6Sopenharmony_ci 566195972f6Sopenharmony_ci/** 567195972f6Sopenharmony_ci * Adjusts the payload pointer to hide headers in the payload. 568195972f6Sopenharmony_ci * 569195972f6Sopenharmony_ci * Adjusts the ->payload pointer so that space for a header 570195972f6Sopenharmony_ci * disappears in the pbuf payload. 571195972f6Sopenharmony_ci * 572195972f6Sopenharmony_ci * The ->payload, ->tot_len and ->len fields are adjusted. 573195972f6Sopenharmony_ci * 574195972f6Sopenharmony_ci * @param p pbuf to change the header size. 575195972f6Sopenharmony_ci * @param header_size_decrement Number of bytes to decrement header size which 576195972f6Sopenharmony_ci * decreases the size of the pbuf. 577195972f6Sopenharmony_ci * If header_size_decrement is 0, this function does nothing and returns successful. 578195972f6Sopenharmony_ci * @return non-zero on failure, zero on success. 579195972f6Sopenharmony_ci * 580195972f6Sopenharmony_ci */ 581195972f6Sopenharmony_ciu8_t 582195972f6Sopenharmony_cipbuf_remove_header(struct pbuf *p, size_t header_size_decrement) 583195972f6Sopenharmony_ci{ 584195972f6Sopenharmony_ci void *payload; 585195972f6Sopenharmony_ci u16_t increment_magnitude; 586195972f6Sopenharmony_ci 587195972f6Sopenharmony_ci LWIP_ASSERT("p != NULL", p != NULL); 588195972f6Sopenharmony_ci if ((p == NULL) || (header_size_decrement > 0xFFFF)) { 589195972f6Sopenharmony_ci return 1; 590195972f6Sopenharmony_ci } 591195972f6Sopenharmony_ci if (header_size_decrement == 0) { 592195972f6Sopenharmony_ci return 0; 593195972f6Sopenharmony_ci } 594195972f6Sopenharmony_ci 595195972f6Sopenharmony_ci increment_magnitude = (u16_t)header_size_decrement; 596195972f6Sopenharmony_ci /* Check that we aren't going to move off the end of the pbuf */ 597195972f6Sopenharmony_ci LWIP_ERROR("increment_magnitude <= p->len", (increment_magnitude <= p->len), return 1;); 598195972f6Sopenharmony_ci 599195972f6Sopenharmony_ci /* remember current payload pointer */ 600195972f6Sopenharmony_ci payload = p->payload; 601195972f6Sopenharmony_ci LWIP_UNUSED_ARG(payload); /* only used in LWIP_DEBUGF below */ 602195972f6Sopenharmony_ci 603195972f6Sopenharmony_ci /* increase payload pointer (guarded by length check above) */ 604195972f6Sopenharmony_ci p->payload = (u8_t *)p->payload + header_size_decrement; 605195972f6Sopenharmony_ci /* modify pbuf length fields */ 606195972f6Sopenharmony_ci p->len = (u16_t)(p->len - increment_magnitude); 607195972f6Sopenharmony_ci p->tot_len = (u16_t)(p->tot_len - increment_magnitude); 608195972f6Sopenharmony_ci 609195972f6Sopenharmony_ci LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_remove_header: old %p new %p (%"U16_F")\n", 610195972f6Sopenharmony_ci (void *)payload, (void *)p->payload, increment_magnitude)); 611195972f6Sopenharmony_ci 612195972f6Sopenharmony_ci return 0; 613195972f6Sopenharmony_ci} 614195972f6Sopenharmony_ci 615195972f6Sopenharmony_cistatic u8_t 616195972f6Sopenharmony_cipbuf_header_impl(struct pbuf *p, s16_t header_size_increment, u8_t force) 617195972f6Sopenharmony_ci{ 618195972f6Sopenharmony_ci if (header_size_increment < 0) { 619195972f6Sopenharmony_ci return pbuf_remove_header(p, (size_t) - header_size_increment); 620195972f6Sopenharmony_ci } else { 621195972f6Sopenharmony_ci return pbuf_add_header_impl(p, (size_t)header_size_increment, force); 622195972f6Sopenharmony_ci } 623195972f6Sopenharmony_ci} 624195972f6Sopenharmony_ci 625195972f6Sopenharmony_ci/** 626195972f6Sopenharmony_ci * Adjusts the payload pointer to hide or reveal headers in the payload. 627195972f6Sopenharmony_ci * 628195972f6Sopenharmony_ci * Adjusts the ->payload pointer so that space for a header 629195972f6Sopenharmony_ci * (dis)appears in the pbuf payload. 630195972f6Sopenharmony_ci * 631195972f6Sopenharmony_ci * The ->payload, ->tot_len and ->len fields are adjusted. 632195972f6Sopenharmony_ci * 633195972f6Sopenharmony_ci * @param p pbuf to change the header size. 634195972f6Sopenharmony_ci * @param header_size_increment Number of bytes to increment header size which 635195972f6Sopenharmony_ci * increases the size of the pbuf. New space is on the front. 636195972f6Sopenharmony_ci * (Using a negative value decreases the header size.) 637195972f6Sopenharmony_ci * If header_size_increment is 0, this function does nothing and returns successful. 638195972f6Sopenharmony_ci * 639195972f6Sopenharmony_ci * PBUF_ROM and PBUF_REF type buffers cannot have their sizes increased, so 640195972f6Sopenharmony_ci * the call will fail. A check is made that the increase in header size does 641195972f6Sopenharmony_ci * not move the payload pointer in front of the start of the buffer. 642195972f6Sopenharmony_ci * @return non-zero on failure, zero on success. 643195972f6Sopenharmony_ci * 644195972f6Sopenharmony_ci */ 645195972f6Sopenharmony_ciu8_t 646195972f6Sopenharmony_cipbuf_header(struct pbuf *p, s16_t header_size_increment) 647195972f6Sopenharmony_ci{ 648195972f6Sopenharmony_ci return pbuf_header_impl(p, header_size_increment, 0); 649195972f6Sopenharmony_ci} 650195972f6Sopenharmony_ci 651195972f6Sopenharmony_ci/** 652195972f6Sopenharmony_ci * Same as pbuf_header but does not check if 'header_size > 0' is allowed. 653195972f6Sopenharmony_ci * This is used internally only, to allow PBUF_REF for RX. 654195972f6Sopenharmony_ci */ 655195972f6Sopenharmony_ciu8_t 656195972f6Sopenharmony_cipbuf_header_force(struct pbuf *p, s16_t header_size_increment) 657195972f6Sopenharmony_ci{ 658195972f6Sopenharmony_ci return pbuf_header_impl(p, header_size_increment, 1); 659195972f6Sopenharmony_ci} 660195972f6Sopenharmony_ci 661195972f6Sopenharmony_ci/** Similar to pbuf_header(-size) but de-refs header pbufs for (size >= p->len) 662195972f6Sopenharmony_ci * 663195972f6Sopenharmony_ci * @param q pbufs to operate on 664195972f6Sopenharmony_ci * @param size The number of bytes to remove from the beginning of the pbuf list. 665195972f6Sopenharmony_ci * While size >= p->len, pbufs are freed. 666195972f6Sopenharmony_ci * ATTENTION: this is the opposite direction as @ref pbuf_header, but 667195972f6Sopenharmony_ci * takes an u16_t not s16_t! 668195972f6Sopenharmony_ci * @return the new head pbuf 669195972f6Sopenharmony_ci */ 670195972f6Sopenharmony_cistruct pbuf * 671195972f6Sopenharmony_cipbuf_free_header(struct pbuf *q, u16_t size) 672195972f6Sopenharmony_ci{ 673195972f6Sopenharmony_ci struct pbuf *p = q; 674195972f6Sopenharmony_ci u16_t free_left = size; 675195972f6Sopenharmony_ci while (free_left && p) { 676195972f6Sopenharmony_ci if (free_left >= p->len) { 677195972f6Sopenharmony_ci struct pbuf *f = p; 678195972f6Sopenharmony_ci free_left = (u16_t)(free_left - p->len); 679195972f6Sopenharmony_ci p = p->next; 680195972f6Sopenharmony_ci f->next = 0; 681195972f6Sopenharmony_ci pbuf_free(f); 682195972f6Sopenharmony_ci } else { 683195972f6Sopenharmony_ci pbuf_remove_header(p, free_left); 684195972f6Sopenharmony_ci free_left = 0; 685195972f6Sopenharmony_ci } 686195972f6Sopenharmony_ci } 687195972f6Sopenharmony_ci return p; 688195972f6Sopenharmony_ci} 689195972f6Sopenharmony_ci 690195972f6Sopenharmony_ci/** 691195972f6Sopenharmony_ci * @ingroup pbuf 692195972f6Sopenharmony_ci * Dereference a pbuf chain or queue and deallocate any no-longer-used 693195972f6Sopenharmony_ci * pbufs at the head of this chain or queue. 694195972f6Sopenharmony_ci * 695195972f6Sopenharmony_ci * Decrements the pbuf reference count. If it reaches zero, the pbuf is 696195972f6Sopenharmony_ci * deallocated. 697195972f6Sopenharmony_ci * 698195972f6Sopenharmony_ci * For a pbuf chain, this is repeated for each pbuf in the chain, 699195972f6Sopenharmony_ci * up to the first pbuf which has a non-zero reference count after 700195972f6Sopenharmony_ci * decrementing. So, when all reference counts are one, the whole 701195972f6Sopenharmony_ci * chain is free'd. 702195972f6Sopenharmony_ci * 703195972f6Sopenharmony_ci * @param p The pbuf (chain) to be dereferenced. 704195972f6Sopenharmony_ci * 705195972f6Sopenharmony_ci * @return the number of pbufs that were de-allocated 706195972f6Sopenharmony_ci * from the head of the chain. 707195972f6Sopenharmony_ci * 708195972f6Sopenharmony_ci * @note MUST NOT be called on a packet queue (Not verified to work yet). 709195972f6Sopenharmony_ci * @note the reference counter of a pbuf equals the number of pointers 710195972f6Sopenharmony_ci * that refer to the pbuf (or into the pbuf). 711195972f6Sopenharmony_ci * 712195972f6Sopenharmony_ci * @internal examples: 713195972f6Sopenharmony_ci * 714195972f6Sopenharmony_ci * Assuming existing chains a->b->c with the following reference 715195972f6Sopenharmony_ci * counts, calling pbuf_free(a) results in: 716195972f6Sopenharmony_ci * 717195972f6Sopenharmony_ci * 1->2->3 becomes ...1->3 718195972f6Sopenharmony_ci * 3->3->3 becomes 2->3->3 719195972f6Sopenharmony_ci * 1->1->2 becomes ......1 720195972f6Sopenharmony_ci * 2->1->1 becomes 1->1->1 721195972f6Sopenharmony_ci * 1->1->1 becomes ....... 722195972f6Sopenharmony_ci * 723195972f6Sopenharmony_ci */ 724195972f6Sopenharmony_ciu8_t 725195972f6Sopenharmony_cipbuf_free(struct pbuf *p) 726195972f6Sopenharmony_ci{ 727195972f6Sopenharmony_ci u8_t alloc_src; 728195972f6Sopenharmony_ci struct pbuf *q; 729195972f6Sopenharmony_ci u8_t count; 730195972f6Sopenharmony_ci 731195972f6Sopenharmony_ci if (p == NULL) { 732195972f6Sopenharmony_ci LWIP_ASSERT("p != NULL", p != NULL); 733195972f6Sopenharmony_ci /* if assertions are disabled, proceed with debug output */ 734195972f6Sopenharmony_ci LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_LEVEL_SERIOUS, 735195972f6Sopenharmony_ci ("pbuf_free(p == NULL) was called.\n")); 736195972f6Sopenharmony_ci return 0; 737195972f6Sopenharmony_ci } 738195972f6Sopenharmony_ci LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_free(%p)\n", (void *)p)); 739195972f6Sopenharmony_ci 740195972f6Sopenharmony_ci PERF_START; 741195972f6Sopenharmony_ci 742195972f6Sopenharmony_ci count = 0; 743195972f6Sopenharmony_ci /* de-allocate all consecutive pbufs from the head of the chain that 744195972f6Sopenharmony_ci * obtain a zero reference count after decrementing*/ 745195972f6Sopenharmony_ci while (p != NULL) { 746195972f6Sopenharmony_ci LWIP_PBUF_REF_T ref; 747195972f6Sopenharmony_ci SYS_ARCH_DECL_PROTECT(old_level); 748195972f6Sopenharmony_ci /* Since decrementing ref cannot be guaranteed to be a single machine operation 749195972f6Sopenharmony_ci * we must protect it. We put the new ref into a local variable to prevent 750195972f6Sopenharmony_ci * further protection. */ 751195972f6Sopenharmony_ci SYS_ARCH_PROTECT(old_level); 752195972f6Sopenharmony_ci /* all pbufs in a chain are referenced at least once */ 753195972f6Sopenharmony_ci LWIP_ASSERT("pbuf_free: p->ref > 0", p->ref > 0); 754195972f6Sopenharmony_ci /* decrease reference count (number of pointers to pbuf) */ 755195972f6Sopenharmony_ci ref = --(p->ref); 756195972f6Sopenharmony_ci SYS_ARCH_UNPROTECT(old_level); 757195972f6Sopenharmony_ci /* this pbuf is no longer referenced to? */ 758195972f6Sopenharmony_ci if (ref == 0) { 759195972f6Sopenharmony_ci /* remember next pbuf in chain for next iteration */ 760195972f6Sopenharmony_ci q = p->next; 761195972f6Sopenharmony_ci LWIP_DEBUGF( PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_free: deallocating %p\n", (void *)p)); 762195972f6Sopenharmony_ci alloc_src = pbuf_get_allocsrc(p); 763195972f6Sopenharmony_ci#if LWIP_SUPPORT_CUSTOM_PBUF 764195972f6Sopenharmony_ci /* is this a custom pbuf? */ 765195972f6Sopenharmony_ci if ((p->flags & PBUF_FLAG_IS_CUSTOM) != 0) { 766195972f6Sopenharmony_ci struct pbuf_custom *pc = (struct pbuf_custom *)p; 767195972f6Sopenharmony_ci LWIP_ASSERT("pc->custom_free_function != NULL", pc->custom_free_function != NULL); 768195972f6Sopenharmony_ci pc->custom_free_function(p); 769195972f6Sopenharmony_ci } else 770195972f6Sopenharmony_ci#endif /* LWIP_SUPPORT_CUSTOM_PBUF */ 771195972f6Sopenharmony_ci { 772195972f6Sopenharmony_ci /* is this a pbuf from the pool? */ 773195972f6Sopenharmony_ci if (alloc_src == PBUF_TYPE_ALLOC_SRC_MASK_STD_MEMP_PBUF_POOL) { 774195972f6Sopenharmony_ci memp_free(MEMP_PBUF_POOL, p); 775195972f6Sopenharmony_ci /* is this a ROM or RAM referencing pbuf? */ 776195972f6Sopenharmony_ci } else if (alloc_src == PBUF_TYPE_ALLOC_SRC_MASK_STD_MEMP_PBUF) { 777195972f6Sopenharmony_ci memp_free(MEMP_PBUF, p); 778195972f6Sopenharmony_ci /* type == PBUF_RAM */ 779195972f6Sopenharmony_ci } else if (alloc_src == PBUF_TYPE_ALLOC_SRC_MASK_STD_HEAP) { 780195972f6Sopenharmony_ci mem_free(p); 781195972f6Sopenharmony_ci } else { 782195972f6Sopenharmony_ci /* @todo: support freeing other types */ 783195972f6Sopenharmony_ci LWIP_ASSERT("invalid pbuf type", 0); 784195972f6Sopenharmony_ci } 785195972f6Sopenharmony_ci } 786195972f6Sopenharmony_ci count++; 787195972f6Sopenharmony_ci /* proceed to next pbuf */ 788195972f6Sopenharmony_ci p = q; 789195972f6Sopenharmony_ci /* p->ref > 0, this pbuf is still referenced to */ 790195972f6Sopenharmony_ci /* (and so the remaining pbufs in chain as well) */ 791195972f6Sopenharmony_ci } else { 792195972f6Sopenharmony_ci LWIP_DEBUGF( PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_free: %p has ref %"U16_F", ending here.\n", (void *)p, (u16_t)ref)); 793195972f6Sopenharmony_ci /* stop walking through the chain */ 794195972f6Sopenharmony_ci p = NULL; 795195972f6Sopenharmony_ci } 796195972f6Sopenharmony_ci } 797195972f6Sopenharmony_ci PERF_STOP("pbuf_free"); 798195972f6Sopenharmony_ci /* return number of de-allocated pbufs */ 799195972f6Sopenharmony_ci return count; 800195972f6Sopenharmony_ci} 801195972f6Sopenharmony_ci 802195972f6Sopenharmony_ci/** 803195972f6Sopenharmony_ci * Count number of pbufs in a chain 804195972f6Sopenharmony_ci * 805195972f6Sopenharmony_ci * @param p first pbuf of chain 806195972f6Sopenharmony_ci * @return the number of pbufs in a chain 807195972f6Sopenharmony_ci */ 808195972f6Sopenharmony_ciu16_t 809195972f6Sopenharmony_cipbuf_clen(const struct pbuf *p) 810195972f6Sopenharmony_ci{ 811195972f6Sopenharmony_ci u16_t len; 812195972f6Sopenharmony_ci 813195972f6Sopenharmony_ci len = 0; 814195972f6Sopenharmony_ci while (p != NULL) { 815195972f6Sopenharmony_ci ++len; 816195972f6Sopenharmony_ci p = p->next; 817195972f6Sopenharmony_ci } 818195972f6Sopenharmony_ci return len; 819195972f6Sopenharmony_ci} 820195972f6Sopenharmony_ci 821195972f6Sopenharmony_ci/** 822195972f6Sopenharmony_ci * @ingroup pbuf 823195972f6Sopenharmony_ci * Increment the reference count of the pbuf. 824195972f6Sopenharmony_ci * 825195972f6Sopenharmony_ci * @param p pbuf to increase reference counter of 826195972f6Sopenharmony_ci * 827195972f6Sopenharmony_ci */ 828195972f6Sopenharmony_civoid 829195972f6Sopenharmony_cipbuf_ref(struct pbuf *p) 830195972f6Sopenharmony_ci{ 831195972f6Sopenharmony_ci /* pbuf given? */ 832195972f6Sopenharmony_ci if (p != NULL) { 833195972f6Sopenharmony_ci SYS_ARCH_SET(p->ref, (LWIP_PBUF_REF_T)(p->ref + 1)); 834195972f6Sopenharmony_ci LWIP_ASSERT("pbuf ref overflow", p->ref > 0); 835195972f6Sopenharmony_ci } 836195972f6Sopenharmony_ci} 837195972f6Sopenharmony_ci 838195972f6Sopenharmony_ci/** 839195972f6Sopenharmony_ci * @ingroup pbuf 840195972f6Sopenharmony_ci * Concatenate two pbufs (each may be a pbuf chain) and take over 841195972f6Sopenharmony_ci * the caller's reference of the tail pbuf. 842195972f6Sopenharmony_ci * 843195972f6Sopenharmony_ci * @note The caller MAY NOT reference the tail pbuf afterwards. 844195972f6Sopenharmony_ci * Use pbuf_chain() for that purpose. 845195972f6Sopenharmony_ci * 846195972f6Sopenharmony_ci * This function explicitly does not check for tot_len overflow to prevent 847195972f6Sopenharmony_ci * failing to queue too long pbufs. This can produce invalid pbufs, so 848195972f6Sopenharmony_ci * handle with care! 849195972f6Sopenharmony_ci * 850195972f6Sopenharmony_ci * @see pbuf_chain() 851195972f6Sopenharmony_ci */ 852195972f6Sopenharmony_civoid 853195972f6Sopenharmony_cipbuf_cat(struct pbuf *h, struct pbuf *t) 854195972f6Sopenharmony_ci{ 855195972f6Sopenharmony_ci struct pbuf *p; 856195972f6Sopenharmony_ci 857195972f6Sopenharmony_ci LWIP_ERROR("(h != NULL) && (t != NULL) (programmer violates API)", 858195972f6Sopenharmony_ci ((h != NULL) && (t != NULL)), return;); 859195972f6Sopenharmony_ci 860195972f6Sopenharmony_ci /* proceed to last pbuf of chain */ 861195972f6Sopenharmony_ci for (p = h; p->next != NULL; p = p->next) { 862195972f6Sopenharmony_ci /* add total length of second chain to all totals of first chain */ 863195972f6Sopenharmony_ci p->tot_len = (u16_t)(p->tot_len + t->tot_len); 864195972f6Sopenharmony_ci } 865195972f6Sopenharmony_ci /* { p is last pbuf of first h chain, p->next == NULL } */ 866195972f6Sopenharmony_ci LWIP_ASSERT("p->tot_len == p->len (of last pbuf in chain)", p->tot_len == p->len); 867195972f6Sopenharmony_ci LWIP_ASSERT("p->next == NULL", p->next == NULL); 868195972f6Sopenharmony_ci /* add total length of second chain to last pbuf total of first chain */ 869195972f6Sopenharmony_ci p->tot_len = (u16_t)(p->tot_len + t->tot_len); 870195972f6Sopenharmony_ci /* chain last pbuf of head (p) with first of tail (t) */ 871195972f6Sopenharmony_ci p->next = t; 872195972f6Sopenharmony_ci /* p->next now references t, but the caller will drop its reference to t, 873195972f6Sopenharmony_ci * so netto there is no change to the reference count of t. 874195972f6Sopenharmony_ci */ 875195972f6Sopenharmony_ci} 876195972f6Sopenharmony_ci 877195972f6Sopenharmony_ci/** 878195972f6Sopenharmony_ci * @ingroup pbuf 879195972f6Sopenharmony_ci * Chain two pbufs (or pbuf chains) together. 880195972f6Sopenharmony_ci * 881195972f6Sopenharmony_ci * The caller MUST call pbuf_free(t) once it has stopped 882195972f6Sopenharmony_ci * using it. Use pbuf_cat() instead if you no longer use t. 883195972f6Sopenharmony_ci * 884195972f6Sopenharmony_ci * @param h head pbuf (chain) 885195972f6Sopenharmony_ci * @param t tail pbuf (chain) 886195972f6Sopenharmony_ci * @note The pbufs MUST belong to the same packet. 887195972f6Sopenharmony_ci * @note MAY NOT be called on a packet queue. 888195972f6Sopenharmony_ci * 889195972f6Sopenharmony_ci * The ->tot_len fields of all pbufs of the head chain are adjusted. 890195972f6Sopenharmony_ci * The ->next field of the last pbuf of the head chain is adjusted. 891195972f6Sopenharmony_ci * The ->ref field of the first pbuf of the tail chain is adjusted. 892195972f6Sopenharmony_ci * 893195972f6Sopenharmony_ci */ 894195972f6Sopenharmony_civoid 895195972f6Sopenharmony_cipbuf_chain(struct pbuf *h, struct pbuf *t) 896195972f6Sopenharmony_ci{ 897195972f6Sopenharmony_ci pbuf_cat(h, t); 898195972f6Sopenharmony_ci /* t is now referenced by h */ 899195972f6Sopenharmony_ci pbuf_ref(t); 900195972f6Sopenharmony_ci LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_chain: %p references %p\n", (void *)h, (void *)t)); 901195972f6Sopenharmony_ci} 902195972f6Sopenharmony_ci 903195972f6Sopenharmony_ci/** 904195972f6Sopenharmony_ci * Dechains the first pbuf from its succeeding pbufs in the chain. 905195972f6Sopenharmony_ci * 906195972f6Sopenharmony_ci * Makes p->tot_len field equal to p->len. 907195972f6Sopenharmony_ci * @param p pbuf to dechain 908195972f6Sopenharmony_ci * @return remainder of the pbuf chain, or NULL if it was de-allocated. 909195972f6Sopenharmony_ci * @note May not be called on a packet queue. 910195972f6Sopenharmony_ci */ 911195972f6Sopenharmony_cistruct pbuf * 912195972f6Sopenharmony_cipbuf_dechain(struct pbuf *p) 913195972f6Sopenharmony_ci{ 914195972f6Sopenharmony_ci struct pbuf *q; 915195972f6Sopenharmony_ci u8_t tail_gone = 1; 916195972f6Sopenharmony_ci /* tail */ 917195972f6Sopenharmony_ci q = p->next; 918195972f6Sopenharmony_ci /* pbuf has successor in chain? */ 919195972f6Sopenharmony_ci if (q != NULL) { 920195972f6Sopenharmony_ci /* assert tot_len invariant: (p->tot_len == p->len + (p->next? p->next->tot_len: 0) */ 921195972f6Sopenharmony_ci LWIP_ASSERT("p->tot_len == p->len + q->tot_len", q->tot_len == p->tot_len - p->len); 922195972f6Sopenharmony_ci /* enforce invariant if assertion is disabled */ 923195972f6Sopenharmony_ci q->tot_len = (u16_t)(p->tot_len - p->len); 924195972f6Sopenharmony_ci /* decouple pbuf from remainder */ 925195972f6Sopenharmony_ci p->next = NULL; 926195972f6Sopenharmony_ci /* total length of pbuf p is its own length only */ 927195972f6Sopenharmony_ci p->tot_len = p->len; 928195972f6Sopenharmony_ci /* q is no longer referenced by p, free it */ 929195972f6Sopenharmony_ci LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_dechain: unreferencing %p\n", (void *)q)); 930195972f6Sopenharmony_ci tail_gone = pbuf_free(q); 931195972f6Sopenharmony_ci if (tail_gone > 0) { 932195972f6Sopenharmony_ci LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, 933195972f6Sopenharmony_ci ("pbuf_dechain: deallocated %p (as it is no longer referenced)\n", (void *)q)); 934195972f6Sopenharmony_ci } 935195972f6Sopenharmony_ci /* return remaining tail or NULL if deallocated */ 936195972f6Sopenharmony_ci } 937195972f6Sopenharmony_ci /* assert tot_len invariant: (p->tot_len == p->len + (p->next? p->next->tot_len: 0) */ 938195972f6Sopenharmony_ci LWIP_ASSERT("p->tot_len == p->len", p->tot_len == p->len); 939195972f6Sopenharmony_ci return ((tail_gone > 0) ? NULL : q); 940195972f6Sopenharmony_ci} 941195972f6Sopenharmony_ci 942195972f6Sopenharmony_ci/** 943195972f6Sopenharmony_ci * @ingroup pbuf 944195972f6Sopenharmony_ci * Create PBUF_RAM copies of pbufs. 945195972f6Sopenharmony_ci * 946195972f6Sopenharmony_ci * Used to queue packets on behalf of the lwIP stack, such as 947195972f6Sopenharmony_ci * ARP based queueing. 948195972f6Sopenharmony_ci * 949195972f6Sopenharmony_ci * @note You MUST explicitly use p = pbuf_take(p); 950195972f6Sopenharmony_ci * 951195972f6Sopenharmony_ci * @note Only one packet is copied, no packet queue! 952195972f6Sopenharmony_ci * 953195972f6Sopenharmony_ci * @param p_to pbuf destination of the copy 954195972f6Sopenharmony_ci * @param p_from pbuf source of the copy 955195972f6Sopenharmony_ci * 956195972f6Sopenharmony_ci * @return ERR_OK if pbuf was copied 957195972f6Sopenharmony_ci * ERR_ARG if one of the pbufs is NULL or p_to is not big 958195972f6Sopenharmony_ci * enough to hold p_from 959195972f6Sopenharmony_ci */ 960195972f6Sopenharmony_cierr_t 961195972f6Sopenharmony_cipbuf_copy(struct pbuf *p_to, const struct pbuf *p_from) 962195972f6Sopenharmony_ci{ 963195972f6Sopenharmony_ci LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_copy(%p, %p)\n", 964195972f6Sopenharmony_ci (const void *)p_to, (const void *)p_from)); 965195972f6Sopenharmony_ci 966195972f6Sopenharmony_ci LWIP_ERROR("pbuf_copy: invalid source", p_from != NULL, return ERR_ARG;); 967195972f6Sopenharmony_ci return pbuf_copy_partial_pbuf(p_to, p_from, p_from->tot_len, 0); 968195972f6Sopenharmony_ci} 969195972f6Sopenharmony_ci 970195972f6Sopenharmony_ci/** 971195972f6Sopenharmony_ci * @ingroup pbuf 972195972f6Sopenharmony_ci * Copy part or all of one packet buffer into another, to a specified offset. 973195972f6Sopenharmony_ci * 974195972f6Sopenharmony_ci * @note Only data in one packet is copied, no packet queue! 975195972f6Sopenharmony_ci * @note Argument order is shared with pbuf_copy, but different than pbuf_copy_partial. 976195972f6Sopenharmony_ci * 977195972f6Sopenharmony_ci * @param p_to pbuf destination of the copy 978195972f6Sopenharmony_ci * @param p_from pbuf source of the copy 979195972f6Sopenharmony_ci * @param copy_len number of bytes to copy 980195972f6Sopenharmony_ci * @param offset offset in destination pbuf where to copy to 981195972f6Sopenharmony_ci * 982195972f6Sopenharmony_ci * @return ERR_OK if copy_len bytes were copied 983195972f6Sopenharmony_ci * ERR_ARG if one of the pbufs is NULL or p_from is shorter than copy_len 984195972f6Sopenharmony_ci * or p_to is not big enough to hold copy_len at offset 985195972f6Sopenharmony_ci * ERR_VAL if any of the pbufs are part of a queue 986195972f6Sopenharmony_ci */ 987195972f6Sopenharmony_cierr_t 988195972f6Sopenharmony_cipbuf_copy_partial_pbuf(struct pbuf *p_to, const struct pbuf *p_from, u16_t copy_len, u16_t offset) 989195972f6Sopenharmony_ci{ 990195972f6Sopenharmony_ci size_t offset_to = offset, offset_from = 0, len_calc; 991195972f6Sopenharmony_ci u16_t len; 992195972f6Sopenharmony_ci 993195972f6Sopenharmony_ci LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_copy_partial_pbuf(%p, %p, %"U16_F", %"U16_F")\n", 994195972f6Sopenharmony_ci (const void *)p_to, (const void *)p_from, copy_len, offset)); 995195972f6Sopenharmony_ci 996195972f6Sopenharmony_ci /* is the copy_len in range? */ 997195972f6Sopenharmony_ci LWIP_ERROR("pbuf_copy_partial_pbuf: copy_len bigger than source", ((p_from != NULL) && 998195972f6Sopenharmony_ci (p_from->tot_len >= copy_len)), return ERR_ARG;); 999195972f6Sopenharmony_ci /* is the target big enough to hold the source? */ 1000195972f6Sopenharmony_ci LWIP_ERROR("pbuf_copy_partial_pbuf: target not big enough", ((p_to != NULL) && 1001195972f6Sopenharmony_ci (p_to->tot_len >= (offset + copy_len))), return ERR_ARG;); 1002195972f6Sopenharmony_ci 1003195972f6Sopenharmony_ci /* iterate through pbuf chain */ 1004195972f6Sopenharmony_ci do { 1005195972f6Sopenharmony_ci /* copy one part of the original chain */ 1006195972f6Sopenharmony_ci if ((p_to->len - offset_to) >= (p_from->len - offset_from)) { 1007195972f6Sopenharmony_ci /* complete current p_from fits into current p_to */ 1008195972f6Sopenharmony_ci len_calc = p_from->len - offset_from; 1009195972f6Sopenharmony_ci } else { 1010195972f6Sopenharmony_ci /* current p_from does not fit into current p_to */ 1011195972f6Sopenharmony_ci len_calc = p_to->len - offset_to; 1012195972f6Sopenharmony_ci } 1013195972f6Sopenharmony_ci len = (u16_t)LWIP_MIN(copy_len, len_calc); 1014195972f6Sopenharmony_ci MEMCPY((u8_t *)p_to->payload + offset_to, (u8_t *)p_from->payload + offset_from, len); 1015195972f6Sopenharmony_ci offset_to += len; 1016195972f6Sopenharmony_ci offset_from += len; 1017195972f6Sopenharmony_ci copy_len -= len; 1018195972f6Sopenharmony_ci LWIP_ASSERT("offset_to <= p_to->len", offset_to <= p_to->len); 1019195972f6Sopenharmony_ci LWIP_ASSERT("offset_from <= p_from->len", offset_from <= p_from->len); 1020195972f6Sopenharmony_ci if (offset_from >= p_from->len) { 1021195972f6Sopenharmony_ci /* on to next p_from (if any) */ 1022195972f6Sopenharmony_ci offset_from = 0; 1023195972f6Sopenharmony_ci p_from = p_from->next; 1024195972f6Sopenharmony_ci LWIP_ERROR("p_from != NULL", (p_from != NULL) || (copy_len == 0), return ERR_ARG;); 1025195972f6Sopenharmony_ci } 1026195972f6Sopenharmony_ci if (offset_to == p_to->len) { 1027195972f6Sopenharmony_ci /* on to next p_to (if any) */ 1028195972f6Sopenharmony_ci offset_to = 0; 1029195972f6Sopenharmony_ci p_to = p_to->next; 1030195972f6Sopenharmony_ci LWIP_ERROR("p_to != NULL", (p_to != NULL) || (copy_len == 0), return ERR_ARG;); 1031195972f6Sopenharmony_ci } 1032195972f6Sopenharmony_ci 1033195972f6Sopenharmony_ci if ((p_from != NULL) && (p_from->len == p_from->tot_len)) { 1034195972f6Sopenharmony_ci /* don't copy more than one packet! */ 1035195972f6Sopenharmony_ci LWIP_ERROR("pbuf_copy_partial_pbuf() does not allow packet queues!", 1036195972f6Sopenharmony_ci (p_from->next == NULL), return ERR_VAL;); 1037195972f6Sopenharmony_ci } 1038195972f6Sopenharmony_ci if ((p_to != NULL) && (p_to->len == p_to->tot_len)) { 1039195972f6Sopenharmony_ci /* don't copy more than one packet! */ 1040195972f6Sopenharmony_ci LWIP_ERROR("pbuf_copy_partial_pbuf() does not allow packet queues!", 1041195972f6Sopenharmony_ci (p_to->next == NULL), return ERR_VAL;); 1042195972f6Sopenharmony_ci } 1043195972f6Sopenharmony_ci } while (copy_len); 1044195972f6Sopenharmony_ci LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_copy_partial_pbuf: copy complete.\n")); 1045195972f6Sopenharmony_ci return ERR_OK; 1046195972f6Sopenharmony_ci} 1047195972f6Sopenharmony_ci 1048195972f6Sopenharmony_ci/** 1049195972f6Sopenharmony_ci * @ingroup pbuf 1050195972f6Sopenharmony_ci * Copy (part of) the contents of a packet buffer 1051195972f6Sopenharmony_ci * to an application supplied buffer. 1052195972f6Sopenharmony_ci * 1053195972f6Sopenharmony_ci * @param buf the pbuf from which to copy data 1054195972f6Sopenharmony_ci * @param dataptr the application supplied buffer 1055195972f6Sopenharmony_ci * @param len length of data to copy (dataptr must be big enough). No more 1056195972f6Sopenharmony_ci * than buf->tot_len will be copied, irrespective of len 1057195972f6Sopenharmony_ci * @param offset offset into the packet buffer from where to begin copying len bytes 1058195972f6Sopenharmony_ci * @return the number of bytes copied, or 0 on failure 1059195972f6Sopenharmony_ci */ 1060195972f6Sopenharmony_ciu16_t 1061195972f6Sopenharmony_cipbuf_copy_partial(const struct pbuf *buf, void *dataptr, u16_t len, u16_t offset) 1062195972f6Sopenharmony_ci{ 1063195972f6Sopenharmony_ci const struct pbuf *p; 1064195972f6Sopenharmony_ci u16_t left = 0; 1065195972f6Sopenharmony_ci u16_t buf_copy_len; 1066195972f6Sopenharmony_ci u16_t copied_total = 0; 1067195972f6Sopenharmony_ci 1068195972f6Sopenharmony_ci LWIP_ERROR("pbuf_copy_partial: invalid buf", (buf != NULL), return 0;); 1069195972f6Sopenharmony_ci LWIP_ERROR("pbuf_copy_partial: invalid dataptr", (dataptr != NULL), return 0;); 1070195972f6Sopenharmony_ci 1071195972f6Sopenharmony_ci /* Note some systems use byte copy if dataptr or one of the pbuf payload pointers are unaligned. */ 1072195972f6Sopenharmony_ci for (p = buf; len != 0 && p != NULL; p = p->next) { 1073195972f6Sopenharmony_ci if ((offset != 0) && (offset >= p->len)) { 1074195972f6Sopenharmony_ci /* don't copy from this buffer -> on to the next */ 1075195972f6Sopenharmony_ci offset = (u16_t)(offset - p->len); 1076195972f6Sopenharmony_ci } else { 1077195972f6Sopenharmony_ci /* copy from this buffer. maybe only partially. */ 1078195972f6Sopenharmony_ci buf_copy_len = (u16_t)(p->len - offset); 1079195972f6Sopenharmony_ci if (buf_copy_len > len) { 1080195972f6Sopenharmony_ci buf_copy_len = len; 1081195972f6Sopenharmony_ci } 1082195972f6Sopenharmony_ci /* copy the necessary parts of the buffer */ 1083195972f6Sopenharmony_ci MEMCPY(&((char *)dataptr)[left], &((char *)p->payload)[offset], buf_copy_len); 1084195972f6Sopenharmony_ci copied_total = (u16_t)(copied_total + buf_copy_len); 1085195972f6Sopenharmony_ci left = (u16_t)(left + buf_copy_len); 1086195972f6Sopenharmony_ci len = (u16_t)(len - buf_copy_len); 1087195972f6Sopenharmony_ci offset = 0; 1088195972f6Sopenharmony_ci } 1089195972f6Sopenharmony_ci } 1090195972f6Sopenharmony_ci return copied_total; 1091195972f6Sopenharmony_ci} 1092195972f6Sopenharmony_ci 1093195972f6Sopenharmony_ci/** 1094195972f6Sopenharmony_ci * @ingroup pbuf 1095195972f6Sopenharmony_ci * Get part of a pbuf's payload as contiguous memory. The returned memory is 1096195972f6Sopenharmony_ci * either a pointer into the pbuf's payload or, if split over multiple pbufs, 1097195972f6Sopenharmony_ci * a copy into the user-supplied buffer. 1098195972f6Sopenharmony_ci * 1099195972f6Sopenharmony_ci * @param p the pbuf from which to copy data 1100195972f6Sopenharmony_ci * @param buffer the application supplied buffer 1101195972f6Sopenharmony_ci * @param bufsize size of the application supplied buffer 1102195972f6Sopenharmony_ci * @param len length of data to copy (dataptr must be big enough). No more 1103195972f6Sopenharmony_ci * than buf->tot_len will be copied, irrespective of len 1104195972f6Sopenharmony_ci * @param offset offset into the packet buffer from where to begin copying len bytes 1105195972f6Sopenharmony_ci * @return the number of bytes copied, or 0 on failure 1106195972f6Sopenharmony_ci */ 1107195972f6Sopenharmony_civoid * 1108195972f6Sopenharmony_cipbuf_get_contiguous(const struct pbuf *p, void *buffer, size_t bufsize, u16_t len, u16_t offset) 1109195972f6Sopenharmony_ci{ 1110195972f6Sopenharmony_ci const struct pbuf *q; 1111195972f6Sopenharmony_ci u16_t out_offset; 1112195972f6Sopenharmony_ci 1113195972f6Sopenharmony_ci LWIP_ERROR("pbuf_get_contiguous: invalid buf", (p != NULL), return NULL;); 1114195972f6Sopenharmony_ci LWIP_ERROR("pbuf_get_contiguous: invalid dataptr", (buffer != NULL), return NULL;); 1115195972f6Sopenharmony_ci LWIP_ERROR("pbuf_get_contiguous: invalid dataptr", (bufsize >= len), return NULL;); 1116195972f6Sopenharmony_ci 1117195972f6Sopenharmony_ci q = pbuf_skip_const(p, offset, &out_offset); 1118195972f6Sopenharmony_ci if (q != NULL) { 1119195972f6Sopenharmony_ci if (q->len >= (out_offset + len)) { 1120195972f6Sopenharmony_ci /* all data in this pbuf, return zero-copy */ 1121195972f6Sopenharmony_ci return (u8_t *)q->payload + out_offset; 1122195972f6Sopenharmony_ci } 1123195972f6Sopenharmony_ci /* need to copy */ 1124195972f6Sopenharmony_ci if (pbuf_copy_partial(q, buffer, len, out_offset) != len) { 1125195972f6Sopenharmony_ci /* copying failed: pbuf is too short */ 1126195972f6Sopenharmony_ci return NULL; 1127195972f6Sopenharmony_ci } 1128195972f6Sopenharmony_ci return buffer; 1129195972f6Sopenharmony_ci } 1130195972f6Sopenharmony_ci /* pbuf is too short (offset does not fit in) */ 1131195972f6Sopenharmony_ci return NULL; 1132195972f6Sopenharmony_ci} 1133195972f6Sopenharmony_ci 1134195972f6Sopenharmony_ci#if LWIP_TCP && TCP_QUEUE_OOSEQ && LWIP_WND_SCALE 1135195972f6Sopenharmony_ci/** 1136195972f6Sopenharmony_ci * This method modifies a 'pbuf chain', so that its total length is 1137195972f6Sopenharmony_ci * smaller than 64K. The remainder of the original pbuf chain is stored 1138195972f6Sopenharmony_ci * in *rest. 1139195972f6Sopenharmony_ci * This function never creates new pbufs, but splits an existing chain 1140195972f6Sopenharmony_ci * in two parts. The tot_len of the modified packet queue will likely be 1141195972f6Sopenharmony_ci * smaller than 64K. 1142195972f6Sopenharmony_ci * 'packet queues' are not supported by this function. 1143195972f6Sopenharmony_ci * 1144195972f6Sopenharmony_ci * @param p the pbuf queue to be split 1145195972f6Sopenharmony_ci * @param rest pointer to store the remainder (after the first 64K) 1146195972f6Sopenharmony_ci */ 1147195972f6Sopenharmony_civoid pbuf_split_64k(struct pbuf *p, struct pbuf **rest) 1148195972f6Sopenharmony_ci{ 1149195972f6Sopenharmony_ci *rest = NULL; 1150195972f6Sopenharmony_ci if ((p != NULL) && (p->next != NULL)) { 1151195972f6Sopenharmony_ci u16_t tot_len_front = p->len; 1152195972f6Sopenharmony_ci struct pbuf *i = p; 1153195972f6Sopenharmony_ci struct pbuf *r = p->next; 1154195972f6Sopenharmony_ci 1155195972f6Sopenharmony_ci /* continue until the total length (summed up as u16_t) overflows */ 1156195972f6Sopenharmony_ci while ((r != NULL) && ((u16_t)(tot_len_front + r->len) >= tot_len_front)) { 1157195972f6Sopenharmony_ci tot_len_front = (u16_t)(tot_len_front + r->len); 1158195972f6Sopenharmony_ci i = r; 1159195972f6Sopenharmony_ci r = r->next; 1160195972f6Sopenharmony_ci } 1161195972f6Sopenharmony_ci /* i now points to last packet of the first segment. Set next 1162195972f6Sopenharmony_ci pointer to NULL */ 1163195972f6Sopenharmony_ci i->next = NULL; 1164195972f6Sopenharmony_ci 1165195972f6Sopenharmony_ci if (r != NULL) { 1166195972f6Sopenharmony_ci /* Update the tot_len field in the first part */ 1167195972f6Sopenharmony_ci for (i = p; i != NULL; i = i->next) { 1168195972f6Sopenharmony_ci i->tot_len = (u16_t)(i->tot_len - r->tot_len); 1169195972f6Sopenharmony_ci LWIP_ASSERT("tot_len/len mismatch in last pbuf", 1170195972f6Sopenharmony_ci (i->next != NULL) || (i->tot_len == i->len)); 1171195972f6Sopenharmony_ci } 1172195972f6Sopenharmony_ci if (p->flags & PBUF_FLAG_TCP_FIN) { 1173195972f6Sopenharmony_ci r->flags |= PBUF_FLAG_TCP_FIN; 1174195972f6Sopenharmony_ci } 1175195972f6Sopenharmony_ci 1176195972f6Sopenharmony_ci /* tot_len field in rest does not need modifications */ 1177195972f6Sopenharmony_ci /* reference counters do not need modifications */ 1178195972f6Sopenharmony_ci *rest = r; 1179195972f6Sopenharmony_ci } 1180195972f6Sopenharmony_ci } 1181195972f6Sopenharmony_ci} 1182195972f6Sopenharmony_ci#endif /* LWIP_TCP && TCP_QUEUE_OOSEQ && LWIP_WND_SCALE */ 1183195972f6Sopenharmony_ci 1184195972f6Sopenharmony_ci/* Actual implementation of pbuf_skip() but returning const pointer... */ 1185195972f6Sopenharmony_cistatic const struct pbuf * 1186195972f6Sopenharmony_cipbuf_skip_const(const struct pbuf *in, u16_t in_offset, u16_t *out_offset) 1187195972f6Sopenharmony_ci{ 1188195972f6Sopenharmony_ci u16_t offset_left = in_offset; 1189195972f6Sopenharmony_ci const struct pbuf *q = in; 1190195972f6Sopenharmony_ci 1191195972f6Sopenharmony_ci /* get the correct pbuf */ 1192195972f6Sopenharmony_ci while ((q != NULL) && (q->len <= offset_left)) { 1193195972f6Sopenharmony_ci offset_left = (u16_t)(offset_left - q->len); 1194195972f6Sopenharmony_ci q = q->next; 1195195972f6Sopenharmony_ci } 1196195972f6Sopenharmony_ci if (out_offset != NULL) { 1197195972f6Sopenharmony_ci *out_offset = offset_left; 1198195972f6Sopenharmony_ci } 1199195972f6Sopenharmony_ci return q; 1200195972f6Sopenharmony_ci} 1201195972f6Sopenharmony_ci 1202195972f6Sopenharmony_ci/** 1203195972f6Sopenharmony_ci * @ingroup pbuf 1204195972f6Sopenharmony_ci * Skip a number of bytes at the start of a pbuf 1205195972f6Sopenharmony_ci * 1206195972f6Sopenharmony_ci * @param in input pbuf 1207195972f6Sopenharmony_ci * @param in_offset offset to skip 1208195972f6Sopenharmony_ci * @param out_offset resulting offset in the returned pbuf 1209195972f6Sopenharmony_ci * @return the pbuf in the queue where the offset is 1210195972f6Sopenharmony_ci */ 1211195972f6Sopenharmony_cistruct pbuf * 1212195972f6Sopenharmony_cipbuf_skip(struct pbuf *in, u16_t in_offset, u16_t *out_offset) 1213195972f6Sopenharmony_ci{ 1214195972f6Sopenharmony_ci const struct pbuf *out = pbuf_skip_const(in, in_offset, out_offset); 1215195972f6Sopenharmony_ci return LWIP_CONST_CAST(struct pbuf *, out); 1216195972f6Sopenharmony_ci} 1217195972f6Sopenharmony_ci 1218195972f6Sopenharmony_ci/** 1219195972f6Sopenharmony_ci * @ingroup pbuf 1220195972f6Sopenharmony_ci * Copy application supplied data into a pbuf. 1221195972f6Sopenharmony_ci * This function can only be used to copy the equivalent of buf->tot_len data. 1222195972f6Sopenharmony_ci * 1223195972f6Sopenharmony_ci * @param buf pbuf to fill with data 1224195972f6Sopenharmony_ci * @param dataptr application supplied data buffer 1225195972f6Sopenharmony_ci * @param len length of the application supplied data buffer 1226195972f6Sopenharmony_ci * 1227195972f6Sopenharmony_ci * @return ERR_OK if successful, ERR_MEM if the pbuf is not big enough 1228195972f6Sopenharmony_ci */ 1229195972f6Sopenharmony_cierr_t 1230195972f6Sopenharmony_cipbuf_take(struct pbuf *buf, const void *dataptr, u16_t len) 1231195972f6Sopenharmony_ci{ 1232195972f6Sopenharmony_ci struct pbuf *p; 1233195972f6Sopenharmony_ci size_t buf_copy_len; 1234195972f6Sopenharmony_ci size_t total_copy_len = len; 1235195972f6Sopenharmony_ci size_t copied_total = 0; 1236195972f6Sopenharmony_ci 1237195972f6Sopenharmony_ci LWIP_ERROR("pbuf_take: invalid buf", (buf != NULL), return ERR_ARG;); 1238195972f6Sopenharmony_ci LWIP_ERROR("pbuf_take: invalid dataptr", (dataptr != NULL), return ERR_ARG;); 1239195972f6Sopenharmony_ci LWIP_ERROR("pbuf_take: buf not large enough", (buf->tot_len >= len), return ERR_MEM;); 1240195972f6Sopenharmony_ci 1241195972f6Sopenharmony_ci if ((buf == NULL) || (dataptr == NULL) || (buf->tot_len < len)) { 1242195972f6Sopenharmony_ci return ERR_ARG; 1243195972f6Sopenharmony_ci } 1244195972f6Sopenharmony_ci 1245195972f6Sopenharmony_ci /* Note some systems use byte copy if dataptr or one of the pbuf payload pointers are unaligned. */ 1246195972f6Sopenharmony_ci for (p = buf; total_copy_len != 0; p = p->next) { 1247195972f6Sopenharmony_ci LWIP_ASSERT("pbuf_take: invalid pbuf", p != NULL); 1248195972f6Sopenharmony_ci buf_copy_len = total_copy_len; 1249195972f6Sopenharmony_ci if (buf_copy_len > p->len) { 1250195972f6Sopenharmony_ci /* this pbuf cannot hold all remaining data */ 1251195972f6Sopenharmony_ci buf_copy_len = p->len; 1252195972f6Sopenharmony_ci } 1253195972f6Sopenharmony_ci /* copy the necessary parts of the buffer */ 1254195972f6Sopenharmony_ci MEMCPY(p->payload, &((const char *)dataptr)[copied_total], buf_copy_len); 1255195972f6Sopenharmony_ci total_copy_len -= buf_copy_len; 1256195972f6Sopenharmony_ci copied_total += buf_copy_len; 1257195972f6Sopenharmony_ci } 1258195972f6Sopenharmony_ci LWIP_ASSERT("did not copy all data", total_copy_len == 0 && copied_total == len); 1259195972f6Sopenharmony_ci return ERR_OK; 1260195972f6Sopenharmony_ci} 1261195972f6Sopenharmony_ci 1262195972f6Sopenharmony_ci/** 1263195972f6Sopenharmony_ci * @ingroup pbuf 1264195972f6Sopenharmony_ci * Same as pbuf_take() but puts data at an offset 1265195972f6Sopenharmony_ci * 1266195972f6Sopenharmony_ci * @param buf pbuf to fill with data 1267195972f6Sopenharmony_ci * @param dataptr application supplied data buffer 1268195972f6Sopenharmony_ci * @param len length of the application supplied data buffer 1269195972f6Sopenharmony_ci * @param offset offset in pbuf where to copy dataptr to 1270195972f6Sopenharmony_ci * 1271195972f6Sopenharmony_ci * @return ERR_OK if successful, ERR_MEM if the pbuf is not big enough 1272195972f6Sopenharmony_ci */ 1273195972f6Sopenharmony_cierr_t 1274195972f6Sopenharmony_cipbuf_take_at(struct pbuf *buf, const void *dataptr, u16_t len, u16_t offset) 1275195972f6Sopenharmony_ci{ 1276195972f6Sopenharmony_ci u16_t target_offset; 1277195972f6Sopenharmony_ci struct pbuf *q = pbuf_skip(buf, offset, &target_offset); 1278195972f6Sopenharmony_ci 1279195972f6Sopenharmony_ci /* return requested data if pbuf is OK */ 1280195972f6Sopenharmony_ci if ((q != NULL) && (q->tot_len >= target_offset + len)) { 1281195972f6Sopenharmony_ci u16_t remaining_len = len; 1282195972f6Sopenharmony_ci const u8_t *src_ptr = (const u8_t *)dataptr; 1283195972f6Sopenharmony_ci /* copy the part that goes into the first pbuf */ 1284195972f6Sopenharmony_ci u16_t first_copy_len; 1285195972f6Sopenharmony_ci LWIP_ASSERT("check pbuf_skip result", target_offset < q->len); 1286195972f6Sopenharmony_ci first_copy_len = (u16_t)LWIP_MIN(q->len - target_offset, len); 1287195972f6Sopenharmony_ci MEMCPY(((u8_t *)q->payload) + target_offset, dataptr, first_copy_len); 1288195972f6Sopenharmony_ci remaining_len = (u16_t)(remaining_len - first_copy_len); 1289195972f6Sopenharmony_ci src_ptr += first_copy_len; 1290195972f6Sopenharmony_ci if (remaining_len > 0) { 1291195972f6Sopenharmony_ci return pbuf_take(q->next, src_ptr, remaining_len); 1292195972f6Sopenharmony_ci } 1293195972f6Sopenharmony_ci return ERR_OK; 1294195972f6Sopenharmony_ci } 1295195972f6Sopenharmony_ci return ERR_MEM; 1296195972f6Sopenharmony_ci} 1297195972f6Sopenharmony_ci 1298195972f6Sopenharmony_ci/** 1299195972f6Sopenharmony_ci * @ingroup pbuf 1300195972f6Sopenharmony_ci * Creates a single pbuf out of a queue of pbufs. 1301195972f6Sopenharmony_ci * 1302195972f6Sopenharmony_ci * @remark: Either the source pbuf 'p' is freed by this function or the original 1303195972f6Sopenharmony_ci * pbuf 'p' is returned, therefore the caller has to check the result! 1304195972f6Sopenharmony_ci * 1305195972f6Sopenharmony_ci * @param p the source pbuf 1306195972f6Sopenharmony_ci * @param layer pbuf_layer of the new pbuf 1307195972f6Sopenharmony_ci * 1308195972f6Sopenharmony_ci * @return a new, single pbuf (p->next is NULL) 1309195972f6Sopenharmony_ci * or the old pbuf if allocation fails 1310195972f6Sopenharmony_ci */ 1311195972f6Sopenharmony_cistruct pbuf * 1312195972f6Sopenharmony_cipbuf_coalesce(struct pbuf *p, pbuf_layer layer) 1313195972f6Sopenharmony_ci{ 1314195972f6Sopenharmony_ci struct pbuf *q; 1315195972f6Sopenharmony_ci if (p->next == NULL) { 1316195972f6Sopenharmony_ci return p; 1317195972f6Sopenharmony_ci } 1318195972f6Sopenharmony_ci q = pbuf_clone(layer, PBUF_RAM, p); 1319195972f6Sopenharmony_ci if (q == NULL) { 1320195972f6Sopenharmony_ci /* @todo: what do we do now? */ 1321195972f6Sopenharmony_ci return p; 1322195972f6Sopenharmony_ci } 1323195972f6Sopenharmony_ci pbuf_free(p); 1324195972f6Sopenharmony_ci return q; 1325195972f6Sopenharmony_ci} 1326195972f6Sopenharmony_ci 1327195972f6Sopenharmony_ci/** 1328195972f6Sopenharmony_ci * @ingroup pbuf 1329195972f6Sopenharmony_ci * Allocates a new pbuf of same length (via pbuf_alloc()) and copies the source 1330195972f6Sopenharmony_ci * pbuf into this new pbuf (using pbuf_copy()). 1331195972f6Sopenharmony_ci * 1332195972f6Sopenharmony_ci * @param layer pbuf_layer of the new pbuf 1333195972f6Sopenharmony_ci * @param type this parameter decides how and where the pbuf should be allocated 1334195972f6Sopenharmony_ci * (@see pbuf_alloc()) 1335195972f6Sopenharmony_ci * @param p the source pbuf 1336195972f6Sopenharmony_ci * 1337195972f6Sopenharmony_ci * @return a new pbuf or NULL if allocation fails 1338195972f6Sopenharmony_ci */ 1339195972f6Sopenharmony_cistruct pbuf * 1340195972f6Sopenharmony_cipbuf_clone(pbuf_layer layer, pbuf_type type, struct pbuf *p) 1341195972f6Sopenharmony_ci{ 1342195972f6Sopenharmony_ci struct pbuf *q; 1343195972f6Sopenharmony_ci err_t err; 1344195972f6Sopenharmony_ci q = pbuf_alloc(layer, p->tot_len, type); 1345195972f6Sopenharmony_ci if (q == NULL) { 1346195972f6Sopenharmony_ci return NULL; 1347195972f6Sopenharmony_ci } 1348195972f6Sopenharmony_ci err = pbuf_copy(q, p); 1349195972f6Sopenharmony_ci LWIP_UNUSED_ARG(err); /* in case of LWIP_NOASSERT */ 1350195972f6Sopenharmony_ci LWIP_ASSERT("pbuf_copy failed", err == ERR_OK); 1351195972f6Sopenharmony_ci return q; 1352195972f6Sopenharmony_ci} 1353195972f6Sopenharmony_ci 1354195972f6Sopenharmony_ci#if LWIP_CHECKSUM_ON_COPY 1355195972f6Sopenharmony_ci/** 1356195972f6Sopenharmony_ci * Copies data into a single pbuf (*not* into a pbuf queue!) and updates 1357195972f6Sopenharmony_ci * the checksum while copying 1358195972f6Sopenharmony_ci * 1359195972f6Sopenharmony_ci * @param p the pbuf to copy data into 1360195972f6Sopenharmony_ci * @param start_offset offset of p->payload where to copy the data to 1361195972f6Sopenharmony_ci * @param dataptr data to copy into the pbuf 1362195972f6Sopenharmony_ci * @param len length of data to copy into the pbuf 1363195972f6Sopenharmony_ci * @param chksum pointer to the checksum which is updated 1364195972f6Sopenharmony_ci * @return ERR_OK if successful, another error if the data does not fit 1365195972f6Sopenharmony_ci * within the (first) pbuf (no pbuf queues!) 1366195972f6Sopenharmony_ci */ 1367195972f6Sopenharmony_cierr_t 1368195972f6Sopenharmony_cipbuf_fill_chksum(struct pbuf *p, u16_t start_offset, const void *dataptr, 1369195972f6Sopenharmony_ci u16_t len, u16_t *chksum) 1370195972f6Sopenharmony_ci{ 1371195972f6Sopenharmony_ci u32_t acc; 1372195972f6Sopenharmony_ci u16_t copy_chksum; 1373195972f6Sopenharmony_ci char *dst_ptr; 1374195972f6Sopenharmony_ci LWIP_ASSERT("p != NULL", p != NULL); 1375195972f6Sopenharmony_ci LWIP_ASSERT("dataptr != NULL", dataptr != NULL); 1376195972f6Sopenharmony_ci LWIP_ASSERT("chksum != NULL", chksum != NULL); 1377195972f6Sopenharmony_ci LWIP_ASSERT("len != 0", len != 0); 1378195972f6Sopenharmony_ci 1379195972f6Sopenharmony_ci if ((start_offset >= p->len) || (start_offset + len > p->len)) { 1380195972f6Sopenharmony_ci return ERR_ARG; 1381195972f6Sopenharmony_ci } 1382195972f6Sopenharmony_ci 1383195972f6Sopenharmony_ci dst_ptr = ((char *)p->payload) + start_offset; 1384195972f6Sopenharmony_ci copy_chksum = LWIP_CHKSUM_COPY(dst_ptr, dataptr, len); 1385195972f6Sopenharmony_ci if ((start_offset & 1) != 0) { 1386195972f6Sopenharmony_ci copy_chksum = SWAP_BYTES_IN_WORD(copy_chksum); 1387195972f6Sopenharmony_ci } 1388195972f6Sopenharmony_ci acc = *chksum; 1389195972f6Sopenharmony_ci acc += copy_chksum; 1390195972f6Sopenharmony_ci *chksum = FOLD_U32T(acc); 1391195972f6Sopenharmony_ci return ERR_OK; 1392195972f6Sopenharmony_ci} 1393195972f6Sopenharmony_ci#endif /* LWIP_CHECKSUM_ON_COPY */ 1394195972f6Sopenharmony_ci 1395195972f6Sopenharmony_ci/** 1396195972f6Sopenharmony_ci * @ingroup pbuf 1397195972f6Sopenharmony_ci * Get one byte from the specified position in a pbuf 1398195972f6Sopenharmony_ci * WARNING: returns zero for offset >= p->tot_len 1399195972f6Sopenharmony_ci * 1400195972f6Sopenharmony_ci * @param p pbuf to parse 1401195972f6Sopenharmony_ci * @param offset offset into p of the byte to return 1402195972f6Sopenharmony_ci * @return byte at an offset into p OR ZERO IF 'offset' >= p->tot_len 1403195972f6Sopenharmony_ci */ 1404195972f6Sopenharmony_ciu8_t 1405195972f6Sopenharmony_cipbuf_get_at(const struct pbuf *p, u16_t offset) 1406195972f6Sopenharmony_ci{ 1407195972f6Sopenharmony_ci int ret = pbuf_try_get_at(p, offset); 1408195972f6Sopenharmony_ci if (ret >= 0) { 1409195972f6Sopenharmony_ci return (u8_t)ret; 1410195972f6Sopenharmony_ci } 1411195972f6Sopenharmony_ci return 0; 1412195972f6Sopenharmony_ci} 1413195972f6Sopenharmony_ci 1414195972f6Sopenharmony_ci/** 1415195972f6Sopenharmony_ci * @ingroup pbuf 1416195972f6Sopenharmony_ci * Get one byte from the specified position in a pbuf 1417195972f6Sopenharmony_ci * 1418195972f6Sopenharmony_ci * @param p pbuf to parse 1419195972f6Sopenharmony_ci * @param offset offset into p of the byte to return 1420195972f6Sopenharmony_ci * @return byte at an offset into p [0..0xFF] OR negative if 'offset' >= p->tot_len 1421195972f6Sopenharmony_ci */ 1422195972f6Sopenharmony_ciint 1423195972f6Sopenharmony_cipbuf_try_get_at(const struct pbuf *p, u16_t offset) 1424195972f6Sopenharmony_ci{ 1425195972f6Sopenharmony_ci u16_t q_idx; 1426195972f6Sopenharmony_ci const struct pbuf *q = pbuf_skip_const(p, offset, &q_idx); 1427195972f6Sopenharmony_ci 1428195972f6Sopenharmony_ci /* return requested data if pbuf is OK */ 1429195972f6Sopenharmony_ci if ((q != NULL) && (q->len > q_idx)) { 1430195972f6Sopenharmony_ci return ((u8_t *)q->payload)[q_idx]; 1431195972f6Sopenharmony_ci } 1432195972f6Sopenharmony_ci return -1; 1433195972f6Sopenharmony_ci} 1434195972f6Sopenharmony_ci 1435195972f6Sopenharmony_ci/** 1436195972f6Sopenharmony_ci * @ingroup pbuf 1437195972f6Sopenharmony_ci * Put one byte to the specified position in a pbuf 1438195972f6Sopenharmony_ci * WARNING: silently ignores offset >= p->tot_len 1439195972f6Sopenharmony_ci * 1440195972f6Sopenharmony_ci * @param p pbuf to fill 1441195972f6Sopenharmony_ci * @param offset offset into p of the byte to write 1442195972f6Sopenharmony_ci * @param data byte to write at an offset into p 1443195972f6Sopenharmony_ci */ 1444195972f6Sopenharmony_civoid 1445195972f6Sopenharmony_cipbuf_put_at(struct pbuf *p, u16_t offset, u8_t data) 1446195972f6Sopenharmony_ci{ 1447195972f6Sopenharmony_ci u16_t q_idx; 1448195972f6Sopenharmony_ci struct pbuf *q = pbuf_skip(p, offset, &q_idx); 1449195972f6Sopenharmony_ci 1450195972f6Sopenharmony_ci /* write requested data if pbuf is OK */ 1451195972f6Sopenharmony_ci if ((q != NULL) && (q->len > q_idx)) { 1452195972f6Sopenharmony_ci ((u8_t *)q->payload)[q_idx] = data; 1453195972f6Sopenharmony_ci } 1454195972f6Sopenharmony_ci} 1455195972f6Sopenharmony_ci 1456195972f6Sopenharmony_ci/** 1457195972f6Sopenharmony_ci * @ingroup pbuf 1458195972f6Sopenharmony_ci * Compare pbuf contents at specified offset with memory s2, both of length n 1459195972f6Sopenharmony_ci * 1460195972f6Sopenharmony_ci * @param p pbuf to compare 1461195972f6Sopenharmony_ci * @param offset offset into p at which to start comparing 1462195972f6Sopenharmony_ci * @param s2 buffer to compare 1463195972f6Sopenharmony_ci * @param n length of buffer to compare 1464195972f6Sopenharmony_ci * @return zero if equal, nonzero otherwise 1465195972f6Sopenharmony_ci * (0xffff if p is too short, diffoffset+1 otherwise) 1466195972f6Sopenharmony_ci */ 1467195972f6Sopenharmony_ciu16_t 1468195972f6Sopenharmony_cipbuf_memcmp(const struct pbuf *p, u16_t offset, const void *s2, u16_t n) 1469195972f6Sopenharmony_ci{ 1470195972f6Sopenharmony_ci u16_t start = offset; 1471195972f6Sopenharmony_ci const struct pbuf *q = p; 1472195972f6Sopenharmony_ci u16_t i; 1473195972f6Sopenharmony_ci 1474195972f6Sopenharmony_ci /* pbuf long enough to perform check? */ 1475195972f6Sopenharmony_ci if (p->tot_len < (offset + n)) { 1476195972f6Sopenharmony_ci return 0xffff; 1477195972f6Sopenharmony_ci } 1478195972f6Sopenharmony_ci 1479195972f6Sopenharmony_ci /* get the correct pbuf from chain. We know it succeeds because of p->tot_len check above. */ 1480195972f6Sopenharmony_ci while ((q != NULL) && (q->len <= start)) { 1481195972f6Sopenharmony_ci start = (u16_t)(start - q->len); 1482195972f6Sopenharmony_ci q = q->next; 1483195972f6Sopenharmony_ci } 1484195972f6Sopenharmony_ci 1485195972f6Sopenharmony_ci /* return requested data if pbuf is OK */ 1486195972f6Sopenharmony_ci for (i = 0; i < n; i++) { 1487195972f6Sopenharmony_ci /* We know pbuf_get_at() succeeds because of p->tot_len check above. */ 1488195972f6Sopenharmony_ci u8_t a = pbuf_get_at(q, (u16_t)(start + i)); 1489195972f6Sopenharmony_ci u8_t b = ((const u8_t *)s2)[i]; 1490195972f6Sopenharmony_ci if (a != b) { 1491195972f6Sopenharmony_ci return (u16_t)LWIP_MIN(i + 1, 0xFFFF); 1492195972f6Sopenharmony_ci } 1493195972f6Sopenharmony_ci } 1494195972f6Sopenharmony_ci return 0; 1495195972f6Sopenharmony_ci} 1496195972f6Sopenharmony_ci 1497195972f6Sopenharmony_ci/** 1498195972f6Sopenharmony_ci * @ingroup pbuf 1499195972f6Sopenharmony_ci * Find occurrence of mem (with length mem_len) in pbuf p, starting at offset 1500195972f6Sopenharmony_ci * start_offset. 1501195972f6Sopenharmony_ci * 1502195972f6Sopenharmony_ci * @param p pbuf to search, maximum length is 0xFFFE since 0xFFFF is used as 1503195972f6Sopenharmony_ci * return value 'not found' 1504195972f6Sopenharmony_ci * @param mem search for the contents of this buffer 1505195972f6Sopenharmony_ci * @param mem_len length of 'mem' 1506195972f6Sopenharmony_ci * @param start_offset offset into p at which to start searching 1507195972f6Sopenharmony_ci * @return 0xFFFF if substr was not found in p or the index where it was found 1508195972f6Sopenharmony_ci */ 1509195972f6Sopenharmony_ciu16_t 1510195972f6Sopenharmony_cipbuf_memfind(const struct pbuf *p, const void *mem, u16_t mem_len, u16_t start_offset) 1511195972f6Sopenharmony_ci{ 1512195972f6Sopenharmony_ci u16_t i; 1513195972f6Sopenharmony_ci u16_t max_cmp_start = (u16_t)(p->tot_len - mem_len); 1514195972f6Sopenharmony_ci if (p->tot_len >= mem_len + start_offset) { 1515195972f6Sopenharmony_ci for (i = start_offset; i <= max_cmp_start; i++) { 1516195972f6Sopenharmony_ci u16_t plus = pbuf_memcmp(p, i, mem, mem_len); 1517195972f6Sopenharmony_ci if (plus == 0) { 1518195972f6Sopenharmony_ci return i; 1519195972f6Sopenharmony_ci } 1520195972f6Sopenharmony_ci } 1521195972f6Sopenharmony_ci } 1522195972f6Sopenharmony_ci return 0xFFFF; 1523195972f6Sopenharmony_ci} 1524195972f6Sopenharmony_ci 1525195972f6Sopenharmony_ci/** 1526195972f6Sopenharmony_ci * Find occurrence of substr with length substr_len in pbuf p, start at offset 1527195972f6Sopenharmony_ci * start_offset 1528195972f6Sopenharmony_ci * WARNING: in contrast to strstr(), this one does not stop at the first \0 in 1529195972f6Sopenharmony_ci * the pbuf/source string! 1530195972f6Sopenharmony_ci * 1531195972f6Sopenharmony_ci * @param p pbuf to search, maximum length is 0xFFFE since 0xFFFF is used as 1532195972f6Sopenharmony_ci * return value 'not found' 1533195972f6Sopenharmony_ci * @param substr string to search for in p, maximum length is 0xFFFE 1534195972f6Sopenharmony_ci * @return 0xFFFF if substr was not found in p or the index where it was found 1535195972f6Sopenharmony_ci */ 1536195972f6Sopenharmony_ciu16_t 1537195972f6Sopenharmony_cipbuf_strstr(const struct pbuf *p, const char *substr) 1538195972f6Sopenharmony_ci{ 1539195972f6Sopenharmony_ci size_t substr_len; 1540195972f6Sopenharmony_ci if ((substr == NULL) || (substr[0] == 0) || (p->tot_len == 0xFFFF)) { 1541195972f6Sopenharmony_ci return 0xFFFF; 1542195972f6Sopenharmony_ci } 1543195972f6Sopenharmony_ci substr_len = strlen(substr); 1544195972f6Sopenharmony_ci if (substr_len >= 0xFFFF) { 1545195972f6Sopenharmony_ci return 0xFFFF; 1546195972f6Sopenharmony_ci } 1547195972f6Sopenharmony_ci return pbuf_memfind(p, substr, (u16_t)substr_len, 0); 1548195972f6Sopenharmony_ci} 1549