(NDIS5.0)协议驱动Ndisprot源码解读

在这里插入图片描述

DriverEntry

主要任务:
生成控制设备,
填写协议特征(协议的派遣例程),
初始化一个全局的链表和锁(用于存放绑定好的网卡),还有一个绑定完成的事件
注册协议返回一个全局的协议句柄(NdisProtocolHandle)。

协议与网卡的绑定

VOID
NdisProtBindAdapter(
OUT PNDIS_STATUS pStatus,
IN NDIS_HANDLE BindContext,
IN PNDIS_STRING pDeviceName,
IN PVOID SystemSpecific1,
IN PVOID SystemSpecific2
)
在DriverEntry中填写了协议特征,这个例程就是用来绑定协议与网卡的。

如果一个协议驱动绑定了一个网卡,那么
1.网卡收到的所有数据都会提交给这个协议
2.协议可以使用这个网卡发送数据
当Windows检测到有网卡时,就会调用每个注册过的协议的BindAdpaterHandler函数(本驱动中用NdisProtBindAdapter填写)。

NdisProtBindAdapter主要是对PNDISPROT_OPEN_CONTEXT的初始化工作,一个打开上下文就是一个网卡的绑定,里面存储了这次绑定的信息和资源,读写队列,包队列,自旋锁,电源事件等等。
结构如下:

typedef struct _NDISPROT_OPEN_CONTEXT
{
    LIST_ENTRY              Link;           // Link into global list
    ULONG                   Flags;          // State information
    ULONG                   RefCount;
    NPROT_LOCK               Lock;

    PFILE_OBJECT            pFileObject;    // Set on OPEN_DEVICE

    NDIS_HANDLE             BindingHandle;
    NDIS_HANDLE             BindingHandle;
    NDIS_HANDLE             SendNetBufferListPool;  
    // let every net buffer list contain one net buffer(don't know how many net buffers can be include in one list.
	NDIS_HANDLE RecvNetBufferListPool;
    ULONG                   MacOptions;
    ULONG                   MaxFrameSize;

    LIST_ENTRY              PendedWrites;   // pended Write IRPs
    ULONG                   PendedSendCount;

    LIST_ENTRY              PendedReads;    // pended Read IRPs
    ULONG                   PendedReadCount;
    LIST_ENTRY              RecvPktQueue;   // queued rcv packets
    ULONG                   RecvPktCount;

    NET_DEVICE_POWER_STATE  PowerState;
    NDIS_EVENT              PoweredUpEvent; // signalled iff PowerState is D0
    NDIS_STRING             DeviceName;     // used in NdisOpenAdapter
    NDIS_STRING                DeviceDescr;    // friendly name

    NDIS_STATUS             BindStatus;     // for Open/CloseAdapter
    NPROT_EVENT              BindEvent;      // for Open/CloseAdapter

    BOOLEAN                 bRunningOnWin9x;// TRUE if Win98/SE/ME, FALSE if NT

    ULONG                   oc_sig;         // Signature for sanity

    UCHAR                   CurrentAddress[NPROT_MAC_ADDR_LEN];
    PIRP                  StatusIndicationIrp;

} NDISPROT_OPEN_CONTEXT, *PNDISPROT_OPEN_CONTEXT;

在函数中为OpenContext分配空间,清空内存,设置特征数据oc_sig,便于识别判错。
初始化自旋锁,三个队列,和一个电源事件。
增加一个引用计数。
将这个打开上下文(绑定的一个网卡)添加到全局链表。 为避免同步问题。这个操作需要调用全局自旋锁
函数结尾调用ndisportCreateBinding进行最终绑定。

ndisportCreateBinding

这是一个比较复杂的函数,因为要使用同步手段防止多线程竞争


NDIS_STATUS
ndisprotCreateBinding(
    IN PNDISPROT_OPEN_CONTEXT                   pOpenContext,
    __in_bcount(BindingInfoLength) IN PUCHAR    pBindingInfo,
    IN ULONG                                    BindingInfoLength
    )
    {
    	……
		//首先在全局打开上下文链表查询网卡是否已经绑定, *** 注意这个操作应增加打开上下文的引用计数,并在成功返回处减少引用计数***
		ndisprotLookupDevice(pBindingInfo, BindingInfoLength);
		……
		//获取pOpenContext的锁,因为只对空闲上下文进行操作
		 NPROT_ACQUIRE_LOCK(&pOpenContext->Lock);
		//因为是使用自旋锁,所以尽量进行简单的操作,用标志位判断同步信息
		//如果不是空闲状态,或者收到了解除绑定要求就直接返回失败,并解开自旋锁
		 if (!NPROT_TEST_FLAGS(pOpenContext->Flags, NUIOO_BIND_FLAGS, NUIOO_BIND_IDLE) ||
            NPROT_TEST_FLAGS(pOpenContext->Flags, NUIOO_UNBIND_FLAGS, NUIOO_UNBIND_RECEIVED))
            {
            ……
            }
         
         //设置标记,表示开始使用打开上下文,并释放锁

 		NPROT_SET_FLAGS(pOpenContext->Flags, NUIOO_BIND_FLAGS, NUIOO_BIND_OPENING);
 		
 		NPROT_RELEASE_LOCK(&pOpenContext->Lock);

		//接下来就是开始绑定了
		分别分配设备名
		……
		分配接收包池
		……
		分配发送包池
		……
		//NDIS中每个以太网包都用一个NDIS_PACKET描述,内容由NDIS_BUFFER描述;
		在这里分配的包池大小就是缓冲区能容纳的包的个数。以后就不用动态分配了。

		//
        //  Assume that the device is powered up.
        //
		
		pOpenContext->PowerState = NetDeviceStateD0;
		//初始化打开事件(绑定)
		NPROT_INIT_EVENT(&pOpenContext->BindEvent);
		
		//填写一个局部变量
		NPROT_ZERO_MEM(&OpenParameters, sizeof(NDIS_OPEN_PARAMETERS));
        OpenParameters.Header.Revision = NDIS_OPEN_PARAMETERS_REVISION_1;
        OpenParameters.Header.Size = sizeof(NDIS_OPEN_PARAMETERS);
        OpenParameters.Header.Type = NDIS_OBJECT_TYPE_OPEN_PARAMETERS;
        OpenParameters.AdapterName = BindParameters->AdapterName;
        OpenParameters.MediumArray = &MediumArray[0];
        OpenParameters.MediumArraySize = sizeof(MediumArray) / sizeof(NDIS_MEDIUM);
        OpenParameters.SelectedMediumIndex = &SelectedMediumIndex;
        OpenParameters.FrameTypeArray = &FrameTypeArray[0];
	OpenParameters.FrameTypeArraySize = sizeof(FrameTypeArray) / sizeof(NET_FRAME_TYPE);
	NDIS_DECLARE_PROTOCOL_OPEN_CONTEXT(NDISPROT_OPEN_CONTEXT)
		//在这里终于绑定了!!!
		NdisOpenAdapter(
            &Status,
            &OpenErrorCode,
            &pOpenContext->BindingHandle, //绑定事件
            &SelectedMediumIndex,
            &MediumArray[0],		//以太网介质类型NdisMedium802_3
            sizeof(MediumArray) / sizeof(NDIS_MEDIUM),
            Globals.NdisProtocolHandle,	
            (NDIS_HANDLE)pOpenContext,
            &pOpenContext->DeviceName,  //要绑定的网卡名
            0,
            NULL);
            //等待绑定事件受信
            if (Status == NDIS_STATUS_PENDING)
        {
            NPROT_WAIT_EVENT(&pOpenContext->BindEvent, 0);
            Status = pOpenContext->BindStatus;
        }
        //如果不成功
        …………
        
        fOpenComplete = TRUE;
        //
        //  Get the friendly name for the adapter. It is not fatal for this
        //  to fail.
        //
        (VOID)NdisQueryAdapterInstanceName(
                &pOpenContext->DeviceDescr,
                pOpenContext->BindingHandle
                );
                //OID:想下层驱动发送请求并返回结果
		//往下层驱动查询MAC地址,最大帧,连接状态,网卡选项....
		…………
		//一些尾部处理,别忘记减少OPEN_CONTEXT的引用计数

	}

查询OID request

OID是NDIS Object identifilers,上层发送OID_XXX请求,下层返回约定结果

例如查询最大帧长,返回结果 TCP/IP进行打包发送。

在Ndisport中封装了查询函数
// 获得最大帧长
Status = ndisprotDoRequest(
pOpenContext,
NdisRequestQueryInformation,
OID_GEN_MAXIMUM_FRAME_SIZE,
&pOpenContext->MaxFrameSize,
sizeof(pOpenContext->MaxFrameSize),
&BytesProcessed
);

    if (Status != NDIS_STATUS_SUCCESS)
    {
        DEBUGP(DL_WARN, ("CreateBinding: qry max frame failed: %x\n",
                Status));
        break;
    }

NDIS_STATUS
ndisprotDoRequest(
    IN PNDISPROT_OPEN_CONTEXT pOpenContext,
    IN NDIS_REQUEST_TYPE    RequestType,
    IN NDIS_OID Oid,
    IN PVOID InformationBuffer,
    IN ULONG InformationBufferLength,
    OUT PULONG pBytesProcessed
    )
{
	NDISPROT_REQUEST ReqContext;  //port自定义的结构 一个请求,一个事件,一个状态
    PNDIS_REQUEST pNdisRequest = &ReqContext.Request; //填充ndis请求
    NDIS_STATUS Status;

    // 初始化一个事件。这个事件会在请求完成函数中被设置,
    // 以便通知请求完成了。
    NPROT_INIT_EVENT(&ReqContext.ReqEvent);  //填充事件
    
	pNdisRequest->RequestType = RequestType;//填充ndis请求中的请求类型,
	//例如查询请求
	switch (RequestType)
    {
        case NdisRequestQueryInformation:
            pNdisRequest->DATA.QUERY_INFORMATION.Oid = Oid;
            pNdisRequest->DATA.QUERY_INFORMATION.InformationBuffer =
                                    InformationBuffer;
            pNdisRequest->DATA.QUERY_INFORMATION.InformationBufferLength =
                                    InformationBufferLength;
            break;

        case NdisRequestSetInformation:
            pNdisRequest->DATA.SET_INFORMATION.Oid = Oid;
            pNdisRequest->DATA.SET_INFORMATION.InformationBuffer =
                                    InformationBuffer;
            pNdisRequest->DATA.SET_INFORMATION.InformationBufferLength =
                                    InformationBufferLength;
            break;

        default:
            NPROT_ASSERT(FALSE);
            break;
    }
        // 发送请求
    NdisRequest(&Status,
                pOpenContext->BindingHandle,
                pNdisRequest);
       //尚未完成 则等待事件受信,受信事件在RequestComplete函数(在协议函数中填写)中设置
    if (Status == NDIS_STATUS_PENDING)
    {
        NPROT_WAIT_EVENT(&ReqContext.ReqEvent, 0);
        Status = ReqContext.Status;
    }
    // 如果成功了的话...
    if (Status == NDIS_STATUS_SUCCESS)
    {
        // 获得结果的长度。这个结果的长度是实际需要的长度。可
        // 能比我们实际提供的长度要长。
        // 请求中记录了完成时的字节长度
        *pBytesProcessed = (RequestType == NdisRequestQueryInformation)?
                            pNdisRequest->DATA.QUERY_INFORMATION.BytesWritten:
                            pNdisRequest->DATA.SET_INFORMATION.BytesRead;

        // 如果结果长度比实际上我们提供的缓冲区要长,那么就简
        // 单的设置为输入参数中缓冲区的最大长度。
        if (*pBytesProcessed > InformationBufferLength)
        {
            *pBytesProcessed = InformationBufferLength;
        }
    }
    return (Status);

}

解除网卡绑定

是Bind的逆操作。

读写与回调

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值