windows网络适配器驱动开发-NetAdapterCx 对象的摘要

1.1 默认父子关系

下图显示了 NetAdapterCx 对象的默认父子关系。父对象位于图的顶部,因此,例如,NETADAPTER 对象默认为 WDFDEVICE 对象的子对象。可以有多个实例的对象由一个双框表示。

1.2 对象说明

WDFDEVICE 对象是表示设备的标准框架对象。NETADAPTER 对象表示网络接口,它是所有网络 I/O 的终结点。每个 WDFDEVICE 可以有多个 NETADAPTER 对象,WDFDEVICE 是每个 NETADAPTER 的父对象。

1.3 多个适配器的情况

大多数网络接口卡(NIC)驱动程序只有一个 NETADAPTER 用于其物理设备,但如果某些客户端驱动程序管理具有多个槽的服务器 NIC,则某些客户端驱动程序可能有多个 NETADAPTER。例如,移动宽带 WDF 类扩展(MBBCx)客户端驱动程序可以管理多个 NETADAPTER 对象,每个对象都表示额外的数据包数据协议(PDP)上下文。

1.4 初始化和创建流程

必须通过调用 netAdapterInitAllocate 和 NetAdapterCreate,在客户端驱动程序的 EVT_WDF_DRIVER_DEVICE_ADD 回调函数中初始化和创建 NETADAPTER 对象。

1.5 启动和设置功能

然后,必须通过调用 NetAdapterStart,从驱动程序的 EVT_WDF_DEVICE_PREPARE_HARDWARE 回调函数中启动它。在调用 NetAdapterStart 之前,驱动程序可以选择设置适配器的功能,例如链接层功能、电源功能、数据路径功能、接收缩放功能和硬件卸载功能。

2. 设备和适配器初始化

下面介绍用于初始化和启动 WDFDEVICE 和 NETADAPTER 对象的 NetAdapterCx 客户端驱动程序的步骤。

2.1 EVT_WDF_DRIVER_DEVICE_ADD

2.1.1 注册回调

当 NetAdapterCx 客户端驱动程序在其 DriverEntry 例程中调用 WdfDriverCreate 时,它会注册 EVT_WDF_DRIVER_DEVICE_ADD 回调函数。

2.1.2 操作顺序

在 EVT_WDF_DRIVER_DEVICE_ADD 中,NetAdapterCx 客户端驱动程序应按顺序执行以下操作:

复制代码
status = NetDeviceInitConfig(DeviceInit);
if (!NT_SUCCESS(status)) 
{
    return status;
}
  • 调用 WdfDeviceCreate,如果设备支持多个 NETADAPTER,建议在设备上下文中存储指向每个适配器的指针。

  • 创建 NETADAPTER 对象。为此,客户端调用 NetAdapterInitAllocate,后跟可选的 NetAdapterInitSetXxx 方法来初始化适配器的属性。最后,客户端调用 NetAdapterCreate。

2.1.3 代码示例(初始化 NETADAPTER)

以下示例演示客户端驱动程序如何初始化 NETADAPTER 对象。请注意,此示例中简化了错误处理。

复制代码
WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attribs, MY_ADAPTER_CONTEXT);

//
// Allocate the initialization structure
//
PNETADAPTER_INIT adapterInit = NetAdapterInitAllocate(device);
if(adapterInit == NULL)
{
    return status;
}        

//
// Optional: set additional attributes
//

// Datapath callbacks for creating packet queues
NET_ADAPTER_DATAPATH_CALLBACKS datapathCallbacks;
NET_ADAPTER_DATAPATH_CALLBACKS_INIT(&datapathCallbacks,
                                    MyEvtAdapterCreateTxQueue,
                                    MyEvtAdapterCreateRxQueue);
NetAdapterInitSetDatapathCallbacks(adapterInit,
                                   datapathCallbacks);
// 
// Required: create the adapter
//
NETADAPTER* netAdapter;
status = NetAdapterCreate(adapterInit, &attribs, netAdapter);
if(!NT_SUCCESS(status))
{
    NetAdapterInitFree(adapterInit);
    adapterInit = NULL;
    return status;
}

//
// Required: free the adapter initialization object even 
// if adapter creation succeeds
//
NetAdapterInitFree(adapterInit);
adapterInit = NULL;

//
// Optional: initialize the adapter's context
//
PMY_ADAPTER_CONTEXT adapterContext = GetMyAdapterContext(&netAdapter);
...

2.1.4 上下文空间建议

可以选择将上下文空间添加到 NETADAPTER 对象。由于可以在任何 WDF 对象上设置上下文,因此可以为 WDFDEVICE 和 NETADAPTER 对象添加单独的上下文空间。在步骤 2.1.3 中的示例中,客户端将 MY_ADAPTER_CONTEXT 添加到 NETADAPTER 对象。

建议在 WDFDEVICE 的上下文中放置与设备相关的数据,并将与网络相关的数据(例如链接层地址)放入 NETADAPTER 上下文中。如果要移植现有的 NDIS 6.x 驱动程序,则可能有一个 MiniportAdapterContext,它将与网络相关和设备相关的数据合并到单个数据结构中。为了简化移植过程,只需将该整个结构转换为 WDFDEVICE 上下文,并使 NETADAPTER 的上下文成为指向 WDFDEVICE 上下文的小型结构。

2.1.5 数据路径回调

可以选择为 NET_ADAPTER_DATAPATH_CALLBACKS_INIT 方法提供 2 个回调:

  • EVT_NET_ADAPTER_CREATE_TXQUEUE

  • EVT_NET_ADAPTER_CREATE_RXQUEUE

2.2 EVT_WDF_DEVICE_PREPARE_HARDWARE

2.2.1 注册回调

许多 NetAdapterCx 客户端驱动程序在 EVT_WDF_DEVICE_PREPARE_HARDWARE 回调函数内启动其适配器,唯独移动宽带类扩展客户端驱动程序除外。若要注册 EVT_WDF_DEVICE_PREPARE_HARDWARE 回调函数,NetAdapterCx 客户端驱动程序必须调用 WdfDeviceInitSetPnpPowerEventCallbacks。

2.2.2 设置功能

在 EVT_WDF_DEVICE_PREPARE_HARDWARE 中,除了其他硬件准备任务外,客户端驱动程序还设置适配器的必需和可选功能。

NetAdapterCx 要求客户端驱动程序设置以下必需功能:

    • 数据路径功能:驱动程序调用 NetAdapterSetDataPathCapabilities 来设置这些功能。
    • 链接层功能:驱动程序调用 NetAdapterSetLinkLayerCapabilities 来设置这些功能。
    • 链接层最大传输单元(MTU)大小:驱动程序调用 NetAdapterSetLinkLayerMtuSize 以设置 MTU 大小。

然后,驱动程序必须调用 NetAdapterStart 来启动其适配器。

2.2.3 启动适配器

以下示例演示客户端驱动程序如何启动 NETADAPTER 对象。请注意,为了简洁和清晰,省略了设置每个适配器功能方法所需的代码,并且简化了错误处理。

复制代码
PMY_DEVICE_CONTEXT deviceContext = GetMyDeviceContext(device);

NETADAPTER netAdapter = deviceContext->NetAdapter;

PMY_ADAPTER_CONTEXT adapterContext = GetMyAdapterContext(netAdapter);

//
// Set required adapter capabilities
//

// Link layer capabilities
...
NetAdapterSetDatapathCapabilities(netAdapter,
                                  &txCapabilities,
                                  &rxCapabilities);
...
NetAdapterSetLinkLayerCapabilities(netAdapter,
                                   &linkLayerCapabilities);
...
NetAdapterSetLinkLayerMtuSize(netAdapter,
                              MY_MAX_PACKET_SIZE - ETHERNET_HEADER_LENGTH);

//
// Set optional adapter capabilities
//

// Link layer capabilities
...
NetAdapterSetPermanentLinkLayerAddress(netAdapter,
                                       &adapterContext->PermanentAddress);
...
NetAdapterSetCurrentLinkLayerAddress(netAdapter,
                                     &adapterContext->CurrentAddress);

// Datapath capabilities
...
NetAdapterSetDatapathCapabilities(netAdapter,
                                  &txCapabilities,
                                  &rxCapabilities);

// Receive scaling capabilities
...
NetAdapterSetReceiveScalingCapabilities(netAdapter,
                                        &receiveScalingCapabilities);

// Hardware offload capabilities
...
NetAdapterOffloadSetChecksumCapabilities(netAdapter,
                                         &checksumCapabilities);
...
NetAdapterOffloadSetLsoCapabilities(netAdapter,
                                    &lsoCapabilities);
...
NetAdapterOffloadSetRscCapabilities(netAdapter,
                                    &rscCapabilities);

//
// Required: start the adapter
//
status = NetAdapterStart(netAdapter);
if(!NT_SUCCESS(status))
{
    return status;
}