Chapter 2 NDIS Protocol Drivers

This describes the features of an NDIS driver that exports a set of ProtocolXxx functions at its lower edge. Such a protocol driver communicates with NDIS to send and receive network packets and to bind to and use an underlying miniport NIC driver or intermediate NDIS driver that exports a MiniportXxx interface at its upper edge.

Such an NDIS protocol driver might support TDI at its upper edge, or it might export a private interface to a higher level kernel-mode driver, possibly through a transport stack of drivers, including one that supports TDI at the top of the stack. For instance, an NDIS protocol driver can be the lowest module of a multimodule transport implementation of a standard protocol such as TCP/IP with TDI support in the highest module.

Protocol drivers that communicate with lower level NDIS drivers to send and receive packets always use NDIS-provided functions to communicate with the lower level NDIS drivers. For instance, a protocol driver must call NdisSend or NdisSendPackets to send a packet or packets to a lower level NDIS driver and must call NdisRequest to make or pass through query- or set-information requests with network related OID_XXXs that are supported by underlying drivers.

NDIS also provides a set of NdisXxx functions that hide the details of the underlying operating system. For instance, a protocol driver can call NdisInitializeEvent to create an event for synchronization purposes and NdisInitializeListHead to create a linked list. Protocol drivers that use the NDIS versions of such functions are more portable across Microsoft operating systems that support the Win32 interface. However, protocol drivers also can call OS-specific kernel-mode support routines, such as KeInitializeEvent to create an event or KeWaitForSingleObject to synchronize two threads of execution. The Windows NT kernel-mode support routines are documented in the Kernel-Mode Drivers Reference.

Pageable and Discardable Code

Every ProtocolXxx function runs at an IRQL in the range from PASSIVE_LEVEL to DISPATCH_LEVEL.

Functions that run exclusively at IRQL PASSIVE_LEVEL should be marked as pageable using the NDIS_PAGABLE_FUNCTION macro. Driver developers should designate code as pageable whenever possible, freeing system space for code that must be memory-resident. A driver function that runs at IRQL PASSIVE_LEVEL can be made pageable as long as it neither calls nor is called by any function that runs at IRQL >= DISPATCH_LEVEL, for instance a function that acquires a spin lock. Acquiring a spin lock causes the IRQL of the acquiring thread to be raised to IRQL DISPATCH_LEVEL. A driver function, such as ProtocolBindAdapter, that runs at IRQL PASSIVE_LEVEL must not call any NdisXxx functions that run at IRQL >= DISPATCH_LEVEL if that driver function is marked as pageable code. See the Network Driver Reference, which specifies the IRQL for each NdisXxx function.

The DriverEntry function of a protocol driver should be specified as initialization-only code, using the NDIS_INIT_FUNCTION macro. Code identified with this macro is assumed to only run once at system initialization time, and as a result, is only mapped during that time. After a function marked as initialization-only returns, it is discarded.

Access to any driver-allocated shared resource must be synchronized if the resource can be simultaneously shared by two driver functions or if the protocol driver can run on an SMP machine such that the same protocol driver function can be attempting to simultaneously access a resource from more than one processor. For instance, if a driver maintains a shared queue, a spin lock can be used to serialize access to that queue. The spin lock should be initialized when such a queue is created.

However, care should be taken not to overprotect a shared resource, such as an internal driver queue. Some read-only operations can be done without serializing access to a queue, but any operation that manipulates the queue links must be serialized. Spin locks always should be used sparingly and held as short a time as possible. See the Kernel-Mode Drivers Design Guide for an in-depth discussion of spin locks.