Porting NDIS miniport drivers to NetAdapterCx

Warning

Some information in this topic relates to prereleased product, which may be substantially modified before it's commercially released. Microsoft makes no warranties, express or implied, with respect to the information provided here.

NetAdapterCx is preview only in Windows 10, version 1903.

Currently, NetAdapterCx client drivers cannot be certified.

This page describes how to convert an NDIS 6.x miniport driver into a NetAdapterCx client driver.

For general information about WDF, please review the WDF Driver Development Guide.

Compilation settings

Open your existing NDIS miniport driver project in Visual Studio and use the following steps to convert it to a KMDF project.

  1. First, navigate to Configuration Properties->Driver Settings->Driver Model and verify that Type of driver is set to KMDF, and that KMDF Version Major and KMDF Version Minor are both empty.

  2. In project properties, open Driver Settings->Network Adapter Driver and set Link to the Network Adapter Class Extension to Yes.

    • If your converted driver will still call NDIS APIs, continue to link against ndis.lib.
  3. Remove NDIS preprocessor macros, like NDIS650_MINIPORT=1.

  4. Add the following headers to every source file (or to your common/precompiled header):

    #include <ntddk.h>
    #include <wdf.h>
    #include <netadaptercx.h>
    
  5. Add standard WDF decorations to your INF:

    [Yourdriver.Wdf]
    KmdfService = Yourdriverservice, Yourdriver.wdfsect
    
    [Yourdriver.wdfsect]
    KmdfLibraryVersion = <insert here>
    
  6. Add new required networking keywords to the NT section of your INF:

Driver initialization

Remove the call to NdisMRegisterMiniportDriver from DriverEntry, and add the following:

WDF_DRIVER_CONFIG_INIT(&config, EvtDriverDeviceAdd);
status = WdfDriverCreate(. . . );
if (!NT_SUCCESS(status)) {
  return status;
}

If it is set, remove the WdfDriverInitNoDispatchOverride flag from the call to WdfDriverCreate.

DriverUnload is an optional routine for a WDF networking client driver, so you can remove it if you like. Do not call NdisMDeregisterMiniportDriver from DriverUnload.

Device initialization

Next, you'll distribute code from MiniportInitializeEx into the appropriate WDF event callback handlers, several of which are optional. For details on the callback sequence, see Power-Up Sequence for an Network Adapter WDF Client Driver.

You'll call the methods equivalent to NdisMSetMiniportAttributes when you're starting your net adapter, but before you call NetAdapterStart. However, instead of calling one routine with a generic NDIS_MINIPORT_ADAPTER_ATTRIBUTES structure, the client driver calls different functions to set different types of capabilities.

For info on the callbacks you'll need to provide and when to start a net adapter, see Device and adapter initialization.

Creating queues to manage control requests

Next, still in EVT_WDF_DRIVER_DEVICE_ADD, set up the object identifier (OID) path. The OID path is modeled like a WDF queue, but you'll be getting OIDs instead of WDFREQUESTs.

There are two high level approaches you might take when porting this. The first option is to register a EVT_NET_REQUEST_DEFAULT handler that receives OID requests in a very similar way to how a miniport driver receives requests from NDIS. This is the easiest port, since you'll likely just need to adjust a function signature from your old MINIPORT_OID_REQUEST handler.

The other option is to break apart your OID handler's switch statement and provide a separate handler for each individual OID. You might choose this option if your device requires OID-specific functionality.

If you used the Direct OID Request Interface in NDIS 6.1, replace it with a parallel WDF queue. Similarly, the regular (serial) request interface in NDIS should become a sequential WDF queue.

For info on registering handlers for OIDs, see Handling control requests.

Reading configuration from the registry

Next, replace calls to NdisOpenConfigurationEx and related functions with the NetConfiguration* methods. The NetConfiguration* methods are similar to the Ndis*Configuration* functions, and you won't need to restructure your code.

For more info, see Accessing configuration information.

Receiving I/O control codes (IOTCLs) from user mode

Read this section if your NDIS driver calls NdisRegisterDeviceEx, a routine used to create a control device object (CDO) to receive IOCTLs from user mode.

Here are two ways to do this in your WDF networking client driver.

The most straightforward port is to create a control device object by calling WdfControlDeviceInitAllocate from the client's EVT_WDF_DRIVER_DEVICE_ADD callback. For more info, see Using Control Device Objects.

However, the recommended solution is to create a device interface, as described in Using Device Interfaces.

Finishing device initialization

At this point in EVT_WDF_DRIVER_DEVICE_ADD, you can do anything else you'd like to initialize your device, like allocating interrupts.

Handling power state change notifications

A WDF client driver does not receive OID_PNP_SET_POWER for power state changes.

Instead, a WDF client registers optional callback functions to receive power state change notifications. For an overview, see Supporting PnP and Power Management in Function Drivers.

Typically, the code in your OID_PNP_SET_POWER handler moves to EVT_WDF_DEVICE_D0_EXIT and EVT_WDF_DEVICE_D0_ENTRY.

Because the WDF power state machine is slightly different, you might need to make minor modifications to the code.

Specifically, in its MiniportInitializeEx callback function, an NDIS miniport driver performs one-time initialization tasks as well as work to bring the device to the D0 state. Then, it repeats the work to go to D0 in its OID_PNP_SET_POWER handler.

In contrast, a WDF client performs one-time initialization tasks in event callbacks before EVT_WDF_DEVICE_D0_ENTRY, during which the device is in a low-power state. Then it does the work to go to D0 in EVT_WDF_DEVICE_D0_ENTRY.

To summarize, in WDF, you put your "go to D0" code in one place, instead of two.

For details on the callback sequence, see Power-Up sequence for a NetAdapterCx client driver.

Querying and setting power management capabilities

Similarly, a WDF client driver does not receive OID_PM_PARAMETERS to query or set power management hardware capabilities of the network adapter.

Instead, the driver queries the necessary wake-on-LAN (WoL) configuration from the NETPOWERSETTINGS object. For more info, see Configuring power management.

The actual flags you get back have the same semantics as they do for an NDIS 6 miniport, so you don't need to make deep changes to the logic. The main difference is that you can now query these flags during the power-down sequence. See Power-down sequence for a NetAdapterCx client driver.

Once you've moved this code around, you can delete your OID handlers for OID_PNP_SET_POWER and OID_PM_PARAMETERS.

Because the NetAdapter framework keeps your device at D0 while the host uses the network interface, the client typically does not implement power logic; the default NetAdapter power behavior is sufficient.

Data path

The data path programming model has changed significantly. Here are some key differences:

Device removal

Device removal for a WDF NIC driver is the same as in any other WDF device driver, with no networking specific processing required. The network data path shuts down first, followed by the WDF device. For info about WDF shutdown, see A User Unplugs a Device.

Your MiniportHaltEx handler will likely be distributed between EVT_WDF_DEVICE_D0_EXIT and EVT_WDF_DEVICE_RELEASE_HARDWARE.

The WDF client does not need to delete the NetAdapter or any of the OID and datapath queues that it created. WDF deletes these objects automatically.

You can delete MiniportShutdownEx, MiniportResetEx and MiniportCheckForHangEx. These callbacks are no longer supported.

NDIS-WDF function equivalents

Most NdisXxx functions can be replaced with a WDF equivalent. In general, you should find that you need very little functionality that is imported from NDIS.SYS.

For a list of function equivalents, see NDIS-WDF function equivalents.

Debugging

See Debugging a NetAdapterCx client driver.

The !ndiskd.netadapter debugger extension shows similar results to what !ndiskd.miniport shows for an NDIS 6 driver.

Conclusion

Using the steps in this topic, you should have a working driver that starts and stops your device.