Copyright © 2004 by Stephan Wolf. All rights reserved
The Windows DDK offers several compile-time flags for NDIS drivers. Most of these flags are described in the DDK documentation in sections "Compile Flags Used by NDIS Drivers" and "Specifying the NDIS Version Number". But there are still some undocumented pitfalls, which you should be aware of.
Note that all the NDIS compile flags discussed in this article are available only in the NT-based DDKs. These compile flags do not apply to the Windows 9x/Me DDKs.
Compile flags are not used in a consistent manner in the DDK's "ndis.h" header file. Some compile flags just need to exist, regardless if they are assigned a value or not. These compile flags are tested with the #ifdef preprocessor directive in "ndis.h".
Other compile flags actually need to be assigned a value. That is, "ndis.h" evaluates the actual value of these compile flags with the #if preprocessor directive.
For the sake of simplicity, I recommend you always assign a value of "1" to the NDIS compile flags that are relevant to your NDIS driver. The only exception to this rule is the BINARY_COMPATIBLE compile flag, which can also be assigned a "0".
You can define compile flags in your SOURCES file, in your source code, or in both. Use the "-D" option to define a compile flag in your SOURCES file as in the following example:
C_DEFINES=$(C_DEFINES) -DNDIS_MINIPORT_DRIVER=1
Or you can use the #define preprocessor directive to define a compile flag in your source code. Note that the definition needs to be placed above the line where "ndis.h" gets included as the following example shows:
#define NDIS_MINIPORT_DRIVER 1
#include <ndis.h>
Here is a list of the NDIS compile flags that are available in the DDK (Windows 2003 and earlier DDKs):
Compile Flag | Required for NDIS Miniports |
Required for NDIS Protocols |
Target Windows Versions |
---|---|---|---|
NDIS_MINIPORT_DRIVER | Yes | No | All |
NDIS51_MINIPORT NDIS50_MINIPORT NDIS40_MINIPORT |
Yes | No | All |
NDIS51 NDIS50 NDIS40 |
No | Yes | All |
BINARY_COMPATIBLE | No (implicitly set) | Optional | Windows 9x/Me |
USE_KLOCKS | Yes, if "deserialized" | Yes | Windows 9x/Me |
NDIS_WDM | Yes, if NDIS-WDM | N/A | All |
_WIN2K_COMPAT_SLIST_USAGE | Yes | Yes | Windows 2000 and earlier |
WIN9X_COMPAT_SPINLOCK | Yes | Yes | Windows 9x/Me |
Note: NDIS intermediate drivers implement both a miniport and a protocol. Thus, all of the compile flags listed here apply to intermediate drivers.
This compile flag is a requirement for every NDIS miniport driver and, by implication, for every intermediate (IM) driver. The NDIS_MINIPORT_DRIVER flag enables the NDIS driver to make use of NdisMXxx() functions (the 'M' after "Ndis" stands for "Miniport").
Note: You should pay attention to the fact that NDIS_MINIPORT_DRIVER implicitly sets "BINARY_COMPATIBLE=1" unless you explicitly define a different value to BINARY_COMPATIBLE before your source code includes "ndis.h". |
NDIS miniport (and intermediate) drivers need to tell "ndis.h" the NDIS version to which they apply. The actual outline of the NDIS_MINIPORT_CHARACTERISTICS structure will depend on the NDIS version that you define.
Specify one of the following NDIS versions for your miniport:
Compile Flag | Maximum NDIS Version of Your Miniport |
---|---|
NDIS51_MINIPORT | NDIS 5.1 |
NDIS50_MINIPORT | NDIS 5.0 |
NDIS40_MINIPORT | NDIS 4.x |
(none) | NDIS 3.x |
Note: NDIS_MINIPORT_CHARACTERISTICS defaults to NDIS version 3.0 if you do not specify the NDIS version used by your miniport.
Each version of the Windows operating system supports a specific NDIS version. Each Windows variant supports NDIS drivers written to this and earlier NDIS versions. Windows will not accept your NDIS driver if either of the following is true:
If you have have specified an NDIS version of 5.0 or higher for your miniport, "ndis.h" makes the following definitions for you, which you can use to set the 'MajorNdisVersion' and 'MinorNdisVersion' fields in the NDIS_MINIPORT_CHARACTERISTICS structure when you call NdisMRegisterMiniport():
NDIS_MINIPORT_MAJOR_VERSION
NDIS_MINIPORT_MINOR_VERSION
Analogous to the NDIS version of miniports, NDIS protocol (and intermediate) drivers need to tell "ndis.h" the NDIS version to which they apply. The actual outline of the NDIS_PROTOCOL_CHARACTERISTICS structure will depend on the NDIS version that you define. Specify one of the following NDIS versions for your protocol:
Compile Flag | Maximum NDIS Version of Your Protocol |
---|---|
NDIS51 | NDIS 5.1 |
NDIS50 | NDIS 5.0 |
NDIS40 | NDIS 4.x |
(none) | NDIS 3.x |
Note: NDIS_PROTOCOL_CHARACTERISTICS defaults to NDIS version 3.0 if you do not specify the NDIS version used by your protocol.
If you have have specified an NDIS version of 5.0 or higher for your protocol, "ndis.h" makes the following definitions for you, which you can use to set the 'MajorNdisVersion' and 'MinorNdisVersion' fields in the NDIS_PROTOCOL_CHARACTERISTICS structure when you call NdisRegisterProtocol():
NDIS_PROTOCOL_MAJOR_VERSION
NDIS_PROTOCOL_MINOR_VERSION
One important feature that the developers of NDIS had in mind was platform independency. The same NDIS driver source code shall compile for any hardware and software platform that supports NDIS. NDIS achieves this compatibility by providing a complete set of interface functions, which NDIS drivers use instead of directly calling system functions provided by the operating system. In other words, NDIS drivers should only call NdisXxx() functions.
A special case is the "Windows on x86" platform. You can define the BINARY_COMPATIBLE compile flag in order to generate an NDIS driver that will run on both the Windows 9x/Me and the Windows NT/2000/XP/2003 platforms.
Note: You do not need to explicitly define BINARY_COMPATIBLE if your driver is an NDIS miniport, because NDIS_MINIPORT_DRIVER already implies BINARY_COMPATIBLE. |
However, for an NDIS protocol driver, you can explicitly set BINARY_COMPATIBLE=1 so the driver can run on all Windows platforms.
What BINARY_COMPATIBLE does is that it tells "ndis.h" to declare prototypes for some particular NdisXxx() functions so your NDIS driver will actually call NDIS library functions. Otherwise, if BINARY_COMPATIBLE is not defined or is defined a zero value, "ndis.h" will define macros for these NdisXxx() function names. The macros heavily depend on the NT-based platforms and thus make your driver incompatible with Windows 9x/Me.
Unfortunately, there are some pitfalls, which can prevent binary-compatibility of your NDIS driver. If your driver is a "serialized" NDIS miniport and you have defined the NDIS_MINIPORT_DRIVER compile flag, your driver will run fine on all Windows platforms.
However, your driver is likely to fail (crash) on the Windows 9x/Me platform, if the following conditions are met:
and
The reason is that NDIS spin lock functions are implemented as no-ops in Windows 9x/Me and NDIS interlocked functions are not synchronized. Thus, follow these guidelines in order to make your NDIS driver ready for the Windows 9x/Me platform:
This compile flag can be used as a workaround for the fact that NDIS spin locks are implemented as dummies (i.e. no-ops) in Windows 9x/Me. So it is a good idea to define this compile flag in order to get spin locks to work as expected on Windows 9x/Me.
See BINARY_COMPATIBLE for more details on NDIS quirks related to the Windows 9x/Me platform.
NDIS miniports were originally "serialized", which means that the NDIS Wrapper library takes care of not calling more than one function concurrently in a serialized miniport at any time. Thus, there is absolutely no need for serialized miniports to use spin locks. As a result, all NDIS spin lock functions are implemented as empty no-op functions in Windows 9x/Me.
When the "full-duplex" attribute NDIS_MAC_OPTION_FULL_DUPLEX was introduced in NDIS 4.0, the miniport's send and receive paths could be called concurrently by the NDIS Wrapper.
Note: You should not use the NDIS_MAC_OPTION_FULL_DUPLEX attribute but use the NDIS_ATTRIBUTE_DESERIALIZE attribute instead.
The "deserialized" attribute NDIS_ATTRIBUTE_DESERIALIZE introduced in NDIS 4.1 as well as the deprecated NDIS_MAC_OPTION_FULL_DUPLEX attribute both require a deserialized miniport to protect its shared resources because the NDIS Wrapper does not serialize calls to deserialized miniports (some exceptions apply, e.g. the Wrapper serializes calls to MiniportQueryInformation()).
The miniport thus now needs to be able to protect any resources that it shares between its MiniportXxx() functions. As a workaround for the no-op spin locks in Windows 9x/Me, you can define the USE_KLOCKS compile flag. The flag tells "ndis.h" to directly map NDIS spin lock functions to kernel spin lock functions, which work as expected on all Windows platforms including Windows 9x/Me.
Define this compile flag if your NDIS miniport talks to a WDM (Windows Driver Model) device driver at its lower edge. The NDIS_WDM flag tells "ndis.h" to include the appropriate WDM header file for you. See section "Miniport Driver with a WDM Lower Interface" in the DDK docs for details on NDIS-WDM miniport drivers.
The BINARY_COMPATIBLE compile flag also affects the NDIS_WDM compile flag. The header file that "ndis.h" will include for your miniport to be able to access a WDM lower-edge driver actually depends on the combination of the two flags as follows:
BINARY_COMPATIBLE Defined As |
NDIS_WDM Defined As |
Header File Included By "ndis.h" |
---|---|---|
1 | 1 | "wdm.h" |
1 | 0 | (none) |
0 | 1 | "ntddk.h" |
0 | 0 | "ntddk.h" |
Note the leading underscore in the compile flag's name.
The XP and later DDKs (i.e. DDK build number 2600+) use an optimized implementation of functions related to the singly linked list (SLIST) data structure. These optimizations are incompatible with Windows 2000 and earlier operating system versions.
Thus, if you use SLISTs in your driver and the same driver file must be used on all Windows platforms, you need to define the _WIN2K_COMPAT_SLIST_USAGE compile flag. The flag tells the XP and later DDKs to use an implementation of SLIST related functions that is compatible with Windows 2000 and earlier versions.
As an alternative to _WIN2K_COMPAT_SLIST_USAGE, you can build your driver with the Windows 2000 DDK. For details, see section "New Slist Implementation" in the DDK release notes (i.e. file "relnote.htm" in the DDK root directory).
You can define this compile flag in order to disable another "optimization", which was first introduced in the Windows 2003 DDK (i.e. DDK build number 3790+).
If you define the WIN9X_COMPAT_SPINLOCK compile flag, which I recommend, the function KeInitializeSpinLock() is an actual library function. Otherwise, i.e. if you do not define WIN9X_COMPAT_SPINLOCK, KeInitializeSpinLock() is implemented as an inline function that is incompatible with Windows 9x/Me.
Thus, if your driver binary file must be used on all Windows platforms including Windows 9x/Me, you should define the WIN9X_COMPAT_SPINLOCK compile flag.
Depending on whether your driver is an NDIS miniport, a protocol, or an intermediate driver, you need to add the following directives to your SOURCES file:
Note: NDIS version 5.1 is assumed here. Make sure to replace the "51" with the NDIS version number that your driver supports. See section "NDIS Versions" in the DDK docs for details on the NDIS versions supported by the various Windows variants.
C_DEFINES=$(C_DEFINES) -DNDIS_MINIPORT_DRIVER=1 -DNDIS51_MINIPORT=1
C_DEFINES=$(C_DEFINES) -DNDIS51=1
C_DEFINES=$(C_DEFINES) -DNDIS_MINIPORT_DRIVER=1 -DNDIS51_MINIPORT=1 -DNDIS51=1
You might ask yourself "Which compile flags do I need for my NDIS driver to run correctly on Windows XYZ?". First of all, you should specify the type of your NDIS driver and the NDIS version that your driver supports.
Then also add one or more of the following directives to your SOURCES file, if your driver must run on one or more of the respective Windows variants:
C_DEFINES=$(C_DEFINES) -DBINARY_COMPATIBLE=1 -DUSE_KLOCKS=1 -DWIN9X_COMPAT_SPINLOCK=1
C_DEFINES=$(C_DEFINES) -D_WIN2K_COMPAT_SLIST_USAGE=1
(nothing to add for XP at the time of this writing)
Stephan Wolf has worked as an independent system software developer since 1988. He lives and works mainly in Germany, but sometimes abroad. His focus is on network driver development (not only) for all Windows variants (including CE). You can contact him at stewo68@hotmail.com (hotmail sometimes "recognizes" serious email as spam, so try again if you get no answer).