在NPF_Write函數中主要調用NdisSend函數完成數據包的底層發送。NdisSend函數的原型如下:
VOID NdisSend(
OUT PNDIS_STATUS Status,
IN NDIS_HANDLE NdisBindingHandle,
IN PNDIS_PACKET Packet
);
);
參數Status指向一個調用着提供的變量,儲存函數返回的狀態。底層驅動決定所返回的NDIS_STATUS_XXX,通常爲下列值。
NDIS_STATUS_SUCCESS
給定的數據包已在網絡上傳輸。
NDIS_STATUS_PENDING
數據包的請求被異步操作,傳輸結束後調用者的ProtocolSendComplete函數將被調用
NDIS_STATUS_INVALID_PACKET
請求傳輸的大小對NIC太大,或者可能NIC指出一個錯誤數據包傳輸給了驅動程序
NDIS_STATUS_CLOSING
底層驅動程序已關閉
NDIS_STATUS_RESET_IN_PROGRESS
底層驅動當前正在復位NIC。
NDIS_STATUS_FAILURE
返回一個不是特定描述的失敗,如果不是上述NDIS_STATUS_XXX的狀態,則返回該狀態。
特定的NDIS_STATUS_XXX返回在一個傳輸操作中設備的I/O錯誤, 依賴於NIC的特性與NIC驅動程序寫函數的判斷力。例如,一個微端口驅動程序可能返回NDIS_STATUS_NO_CABLE,如果它的NIC爲驅動程序指明瞭這種情況。
參數Packet指向調用者所提供的數據包描述,由NdisAllocatePacket分配, 其把底層NIC驅動應該傳輸到網線上的數據進行鏈裝。
1.8.1.2 NPF_SendComplete函數
只要NdisSend函數返回NDIS_STATUS_PENDING狀態,驅動程序的ProtocolSendComplete函數(此處爲NPF_SendComplete函數)在數據包被髮送後就被調用。ProtocolSendComplete 對一個完成的傳輸操作執行任何必要的後處理,諸如提示最初的請求發送已經完成。NPF_SendComplete函數原型如下:
VOID NPF_SendComplete(
IN NDIS_HANDLE ProtocolBindingContext,
IN PNDIS_PACKET pPacket,
IN NDIS_STATUS Status
)
參數ProtocolBindingContext 描述一個協議驅動分配的上下文的句柄,驅動程序調用NdisOpenAdapter獲得該句柄。參數pPacket 指向協議驅動提供的已完成發送的數據包的描述符。參數Status描述了發送操作的最終狀態。
NPF_SendComplete函數針對NPF_BufferedWrite與NPF_Write()作不同的處理。針對NPF_Write操作,如果發送數據包緩衝池中空閒數據包不少於一半,就給出數據包可寫入的事件通知,同時檢測如果待發數據包爲0 ,那麼產生數據包發送完畢的事件通知。並釋放各種必要的資源、遞減掛起待發數據包的數目。針對NPF_BufferedWrite操作,就給出數據包可寫入的事件通知,並釋放各種必要的資源、遞減掛起待發數據包的數目。
VOID NPF_SendComplete(
IN NDIS_HANDLE ProtocolBindingContext,
IN PNDIS_PACKET pPacket,
IN NDIS_STATUS Status
)
{
POPEN_INSTANCE Open;
PMDL TmpMdl;
Open= (POPEN_INSTANCE)ProtocolBindingContext;
if( RESERVED(pPacket)->FreeBufAfterWrite )
{
/* 數據包由NPF_BufferedWrite函數發送*/
//釋放與數據包關聯的MDL
NdisUnchainBufferAtFront(pPacket, &TmpMdl);
IoFreeMdl(TmpMdl);
NdisFreePacket(pPacket);
//遞減掛起待發數據包的數目
InterlockedDecrement(&Open->Multiple_Write_Counter);
NdisSetEvent(&Open->WriteEvent);
return;
}
else
{
/*數據包由NPF_Write函數發送*/
//遞減掛起待發數據包的數目
ULONG stillPendingPackets =
InterlockedDecrement(&Open->TransmitPendingPackets);
//把該數據包劃回釋放列表中
NdisFreePacket(pPacket);
//如果提交給NdisSend並且未得到確認的數據包個數
//低於TX數據包緩衝池中數據包的一半,
//將喚醒任意正在等待TX數據包緩衝池中具有可用數據包空間的發送者。
if (stillPendingPackets < TRANSMIT_PACKETS/2)
{
NdisSetEvent(&Open->WriteEvent);
}
else
{
//否則,復位該事件,因此我們確認
//NPF_Write最終將阻塞去等待TX數據包緩衝池中數據包的可用性
NdisResetEvent(&Open->WriteEvent);
}
if(stillPendingPackets == 0)
{
//當所有數據包被NdisSend成功發送後
//產生NdisWriteCompleteEvent事件通知
NdisSetEvent(&Open->NdisWriteCompleteEvent);
}
return;
}
}
在NPF_Write與NPF_SendComplete兩函數中注意Open->TransmitPendingPackets、Open->WriteEvent與Open->NdisWriteCompleteEvent的改變。
NPF_Write函數在進入重複發送的while循環前,代碼行Open->TransmitPendingPackets = 0設置掛起並等待發送完成的數據包個數爲0,進入循環後,在調用NdisSend函數發送數據包前,代碼行InterlockedIncrement(&Open->TransmitPendingPackets)原子增加掛起待發數據包的個數;在NPF_SendComplete函數中代碼行ULONG stillPendingPackets = InterlockedDecrement(&Open->TransmitPendingPackets)原子減少掛起待發數據包的個數。
NPF_Write函數在進入重複發送的while循環前,代碼行NdisResetEvent(&Open->WriteEvent)復位WriteEvent 事件,進入循環後,調用NdisAllocatePacket函數,如果發送數據包緩衝池中沒有空閒數據包可用,需要等待一段時間,通過NdisWaitEvent(&Open->WriteEvent,1)代碼行等待NPF_SendComplete完成句柄函數中發送的事件通知。在NPF_SendComplete中,如果發送數據包緩衝池中空閒數據包不少於一半,將通過NdisSetEvent(&Open->WriteEvent)代碼行發送事件通知,否則代碼行NdisResetEvent(&Open->WriteEvent)復位該事件。
NPF_Write函數檢查如果可以進行寫操作,代碼行NdisResetEvent(&Open->NdisWriteCompleteEvent)復位NdisWriteCompleteEvent事件;進入循環後,在每次調用NdisSend函數發送數據包前,代碼行NdisResetEvent(&Open->NdisWriteCompleteEvent)復位該事件;循環結束後代碼行NdisWaitEvent(&Open->NdisWriteCompleteEvent, 0)等待數據包發送完畢。在NPF_SendComplete函數中代碼
if(stillPendingPackets == 0)
{
NdisSetEvent(&Open->NdisWriteCompleteEvent);
}
檢測如果待發數據包爲0 ,那麼產生NdisWriteCompleteEvent事件通知數據包發送完畢。NPF_Write函數等待結束。
注意自旋鎖的使用,先看NPF_Write後面的簡單使用
NdisAcquireSpinLock(&Open->WriteLock);
Open->WriteInProgress = FALSE;
NdisReleaseSpinLock(&Open->WriteLock);
自旋鎖Open->WriteLock保護Open->WriteInProgress變量訪問的唯一性,自選所得使用必須配對使用,否則導致死鎖,下面是較複雜的使用:
NdisAcquireSpinLock(&Open->WriteLock);
if(Open->WriteInProgress)
{
NdisReleaseSpinLock(&Open->WriteLock);
…
return STATUS_UNSUCCESSFUL;
}
else
{
Open->WriteInProgress = TRUE;
NdisResetEvent(&Open->NdisWriteCompleteEvent);
}
NdisReleaseSpinLock(&Open->WriteLock);
本文出自 “千江月” 博客,請務必保留此出處http://eslxf.blog.51cto.com/918801/217016