嵌入式LwIP学习笔记之数据包管理2
一、其他数据包操作函数
本章接上篇《嵌入式LwIP学习笔记之数据包管理1》,继续讲解其他的数据包操作函数,
pbuf_realloc 函数、pbuf_header 函数、pbuf_take 函数的具体流程。
二、pbuf_realloc 函数
pbuf_realloc 函数在相应 pbuf(链表)尾部释放一定的空间,将数据包 pbuf 中的数
据长度减少为某个长度值。对于 PBUF_RAM 类型的 pbuf,函数将调用内存堆管理中介绍到的 mem_realloc 函数,释放这些多余的空间;对于其他三种类型的 pbuf,该函数只是修改 pbuf 中的长度字段值,并不释放对应的内存池空间。
/**
* Shrink a pbuf chain to a desiredlength.
*
* @param p pbuf to shrink.
* @param new_len desired new lengthof pbuf chain
*
* Depending on the desired length,the first few pbufs in a chain might
* be skipped and left unchanged.The new last pbuf in the chain will be
* resized, and any remaining pbufswill be freed.
*
* @note If the pbuf is ROM/REF,only the ->tot_len and ->len fields are adjusted.
* @note May not be called on a packetqueue.
*
* @note Despite its name,pbuf_realloc cannot grow the size of a pbuf (chain).
*/
//将数据链表pbuf的尾部释放一定的空间,以期获得指定长度的pbuf
//p 需要释放的数据链表pbuf
//new_len 释放后的pbuf的数据长度
void pbuf_realloc(struct pbuf *p, u16_t new_len)
{
struct pbuf *q;
u16_t rem_len; /*用于指定当前pbuf的剩余长度*/
s32_t grow;
//无效值判断,及故障信息打印
LWIP_ASSERT("pbuf_realloc: p!= NULL", p != NULL);
LWIP_ASSERT("pbuf_realloc:sane p->type", p->type == PBUF_POOL ||
p->type == PBUF_ROM||
p->type == PBUF_RAM||
p->type ==PBUF_REF);
/* desired length larger thancurrent length? */
if (new_len >= p->tot_len) { //新的pbuf数据长度应在原总的数据长度内
/* enlarging not yet supported*/
return;
}
grow = new_len - p->tot_len; //获取需要释放的数据
rem_len = new_len; //保留当前应该剩余的数据长度
q = p; //使用q指向数据链表的p的首地址
//剩余数据的长度大于数据链表q的数据长度,则进入循环,否则说明已经找到需要释
//放数据的pbuf在链表中的位置
while (rem_len > q->len) {
rem_len -= q->len; //剩余的长度减去当前的pbuf的数据长度
/* decrease total lengthindicator */
LWIP_ASSERT("grow
q->tot_len += (u16_t)grow; //减少总的数据长度
q = q->next; //指向数据链表中的下一个pbuf
LWIP_ASSERT("pbuf_realloc:q != NULL", q != NULL);//判断q是否为NULL
}
//执行到这一步,说明我们找到了要释放数据的pbuf的位置
//为PBUF_RAM类型时,释放内存空间,其他的类型,只是减少数据长度
if ((q->type == PBUF_RAM)&& (rem_len != q->len)) {
/* reallocate and adjust thelength of the pbuf that will be split */
q = (struct pbuf *)mem_trim(q,(u16_t)((u8_t *)q->payload - (u8_t *)q) + rem_len);
LWIP_ASSERT("mem_trimreturned q == NULL", q != NULL);
}
/* adjust length fields for newlast pbuf */
q->len = rem_len; //最后一个pbuf的数据长度为最后的剩余长度
q->tot_len = q->len; //最后一个pbuf的数据总长度为最后的剩余长度,因为后面没有pbuf
/*如果pbuf后还接有链表,则释放掉*/
if (q->next != NULL) {
/* free remaining pbufs in chain*/
pbuf_free(q->next);
}
/* q is last packet in chain */
q->next = NULL; //数据释放后,应置空
}
三、pbuf_header 函数
pbuf_header 函数用于调整 pbuf 的 payload 指针(向前或向后移动一定的字节数),
在前面也说到过了,在 pbuf 的数据区前可能会预留一些协议首部空间,而pbuf 被创建时,payload 指针是指向数据区的,为了实现对这些预留空间的操作,可以调用函数pbuf_header 使 payload 指针指向数据区前的首部字段,这就为各层对数据包首部的操作提供了方便。当然,进行这个操作的时候,len和 tot_len 字段值也会随之更新。
/**
*Adjusts the payload pointer to hide or reveal headers in the payload.
* @param p pbuf to change the header size.
*@param header_size_increment Number of bytes to increment header size which
*increases the size of the pbuf. New space is on the front.
*/
//函数功能:调整 pbuf 的 payload 指针,指向数据区或pbuf的数据区的首部字段
//pbuf 需要更改的数据表pbuf
//header_size_increment 更改payload的指向位置,向前或者向后
u8_t pbuf_header(struct pbuf *p, s16_theader_size_increment)
{
u16_t type;
void*payload; //指向pbuf的数据区首地址
u16_t increment_magnitude; //描述指针的改变值
LWIP_ASSERT("p != NULL", p != NULL);
//位置改变量为0,说明不要更改,返回
//数据区p为空,则无数据可操作,返回
if ((header_size_increment == 0) || (p == NULL)) {
return 0;
}
if(header_size_increment < 0){ // header_size_increment小于0,表示向数据区的首部移动
increment_magnitude = -header_size_increment; //获取该变量
/*Check that we aren't going to move off the end of the pbuf */
LWIP_ERROR("increment_magnitude <= p->len",(increment_magnitude <= p->len), return 1;);
}else {
increment_magnitude = header_size_increment;
}
type= p->type; //获取pbuf的类型
payload = p->payload; //获取数据区的首地址
//为PBUF_RAM或PBUF_POOL类型
if(type == PBUF_RAM || type == PBUF_POOL) {
/*set new payload pointer */
p->payload = (u8_t *)p->payload - header_size_increment; //设置新的payload指向地址
/*新的payload指向地址超出最大值*/
if ((u8_t *)p->payload < (u8_t *)p +SIZEOF_STRUCT_PBUF) {
LWIP_DEBUGF( PBUF_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
("pbuf_header: failed as %p < %p (not enough space for newheader size)\n",
(void *)p->payload, (void *)(p + 1)));
/* restore old payload pointer */
p->payload = payload; //更新原来的payload到pbuf的payload
/* bail out unsuccesfully */
return 1; //返回失败
}
/*为PBUF_REF或PBUF_ROM等外部内存*/
}else if (type == PBUF_REF || type == PBUF_ROM) {
/*pbuf的payload指针不能向前移动,只能向增加的方向移动*/
if((header_size_increment < 0) && (increment_magnitude <=p->len)) {
p->payload = (u8_t *)p->payload - header_size_increment; //增加payload的指向地址
}else {
/*指针不能向前移动,所以返回失败*/
return 1;
}
}else {
/*Unknown type */
LWIP_ASSERT("bad pbuf type", 0);
return 1;
}
/*modify pbuf length fields */
p->len += header_size_increment; //增加pbuf的数据区的数据长度
p->tot_len += header_size_increment; //增加pbuf的数据区的数据总长度
LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_header: old %p new%p (%"S16_F")\n", (void *)payload, (void *)p->payload,header_size_increment));
return 0; //返回移动指针成功
}
四、pbuf_take 函数
pbuf_take 函数用于向 pbuf 的数据区域拷贝数据;pbuf_copy 函数用于将一个任何类型的 pbuf中的数据拷贝到一个 PBUF_RAM 类型的 pbuf 中。pbuf_chain 函数用于连接两个 pbuf(链表)为一个 pbuf 链表;pbuf_ref 函数用于将 pbuf 中的 ref 值加 1。
/**
* Copyapplication supplied data into a pbuf.
* Thisfunction can only be used to copy the equivalent of buf->tot_len data.
*
* @param bufpbuf to fill with data
* @paramdataptr application supplied data buffer
* @param lenlength of the application supplied data buffer
*
* @returnERR_OK if successful, ERR_MEM if the pbuf is not big enough
*/
//向pbuf的数据区拷贝指定长度的数据
//buf 数据链表buf
//len 拷贝的数据长度
//dataptr 拷贝数据后存入的缓存区
err_t pbuf_take(struct pbuf *buf, const void*dataptr, u16_t len)
{
struct pbuf*p; //定义数据表pbuf
u16_tbuf_copy_len; //每个pbuf中需要拷贝的数据长度
u16_ttotal_copy_len = len; //需要拷贝的总的数据长度
u16_tcopied_total = 0;
LWIP_ERROR("pbuf_take: invalid buf", (buf != NULL), return0;);
LWIP_ERROR("pbuf_take: invalid dataptr", (dataptr != NULL),return 0;);
//数据有效性判断
if ((buf ==NULL) || (dataptr == NULL) || (buf->tot_len < len)) {
returnERR_ARG;
}
/* Notesome systems use byte copy if dataptr or one of the pbuf payload pointers areunaligned. */
//遍历拷贝数据链表buf,直到拷贝完len的数据
for(p =buf; total_copy_len != 0; p = p->next) {
LWIP_ASSERT("pbuf_take: invalid pbuf", p != NULL);
buf_copy_len = total_copy_len; //剩余需要拷贝的数据长度
if (buf_copy_len> p->len) {
/* thispbuf cannot hold all remaining data */
buf_copy_len = p->len; //本次需要从pbuf数据区中拷贝的数据长度
}
/*拷贝pbuf中的数据区到缓存区中*/
MEMCPY(p->payload, &((char*)dataptr)[copied_total],buf_copy_len);
total_copy_len -= buf_copy_len; //更新还需要拷贝的数据长度
copied_total += buf_copy_len; //标记已经拷贝的数据长度
}
LWIP_ASSERT("did not copy all data", total_copy_len == 0&& copied_total == len);
returnERR_OK; //返回数据拷贝成功
}
五、pbuf_fill_chksum函数
/**
*Copies data into a single pbuf (*not* into a pbuf queue!) and updates
*the checksum while copying
*
*@param p the pbuf to copy data into
*@param start_offset offset of p->payload where to copy the data to
*@param dataptr data to copy into the pbuf
*@param len length of data to copy into the pbuf
*@param chksum pointer to the checksum which is updated
*@return ERR_OK if successful, another error if the data does not fit
* within the (first) pbuf (no pbuf queues!)
*/
err_t
pbuf_fill_chksum(struct pbuf *p, u16_tstart_offset, const void *dataptr,
u16_t len, u16_t *chksum)
{
u32_t acc;
u16_t copy_chksum;
char *dst_ptr;
LWIP_ASSERT("p != NULL", p != NULL);
LWIP_ASSERT("dataptr != NULL", dataptr != NULL);
LWIP_ASSERT("chksum!= NULL", chksum != NULL);
LWIP_ASSERT("len != 0", len != 0);
if((start_offset >= p->len) || (start_offset + len > p->len)) {
return ERR_ARG;
}
dst_ptr = ((char*)p->payload) + start_offset; //目的地址为数据区首地址 + 偏移地址
//以目的地址为首地址拷贝指定长度len的数据到dataptr中
copy_chksum = LWIP_CHKSUM_COPY(dst_ptr, dataptr, len);
if((start_offset & 1) != 0) { //若偏移地址为奇数,需要补齐计算校验和
copy_chksum = SWAP_BYTES_IN_WORD(copy_chksum);
}
acc= *chksum; //获取已有数据的校验和
acc+= copy_chksum; //加上当前新拷贝的数据的检验和
*chksum = FOLD_U32T(acc); //取校验和的反码
return ERR_OK;
}