1.8 驅動程序中對應的函數接口
在NPF中,提供了NPF_Write、NPF_BufferedWrite與NPF_IoControl函數,實現把數據包傳遞給NDIS層,最終調用NdisSend函數把數據包發送出去。
1.8.1 發送單個數據包的接口實現
1.8.1.1 NPF_Write函數
庫packet.dll的PacketSendPacket函數執行WriteFile系統調用時,該函數被調用(響應IRP_MJ_WRITE)。
DriverObject->MajorFunction[IRP_MJ_WRITE] = NPF_Write;
由NPF_Write函數執行數據包的發送,根據Open->Nwrites的值執行發送次數,該數值在NPF_Open()函數中設置爲默認值爲1,通過NPF_IoControl函數可修改該值。
函數NPF_Write的原型如下:
NTSTATUS NPF_Write(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
參數DeviceObject指向用戶所使用的設備驅動對象,參數Irp指向包含用戶請求的IRP。
函數返回操作的狀態。
具體代碼實現如下:
NTSTATUS NPF_Write(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
{
POPEN_INSTANCE Open;
PIO_STACK_LOCATION IrpSp;
PNDIS_PACKET pPacket;
NDIS_STATUS Status;
ULONG NumSends;
ULONG numSentPackets;
/*獲得調用者在給定IRP中的堆棧位置*/
IrpSp = IoGetCurrentIrpStackLocation(Irp);
/*獲得POPEN_INSTANCE的實例open*/
Open=IrpSp->FileObject->FsContext;
/*獲得重複發送的次數*/
NumSends = Open->Nwrites;
/*驗證重複發送次數的有效性,必須大於0,否則函數返回*/
if (NumSends == 0)
{
Irp->IoStatus.Information = 0;
Irp->IoStatus.Status = STATUS_SUCCESS;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
}
/*驗證輸入參數的有效性:*/
//1. 數據包的大小應該大於0
//2. 小於等於數據鏈路層最大幀的大小,並且
//3. 最大幀大小不應該爲0。
//檢查用戶提供的緩衝區不爲空
if(IrpSp ->Parameters.Write.Length == 0 ||
// 檢查MaxFrameSize被正確初始化
Open->MaxFrameSize == 0 ||
Irp->MdlAddress == NULL ||
//檢查幀大小小於等於MTU
IrpSp->Parameters.Write.Length > Open->MaxFrameSize)
{ //輸入參數的有效性檢查失敗,否則函數返回
Irp->IoStatus.Information = 0;
Irp->IoStatus.Status = STATUS_UNSUCCESSFUL;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_UNSUCCESSFUL;
}
/*如果可能,增加綁定句柄的引用計數*/
if(NPF_StartUsingBinding(Open) == FALSE)
{
//適配器沒有被綁定,不能發送數據包,函數返回
Irp->IoStatus.Information = 0;
Irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_INVALID_DEVICE_REQUEST;
}
/*獲取保護WriteInProgress變量的自旋鎖*/
NdisAcquireSpinLock(&Open->WriteLock);
if(Open->WriteInProgress)
{
// 另一個寫操作當前正在處理中,
//釋放保護WriteInProgress變量的自旋鎖,函數返回
NdisReleaseSpinLock(&Open->WriteLock);
NPF_StopUsingBinding(Open);
Irp->IoStatus.Information = 0;
Irp->IoStatus.Status = STATUS_UNSUCCESSFUL;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_UNSUCCESSFUL;
}
else
{ //無正在處理中的寫操作,可開始寫操作,並設置爲正在執行寫操作的狀態
Open->WriteInProgress = TRUE;
//復位NdisWriteCompleteEvent事件
NdisResetEvent(&Open->NdisWriteCompleteEvent);
}
/* 釋放保護WriteInProgress變量的自旋鎖*/
NdisReleaseSpinLock(&Open->WriteLock);
/*復位掛起在等待SendComplete的數據包個數爲0*/
Open->TransmitPendingPackets = 0;
/*復位同步多個寫進程的事件WriteEvent*/
NdisResetEvent(&Open->WriteEvent);
numSentPackets = 0; //已發數據包次數初始化爲0
/*進入重複發送的while循環中*/
while( numSentPackets < NumSends )
{
//分配並初始化一個數據包描述符,
//由pPacket返回所分配的數據包描述符
NdisAllocatePacket(
&Status,
&pPacket,
Open->PacketPool
);
if (Status == NDIS_STATUS_SUCCESS)
{
//緩衝池中有空閒數據包可用,準備用NdisSend發送該數據包
//如果有要求,爲該數據包設置SkipSentPackets標誌
//目前,我們只在禁止接受迴環數據包時設置該標誌,
//比如,拒收由我們自己發送的數據包
if(Open->SkipSentPackets)
{
NdisSetPacketFlags(
pPacket,
g_SendPacketFlags);
}
//數據包沒有一個緩衝區,不需要每次單個寫操作後執行內存釋放
RESERVED(pPacket)->FreeBufAfterWrite = FALSE;
//把寫緩衝區附加給該數據包
NdisChainBufferAtFront(pPacket,Irp->MdlAddress);
//遞增掛起待發數據包的數目
InterlockedIncrement(
&Open->TransmitPendingPackets);
//復位NdisWriteCompleteEvent事件
NdisResetEvent(&Open->NdisWriteCompleteEvent);
//向低層的MAC層請求數據包發送
NdisSend(&Status,Open->AdapterHandle,pPacket);
if (Status != NDIS_STATUS_PENDING)
{
// 數據包的發送沒有被掛起,立即調用完成句柄函數
NPF_SendComplete(Open,pPacket,Status);
}
numSentPackets ++; //已發數據包增加1個
}
else
{
//傳輸池中沒有空閒數據包可用,需要等待一段時間。
//當至少一半TX數據包緩衝池是可用的時候,
//Open->WriteEvent事件獲得通知,
//NPF_SendComplete完成句柄函數可發送該通知。
NdisWaitEvent(&Open->WriteEvent,1);
}
}
//當程序運行到此位置時,所有的數據包已在NdisSend中排隊等待發送了,
//我們僅僅需要通過SendComplete
//完成句柄函數等待所有的數據包發送結束
//(如果任何一個NdisSend請求返回STATUS_PENDING)。
NdisWaitEvent(&Open->NdisWriteCompleteEvent, 0);
//所有的數據包被髮送,釋放適配器的綁定
NPF_StopUsingBinding(Open);
//沒有寫操作正在處理
NdisAcquireSpinLock(&Open->WriteLock);
Open->WriteInProgress = FALSE;
NdisReleaseSpinLock(&Open->WriteLock);
//完成該Irp並返回成功
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information =
IrpSp->Parameters.Write.Length;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
}
NPF_Write函數的主要正常流程如下爲:
首先獲取各個必要參數,檢查輸入參數的有效性;
開始使用適配器的綁定;
檢測是否可執行寫操作;
執行一次或多次數據包發送;
等待發送結束;
停止使用綁定;
設置爲可執行寫操作狀態( Open->WriteInProgress = FALSE;)
函數結束,返回Irp的信息;
本文出自 “千江月” 博客,請務必保留此出處http://eslxf.blog.51cto.com/918801/217015