//Copy the packet into a buffer while (CurrentMdl && DataLength > 0) { NdisQueryMdl(CurrentMdl, &pSrc, &CurrLength, NormalPagePriority); if (pSrc == NULL) { bytesCopied = 0; break; } // // Current buffer length is greater than the offset to the buffer // if (CurrLength > Offset) { pSrc += Offset; CurrLength -= Offset; if (CurrLength > DataLength) { CurrLength = DataLength; } DataLength -= CurrLength; NdisMoveMemory(pDest, pSrc, CurrLength); bytesCopied += CurrLength; pDest += CurrLength; Offset = 0; } else { Offset -= CurrLength; } NdisGetNextMdl(CurrentMdl, &CurrentMdl); }
这段代码就是拷贝数据到缓冲区。其中CurrMDL,offset和dataLength来自Net_Buffer:
pNetBuffer = NET_BUFFER_LIST_FIRST_NB(pNetBufferList); // // Try to get the 1p data from OOB data // if(NET_BUFFER_LIST_INFO(pNetBufferList, Ieee8021QNetBufferListInfo) != 0) { Ndis8021QInfo.Value = NET_BUFFER_LIST_INFO(pNetBufferList, Ieee8021QNetBufferListInfo); UserPriority = (UCHAR)Ndis8021QInfo.TagHeader.UserPriority; } //copy the packet into a buffer pDest = packetBuffer; CurrentMdl = pNetBuffer->MdlChain; Offset = pNetBuffer->DataOffset; DataLength = pNetBuffer->DataLength;
其中NET_BUFFER_LIST_FIRST_NB返回的是第一个Net_Buffer. CurrentMDL指向MDL的list.现在来看看如何拷贝数据的。其中NdisQueryMdl返回MDL虚拟地址的基地址Psrc,和对应的缓冲区长度CurrLength
1、如果CurrLenght > offset,那么把Psrc+offset,指向used data.同样,CurrLength-offset,表示当前的MDL中有用的数据长度。然后执行NdisMoveMemory的拷贝操作.紧接着把,dataLength-CurrLength,因为已经拷贝好了一部分,然后再把offset置零,因为现在指针已经到了可用数据区。
2、当再次得到下一个MDL的Psrc,和CurrLength时,因为offset已经=0,所以CurrLength肯定大于0,所以即使执行PSrc+=offset, CurrLength-=offset.最后拷贝整个长度的空间(以图为例,不都是这样!!),dataLength-CurrLength.
3、最后第三次得到pSrc,和CurrLength,但是CurrLength,已经大于dataLength,所以只拷贝dataLength的长度数据。就完成了所有数据拷贝。
4、但是如果第一次的时候offset>CurrLength,那么就是比如图中第一个MDL为unused data区,执行offset-=CurrLength.如下图:
这样之后又回到了前面所说的CurrLength>offset了。
这就是拷贝数据到缓冲区的过程。不知有何疏漏,或者错误,见谅~