NDIS小端口驱动ndisEdge学习二——小端口驱动的初始化

目录

 

1 首先进入DriverEntry检查版本号(可选)

2 WDF_NO_EVENT_CALLBACK初始化驱动标志

3 设置WdfDriverInitNoDispatchOverride表示框架不能拦截IO直接发给驱动的Irps

4 创建WDFDriver对象

5 初始化一个包装句柄(Wrapper Handler)

 6 填写小端口特征

7 注册小端口 其中因为要保护全局变量 所以需要有一个锁 另外需要有一个链表储存需要的信息 所以要初始化它们

8 小端口驱动的Adapter结构

9 配置信息的读取

10 设置适配器上下文(adapter context)

11 最后 MPInitialize的实现以及MPHalt的实现


1 首先进入DriverEntry检查版本号(可选)

// 检查版本号。这里主版本号为5,次版本号为0,要求最低版本为5.0
 if (NdisGetVersion() < ((MP_NDIS_MAJOR_VERSION << 16) | MP_NDIS_MINOR_VERSION)
{
     DEBUGP(MP_ERROR, ("This version of driver is not support on this OS\n"));
// 如果版本太低,则直接返回失败即可。
     return NDIS_STATUS_FAILURE;
}

2 WDF_NO_EVENT_CALLBACK初始化驱动标志

WDF_DRIVER_CONFIG_INIT(&config, WDF_NO_EVENT_CALLBACK);

3 设置WdfDriverInitNoDispatchOverride表示框架不能拦截IO直接发给驱动的Irps

具体参考WDF_DRIVER_INIT_FLAGS枚举

    config.DriverInitFlags |= WdfDriverInitNoDispatchOverride;

4 创建WDFDriver对象

 ntStatus = WdfDriverCreate(DriverObject,
                                            RegistryPath,
                                            WDF_NO_OBJECT_ATTRIBUTES,
                                            &config,
                                            WDF_NO_HANDLE);
    if (!NT_SUCCESS(ntStatus)){
        DEBUGP(MP_ERROR, ("WdfDriverCreate failed\n"));
        return NDIS_STATUS_FAILURE;
    }

5 初始化一个包装句柄(Wrapper Handler)

NDIS使用这个包装句柄来管理小端口的配置信息,但是我们不需要调用这个句柄获取任何信息,只是需要持有它来管理API调用

    // 初始化包装句柄。这个句柄是注册小端口必须的。但是对小端口
    // 驱动的开发者而言,除了调用一些NDIS函数需要提供这个句柄之
    // 外,并没有什么实质的意义。
    NdisMInitializeWrapper(
            &NdisWrapperHandle,
            DriverObject,
            RegistryPath,
            NULL
            );
    if (!NdisWrapperHandle){
        DEBUGP(MP_ERROR, ("NdisMInitializeWrapper failed\n"));
        return NDIS_STATUS_FAILURE;
    }

 6 填写小端口特征

    // 然后开始填写小端口特征。
    MPChar.MajorNdisVersion          = MP_NDIS_MAJOR_VERSION;
    MPChar.MinorNdisVersion          = MP_NDIS_MINOR_VERSION;
    MPChar.InitializeHandler         = MPInitialize;
    MPChar.HaltHandler               = MPHalt;
    MPChar.SetInformationHandler     = MPSetInformation;
    MPChar.QueryInformationHandler   = MPQueryInformation;
    MPChar.SendPacketsHandler        = MPSendPackets;
    MPChar.ReturnPacketHandler       = MPReturnPacket;
    MPChar.ResetHandler              = NULL;//MPReset;
    MPChar.CheckForHangHandler       = MPCheckForHang; //optional
#ifdef NDIS51_MINIPORT
    MPChar.CancelSendPacketsHandler = MPCancelSendPackets;
    MPChar.PnPEventNotifyHandler    = MPPnPEventNotify;
    MPChar.AdapterShutdownHandler   = MPShutdown;
#endif

    DEBUGP(MP_LOUD, ("Calling NdisMRegisterMiniport...\n"));

7 注册小端口 其中因为要保护全局变量 所以需要有一个锁 另外需要有一个链表储存需要的信息 所以要初始化它们

另外为了释放这些全局资源,防止内存泄露 还需要注册一个Unload函数在驱动卸载的时候调用

    // 注册小端口。注意需要包装句柄与小端口特征。
    Status = NdisMRegisterMiniport(
                    NdisWrapperHandle,
                    &MPChar,
                    sizeof(NDIS_MINIPORT_CHARACTERISTICS));
    if (Status != NDIS_STATUS_SUCCESS) {

        DEBUGP(MP_ERROR, ("Status = 0x%08x\n", Status));
        NdisTerminateWrapper(NdisWrapperHandle, NULL);

    } else {

        // 初始化全局变量。这些全局变量是在整个驱动中使用的
        NdisAllocateSpinLock(&GlobalData.Lock);
        NdisInitializeListHead(&GlobalData.AdapterList);

        // 注册一个Unload函数。请注意Unload是整个驱动卸载的时候调用。
        // 而协议特征中的MPHalt则是每个实例(网卡)卸载的时候调用的。
        NdisMRegisterUnloadHandler(NdisWrapperHandle, MPUnload);
    }

到这里 小端口DriverEntry就结束了,逻辑很简单 和协议驱动完全一致 

下面就是DriverEntry中提到的链表和锁结构介绍 

8 小端口驱动的Adapter结构

一个小端口驱动对应一个网卡(物理或者虚拟均可) 这个结构包含了我们需要的所有信息,所以部分回调函数会使用这个结构作为形参,

而且一台电脑上可能会有多块网卡(物理或虚拟的),小端口需要为每个网卡设置一个Adapter结构保存相关信息,这就是为什么需要链表(存储信息)以及锁(同步)的原因

结构太长可以不看 

我们只需要知道这个结构包含;链表结点、引用计数、锁、事件、物理设备和功能设备、缓冲池、句柄、适配器名字等信息

typedef struct _MP_ADAPTER
{
    LIST_ENTRY              List;
    LONG                    RefCount;
    NDIS_SPIN_LOCK          Lock;
    NDIS_EVENT              InitEvent;
    NDIS_EVENT              HaltEvent;

    //
    // Keep track of various device objects.
    //
    PDEVICE_OBJECT          Pdo;
    PDEVICE_OBJECT          Fdo;
    PDEVICE_OBJECT          NextDeviceObject;
    WDFDEVICE               WdfDevice;
    NDIS_HANDLE             AdapterHandle;
    WCHAR                   AdapterName[NIC_ADAPTER_NAME_SIZE];
    WCHAR                   AdapterDesc[NIC_ADAPTER_NAME_SIZE];
    ULONG                   Flags;
    UCHAR                   PermanentAddress[ETH_LENGTH_OF_ADDRESS];
    UCHAR                   CurrentAddress[ETH_LENGTH_OF_ADDRESS];
    WDFWORKITEM             WorkItemForNdisRequest;
    BOOLEAN                 Promiscuous;
    BOOLEAN                 RequestPending;
    BOOLEAN                 ResetPending;
    BOOLEAN                 IsHardwareDevice;
    BOOLEAN                 IsTargetSupportChainedMdls;
    //
    // Variables to track resources for the send operation
    //
    NDIS_HANDLE             SendBufferPoolHandle;
    LIST_ENTRY              SendFreeList;
    LIST_ENTRY              SendWaitList;
    LIST_ENTRY              SendBusyList;
    PUCHAR                  TCBMem;
    LONG                    nBusySend;
    NDIS_SPIN_LOCK          SendLock;

    //
    // Variables to track resources for the Receive operation
    //
    LIST_ENTRY              RecvFreeList;
    LIST_ENTRY              RecvBusyList;
    NDIS_SPIN_LOCK          RecvLock;
    LONG                    nBusyRecv;
    NDIS_HANDLE             RecvPacketPoolHandle;
    NDIS_HANDLE             RecvBufferPoolHandle;
    PUCHAR                  RCBMem;
    WDFWORKITEM             ReadWorkItem;
    WDFWORKITEM             ExecutiveCallbackWorkItem;
    LONG                    IsReadWorkItemQueued;
    //
    // Packet Filter and look ahead size.
    //
    ULONG                   PacketFilter;
    ULONG                   ulLookAhead;
    ULONG                   ulLinkSpeed;

    // multicast list
    ULONG                   ulMCListSize;
    UCHAR                   MCList[NIC_MAX_MCAST_LIST][ETH_LENGTH_OF_ADDRESS];

    // Packet counts
    ULONG64                 GoodTransmits;
    ULONG64                 GoodReceives;
    ULONG                   NumTxSinceLastAdjust;

    // Count of transmit errors
    ULONG                   TxAbortExcessCollisions;
    ULONG                   TxLateCollisions;
    ULONG                   TxDmaUnderrun;
    ULONG                   TxLostCRS;
    ULONG                   TxOKButDeferred;
    ULONG                   OneRetry;
    ULONG                   MoreThanOneRetry;
    ULONG                   TotalRetries;
    ULONG                   TransmitFailuresOther;

    // Count of receive errors
    ULONG                   RcvCrcErrors;
    ULONG                   RcvAlignmentErrors;
    ULONG                   RcvResourceErrors;
    ULONG                   RcvDmaOverrunErrors;
    ULONG                   RcvCdtFrames;
    ULONG                   RcvRuntErrors;

    //
    // Talking to NDISPROT protocol
    //
    HANDLE                  FileHandle;
    PFILE_OBJECT            FileObject;
    WDFIOTARGET             IoTarget;
    UCHAR                   PhyNicMacAddress[ETH_LENGTH_OF_ADDRESS];
    PCALLBACK_OBJECT        CallbackObject;
    PVOID                   CallbackRegisterationHandle;
    WDFREQUEST              StatusIndicationRequest;
    NDISPROT_INDICATE_STATUS NdisProtIndicateStatus;
    WDFMEMORY               WdfStatusIndicationBuffer;
    //
    // Statistic for debugging & validation purposes
    //
    ULONG                   nReadsPosted;
    ULONG                   nReadsCompletedWithError;
    ULONG                   nPacketsIndicated;
    ULONG                   nPacketsReturned;
    ULONG                   nBytesRead;
    ULONG                   nPacketsArrived;
    ULONG                   nWritesPosted;
    ULONG                   nWritesCompletedWithError;
    ULONG                   nBytesArrived;
    ULONG                   nBytesWritten;
    ULONG                   nReadWorkItemScheduled;
} MP_ADAPTER, *PMP_ADAPTER;

9 配置信息的读取

配置信息读取是在NICReadRegParameters中实现的,这个函数又是在MPInitialize中被调用。

而实现的主要步骤是利用NdisOpneConfiguration函数打开包装句柄,然后利用NdisReadConfiguration函数读取信息

NDIS_STATUS
NICReadRegParameters(
    PMP_ADAPTER Adapter,
    NDIS_HANDLE WrapperConfigurationContext)
/*++
Routine Description:

    Read device configuration parameters from the registry

Arguments:

    Adapter                         Pointer to our adapter
    WrapperConfigurationContext     For use by NdisOpenConfiguration

    Should be called at IRQL = PASSIVE_LEVEL.

Return Value:

    NDIS_STATUS_SUCCESS
    NDIS_STATUS_FAILURE
    NDIS_STATUS_RESOURCES

--*/
{
    NDIS_STATUS     Status = NDIS_STATUS_SUCCESS;
    NDIS_HANDLE     ConfigurationHandle;
    PUCHAR          NetworkAddress;
    UINT            Length;
    PUCHAR          pAddr;
    PNDIS_CONFIGURATION_PARAMETER pParameterValue;
    NDIS_STRING strMiniportName = NDIS_STRING_CONST("MiniportName");
    NDIS_STRING strFilterName = NDIS_STRING_CONST("Promiscuous");

    DEBUGP(MP_TRACE, ("--> NICReadRegParameters\n"));

    PAGED_CODE();

    //
    // Open the registry for this adapter to read advanced
    // configuration parameters stored by the INF file.
    //
    NdisOpenConfiguration(
        &Status,
        &ConfigurationHandle,
        WrapperConfigurationContext);
    if (Status != NDIS_STATUS_SUCCESS)
    {
        DEBUGP(MP_ERROR, ("NdisOpenConfiguration failed\n"));
        return NDIS_STATUS_FAILURE;
    }

    do
    {
        //
        // Read the Miniport Name.
        // This feature is available only XP and above.
        //

        NdisReadConfiguration(&Status,
                              &pParameterValue,
                              ConfigurationHandle,
                              &strMiniportName,
                              NdisParameterString);

        if (Status != NDIS_STATUS_SUCCESS) {

            DEBUGP(MP_ERROR, ("NdisReadConfiguration for miniport name failed\n"));

        } else {
            Length = min(NIC_ADAPTER_NAME_SIZE-1,
              pParameterValue->ParameterData.StringData.Length/sizeof(WCHAR));

            RtlStringCchCopyW(Adapter->AdapterName, Length+1,
                    (PWCHAR)pParameterValue->ParameterData.StringData.Buffer);
        }

        //
        // Read the Promiscuous filter value.
        //

        NdisReadConfiguration(&Status,
                              &pParameterValue,
                              ConfigurationHandle,
                              &strFilterName,
                              NdisParameterInteger);

        if (Status != NDIS_STATUS_SUCCESS)
        {
            DEBUGP(MP_ERROR, ("NdisReadConfiguration for promiscuous key failed\n"));
            break;
        }

        Adapter->Promiscuous = (BOOLEAN)pParameterValue->ParameterData.IntegerData;

    } WHILE (FALSE);

10 设置适配器上下文(adapter context)

适配器上下文是一个指针,这个指针指向的空间就是适配器结构,这个指针必须在MPInitialize中设置好,然后在多个小端口特征中的回调函数中作为实参传入,而设置适配器上下文是通过NdisMSetAttributesEx这个NDIS内核API函数做到的

这个函数原型如下:

VOID NdisMSetAttributesEx(
  _In_     NDIS_HANDLE         MiniportAdapterHandle,
  _In_     NDIS_HANDLE         MiniportAdapterContext,
  _In_opt_ UINT                CheckForHangTimeInSeconds,
  _In_     ULONG               AttributeFlags,
  _In_opt_ NDIS_INTERFACE_TYPE AdapterType
);

对于形参的解释请参考MSDN

 

11 最后 MPInitialize的实现以及MPHalt的实现

MPInitialize就是对应小端口特征中的InitializeHandler特征的回调函数

这个函数在发现每个实例(网卡)时被Windows内核调用,因此我们应该在这个函数中实现对adapter结构的初始化

另外需要在这个回调中生成一个小端口设备对象

 

然后就是MPHalt,这个回调负责网卡被拔出或者停止工作时负责释放所有资源,在MPHalt中有一个MPShutdown函数,在MPSHoutdown中完成关闭网卡硬件等操作:

1 释放并解除所有映射过的IO端口的映射

2 取消注册的所有中断

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值