/** * Allocate memory: determine the smallest pool that is big enough * to contain an element of 'size' and get an element from that pool. * * @param size the size in bytes of the memory needed * @return a pointer to the allocated memory or NULL if the pool is empty */ void * mem_malloc(mem_size_t size) { void *ret; struct memp_malloc_helper *element; memp_t poolnr; mem_size_t required_size = size + LWIP_MEM_ALIGN_SIZE(sizeof(struct memp_malloc_helper)); for (poolnr = MEMP_POOL_FIRST; poolnr <= MEMP_POOL_LAST; poolnr = (memp_t)(poolnr + 1)) { #if MEM_USE_POOLS_TRY_BIGGER_POOL again: #endif /* MEM_USE_POOLS_TRY_BIGGER_POOL */ /* is this pool big enough to hold an element of the required size plus a struct memp_malloc_helper that saves the pool this element came from? */ if (required_size <= memp_pools[poolnr]->size) { break; } } if (poolnr > MEMP_POOL_LAST) { LWIP_ASSERT("mem_malloc(): no pool is that big!", 0); MEM_STATS_INC(err); return NULL; } element = (struct memp_malloc_helper*)memp_malloc(poolnr); if (element == NULL) { /* No need to DEBUGF or ASSERT: This error is already taken care of in memp.c */ #if MEM_USE_POOLS_TRY_BIGGER_POOL /** Try a bigger pool if this one is empty! */ if (poolnr < MEMP_POOL_LAST) { poolnr++; goto again; } #endif /* MEM_USE_POOLS_TRY_BIGGER_POOL */ MEM_STATS_INC(err); return NULL; } /* save the pool number this element came from */ element->poolnr = poolnr; /* and return a pointer to the memory directly after the struct memp_malloc_helper */ ret = (u8_t*)element + LWIP_MEM_ALIGN_SIZE(sizeof(struct memp_malloc_helper)); #if MEMP_OVERFLOW_CHECK || (LWIP_STATS && MEM_STATS) /* truncating to u16_t is safe because struct memp_desc::size is u16_t */ element->size = (u16_t)size; MEM_STATS_INC_USED(used, element->size); #endif /* MEMP_OVERFLOW_CHECK || (LWIP_STATS && MEM_STATS) */ #if MEMP_OVERFLOW_CHECK /* initialize unused memory (diff between requested size and selected pool's size) */ memset((u8_t*)ret + size, 0xcd, memp_pools[poolnr]->size - size); #endif /* MEMP_OVERFLOW_CHECK */ return ret; }
/** * @ingroup pbuf * Initialize a custom pbuf (already allocated). * * @param l flag to define header size * @param length size of the pbuf's payload * @param type type of the pbuf (only used to treat the pbuf accordingly, as * this function allocates no memory) * @param p pointer to the custom pbuf to initialize (already allocated) * @param payload_mem pointer to the buffer that is used for payload and headers, * must be at least big enough to hold 'length' plus the header size, * may be NULL if set later. * ATTENTION: The caller is responsible for correct alignment of this buffer!! * @param payload_mem_len the size of the 'payload_mem' buffer, must be at least * big enough to hold 'length' plus the header size */ struct pbuf* pbuf_alloced_custom(pbuf_layer l, u16_t length, pbuf_type type, struct pbuf_custom *p, void *payload_mem, u16_t payload_mem_len) { u16_t offset; LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_alloced_custom(length=%"U16_F")\n", length)); /* determine header offset */ switch (l) { case PBUF_TRANSPORT: /* add room for transport (often TCP) layer header */ offset = PBUF_LINK_ENCAPSULATION_HLEN + PBUF_LINK_HLEN + PBUF_IP_HLEN + PBUF_TRANSPORT_HLEN; break; case PBUF_IP: /* add room for IP layer header */ offset = PBUF_LINK_ENCAPSULATION_HLEN + PBUF_LINK_HLEN + PBUF_IP_HLEN; break; case PBUF_LINK: /* add room for link layer header */ offset = PBUF_LINK_ENCAPSULATION_HLEN + PBUF_LINK_HLEN; break; case PBUF_RAW_TX: /* add room for encapsulating link layer headers (e.g. 802.11) */ offset = PBUF_LINK_ENCAPSULATION_HLEN; break; case PBUF_RAW: offset = 0; break; default: LWIP_ASSERT("pbuf_alloced_custom: bad pbuf layer", 0); return NULL; } if (LWIP_MEM_ALIGN_SIZE(offset) + length > payload_mem_len) { LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_LEVEL_WARNING, ("pbuf_alloced_custom(length=%"U16_F") buffer too short\n", length)); return NULL; } p->pbuf.next = NULL; if (payload_mem != NULL) { p->pbuf.payload = (u8_t *)payload_mem + LWIP_MEM_ALIGN_SIZE(offset); } else { p->pbuf.payload = NULL; } p->pbuf.flags = PBUF_FLAG_IS_CUSTOM; p->pbuf.len = p->pbuf.tot_len = length; p->pbuf.type = type; p->pbuf.ref = 1; return &p->pbuf; }
struct pbuf * pbuf_alloc(pbuf_layer layer, u16_t length, pbuf_type type) { struct pbuf *p; u16_t offset = 0; offset += 16; /* If pbuf is to be allocated in RAM, allocate memory for it. */ p = (struct pbuf*)rt_malloc(LWIP_MEM_ALIGN_SIZE(SIZEOF_STRUCT_PBUF + offset) + LWIP_MEM_ALIGN_SIZE(length)); if (p == RT_NULL) return RT_NULL; /* Set up internal structure of the pbuf. */ p->payload = LWIP_MEM_ALIGN((void *)((u8_t *)p + SIZEOF_STRUCT_PBUF + offset)); p->len = length; return p; }
/** * Free memory previously allocated by mem_malloc. Loads the pool number * and calls memp_free with that pool number to put the element back into * its pool * * @param rmem the memory element to free */ void mem_free(void *rmem) { struct memp_malloc_helper *hmem; LWIP_ASSERT("rmem != NULL", (rmem != NULL)); LWIP_ASSERT("rmem == MEM_ALIGN(rmem)", (rmem == LWIP_MEM_ALIGN(rmem))); /* get the original struct memp_malloc_helper */ /* cast through void* to get rid of alignment warnings */ hmem = (struct memp_malloc_helper*)(void*)((u8_t*)rmem - LWIP_MEM_ALIGN_SIZE(sizeof(struct memp_malloc_helper))); LWIP_ASSERT("hmem != NULL", (hmem != NULL)); LWIP_ASSERT("hmem == MEM_ALIGN(hmem)", (hmem == LWIP_MEM_ALIGN(hmem))); LWIP_ASSERT("hmem->poolnr < MEMP_MAX", (hmem->poolnr < MEMP_MAX)); MEM_STATS_DEC_USED(used, hmem->size); #if MEMP_OVERFLOW_CHECK { u16_t i; LWIP_ASSERT("MEM_USE_POOLS: invalid chunk size", hmem->size <= memp_pools[hmem->poolnr]->size); /* check that unused memory remained untouched (diff between requested size and selected pool's size) */ for (i = hmem->size; i < memp_pools[hmem->poolnr]->size; i++) { u8_t data = *((u8_t*)rmem + i); LWIP_ASSERT("MEM_USE_POOLS: mem overflow detected", data == 0xcd); } } #endif /* MEMP_OVERFLOW_CHECK */ /* and put it in the pool we saved earlier */ memp_free(hmem->poolnr, hmem); }
/** * Free memory previously allocated by mem_malloc. Loads the pool number * and calls memp_free with that pool number to put the element back into * its pool * * @param rmem the memory element to free */ void mem_free(void *rmem) { struct memp_malloc_helper *hmem; LWIP_ASSERT("rmem != NULL", (rmem != NULL)); LWIP_ASSERT("rmem == MEM_ALIGN(rmem)", (rmem == LWIP_MEM_ALIGN(rmem))); /* get the original struct memp_malloc_helper */ hmem = (struct memp_malloc_helper*)(void*)((u8_t*)rmem - LWIP_MEM_ALIGN_SIZE(sizeof(struct memp_malloc_helper))); LWIP_ASSERT("hmem != NULL", (hmem != NULL)); LWIP_ASSERT("hmem == MEM_ALIGN(hmem)", (hmem == LWIP_MEM_ALIGN(hmem))); LWIP_ASSERT("hmem->poolnr < MEMP_MAX", (hmem->poolnr < MEMP_MAX)); #if MEMP_OVERFLOW_CHECK { u16_t i; LWIP_ASSERT("MEM_USE_POOLS: invalid chunk size", hmem->size <= memp_sizes[hmem->poolnr]); /* check that unused memory remained untouched */ for (i = hmem->size; i < memp_sizes[hmem->poolnr]; i++) { u8_t data = *((u8_t*)rmem + i); LWIP_ASSERT("MEM_USE_POOLS: mem overflow detected", data == 0xcd); } } #endif /* MEMP_OVERFLOW_CHECK */ /* and put it in the pool we saved earlier */ memp_free(hmem->poolnr, hmem); }
/** * Allocate memory: determine the smallest pool that is big enough * to contain an element of 'size' and get an element from that pool. * * @param size the size in bytes of the memory needed * @return a pointer to the allocated memory or NULL if the pool is empty */ void *mem_malloc(mem_size_t size) { void *ret; struct memp_malloc_helper *element; memp_t poolnr; mem_size_t required_size = size + LWIP_MEM_ALIGN_SIZE(sizeof(struct memp_malloc_helper)); for (poolnr = MEMP_POOL_FIRST; poolnr <= MEMP_POOL_LAST; poolnr = (memp_t)(poolnr + 1)) { #if MEM_USE_POOLS_TRY_BIGGER_POOL again: #endif /* MEM_USE_POOLS_TRY_BIGGER_POOL */ /* is this pool big enough to hold an element of the required size plus a struct memp_malloc_helper that saves the pool this element came from? */ if (required_size <= memp_sizes[poolnr]) { break; } } if (poolnr > MEMP_POOL_LAST) { LWIP_ASSERT("mem_malloc(): no pool is that big!", 0); return NULL; } element = (struct memp_malloc_helper *) memp_malloc(poolnr); if (element == NULL) { /* No need to DEBUGF or ASSERT: This error is already taken care of in memp.c */ #if MEM_USE_POOLS_TRY_BIGGER_POOL /** Try a bigger pool if this one is empty! */ if (poolnr < MEMP_POOL_LAST) { poolnr++; goto again; } #endif /* MEM_USE_POOLS_TRY_BIGGER_POOL */ return NULL; } /* save the pool number this element came from */ element->poolnr = poolnr; /* and return a pointer to the memory directly after the struct * memp_malloc_helper */ ret = (u8_t *) element + LWIP_MEM_ALIGN_SIZE(sizeof(struct memp_malloc_helper)); return ret; }
//lwip内核部分,内存申请 //返回值:0,成功; // 其他,失败 u8 lwip_comm_mem_malloc(void) { u32 mempsize; u32 ramheapsize; mempsize=memp_get_memorysize(); //得到memp_memory数组大小 memp_memory=mymalloc(SRAMIN,mempsize); //为memp_memory申请内存 ramheapsize=LWIP_MEM_ALIGN_SIZE(MEM_SIZE)+2*LWIP_MEM_ALIGN_SIZE(4*3)+MEM_ALIGNMENT;//得到ram heap大小 ram_heap=mymalloc(SRAMIN,ramheapsize); //为ram_heap申请内存 TCPIP_THREAD_TASK_STK=mymalloc(SRAMIN,TCPIP_THREAD_STACKSIZE*4);//给内核任务申请堆栈 LWIP_DHCP_TASK_STK=mymalloc(SRAMIN,LWIP_DHCP_STK_SIZE*4); //给dhcp任务堆栈申请内存空间 if(!memp_memory||!ram_heap||!TCPIP_THREAD_TASK_STK||!LWIP_DHCP_TASK_STK)//有申请失败的 { lwip_comm_mem_free(); return 1; } return 0; }
/** * @ingroup pbuf * Initialize a custom pbuf (already allocated). * Example of custom pbuf usage: @ref zerocopyrx * * @param l header size * @param length size of the pbuf's payload * @param type type of the pbuf (only used to treat the pbuf accordingly, as * this function allocates no memory) * @param p pointer to the custom pbuf to initialize (already allocated) * @param payload_mem pointer to the buffer that is used for payload and headers, * must be at least big enough to hold 'length' plus the header size, * may be NULL if set later. * ATTENTION: The caller is responsible for correct alignment of this buffer!! * @param payload_mem_len the size of the 'payload_mem' buffer, must be at least * big enough to hold 'length' plus the header size */ struct pbuf * pbuf_alloced_custom(pbuf_layer l, u16_t length, pbuf_type type, struct pbuf_custom *p, void *payload_mem, u16_t payload_mem_len) { u16_t offset = (u16_t)l; void *payload; LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_alloced_custom(length=%"U16_F")\n", length)); if (LWIP_MEM_ALIGN_SIZE(offset) + length > payload_mem_len) { LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_LEVEL_WARNING, ("pbuf_alloced_custom(length=%"U16_F") buffer too short\n", length)); return NULL; } if (payload_mem != NULL) { payload = (u8_t *)payload_mem + LWIP_MEM_ALIGN_SIZE(offset); } else { payload = NULL; } pbuf_init_alloced_pbuf(&p->pbuf, payload, length, length, type, PBUF_FLAG_IS_CUSTOM); return &p->pbuf; }
//lwip内核部分,内存申请 //返回值:0,成功; // 其他,失败 uint8_t lwip_comm_mem_malloc(void) { uint32_t mempsize; uint32_t ramheapsize; mempsize=memp_get_memorysize(); //得到memp_memory数组大小 memp_memory=mymalloc(SRAMEX,mempsize); //为memp_memory申请内存 printf("memp_memory内存大小为:%d\r\n",mempsize); ramheapsize=LWIP_MEM_ALIGN_SIZE(MEM_SIZE)+2*LWIP_MEM_ALIGN_SIZE(4*3)+MEM_ALIGNMENT;//得到ram heap大小 ram_heap=mymalloc(SRAMEX,ramheapsize); //为ram_heap申请内存 printf("ram_heap内存大小为:%d\r\n",ramheapsize); TCPIP_THREAD_TASK_STK=mymalloc(SRAMEX,TCPIP_THREAD_STACKSIZE*4); //给内核任务申请堆栈 //LWIP_DHCP_TASK_STK=mymalloc(SRAMEX,LWIP_DHCP_STK_SIZE*4); //给dhcp任务申请堆栈 if(!memp_memory) { lwip_comm_mem_free(); return 1; } if(!ram_heap) { lwip_comm_mem_free(); return 1; } if(!TCPIP_THREAD_TASK_STK) { lwip_comm_mem_free(); return 1; } // if((!memp_memory)||(!ram_heap)||(!TCPIP_THREAD_TASK_STK));//||!LWIP_DHCP_TASK_STK) //有申请失败的 // { // lwip_comm_mem_free(); // return 1; // } return 0; }
static struct pbuf *ICACHE_FLASH_ATTR tcp_pbuf_prealloc(pbuf_layer layer, u16_t length, u16_t max_length, u16_t *oversize, struct tcp_pcb *pcb, u8_t apiflags, u8_t first_seg) { struct pbuf *p; u16_t alloc = length; #if LWIP_NETIF_TX_SINGLE_PBUF LWIP_UNUSED_ARG(max_length); LWIP_UNUSED_ARG(pcb); LWIP_UNUSED_ARG(apiflags); LWIP_UNUSED_ARG(first_seg); /* always create MSS-sized pbufs */ alloc = TCP_MSS; #else /* LWIP_NETIF_TX_SINGLE_PBUF */ if (length < max_length) { /* Should we allocate an oversized pbuf, or just the minimum * length required? If tcp_write is going to be called again * before this segment is transmitted, we want the oversized * buffer. If the segment will be transmitted immediately, we can * save memory by allocating only length. We use a simple * heuristic based on the following information: * * Did the user set TCP_WRITE_FLAG_MORE? * * Will the Nagle algorithm defer transmission of this segment? */ if ((apiflags & TCP_WRITE_FLAG_MORE) || (!(pcb->flags & TF_NODELAY) && (!first_seg || pcb->unsent != NULL || pcb->unacked != NULL))) { alloc = LWIP_MIN(max_length, LWIP_MEM_ALIGN_SIZE(length + TCP_OVERSIZE)); } } #endif /* LWIP_NETIF_TX_SINGLE_PBUF */ p = pbuf_alloc(layer, alloc, PBUF_RAM); if (p == NULL) { return NULL; } LWIP_ASSERT("need unchained pbuf", p->next == NULL); *oversize = p->len - length; /* trim p->len to the currently used size */ p->len = p->tot_len = length; return p; }
/** * Free memory previously allocated by mem_malloc. Loads the pool number * and calls memp_free with that pool number to put the element back into * its pool * * @param rmem the memory element to free */ void mem_free(void *rmem) { struct memp_malloc_helper *hmem; LWIP_ASSERT("rmem != NULL", (rmem != NULL)); LWIP_ASSERT("rmem == MEM_ALIGN(rmem)", (rmem == LWIP_MEM_ALIGN(rmem))); /* get the original struct memp_malloc_helper */ hmem = (struct memp_malloc_helper*)(void*)((u8_t*)rmem - LWIP_MEM_ALIGN_SIZE(sizeof(struct memp_malloc_helper))); LWIP_ASSERT("hmem != NULL", (hmem != NULL)); LWIP_ASSERT("hmem == MEM_ALIGN(hmem)", (hmem == LWIP_MEM_ALIGN(hmem))); LWIP_ASSERT("hmem->poolnr < MEMP_MAX", (hmem->poolnr < MEMP_MAX)); /* and put it in the pool we saved earlier */ memp_free(hmem->poolnr, hmem); }
/** * Zero the heap and initialize start, end and lowest-free */ void mem_init(LWIP_MEM_CFG *mem_cfg) { struct mem *mem; BOOL en = mem_cfg->enable; UINT8 *start = mem_cfg->start; UINT32 length = LWIP_MEM_ALIGN_SIZE(mem_cfg->length); LWIP_ASSERT("Sanity check alignment", (SIZEOF_STRUCT_MEM & (MEM_ALIGNMENT-1)) == 0); if(en == TRUE) { ram_heap = (UINT8 *)start; MEM_SIZE_ALIGNED = (length - (2*SIZEOF_STRUCT_MEM) - MEM_ALIGNMENT); } else { ram_heap = (UINT8 *)MALLOC(MEM_SIZE); MEM_SIZE_ALIGNED = (MEM_SIZE - (2*SIZEOF_STRUCT_MEM) - MEM_ALIGNMENT); } /* align the heap */ ram = LWIP_MEM_ALIGN(ram_heap); /* initialize the start of the heap */ mem = (struct mem *)ram; mem->next = MEM_SIZE_ALIGNED; mem->prev = 0; mem->used = 0; /* initialize the end of the heap */ ram_end = (struct mem *)&ram[MEM_SIZE_ALIGNED]; ram_end->used = 1; ram_end->next = MEM_SIZE_ALIGNED; ram_end->prev = MEM_SIZE_ALIGNED; mem_sem = sys_sem_new(1); /* initialize the lowest-free pointer to the start of the heap */ lfree = (struct mem *)ram; #if MEM_STATS lwip_stats.mem.avail = MEM_SIZE_ALIGNED; #endif /* MEM_STATS */ }
/** * @ingroup pbuf * Allocates a pbuf of the given type (possibly a chain for PBUF_POOL type). * * 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). * * @param layer flag to define header size * @param length size of the pbuf's payload * @param type this 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. * - PBUF_ROM: no buffer memory is allocated for the pbuf, even for * protocol headers. Additional headers must be prepended * by allocating another pbuf and chain in to the front of * 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_take should be called to copy the buffer. * - PBUF_POOL: the pbuf is allocated as a pbuf chain, with pbufs from * the pbuf pool that is allocated during pbuf_init(). * * @return the allocated pbuf. If multiple pbufs where allocated, this * is the first pbuf of a pbuf chain. */ struct pbuf * pbuf_alloc(pbuf_layer layer, u16_t length, pbuf_type type) { struct pbuf *p, *q, *r; u16_t offset; s32_t rem_len; /* remaining length */ LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_alloc(length=%"U16_F")\n", length)); /* determine header offset */ switch (layer) { case PBUF_TRANSPORT: /* add room for transport (often TCP) layer header */ offset = PBUF_LINK_ENCAPSULATION_HLEN + PBUF_LINK_HLEN + PBUF_IP_HLEN + PBUF_TRANSPORT_HLEN; break; case PBUF_IP: /* add room for IP layer header */ offset = PBUF_LINK_ENCAPSULATION_HLEN + PBUF_LINK_HLEN + PBUF_IP_HLEN; break; case PBUF_LINK: /* add room for link layer header */ offset = PBUF_LINK_ENCAPSULATION_HLEN + PBUF_LINK_HLEN; break; case PBUF_RAW_TX: /* add room for encapsulating link layer headers (e.g. 802.11) */ offset = PBUF_LINK_ENCAPSULATION_HLEN; break; case PBUF_RAW: /* no offset (e.g. RX buffers or chain successors) */ offset = 0; break; default: LWIP_ASSERT("pbuf_alloc: bad pbuf layer", 0); return NULL; } switch (type) { case PBUF_POOL: /* allocate head of pbuf chain into p */ p = (struct pbuf *)memp_malloc(MEMP_PBUF_POOL); LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_alloc: allocated pbuf %p\n", (void *)p)); if (p == NULL) { PBUF_POOL_IS_EMPTY(); return NULL; } p->type = type; p->next = NULL; p->if_idx = NETIF_NO_INDEX; /* make the payload pointer point 'offset' bytes into pbuf data memory */ p->payload = LWIP_MEM_ALIGN((void *)((u8_t *)p + (SIZEOF_STRUCT_PBUF + offset))); LWIP_ASSERT("pbuf_alloc: pbuf p->payload properly aligned", ((mem_ptr_t)p->payload % MEM_ALIGNMENT) == 0); /* the total length of the pbuf chain is the requested size */ p->tot_len = length; /* set the length of the first pbuf in the chain */ p->len = LWIP_MIN(length, PBUF_POOL_BUFSIZE_ALIGNED - LWIP_MEM_ALIGN_SIZE(offset)); LWIP_ASSERT("check p->payload + p->len does not overflow pbuf", ((u8_t*)p->payload + p->len <= (u8_t*)p + SIZEOF_STRUCT_PBUF + PBUF_POOL_BUFSIZE_ALIGNED)); LWIP_ASSERT("PBUF_POOL_BUFSIZE must be bigger than MEM_ALIGNMENT", (PBUF_POOL_BUFSIZE_ALIGNED - LWIP_MEM_ALIGN_SIZE(offset)) > 0 ); /* set reference count (needed here in case we fail) */ p->ref = 1; /* now allocate the tail of the pbuf chain */ /* remember first pbuf for linkage in next iteration */ r = p; /* remaining length to be allocated */ rem_len = length - p->len; /* any remaining pbufs to be allocated? */ while (rem_len > 0) { q = (struct pbuf *)memp_malloc(MEMP_PBUF_POOL); if (q == NULL) { PBUF_POOL_IS_EMPTY(); /* free chain so far allocated */ pbuf_free(p); /* bail out unsuccessfully */ return NULL; } q->type = type; q->flags = 0; q->next = NULL; /* make previous pbuf point to this pbuf */ r->next = q; /* set total length of this pbuf and next in chain */ LWIP_ASSERT("rem_len < max_u16_t", rem_len < 0xffff); q->tot_len = (u16_t)rem_len; /* this pbuf length is pool size, unless smaller sized tail */ q->len = LWIP_MIN((u16_t)rem_len, PBUF_POOL_BUFSIZE_ALIGNED); q->payload = (void *)((u8_t *)q + SIZEOF_STRUCT_PBUF); LWIP_ASSERT("pbuf_alloc: pbuf q->payload properly aligned", ((mem_ptr_t)q->payload % MEM_ALIGNMENT) == 0); LWIP_ASSERT("check p->payload + p->len does not overflow pbuf", ((u8_t*)p->payload + p->len <= (u8_t*)p + SIZEOF_STRUCT_PBUF + PBUF_POOL_BUFSIZE_ALIGNED)); q->ref = 1; /* calculate remaining length to be allocated */ rem_len -= q->len; /* remember this pbuf for linkage in next iteration */ r = q; } /* end of chain */ /*r->next = NULL;*/ break; case PBUF_RAM: { mem_size_t alloc_len = LWIP_MEM_ALIGN_SIZE(SIZEOF_STRUCT_PBUF + offset) + LWIP_MEM_ALIGN_SIZE(length); /* bug #50040: Check for integer overflow when calculating alloc_len */ if (alloc_len < LWIP_MEM_ALIGN_SIZE(length)) { return NULL; } /* If pbuf is to be allocated in RAM, allocate memory for it. */ p = (struct pbuf*)mem_malloc(alloc_len); } if (p == NULL) { return NULL; } /* Set up internal structure of the pbuf. */ p->payload = LWIP_MEM_ALIGN((void *)((u8_t *)p + SIZEOF_STRUCT_PBUF + offset)); p->len = p->tot_len = length; p->next = NULL; p->type = type; LWIP_ASSERT("pbuf_alloc: pbuf->payload properly aligned", ((mem_ptr_t)p->payload % MEM_ALIGNMENT) == 0); break; /* pbuf references existing (non-volatile static constant) ROM payload? */ case PBUF_ROM: /* pbuf references existing (externally allocated) RAM payload? */ case PBUF_REF: /* only allocate memory for the pbuf structure */ p = (struct pbuf *)memp_malloc(MEMP_PBUF); if (p == NULL) { LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("pbuf_alloc: Could not allocate MEMP_PBUF for PBUF_%s.\n", (type == PBUF_ROM) ? "ROM" : "REF")); return NULL; } /* caller must set this field properly, afterwards */ p->payload = NULL; p->len = p->tot_len = length; p->next = NULL; p->type = type; break; default: LWIP_ASSERT("pbuf_alloc: erroneous type", 0); return NULL; } /* set reference count */ p->ref = 1; /* set flags */ p->flags = 0; LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_alloc(length=%"U16_F") == %p\n", length, (void *)p)); return p; }
/** * Adam's mem_malloc() plus solution for bug #17922 * Allocate a block of memory with a minimum of 'size' bytes. * * @param size is the minimum size of the requested block in bytes. * @return pointer to allocated memory or NULL if no free memory was found. * * Note that the returned value will always be aligned (as defined by MEM_ALIGNMENT). */ void * mem_malloc(mem_size_t size) { mem_size_t ptr, ptr2; struct mem *mem, *mem2; #if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT u8_t local_mem_free_count = 0; #endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */ LWIP_MEM_ALLOC_DECL_PROTECT(); if (size == 0) { return NULL; } /* Expand the size of the allocated memory region so that we can adjust for alignment. */ size = LWIP_MEM_ALIGN_SIZE(size); if(size < MIN_SIZE_ALIGNED) { /* every data block must be at least MIN_SIZE_ALIGNED long */ size = MIN_SIZE_ALIGNED; } if (size > MEM_SIZE_ALIGNED) { return NULL; } /* protect the heap from concurrent access */ sys_mutex_lock(&mem_mutex); LWIP_MEM_ALLOC_PROTECT(); #if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT /* run as long as a mem_free disturbed mem_malloc or mem_trim */ do { local_mem_free_count = 0; #endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */ /* Scan through the heap searching for a free block that is big enough, * beginning with the lowest free block. */ for (ptr = (mem_size_t)((u8_t *)lfree - ram); ptr < MEM_SIZE_ALIGNED - size; ptr = ((struct mem *)(void *)&ram[ptr])->next) { mem = (struct mem *)(void *)&ram[ptr]; #if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT mem_free_count = 0; LWIP_MEM_ALLOC_UNPROTECT(); /* allow mem_free or mem_trim to run */ LWIP_MEM_ALLOC_PROTECT(); if (mem_free_count != 0) { /* If mem_free or mem_trim have run, we have to restart since they could have altered our current struct mem. */ local_mem_free_count = 1; break; } #endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */ if ((!mem->used) && (mem->next - (ptr + SIZEOF_STRUCT_MEM)) >= size) { /* mem is not used and at least perfect fit is possible: * mem->next - (ptr + SIZEOF_STRUCT_MEM) gives us the 'user data size' of mem */ if (mem->next - (ptr + SIZEOF_STRUCT_MEM) >= (size + SIZEOF_STRUCT_MEM + MIN_SIZE_ALIGNED)) { /* (in addition to the above, we test if another struct mem (SIZEOF_STRUCT_MEM) containing * at least MIN_SIZE_ALIGNED of data also fits in the 'user data space' of 'mem') * -> split large block, create empty remainder, * remainder must be large enough to contain MIN_SIZE_ALIGNED data: if * mem->next - (ptr + (2*SIZEOF_STRUCT_MEM)) == size, * struct mem would fit in but no data between mem2 and mem2->next * @todo we could leave out MIN_SIZE_ALIGNED. We would create an empty * region that couldn't hold data, but when mem->next gets freed, * the 2 regions would be combined, resulting in more free memory */ ptr2 = ptr + SIZEOF_STRUCT_MEM + size; /* create mem2 struct */ mem2 = (struct mem *)(void *)&ram[ptr2]; mem2->used = 0; mem2->next = mem->next; mem2->prev = ptr; /* and insert it between mem and mem->next */ mem->next = ptr2; mem->used = 1; if (mem2->next != MEM_SIZE_ALIGNED) { ((struct mem *)(void *)&ram[mem2->next])->prev = ptr2; } MEM_STATS_INC_USED(used, (size + SIZEOF_STRUCT_MEM)); } else { /* (a mem2 struct does no fit into the user data space of mem and mem->next will always * be used at this point: if not we have 2 unused structs in a row, plug_holes should have * take care of this). * -> near fit or excact fit: do not split, no mem2 creation * also can't move mem->next directly behind mem, since mem->next * will always be used at this point! */ mem->used = 1; MEM_STATS_INC_USED(used, mem->next - (mem_size_t)((u8_t *)mem - ram)); } #if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT mem_malloc_adjust_lfree: #endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */ if (mem == lfree) { struct mem *cur = lfree; /* Find next free block after mem and update lowest free pointer */ while (cur->used && cur != ram_end) { #if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT mem_free_count = 0; LWIP_MEM_ALLOC_UNPROTECT(); /* prevent high interrupt latency... */ LWIP_MEM_ALLOC_PROTECT(); if (mem_free_count != 0) { /* If mem_free or mem_trim have run, we have to restart since they could have altered our current struct mem or lfree. */ goto mem_malloc_adjust_lfree; } #endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */ cur = (struct mem *)(void *)&ram[cur->next]; } lfree = cur; LWIP_ASSERT("mem_malloc: !lfree->used", ((lfree == ram_end) || (!lfree->used))); } LWIP_MEM_ALLOC_UNPROTECT(); sys_mutex_unlock(&mem_mutex); LWIP_ASSERT("mem_malloc: allocated memory not above ram_end.", (mem_ptr_t)mem + SIZEOF_STRUCT_MEM + size <= (mem_ptr_t)ram_end); LWIP_ASSERT("mem_malloc: allocated memory properly aligned.", ((mem_ptr_t)mem + SIZEOF_STRUCT_MEM) % MEM_ALIGNMENT == 0); LWIP_ASSERT("mem_malloc: sanity check alignment", (((mem_ptr_t)mem) & (MEM_ALIGNMENT-1)) == 0); return (u8_t *)mem + SIZEOF_STRUCT_MEM; } } #if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT /* if we got interrupted by a mem_free, try again */ } while(local_mem_free_count != 0); #endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */ LWIP_DEBUGF(MEM_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("mem_malloc: could not allocate %"S16_F" bytes\n", (s16_t)size)); MEM_STATS_INC(err); LWIP_MEM_ALLOC_UNPROTECT(); sys_mutex_unlock(&mem_mutex); return NULL; }
/** * Shrink memory returned by mem_malloc(). * * @param rmem pointer to memory allocated by mem_malloc the is to be shrinked * @param newsize required size after shrinking (needs to be smaller than or * equal to the previous size) * @return for compatibility reasons: is always == rmem, at the moment * or NULL if newsize is > old size, in which case rmem is NOT touched * or freed! */ void * mem_trim(void *rmem, mem_size_t newsize) { mem_size_t size; mem_size_t ptr, ptr2; struct mem *mem, *mem2; /* use the FREE_PROTECT here: it protects with sem OR SYS_ARCH_PROTECT */ LWIP_MEM_FREE_DECL_PROTECT(); /* Expand the size of the allocated memory region so that we can adjust for alignment. */ newsize = LWIP_MEM_ALIGN_SIZE(newsize); if(newsize < MIN_SIZE_ALIGNED) { /* every data block must be at least MIN_SIZE_ALIGNED long */ newsize = MIN_SIZE_ALIGNED; } if (newsize > MEM_SIZE_ALIGNED) { return NULL; } LWIP_ASSERT("mem_trim: legal memory", (u8_t *)rmem >= (u8_t *)ram && (u8_t *)rmem < (u8_t *)ram_end); if ((u8_t *)rmem < (u8_t *)ram || (u8_t *)rmem >= (u8_t *)ram_end) { SYS_ARCH_DECL_PROTECT(lev); LWIP_DEBUGF(MEM_DEBUG | LWIP_DBG_LEVEL_SEVERE, ("mem_trim: illegal memory\n")); /* protect mem stats from concurrent access */ SYS_ARCH_PROTECT(lev); MEM_STATS_INC(illegal); SYS_ARCH_UNPROTECT(lev); return rmem; } /* Get the corresponding struct mem ... */ mem = (struct mem *)(void *)((u8_t *)rmem - SIZEOF_STRUCT_MEM); /* ... and its offset pointer */ ptr = (mem_size_t)((u8_t *)mem - ram); size = mem->next - ptr - SIZEOF_STRUCT_MEM; LWIP_ASSERT("mem_trim can only shrink memory", newsize <= size); if (newsize > size) { /* not supported */ return NULL; } if (newsize == size) { /* No change in size, simply return */ return rmem; } /* protect the heap from concurrent access */ LWIP_MEM_FREE_PROTECT(); mem2 = (struct mem *)(void *)&ram[mem->next]; if(mem2->used == 0) { /* The next struct is unused, we can simply move it at little */ mem_size_t next; /* remember the old next pointer */ next = mem2->next; /* create new struct mem which is moved directly after the shrinked mem */ ptr2 = ptr + SIZEOF_STRUCT_MEM + newsize; if (lfree == mem2) { lfree = (struct mem *)(void *)&ram[ptr2]; } mem2 = (struct mem *)(void *)&ram[ptr2]; mem2->used = 0; /* restore the next pointer */ mem2->next = next; /* link it back to mem */ mem2->prev = ptr; /* link mem to it */ mem->next = ptr2; /* last thing to restore linked list: as we have moved mem2, * let 'mem2->next->prev' point to mem2 again. but only if mem2->next is not * the end of the heap */ if (mem2->next != MEM_SIZE_ALIGNED) { ((struct mem *)(void *)&ram[mem2->next])->prev = ptr2; } MEM_STATS_DEC_USED(used, (size - newsize)); /* no need to plug holes, we've already done that */ } else if (newsize + SIZEOF_STRUCT_MEM + MIN_SIZE_ALIGNED <= size) { /* Next struct is used but there's room for another struct mem with * at least MIN_SIZE_ALIGNED of data. * Old size ('size') must be big enough to contain at least 'newsize' plus a struct mem * ('SIZEOF_STRUCT_MEM') with some data ('MIN_SIZE_ALIGNED'). * @todo we could leave out MIN_SIZE_ALIGNED. We would create an empty * region that couldn't hold data, but when mem->next gets freed, * the 2 regions would be combined, resulting in more free memory */ ptr2 = ptr + SIZEOF_STRUCT_MEM + newsize; mem2 = (struct mem *)(void *)&ram[ptr2]; if (mem2 < lfree) { lfree = mem2; } mem2->used = 0; mem2->next = mem->next; mem2->prev = ptr; mem->next = ptr2; if (mem2->next != MEM_SIZE_ALIGNED) { ((struct mem *)(void *)&ram[mem2->next])->prev = ptr2; } MEM_STATS_DEC_USED(used, (size - newsize)); /* the original mem->next is used, so no need to plug holes! */ } /* else { next struct mem is used but size between mem and mem2 is not big enough to create another struct mem -> don't do anyhting. -> the remaining space stays unused since it is too small } */ #if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT mem_free_count = 1; #endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */ LWIP_MEM_FREE_UNPROTECT(); return rmem; }
} /* the datagram is not (yet?) reassembled completely */ LWIP_DEBUGF(IP_REASS_DEBUG,("ip_reass_pbufcount: %d out\n", ip_reass_pbufcount)); return NULL; nullreturn: LWIP_DEBUGF(IP_REASS_DEBUG,("ip_reass: nullreturn\n")); IPFRAG_STATS_INC(ip_frag.drop); pbuf_free(p); return NULL; } #endif /* IP_REASSEMBLY */ #if IP_FRAG #if IP_FRAG_USES_STATIC_BUF static u8_t buf[LWIP_MEM_ALIGN_SIZE(IP_FRAG_MAX_MTU + MEM_ALIGNMENT - 1)]; #else /* IP_FRAG_USES_STATIC_BUF */ #if !LWIP_NETIF_TX_SINGLE_PBUF /** Allocate a new struct pbuf_custom_ref */ static struct pbuf_custom_ref* ip_frag_alloc_pbuf_custom_ref(void) { return (struct pbuf_custom_ref*)memp_malloc(MEMP_FRAG_PBUF); } /** Free a struct pbuf_custom_ref */ static void ip_frag_free_pbuf_custom_ref(struct pbuf_custom_ref* p) { LWIP_ASSERT("p != NULL", p != NULL);
/** * Adam's mem_malloc() plus solution for bug #17922 * Allocate a block of memory with a minimum of 'size' bytes. * * @param size is the minimum size of the requested block in bytes. * @return pointer to allocated memory or NULL if no free memory was found. * * Note that the returned value will always be aligned (as defined by MEM_ALIGNMENT). */ void * mem_malloc(mem_size_t size) { mem_size_t ptr, ptr2; struct mem *mem, *mem2; if (size == 0) { return NULL; } /* Expand the size of the allocated memory region so that we can adjust for alignment. */ size = LWIP_MEM_ALIGN_SIZE(size); if(size < MIN_SIZE_ALIGNED) { /* every data block must be at least MIN_SIZE_ALIGNED long */ size = MIN_SIZE_ALIGNED; } if (size > MEM_SIZE_ALIGNED) { return NULL; } /* protect the heap from concurrent access */ sys_arch_sem_wait(mem_sem, 0); /* Scan through the heap searching for a free block that is big enough, * beginning with the lowest free block. */ for (ptr = (u8_t *)lfree - ram; ptr < MEM_SIZE_ALIGNED - size; ptr = ((struct mem *)&ram[ptr])->next) { mem = (struct mem *)&ram[ptr]; if ((!mem->used) && (mem->next - (ptr + SIZEOF_STRUCT_MEM)) >= size) { /* mem is not used and at least perfect fit is possible: * mem->next - (ptr + SIZEOF_STRUCT_MEM) gives us the 'user data size' of mem */ if (mem->next - (ptr + SIZEOF_STRUCT_MEM) >= (size + SIZEOF_STRUCT_MEM + MIN_SIZE_ALIGNED)) { /* (in addition to the above, we test if another struct mem (SIZEOF_STRUCT_MEM) containing * at least MIN_SIZE_ALIGNED of data also fits in the 'user data space' of 'mem') * -> split large block, create empty remainder, * remainder must be large enough to contain MIN_SIZE_ALIGNED data: if * mem->next - (ptr + (2*SIZEOF_STRUCT_MEM)) == size, * struct mem would fit in but no data between mem2 and mem2->next * @todo we could leave out MIN_SIZE_ALIGNED. We would create an empty * region that couldn't hold data, but when mem->next gets freed, * the 2 regions would be combined, resulting in more free memory */ ptr2 = ptr + SIZEOF_STRUCT_MEM + size; /* create mem2 struct */ mem2 = (struct mem *)&ram[ptr2]; mem2->used = 0; mem2->next = mem->next; mem2->prev = ptr; /* and insert it between mem and mem->next */ mem->next = ptr2; mem->used = 1; if (mem2->next != MEM_SIZE_ALIGNED) { ((struct mem *)&ram[mem2->next])->prev = ptr2; } #if MEM_STATS lwip_stats.mem.used += (size + SIZEOF_STRUCT_MEM); if (lwip_stats.mem.max < lwip_stats.mem.used) { lwip_stats.mem.max = lwip_stats.mem.used; } #endif /* MEM_STATS */ } else { /* (a mem2 struct does no fit into the user data space of mem and mem->next will always * be used at this point: if not we have 2 unused structs in a row, plug_holes should have * take care of this). * -> near fit or excact fit: do not split, no mem2 creation * also can't move mem->next directly behind mem, since mem->next * will always be used at this point! */ mem->used = 1; #if MEM_STATS lwip_stats.mem.used += mem->next - ((u8_t *)mem - ram); if (lwip_stats.mem.max < lwip_stats.mem.used) { lwip_stats.mem.max = lwip_stats.mem.used; } #endif /* MEM_STATS */ } if (mem == lfree) { /* Find next free block after mem and update lowest free pointer */ while (lfree->used && lfree != ram_end) { lfree = (struct mem *)&ram[lfree->next]; } LWIP_ASSERT("mem_malloc: !lfree->used", ((lfree == ram_end) || (!lfree->used))); } sys_sem_signal(mem_sem); LWIP_ASSERT("mem_malloc: allocated memory not above ram_end.", (mem_ptr_t)mem + SIZEOF_STRUCT_MEM + size <= (mem_ptr_t)ram_end); LWIP_ASSERT("mem_malloc: allocated memory properly aligned.", (unsigned long)((u8_t *)mem + SIZEOF_STRUCT_MEM) % MEM_ALIGNMENT == 0); LWIP_ASSERT("mem_malloc: sanity check alignment", (((mem_ptr_t)mem) & (MEM_ALIGNMENT-1)) == 0); return (u8_t *)mem + SIZEOF_STRUCT_MEM; } } LWIP_DEBUGF(MEM_DEBUG | 2, ("mem_malloc: could not allocate %"S16_F" bytes\n", (s16_t)size)); #if MEM_STATS ++lwip_stats.mem.err; #endif /* MEM_STATS */ sys_sem_signal(mem_sem); return NULL; }
/** * Create a TCP segment with prefilled header. * * Called by tcp_write and tcp_enqueue_flags. * * @param pcb Protocol control block for the TCP connection. * @param p pbuf that is used to hold the TCP header. * @param flags TCP flags for header. * @param seqno TCP sequence number of this packet * @param optflags options to include in TCP header * @return a new tcp_seg pointing to p, or NULL. * The TCP header is filled in except ackno and wnd. * p is freed on failure. */ static struct tcp_seg * tcp_create_segment(struct tcp_pcb *pcb, struct pbuf *p, u8_t flags, u32_t seqno, u8_t optflags) { struct tcp_seg *seg; u8_t optlen = LWIP_TCP_OPT_LENGTH(optflags); #if LWIP_3RD_PARTY_BUFS if ((seg = external_tcp_seg_alloc(pcb)) == NULL) { #else if ((seg = (struct tcp_seg *)memp_malloc(MEMP_TCP_SEG)) == NULL) { #endif LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_create_segment: no memory.\n")); tcp_tx_pbuf_free(pcb, p); return NULL; } seg->flags = optflags; seg->next = NULL; seg->p = p; seg->dataptr = p->payload; seg->len = p->tot_len - optlen; #if TCP_OVERSIZE_DBGCHECK seg->oversize_left = 0; #endif /* TCP_OVERSIZE_DBGCHECK */ #if TCP_CHECKSUM_ON_COPY seg->chksum = 0; seg->chksum_swapped = 0; /* check optflags */ LWIP_ASSERT("invalid optflags passed: TF_SEG_DATA_CHECKSUMMED", (optflags & TF_SEG_DATA_CHECKSUMMED) == 0); #endif /* TCP_CHECKSUM_ON_COPY */ seg->seqno = seqno; /* build TCP header */ if (pbuf_header(p, TCP_HLEN)) { LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_create_segment: no room for TCP header in pbuf.\n")); TCP_STATS_INC(tcp.err); tcp_tx_seg_free(pcb, seg); return NULL; } seg->tcphdr = (struct tcp_hdr *)seg->p->payload; seg->tcphdr->src = htons(pcb->local_port); seg->tcphdr->dest = htons(pcb->remote_port); seg->tcphdr->seqno = htonl(seqno); /* ackno is set in tcp_output */ TCPH_HDRLEN_FLAGS_SET(seg->tcphdr, (5 + optlen / 4), flags); /* wnd and chksum are set in tcp_output */ seg->tcphdr->urgp = 0; return seg; } /** * Allocate a PBUF_RAM pbuf, perhaps with extra space at the end. * * This function is like pbuf_alloc(layer, length, PBUF_RAM) except * there may be extra bytes available at the end. * * @param layer flag to define header size. * @param length size of the pbuf's payload. * @param max_length maximum usable size of payload+oversize. * @param oversize pointer to a u16_t that will receive the number of usable tail bytes. * @param pcb The TCP connection that willo enqueue the pbuf. * @param apiflags API flags given to tcp_write. * @param first_seg true when this pbuf will be used in the first enqueued segment. * @param */ static struct pbuf * tcp_pbuf_prealloc(u16_t length, u16_t max_length, u16_t *oversize, struct tcp_pcb *pcb, u8_t apiflags, u8_t first_seg) { struct pbuf *p; u16_t alloc = length; if (length < max_length) { /* Should we allocate an oversized pbuf, or just the minimum * length required? If tcp_write is going to be called again * before this segment is transmitted, we want the oversized * buffer. If the segment will be transmitted immediately, we can * save memory by allocating only length. We use a simple * heuristic based on the following information: * * Did the user set TCP_WRITE_FLAG_MORE? * * Will the Nagle algorithm defer transmission of this segment? */ if ((apiflags & TCP_WRITE_FLAG_MORE) || (!(pcb->flags & TF_NODELAY) && (!first_seg || pcb->unsent != NULL || pcb->unacked != NULL))) { alloc = LWIP_MIN(max_length, LWIP_MEM_ALIGN_SIZE(length + pcb->tcp_oversize_val)); } } p = tcp_tx_pbuf_alloc(pcb, alloc, PBUF_RAM); if (p == NULL) { return NULL; } LWIP_ASSERT("need unchained pbuf", p->next == NULL); *oversize = p->len - length; /* trim p->len to the currently used size */ p->len = p->tot_len = length; return p; } /** Checks if tcp_write is allowed or not (checks state, snd_buf and snd_queuelen). * * @param pcb the tcp pcb to check for * @param len length of data to send (checked agains snd_buf) * @return ERR_OK if tcp_write is allowed to proceed, another err_t otherwise */ static err_t tcp_write_checks(struct tcp_pcb *pcb, u32_t len) { /* connection is in invalid state for data transmission? */ if ((get_tcp_state(pcb) != ESTABLISHED) && (get_tcp_state(pcb) != CLOSE_WAIT) && (get_tcp_state(pcb) != SYN_SENT) && (get_tcp_state(pcb) != SYN_RCVD)) { LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_STATE | LWIP_DBG_LEVEL_SEVERE, ("tcp_write() called in invalid state\n")); return ERR_CONN; } else if (len == 0) { return ERR_OK; } /* fail on too much data */ if (len > pcb->snd_buf) { LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 3, ("tcp_write: too much data (len=%"U32_F" > snd_buf=%"U32_F")\n", len, pcb->snd_buf)); pcb->flags |= TF_NAGLEMEMERR; return ERR_MEM; } LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_write: queuelen: %"U32_F"\n", (u32_t)pcb->snd_queuelen)); /* If total number of pbufs on the unsent/unacked queues exceeds the * configured maximum, return an error */ /* check for configured max queuelen and possible overflow */ if ((pcb->snd_queuelen >= pcb->max_unsent_len) || (pcb->snd_queuelen > TCP_SNDQUEUELEN_OVERFLOW)) { LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 3, ("tcp_write: too long queue %"U32_F" (max %"U32_F")\n", pcb->snd_queuelen, pcb->max_unsent_len)); TCP_STATS_INC(tcp.memerr); pcb->flags |= TF_NAGLEMEMERR; return ERR_MEM; } if (pcb->snd_queuelen != 0) { } else { LWIP_ASSERT("tcp_write: no pbufs on queue => both queues empty", pcb->unacked == NULL && pcb->unsent == NULL); } return ERR_OK; } /** * Write data for sending (but does not send it immediately). * * It waits in the expectation of more data being sent soon (as * it can send them more efficiently by combining them together). * To prompt the system to send data now, call tcp_output() after * calling tcp_write(). * * @param pcb Protocol control block for the TCP connection to enqueue data for. * @param arg Pointer to the data to be enqueued for sending. * @param len Data length in bytes * @param apiflags combination of following flags : * - TCP_WRITE_FLAG_COPY (0x01) data will be copied into memory belonging to the stack * - TCP_WRITE_FLAG_MORE (0x02) for TCP connection, PSH flag will be set on last segment sent, * @return ERR_OK if enqueued, another err_t on error */ err_t tcp_write(struct tcp_pcb *pcb, const void *arg, u32_t len, u8_t apiflags) { struct pbuf *concat_p = NULL; struct tcp_seg *seg = NULL, *prev_seg = NULL, *queue = NULL; u32_t pos = 0; /* position in 'arg' data */ u32_t queuelen; u8_t optlen = 0; u8_t optflags = 0; #if TCP_OVERSIZE u16_t oversize = 0; u16_t oversize_used = 0; #endif /* TCP_OVERSIZE */ #if TCP_CHECKSUM_ON_COPY u16_t concat_chksum = 0; u8_t concat_chksum_swapped = 0; u16_t concat_chksummed = 0; #endif /* TCP_CHECKSUM_ON_COPY */ err_t err; /* don't allocate segments bigger than half the maximum window we ever received */ u16_t mss_local = LWIP_MIN(pcb->mss, pcb->snd_wnd_max/2); mss_local = mss_local ? mss_local : pcb->mss; int byte_queued = pcb->snd_nxt - pcb->lastack; if ( len < pcb->mss) pcb->snd_sml_add = (pcb->unacked ? pcb->unacked->len : 0) + byte_queued; #if LWIP_NETIF_TX_SINGLE_PBUF /* Always copy to try to create single pbufs for TX */ apiflags |= TCP_WRITE_FLAG_COPY; #endif /* LWIP_NETIF_TX_SINGLE_PBUF */ LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_write(pcb=%p, data=%p, len=%"U16_F", apiflags=%"U16_F")\n", (void *)pcb, arg, len, (u16_t)apiflags)); LWIP_ERROR("tcp_write: arg == NULL (programmer violates API)", arg != NULL, return ERR_ARG;); err = tcp_write_checks(pcb, len); if (err != ERR_OK) { return err; } queuelen = pcb->snd_queuelen; #if LWIP_TCP_TIMESTAMPS if ((pcb->flags & TF_TIMESTAMP)) { optflags = TF_SEG_OPTS_TS; /* ensure that segments can hold at least one data byte... */ mss_local = LWIP_MAX(mss_local, LWIP_TCP_OPT_LEN_TS + 1); } #endif /* LWIP_TCP_TIMESTAMPS */ optlen = LWIP_TCP_OPT_LENGTH( optflags ); /* * TCP segmentation is done in three phases with increasing complexity: * * 1. Copy data directly into an oversized pbuf. * 2. Chain a new pbuf to the end of pcb->unsent. * 3. Create new segments. * * We may run out of memory at any point. In that case we must * return ERR_MEM and not change anything in pcb. Therefore, all * changes are recorded in local variables and committed at the end * of the function. Some pcb fields are maintained in local copies: * * queuelen = pcb->snd_queuelen * oversize = pcb->unsent_oversize * * These variables are set consistently by the phases: * * seg points to the last segment tampered with. * * pos records progress as data is segmented. */ /* Find the tail of the unsent queue. */ if (pcb->unsent != NULL) { u16_t space; u16_t unsent_optlen; if (!pcb->last_unsent || pcb->last_unsent->next) { /* @todo: this could be sped up by keeping last_unsent in the pcb */ for (pcb->last_unsent = pcb->unsent; pcb->last_unsent->next != NULL; pcb->last_unsent = pcb->last_unsent->next); } /* Usable space at the end of the last unsent segment */ unsent_optlen = LWIP_TCP_OPT_LENGTH(pcb->last_unsent->flags); LWIP_ASSERT("mss_local is too small", mss_local >= pcb->last_unsent->len + unsent_optlen); space = mss_local - (pcb->last_unsent->len + unsent_optlen); /* * Phase 1: Copy data directly into an oversized pbuf. * * The number of bytes copied is recorded in the oversize_used * variable. The actual copying is done at the bottom of the * function. */ #if TCP_OVERSIZE #if TCP_OVERSIZE_DBGCHECK /* check that pcb->unsent_oversize matches last_unsent->unsent_oversize */ LWIP_ASSERT("unsent_oversize mismatch (pcb vs. last_unsent)", pcb->unsent_oversize == pcb->last_unsent->oversize_left); #endif /* TCP_OVERSIZE_DBGCHECK */ oversize = pcb->unsent_oversize; if (oversize > 0) { LWIP_ASSERT("inconsistent oversize vs. space", oversize_used <= space); seg = pcb->last_unsent; oversize_used = oversize < len ? oversize : len; pos += oversize_used; oversize -= oversize_used; space -= oversize_used; } /* now we are either finished or oversize is zero */ LWIP_ASSERT("inconsistend oversize vs. len", (oversize == 0) || (pos == len)); #endif /* TCP_OVERSIZE */ /* * Phase 2: Chain a new pbuf to the end of pcb->unsent. * * We don't extend segments containing SYN/FIN flags or options * (len==0). The new pbuf is kept in concat_p and pbuf_cat'ed at * the end. */ if ((pos < len) && (space > 0) && (pcb->last_unsent->len > 0)) { u16_t seglen = space < len - pos ? space : len - pos; seg = pcb->last_unsent; /* Create a pbuf with a copy or reference to seglen bytes. We * can use PBUF_RAW here since the data appears in the middle of * a segment. A header will never be prepended. */ if (apiflags & TCP_WRITE_FLAG_COPY) { /* Data is copied */ if ((concat_p = tcp_pbuf_prealloc(seglen, space, &oversize, pcb, apiflags, 1)) == NULL) { LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_write : could not allocate memory for pbuf copy size %"U16_F"\n", seglen)); goto memerr; } #if TCP_OVERSIZE_DBGCHECK pcb->last_unsent->oversize_left += oversize; #endif /* TCP_OVERSIZE_DBGCHECK */ TCP_DATA_COPY2(concat_p->payload, (u8_t*)arg + pos, seglen, &concat_chksum, &concat_chksum_swapped); #if TCP_CHECKSUM_ON_COPY concat_chksummed += seglen; #endif /* TCP_CHECKSUM_ON_COPY */ } else { LWIP_ASSERT("tcp_write : we are never here", 0); goto memerr; } pos += seglen; queuelen += pbuf_clen(concat_p); } } else { #if TCP_OVERSIZE pcb->last_unsent = NULL; LWIP_ASSERT("unsent_oversize mismatch (pcb->unsent is NULL)", pcb->unsent_oversize == 0); #endif /* TCP_OVERSIZE */ } /* * Phase 3: Create new segments. * * The new segments are chained together in the local 'queue' * variable, ready to be appended to pcb->unsent. */ while (pos < len) { struct pbuf *p; u32_t left = len - pos; u16_t max_len = mss_local - optlen; u16_t seglen = left > max_len ? max_len : left; if (apiflags & TCP_WRITE_FLAG_COPY) { /* If copy is set, memory should be allocated and data copied * into pbuf */ if ((p = tcp_pbuf_prealloc(seglen + optlen, mss_local, &oversize, pcb, apiflags, queue == NULL)) == NULL) { LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_write : could not allocate memory for pbuf copy size %"U16_F"\n", seglen)); goto memerr; } LWIP_ASSERT("tcp_write: check that first pbuf can hold the complete seglen", (p->len >= seglen)); TCP_DATA_COPY2((char *)p->payload + optlen, (u8_t*)arg + pos, seglen, &chksum, &chksum_swapped); } else { LWIP_ASSERT("tcp_write: we are never here",0); goto memerr; } queuelen += pbuf_clen(p); /* Now that there are more segments queued, we check again if the * length of the queue exceeds the configured maximum or * overflows. */ if ((queuelen > pcb->max_unsent_len) || (queuelen > TCP_SNDQUEUELEN_OVERFLOW)) { LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_write: queue too long %"U32_F" (%"U32_F")\n", queuelen, pcb->max_unsent_len)); tcp_tx_pbuf_free(pcb, p); goto memerr; } if ((seg = tcp_create_segment(pcb, p, 0, pcb->snd_lbb + pos, optflags)) == NULL) { goto memerr; } #if TCP_OVERSIZE_DBGCHECK seg->oversize_left = oversize; #endif /* TCP_OVERSIZE_DBGCHECK */ #if TCP_CHECKSUM_ON_COPY seg->chksum = chksum; seg->chksum_swapped = chksum_swapped; seg->flags |= TF_SEG_DATA_CHECKSUMMED; #endif /* TCP_CHECKSUM_ON_COPY */ /* Fix dataptr for the nocopy case */ if ((apiflags & TCP_WRITE_FLAG_COPY) == 0) { seg->dataptr = (u8_t*)arg + pos; } /* first segment of to-be-queued data? */ if (queue == NULL) { queue = seg; } else { /* Attach the segment to the end of the queued segments */ LWIP_ASSERT("prev_seg != NULL", prev_seg != NULL); prev_seg->next = seg; } /* remember last segment of to-be-queued data for next iteration */ prev_seg = seg; LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_TRACE, ("tcp_write: queueing %"U32_F":%"U32_F"\n", ntohl(seg->tcphdr->seqno), ntohl(seg->tcphdr->seqno) + TCP_TCPLEN(seg))); pos += seglen; } /* * All three segmentation phases were successful. We can commit the * transaction. */ /* * Phase 1: If data has been added to the preallocated tail of * last_unsent, we update the length fields of the pbuf chain. */ #if TCP_OVERSIZE if (oversize_used > 0) { struct pbuf *p; /* Bump tot_len of whole chain, len of tail */ for (p = pcb->last_unsent->p; p; p = p->next) { p->tot_len += oversize_used; if (p->next == NULL) { TCP_DATA_COPY((char *)p->payload + p->len, arg, oversize_used, pcb->last_unsent); p->len += oversize_used; } } pcb->last_unsent->len += oversize_used; #if TCP_OVERSIZE_DBGCHECK pcb->last_unsent->oversize_left -= oversize_used; #endif /* TCP_OVERSIZE_DBGCHECK */ } pcb->unsent_oversize = oversize; #endif /* TCP_OVERSIZE */ /* * Phase 2: concat_p can be concatenated onto pcb->last_unsent->p */ if (concat_p != NULL) { LWIP_ASSERT("tcp_write: cannot concatenate when pcb->unsent is empty", (pcb->last_unsent != NULL)); pbuf_cat(pcb->last_unsent->p, concat_p); pcb->last_unsent->len += concat_p->tot_len; #if TCP_CHECKSUM_ON_COPY if (concat_chksummed) { tcp_seg_add_chksum(concat_chksum, concat_chksummed, &pcb->last_unsent->chksum, &pcb->last_unsent->chksum_swapped); pcb->last_unsent->flags |= TF_SEG_DATA_CHECKSUMMED; } #endif /* TCP_CHECKSUM_ON_COPY */ } /* * Phase 3: Append queue to pcb->unsent. Queue may be NULL, but that * is harmless */ if (pcb->last_unsent == NULL) { pcb->unsent = queue; } else { pcb->last_unsent->next = queue; } pcb->last_unsent = seg; /* * Finally update the pcb state. */ pcb->snd_lbb += len; pcb->snd_buf -= len; pcb->snd_queuelen = queuelen; LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_write: %"S16_F" (after enqueued)\n", pcb->snd_queuelen)); if (pcb->snd_queuelen != 0) { LWIP_ASSERT("tcp_write: valid queue length", pcb->unacked != NULL || pcb->unsent != NULL); } /* Set the PSH flag in the last segment that we enqueued. */ if (seg != NULL && seg->tcphdr != NULL) { TCPH_SET_FLAG(seg->tcphdr, TCP_PSH); } return ERR_OK; memerr: pcb->flags |= TF_NAGLEMEMERR; TCP_STATS_INC(tcp.memerr); if (concat_p != NULL) { tcp_tx_pbuf_free(pcb, concat_p); } if (queue != NULL) { tcp_tx_segs_free(pcb, queue); } if (pcb->snd_queuelen != 0) { LWIP_ASSERT("tcp_write: valid queue length", pcb->unacked != NULL || pcb->unsent != NULL); } LWIP_DEBUGF(TCP_QLEN_DEBUG | LWIP_DBG_STATE, ("tcp_write: %"S16_F" (with mem err)\n", pcb->snd_queuelen)); return ERR_MEM; }
static struct pbuf* low_level_input(struct netif *netif) { u16_t l, temp_l; struct pbuf *first_pbuf, *next_pbuf, *q; u16_t len; #ifdef ENET_LITTLE_ENDIAN u8_t *data_temp; #endif u8_t more_pkts = 1, processing_error = 0; (void)netif; /* initial pkt handling */ if (!(rx_bd[rx_next_buf].status & ENET_RX_BD_E)) { /* if pkt is filled */ if (rx_bd[rx_next_buf].status & ENET_RX_BD_L) { more_pkts = 0; if (rx_bd[rx_next_buf].status & (ENET_RX_BD_LG | ENET_RX_BD_NO | ENET_RX_BD_CR | ENET_RX_BD_OV)) { /* bad packet */ LINK_STATS_INC(link.memerr); LINK_STATS_INC(link.drop); goto EXIT_RX_PKT; } else { #ifdef ENET_LITTLE_ENDIAN len = __REVSH(rx_bd[rx_next_buf].length); #else len = rx_bd[rx_next_buf].length; #endif LINK_STATS_INC(link.recv); } } else /* if not L bit, then buffer's length */ len = ENET_RX_BUF_SIZE; if ((first_pbuf = pbuf_alloc(PBUF_RAW, len, PBUF_POOL)) != NULL) { /* get data */ l = 0; temp_l = 0; /* We iterate over the pbuf chain until we have read the entire * packet into the pbuf. */ for (q = first_pbuf; q != NULL; q = q->next) { /* Read enough bytes to fill this pbuf in the chain. The * available data in the pbuf is given by the q->len * variable. * This does not necessarily have to be a memcpy, you can also preallocate * pbufs for a DMA-enabled MAC and after receiving truncate it to the * actually received size. In this case, ensure the tot_len member of the * pbuf is the sum of the chained pbuf len members. */ temp_l = LWIP_MIN(len, LWIP_MEM_ALIGN_SIZE(PBUF_POOL_BUFSIZE)); #ifdef ENET_LITTLE_ENDIAN data_temp = (u8_t *)__REV((u32_t)rx_bd[ rx_next_buf ].data); memcpy((u8_t*)q->payload, &( data_temp[l] ), temp_l); #else memcpy((u8_t*)q->payload, &( rx_bd[ rx_next_buf ].data[l] ), temp_l); #endif l += temp_l; len -= temp_l; } } else { /* bad buffers */ LINK_STATS_INC(link.memerr); LINK_STATS_INC(link.drop); processing_error = 1; } EXIT_RX_PKT: rx_bd[rx_next_buf++].status |= ENET_RX_BD_E; /* consumed pkt */ ENET_RDAR = ENET_RDAR_RDAR_MASK; if (rx_next_buf >= NUM_ENET_RX_BUFS) rx_next_buf = 0; } else return (struct pbuf*)NULL; /* special NULL case */ /* more pkts handling */ while (more_pkts) { //if(!(rx_bd[ rx_next_buf ].status & RX_BD_E) ) ///*if pkt is filled*/ //{ if (rx_bd[rx_next_buf].status & ENET_RX_BD_L) { more_pkts = 0; if (rx_bd[rx_next_buf].status & (ENET_RX_BD_LG | ENET_RX_BD_NO | ENET_RX_BD_CR | ENET_RX_BD_OV)) { /* bad packet */ LINK_STATS_INC(link.memerr); LINK_STATS_INC(link.drop); goto EXIT_RX_PKT2; } else { #ifdef ENET_LITTLE_ENDIAN len = __REVSH(rx_bd[rx_next_buf].length); #else len = rx_bd[rx_next_buf].length; #endif /* buffer with L bit has total frame's length instead of remaining bytes from frame's lenght */ len %= ENET_RX_BUF_SIZE; LINK_STATS_INC(link.recv); } } else /* if not L bit, then buffer's length */ len = ENET_RX_BUF_SIZE; if (((next_pbuf = pbuf_alloc(PBUF_RAW, len, PBUF_POOL)) != NULL) && (!processing_error)) { /* get data */ l = 0; temp_l = 0; /* We iterate over the pbuf chain until we have read the entire * packet into the pbuf. */ for (q = next_pbuf; q != NULL; q = q->next) { /* Read enough bytes to fill this pbuf in the chain. The * available data in the pbuf is given by the q->len * variable. * This does not necessarily have to be a memcpy, you can also preallocate * pbufs for a DMA-enabled MAC and after receiving truncate it to the * actually received size. In this case, ensure the tot_len member of the * pbuf is the sum of the chained pbuf len members. */ temp_l = LWIP_MIN(len, LWIP_MEM_ALIGN_SIZE(PBUF_POOL_BUFSIZE)); #ifdef ENET_LITTLE_ENDIAN data_temp = (u8_t *)__REV((u32_t)rx_bd[rx_next_buf].data); memcpy((u8_t*)q->payload, &(data_temp[l]), temp_l); #else memcpy((u8_t*)q->payload, &(rx_bd[rx_next_buf].data[l] ), temp_l); #endif l += temp_l; len -= temp_l; } /* link pbufs */ pbuf_cat(first_pbuf, next_pbuf); } else { /* bad buffer - out of lwip buffers */ LINK_STATS_INC(link.memerr); LINK_STATS_INC(link.drop); processing_error = 1; } EXIT_RX_PKT2: rx_bd[rx_next_buf++].status |= ENET_RX_BD_E; /* consumed pkt */ ENET_RDAR = ENET_RDAR_RDAR_MASK; if (rx_next_buf >= NUM_ENET_RX_BUFS) rx_next_buf = 0; } return first_pbuf; }
/** * Allocates a pbuf of the given type (possibly a chain for PBUF_POOL type). * * 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). * * @param layer flag to define header size * @param length size of the pbuf's payload * @param type this 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. * - PBUF_ROM: no buffer memory is allocated for the pbuf, even for * protocol headers. Additional headers must be prepended * by allocating another pbuf and chain in to the front of * 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_take should be called to copy the buffer. * - PBUF_POOL: the pbuf is allocated as a pbuf chain, with pbufs from * the pbuf pool that is allocated during pbuf_init(). * * @return the allocated pbuf. If multiple pbufs where allocated, this * is the first pbuf of a pbuf chain. */ struct pbuf * pbuf_alloc(pbuf_layer layer, u16_t length, pbuf_type type) { struct pbuf *p, *q, *r; u16_t offset; s32_t rem_len; /* remaining length */ LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_alloc(length=%"U16_F")\n", length)); /* determine header offset */ offset = 0; switch (layer) { case PBUF_TRANSPORT: /* add room for transport (often TCP) layer header */ offset += PBUF_TRANSPORT_HLEN; /* FALLTHROUGH */ case PBUF_IP: /* add room for IP layer header */ offset += PBUF_IP_HLEN; /* FALLTHROUGH */ case PBUF_LINK: /* add room for link layer header */ offset += PBUF_LINK_HLEN; #ifdef PBUF_RSV_FOR_WLAN /* * 1. LINK_HLEN 14Byte will be remove in WLAN layer * 2. IEEE80211_HDR_MAX_LEN needs 40 bytes. * 3. encryption needs exra 4 bytes ahead of actual data payload, and require * DAddr and SAddr to be 4-byte aligned. * 4. TRANSPORT and IP are all 20, 4 bytes aligned, nice... * 5. LCC add 6 bytes more, We don't consider WAPI yet... * 6. define LWIP_MEM_ALIGN to be 4 Byte aligned, pbuf struct is 16B, Only thing may be * matter is ether_hdr is not 4B aligned. * * So, we need extra (40 + 4 - 14) = 30 and it's happen to be 4-Byte aligned * * 1. lwip * | empty 30B | eth_hdr (14B) | payload ...| * total: 44B ahead payload * 2. net80211 * | max 80211 hdr, 32B | ccmp/tkip iv (8B) | sec rsv(4B) | payload ...| * total: 40B ahead sec_rsv and 44B ahead payload * */ offset += EP_OFFSET; //remove LINK hdr in wlan #endif /* PBUF_RSV_FOR_WLAN */ break; case PBUF_RAW: #ifdef PBUF_RSV_FOR_WLAN /* * RAW pbuf suppose */ offset += EP_OFFSET; //remove LINK hdr in wlan #endif /* PBUF_RAW */ break; default: LWIP_ASSERT("pbuf_alloc: bad pbuf layer", 0); return NULL; } switch (type) { case PBUF_POOL: /* allocate head of pbuf chain into p */ p = (struct pbuf *)memp_malloc(MEMP_PBUF_POOL); LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_alloc: allocated pbuf %p\n", (void *)p)); if (p == NULL) { PBUF_POOL_IS_EMPTY(); return NULL; } p->type = type; p->next = NULL; /* make the payload pointer point 'offset' bytes into pbuf data memory */ p->payload = LWIP_MEM_ALIGN((void *)((u8_t *)p + (SIZEOF_STRUCT_PBUF + offset))); LWIP_ASSERT("pbuf_alloc: pbuf p->payload properly aligned", ((mem_ptr_t)p->payload % MEM_ALIGNMENT) == 0); /* the total length of the pbuf chain is the requested size */ p->tot_len = length; /* set the length of the first pbuf in the chain */ p->len = LWIP_MIN(length, PBUF_POOL_BUFSIZE_ALIGNED - LWIP_MEM_ALIGN_SIZE(offset)); LWIP_ASSERT("check p->payload + p->len does not overflow pbuf", ((u8_t*)p->payload + p->len <= (u8_t*)p + SIZEOF_STRUCT_PBUF + PBUF_POOL_BUFSIZE_ALIGNED)); LWIP_ASSERT("PBUF_POOL_BUFSIZE must be bigger than MEM_ALIGNMENT", (PBUF_POOL_BUFSIZE_ALIGNED - LWIP_MEM_ALIGN_SIZE(offset)) > 0 ); /* set reference count (needed here in case we fail) */ p->ref = 1; /* now allocate the tail of the pbuf chain */ /* remember first pbuf for linkage in next iteration */ r = p; /* remaining length to be allocated */ rem_len = length - p->len; /* any remaining pbufs to be allocated? */ while (rem_len > 0) { q = (struct pbuf *)memp_malloc(MEMP_PBUF_POOL); if (q == NULL) { PBUF_POOL_IS_EMPTY(); /* free chain so far allocated */ pbuf_free(p); /* bail out unsuccesfully */ return NULL; } q->type = type; q->flags = 0; q->next = NULL; /* make previous pbuf point to this pbuf */ r->next = q; /* set total length of this pbuf and next in chain */ LWIP_ASSERT("rem_len < max_u16_t", rem_len < 0xffff); q->tot_len = (u16_t)rem_len; /* this pbuf length is pool size, unless smaller sized tail */ q->len = LWIP_MIN((u16_t)rem_len, PBUF_POOL_BUFSIZE_ALIGNED); q->payload = (void *)((u8_t *)q + SIZEOF_STRUCT_PBUF); LWIP_ASSERT("pbuf_alloc: pbuf q->payload properly aligned", ((mem_ptr_t)q->payload % MEM_ALIGNMENT) == 0); LWIP_ASSERT("check p->payload + p->len does not overflow pbuf", ((u8_t*)p->payload + p->len <= (u8_t*)p + SIZEOF_STRUCT_PBUF + PBUF_POOL_BUFSIZE_ALIGNED)); q->ref = 1; /* calculate remaining length to be allocated */ rem_len -= q->len; /* remember this pbuf for linkage in next iteration */ r = q; } /* end of chain */ /*r->next = NULL;*/ break; case PBUF_RAM: /* If pbuf is to be allocated in RAM, allocate memory for it. */ p = (struct pbuf*)mem_malloc(LWIP_MEM_ALIGN_SIZE(SIZEOF_STRUCT_PBUF + offset) + LWIP_MEM_ALIGN_SIZE(length)); if (p == NULL) { return NULL; } /* Set up internal structure of the pbuf. */ p->payload = LWIP_MEM_ALIGN((void *)((u8_t *)p + SIZEOF_STRUCT_PBUF + offset)); p->len = p->tot_len = length; p->next = NULL; p->type = type; p->eb = NULL; LWIP_ASSERT("pbuf_alloc: pbuf->payload properly aligned", ((mem_ptr_t)p->payload % MEM_ALIGNMENT) == 0); break; #ifdef EBUF_LWIP case PBUF_ESF_RX: #endif /* ESF_LWIP */ /* pbuf references existing (non-volatile static constant) ROM payload? */ case PBUF_ROM: /* pbuf references existing (externally allocated) RAM payload? */ case PBUF_REF: /* only allocate memory for the pbuf structure */ p = (struct pbuf *)memp_malloc(MEMP_PBUF); if (p == NULL) { LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("pbuf_alloc: Could not allocate MEMP_PBUF for PBUF_%s.\n", (type == PBUF_ROM) ? "ROM" : "REF")); return NULL; } /* caller must set this field properly, afterwards */ p->payload = NULL; p->len = p->tot_len = length; p->next = NULL; p->type = type; break; default: LWIP_ASSERT("pbuf_alloc: erroneous type", 0); return NULL; } /* set reference count */ p->ref = 1; /* set flags */ p->flags = 0; LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_alloc(length=%"U16_F") == %p\n", length, (void *)p)); return p; }
/* layer是pbuf_layer类型,预留不同的首部空间。可以是 * PBUF_TRANSPORT, * PBUF_IP, * PBUF_LINK, * PBUF_RAW * length是要申请的数据区长度 * type是要申请的pbuf类型,可以是 * PBUF_RAM * PBUF_ROM * PBUF_REF * PBUF_POOL */ struct pbuf * pbuf_alloc(pbuf_layer layer, u16_t length, pbuf_type type) { struct pbuf *p, *q, *r; u16_t offset; s32_t rem_len; /* remaining length */ LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_alloc(length=%"U16_F")\n", length)); /* determine header offset */ offset = 0; /* 根据申请层次不同,选择不同的预留空间 */ switch (layer) { /* 传输层预留TCP首部空间,注意这里没有break;,继续累加 */ case PBUF_TRANSPORT: /* add room for transport (often TCP) layer header */ offset += PBUF_TRANSPORT_HLEN; /* FALLTHROUGH */ /* 预留网络层首部空间 */ case PBUF_IP: /* add room for IP layer header */ offset += PBUF_IP_HLEN; /* FALLTHROUGH */ /* 预留链路层首部空间 */ case PBUF_LINK: /* add room for link layer header */ offset += PBUF_LINK_HLEN; break; /* 不预留首部空间 */ case PBUF_RAW: break; /* 层次参数错误,返回错误信息 */ default: LWIP_ASSERT("pbuf_alloc: bad pbuf layer", 0); return NULL; } /* 根据要申请的type,分配具体类型的pbuf */ switch (type) { /* 在内存池分配pbuf和数据区 * PBUF_POOL类型可能需要分配多个MEMP_PBUF_POOL,以满足申请的空间 */ case PBUF_POOL: /* allocate head of pbuf chain into p */ /* MEMP_PBUF_POOL申请的大小是pbuf结构+PBUF_POOL_BUFSIZE。 * PBUF_POOL_BUFSIZE是1460(TCP的MSS)+20+20+14=1514, */ p = memp_malloc(MEMP_PBUF_POOL); LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_alloc: allocated pbuf %p\n", (void *)p)); /* 分配失败,则返回NULL */ if (p == NULL) { PBUF_POOL_IS_EMPTY(); return NULL; } /* 初始化type和next字段 */ p->type = type; p->next = NULL; /* make the payload pointer point 'offset' bytes into pbuf data memory */ /* 设置数据字段指针,预留出pbuf和offset大小 */ p->payload = LWIP_MEM_ALIGN((void *)((u8_t *)p + (SIZEOF_STRUCT_PBUF + offset))); LWIP_ASSERT("pbuf_alloc: pbuf p->payload properly aligned", ((mem_ptr_t)p->payload % MEM_ALIGNMENT) == 0); /* the total length of the pbuf chain is the requested size */ /* 设置pbuf链表第一个pbuf中的tot_len字段 */ p->tot_len = length; /* set the length of the first pbuf in the chain */ /* 第一个pbuf中的数据段长度是PBUF_POOL_BUFSIZE(1514) - 预留大小 */ p->len = LWIP_MIN(length, PBUF_POOL_BUFSIZE_ALIGNED - LWIP_MEM_ALIGN_SIZE(offset)); LWIP_ASSERT("check p->payload + p->len does not overflow pbuf", ((u8_t*)p->payload + p->len <= (u8_t*)p + SIZEOF_STRUCT_PBUF + PBUF_POOL_BUFSIZE_ALIGNED)); LWIP_ASSERT("PBUF_POOL_BUFSIZE must be bigger than MEM_ALIGNMENT", (PBUF_POOL_BUFSIZE_ALIGNED - LWIP_MEM_ALIGN_SIZE(offset)) > 0 ); /* set reference count (needed here in case we fail) */ /* 引用次数设为1 */ p->ref = 1; /* now allocate the tail of the pbuf chain */ /* 检查分配的第一个pbuf空间是否满足申请的length,不满足则继续申请,构造 * pbuf链表 */ /* remember first pbuf for linkage in next iteration */ /* r是pbuf上最后一个节点指针,用于把新节点放入链表 * p是pbuf链表头节点指针 */ r = p; /* remaining length to be allocated */ /* 还需申请的空间 */ rem_len = length - p->len; /* any remaining pbufs to be allocated? */ /* 仍然需要申请 */ while (rem_len > 0) { /* 申请新的pbuf,包括pbuf结构大小+1514 */ q = memp_malloc(MEMP_PBUF_POOL); /* 申请失败,则释放pbuf链表所有节点 */ if (q == NULL) { PBUF_POOL_IS_EMPTY(); /* free chain so far allocated */ pbuf_free(p); /* bail out unsuccesfully */ return NULL; } /* 设置新申请节点的类型 */ q->type = type; q->flags = 0; /* 新节点next指针设为0 */ q->next = NULL; /* make previous pbuf point to this pbuf */ /* 新节点放入链表 */ r->next = q; /* set total length of this pbuf and next in chain */ LWIP_ASSERT("rem_len < max_u16_t", rem_len < 0xffff); /* 新节点和后续所有所有节点数据段总长度 */ q->tot_len = (u16_t)rem_len; /* this pbuf length is pool size, unless smaller sized tail */ /* 新节点本身数据段长度 * 新节点数据段可承载的最大长度可能大于要申请的长度, * 所以len要设置为实际申请的长度和最大长度的最小值 */ q->len = LWIP_MIN((u16_t)rem_len, PBUF_POOL_BUFSIZE_ALIGNED); /* 新节点的数据段指针 */ q->payload = (void *)((u8_t *)q + SIZEOF_STRUCT_PBUF); LWIP_ASSERT("pbuf_alloc: pbuf q->payload properly aligned", ((mem_ptr_t)q->payload % MEM_ALIGNMENT) == 0); LWIP_ASSERT("check p->payload + p->len does not overflow pbuf", ((u8_t*)p->payload + p->len <= (u8_t*)p + SIZEOF_STRUCT_PBUF + PBUF_POOL_BUFSIZE_ALIGNED)); /* 新节点引用次数 */ q->ref = 1; /* 还需申请的长度 */ /* calculate remaining length to be allocated */ rem_len -= q->len; /* remember this pbuf for linkage in next iteration */ /* 保存pbuf最后一个节点指针 */ r = q; } /* end of chain */ /*r->next = NULL;*/ break; /* 在内存堆分配pbuf结构和数据区域 */ case PBUF_RAM: /* If pbuf is to be allocated in RAM, allocate memory for it. */ /* 申请总大小是pbuf结构大小+offset+数据区域大小 */ p = (struct pbuf*)mem_malloc(LWIP_MEM_ALIGN_SIZE(SIZEOF_STRUCT_PBUF + offset) + LWIP_MEM_ALIGN_SIZE(length)); if (p == NULL) { /* 申请失败,则返回NULL */ return NULL; } /* Set up internal structure of the pbuf. */ /* 设置pbuf的数据区指针 */ p->payload = LWIP_MEM_ALIGN((void *)((u8_t *)p + SIZEOF_STRUCT_PBUF + offset)); /* 从内存堆分配的pbuf只需要一个,不需要链表。因为第一个节点就可以获得要申请的数据区长度 * 数据区总长度 */ p->len = p->tot_len = length; /* 仅有一个节点 */ p->next = NULL; p->type = type; LWIP_ASSERT("pbuf_alloc: pbuf->payload properly aligned", ((mem_ptr_t)p->payload % MEM_ALIGNMENT) == 0); break; /* pbuf references existing (non-volatile static constant) ROM payload? */ /* PBUF_ROM和PBUF_REF类型,只分配PBUF结构,payload指向的数据区不需申请 */ case PBUF_ROM: /* pbuf references existing (externally allocated) RAM payload? */ case PBUF_REF: /* only allocate memory for the pbuf structure */ /* 从内存池中申请MEMP_PBUF类型的内存,详见memp_std.h */ p = memp_malloc(MEMP_PBUF); /* 申请失败,则返回NULL */ if (p == NULL) { LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("pbuf_alloc: Could not allocate MEMP_PBUF for PBUF_%s.\n", (type == PBUF_ROM) ? "ROM" : "REF")); return NULL; } /* 设置pbuf结构字段 */ /* caller must set this field properly, afterwards */ /* PBUF_ROM和PBUF_REF类型的pbuf,必须由调用者设置payload字段 */ p->payload = NULL; p->len = p->tot_len = length; p->next = NULL; p->type = type; break; default: LWIP_ASSERT("pbuf_alloc: erroneous type", 0); return NULL; } /* set reference count */ /* 除了PBUF_POOL类型,都没有设置ref。在这里同一设置ref */ p->ref = 1; /* set flags */ p->flags = 0; LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_alloc(length=%"U16_F") == %p\n", length, (void *)p)); return p; }
/** * @ingroup pbuf * Allocates a pbuf of the given type (possibly a chain for PBUF_POOL type). * * 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). * * @param layer header size * @param length size of the pbuf's payload * @param type this 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. * - PBUF_ROM: no buffer memory is allocated for the pbuf, even for * protocol headers. Additional headers must be prepended * by allocating another pbuf and chain in to the front of * 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_take should be called to copy the buffer. * - PBUF_POOL: the pbuf is allocated as a pbuf chain, with pbufs from * the pbuf pool that is allocated during pbuf_init(). * * @return the allocated pbuf. If multiple pbufs where allocated, this * is the first pbuf of a pbuf chain. */ struct pbuf * pbuf_alloc(pbuf_layer layer, u16_t length, pbuf_type type) { struct pbuf *p; u16_t offset = (u16_t)layer; LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_alloc(length=%"U16_F")\n", length)); switch (type) { case PBUF_REF: /* fall through */ case PBUF_ROM: p = pbuf_alloc_reference(NULL, length, type); break; case PBUF_POOL: { struct pbuf *q, *last; u16_t rem_len; /* remaining length */ p = NULL; last = NULL; rem_len = length; do { u16_t qlen; q = (struct pbuf *)memp_malloc(MEMP_PBUF_POOL); if (q == NULL) { PBUF_POOL_IS_EMPTY(); /* free chain so far allocated */ if (p) { pbuf_free(p); } /* bail out unsuccessfully */ return NULL; } qlen = LWIP_MIN(rem_len, (u16_t)(PBUF_POOL_BUFSIZE_ALIGNED - LWIP_MEM_ALIGN_SIZE(offset))); pbuf_init_alloced_pbuf(q, LWIP_MEM_ALIGN((void *)((u8_t *)q + SIZEOF_STRUCT_PBUF + offset)), rem_len, qlen, type, 0); LWIP_ASSERT("pbuf_alloc: pbuf q->payload properly aligned", ((mem_ptr_t)q->payload % MEM_ALIGNMENT) == 0); LWIP_ASSERT("PBUF_POOL_BUFSIZE must be bigger than MEM_ALIGNMENT", (PBUF_POOL_BUFSIZE_ALIGNED - LWIP_MEM_ALIGN_SIZE(offset)) > 0 ); if (p == NULL) { /* allocated head of pbuf chain (into p) */ p = q; } else { /* make previous pbuf point to this pbuf */ last->next = q; } last = q; rem_len = (u16_t)(rem_len - qlen); offset = 0; } while (rem_len > 0); break; } case PBUF_RAM: { u16_t payload_len = (u16_t)(LWIP_MEM_ALIGN_SIZE(offset) + LWIP_MEM_ALIGN_SIZE(length)); mem_size_t alloc_len = (mem_size_t)(LWIP_MEM_ALIGN_SIZE(SIZEOF_STRUCT_PBUF) + payload_len); /* bug #50040: Check for integer overflow when calculating alloc_len */ if ((payload_len < LWIP_MEM_ALIGN_SIZE(length)) || (alloc_len < LWIP_MEM_ALIGN_SIZE(length))) { return NULL; } /* If pbuf is to be allocated in RAM, allocate memory for it. */ p = (struct pbuf *)mem_malloc(alloc_len); if (p == NULL) { return NULL; } pbuf_init_alloced_pbuf(p, LWIP_MEM_ALIGN((void *)((u8_t *)p + SIZEOF_STRUCT_PBUF + offset)), length, length, type, 0); LWIP_ASSERT("pbuf_alloc: pbuf->payload properly aligned", ((mem_ptr_t)p->payload % MEM_ALIGNMENT) == 0); break; } default: LWIP_ASSERT("pbuf_alloc: erroneous type", 0); return NULL; } LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_alloc(length=%"U16_F") == %p\n", length, (void *)p)); return p; }
/** * Allocates a pbuf of the given type (possibly a chain for PBUF_POOL type). * * 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). * * @param layer flag to define header size * @param length size of the pbuf's payload * @param type this 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. * - PBUF_ROM: no buffer memory is allocated for the pbuf, even for * protocol headers. Additional headers must be prepended * by allocating another pbuf and chain in to the front of * 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_take should be called to copy the buffer. * - PBUF_POOL: the pbuf is allocated as a pbuf chain, with pbufs from * the pbuf pool that is allocated during pbuf_init(). * * @return the allocated pbuf. If multiple pbufs where allocated, this * is the first pbuf of a pbuf chain. */ struct pbuf *pbuf_alloc(pbuf_layer layer, uint16_t length, pbuf_type type) { struct pbuf *p, *q, *r; uint16_t offset; int32_t rem_len; /* remaining length */ LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_alloc(length=%" U16_F ")\n", length)); /* determine header offset */ switch (layer) { case PBUF_TRANSPORT: /* add room for transport (often TCP) layer header */ offset = PBUF_LINK_HLEN + PBUF_IP_HLEN + PBUF_TRANSPORT_HLEN; break; case PBUF_IP: /* add room for IP layer header */ offset = PBUF_LINK_HLEN + PBUF_IP_HLEN; break; case PBUF_LINK: /* add room for link layer header */ offset = PBUF_LINK_HLEN; break; case PBUF_RAW: offset = 0; break; default: return NULL; } switch (type) { case PBUF_POOL: /* allocate head of pbuf chain into p */ p = (struct pbuf *)memp_malloc(MEMP_PBUF_POOL); LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_alloc: allocated pbuf %p\n", (void *)p)); if (p == NULL) { PBUF_POOL_IS_EMPTY(); return NULL; } p->type = type; p->next = NULL; /* make the payload pointer point 'offset' bytes into pbuf data memory */ p->payload = LWIP_MEM_ALIGN((void *)((uint8_t *) p + (SIZEOF_STRUCT_PBUF + offset))); /* the total length of the pbuf chain is the requested size */ p->tot_len = length; /* set the length of the first pbuf in the chain */ p->len = LWIP_MIN(length, PBUF_POOL_BUFSIZE_ALIGNED - LWIP_MEM_ALIGN_SIZE(offset)); /* set reference count (needed here in case we fail) */ p->ref = 1; /* now allocate the tail of the pbuf chain */ /* remember first pbuf for linkage in next iteration */ r = p; /* remaining length to be allocated */ rem_len = length - p->len; /* any remaining pbufs to be allocated? */ while (rem_len > 0) { q = (struct pbuf *)memp_malloc(MEMP_PBUF_POOL); if (q == NULL) { PBUF_POOL_IS_EMPTY(); /* free chain so far allocated */ pbuf_free(p); /* bail out unsuccesfully */ return NULL; } q->type = type; q->flags = 0; q->next = NULL; /* make previous pbuf point to this pbuf */ r->next = q; /* set total length of this pbuf and next in chain */ q->tot_len = (uint16_t) rem_len; /* this pbuf length is pool size, unless smaller sized tail */ q->len = LWIP_MIN((uint16_t) rem_len, PBUF_POOL_BUFSIZE_ALIGNED); q->payload = (void *)((uint8_t *) q + SIZEOF_STRUCT_PBUF); q->ref = 1; /* calculate remaining length to be allocated */ rem_len -= q->len; /* remember this pbuf for linkage in next iteration */ r = q; } /* end of chain */ /*r->next = NULL; */ break; case PBUF_RAM: /* If pbuf is to be allocated in RAM, allocate memory for it. */ p = (struct pbuf *)mem_malloc(LWIP_MEM_ALIGN_SIZE(SIZEOF_STRUCT_PBUF + offset) + LWIP_MEM_ALIGN_SIZE(length)); if (p == NULL) { return NULL; } /* Set up internal structure of the pbuf. */ p->payload = LWIP_MEM_ALIGN((void *)((uint8_t *) p + SIZEOF_STRUCT_PBUF + offset)); p->len = p->tot_len = length; p->next = NULL; p->type = type; break; /* pbuf references existing (non-volatile static constant) ROM payload? */ case PBUF_ROM: /* pbuf references existing (externally allocated) RAM payload? */ case PBUF_REF: /* only allocate memory for the pbuf structure */ p = (struct pbuf *)memp_malloc(MEMP_PBUF); if (p == NULL) { LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("pbuf_alloc: Could not allocate MEMP_PBUF for PBUF_%s.\n", (type == PBUF_ROM) ? "ROM" : "REF")); return NULL; } /* caller must set this field properly, afterwards */ p->payload = NULL; p->len = p->tot_len = length; p->next = NULL; p->type = type; break; default: return NULL; } /* set reference count */ p->ref = 1; /* set flags */ p->flags = 0; LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_alloc(length=%" U16_F ") == %p\n", length, (void *)p)); return p; }