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