1.8    驱动程序中对应的函数接口

NPF中,提供了NPF_WriteNPF_BufferedWriteNPF_IoControl函数,实现把数据包传递给NDIS层,最终调用NdisSend函数把数据包发送出去。

1.8.1    发送单个数据包的接口实现

1.8.1.1             NPF_Write函数

packet.dllPacketSendPacket函数执行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