Porting NDIS miniport drivers to NetAdapterCx
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.
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.
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
.
- If your converted driver will still call NDIS APIs, continue to link against
Remove NDIS preprocessor macros, like
NDIS650_MINIPORT=1
.Add the following headers to every source file (or to your common/precompiled header):
#include <ntddk.h> #include <wdf.h> #include <netadaptercx.h>
Add standard WDF decorations to your INF:
[Yourdriver.Wdf] KmdfService = Yourdriverservice, Yourdriver.wdfsect [Yourdriver.wdfsect] KmdfLibraryVersion = <insert here>
Add new required networking keywords to the NT section of your INF:
*IfConnectorPresent
*ConnectionType
*DirectionType
*AccessType
*HardwareLoopback
For more information about these keywords and an example, see INF files for NetAdapterCx client drivers.
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 a 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.
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 (IOCTLs) 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:
- In the NetAdapter model, network traffic is no longer per adapter, as in NDIS, but rather per WDF queue. See Creating I/O Queues.
- Instead of NET_BUFFER_LIST and NET_BUFFER pools, NetAdapterCx introduces a ring buffer that is comprised of net packets, which map to NDIS as follows:
- A NET_PACKET is similar to a NET_BUFFER_LIST + NET_BUFFER.
- A NET_PACKET_FRAGMENT is similar to a memory descriptor list (MDL). Each NET_PACKET has one or more of these.
- For details on the replacement structures and how to use them, see Packet descriptors and extensions.
- In NDIS 6.x, the miniport needs to handle start and pause semantics. In the NetAdapterCx model, this is no longer the case.
- The EVT_RXQUEUE_ADVANCE callback is similar to MINIPORT_RETURN_NET_BUFFER_LISTS in NDIS 6.x.
- The EVT_TXQUEUE_ADVANCE callback is similar to MINIPORT_SEND_NET_BUFFER_LISTS in NDIS 6.x.
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 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.
Note: NetAdapterCx doesn't currently support iSCSI boot.