Using WDF in an NDIS driver
WDF is a framework that makes it easier to write Windows drivers. NDIS is a framework for writing low-level Windows network drivers. The purposes of these frameworks overlap a bit, and some people (okay, probably many people) are confused about the relationship between NDIS and WDF. Today we’ll set down a few guidelines. But first – let’s dispel one tenacious myth.
Myth: Some people think that NDIS drivers cannot use WDF.
In reality, you can use WDF in your NDIS driver. I know this works rather well, because I have personally written several WDF-based NDIS drivers.
So where do people get the idea that WDF is incompatible with NDIS? There are a few sources of this idea:
- When writing an NDIS miniport driver, certain parts of WDF are not compatible with NDIS. You must put WDF into a mode sometimes referred to as “miniport mode”. Not all WDF APIs are available in miniport mode. See the step-by-step checklist here. Note that this restriction only applies to NDIS miniport (and IM) drivers; protocols and LWFs can use the full breadth of WDF functionality.
- Miniport drivers must also put NDIS into a special mode, called NDIS-WDM mode. This is a poor name, because it seems to indicate that you must use WDM. The reality is that NDIS-WDM mode just means your driver can use any non-NDIS framework. (At the time that NDIS-WDM mode was invented, there were no other frameworks besides WDM, so the name didn’t seem to be too constraining. If it helps, you can think of it as NDIS-WD* mode.)
- Most of the NDIS drivers that are included with Windows (like TCPIP) don’t use WDF. But this isn’t because Windows developers are avoiding WDF; it’s because most inbox drivers simply predate WDF. If we were writing the network stack from scratch, we’d use more WDF. New drivers like MSLLDP, an NDIS protocol driver included with Windows 8, are indeed based on WDF.
Now that we know you can combine WDF with NDIS, let’s talk about whether you should combine WDF with NDIS. In nearly all cases, an NDIS driver will work with or without WDF. So you rarely have the decision forced upon you by the technology. Ultimately, it will come down to what you decide, based (hopefully) on a good engineering judgment call. Let’s collect some evidence to help you make that decision.
- Your engineering team is already familiar with WDF.
- You will be developing several drivers, including non-networking drivers. (Might as well learn WDF now, and maybe you can share some library code between your drivers.)
- Your driver already uses WDF.
- You are writing an NDIS miniport that uses IRPs on its lower edge (USB, SDIO, etc.)
- You are writing a protocol or LWF that interacts with non-NDIS parts of the OS (usermode IOCTLs, WSK requests, etc.)
- Your code would benefit from WDF’s clever object management system to avoid memory leaks.
- You are new to Windows driver development, and have no idea where to start 😰
Generally speaking, it’s a good idea to consider WDF. But there are a few reasons why WDF might not be very useful to your NDIS driver:
- Your engineering team is already very familiar with NDIS, but has no experience with WDF.
- You are maintaining a mature driver that does not use WDF.
- You are writing a simple NDIS miniport on a directly-connected bus (like PCI).
- You are writing a protocol or LWF that has minimal interaction with the rest of the OS. This driver mostly only calls NDIS APIs.
- Your codebase must be compatible with platforms where WDF is not available (like Windows CE).
Mind you, it’s still quite possible to link against WDF in these situations. But you’ll probably find that there aren’t a lot of opportunities to actually use WDF APIs. Integrating with WDF doesn’t give a lot of value if you don’t call its APIs. In those cases, the pragmatic engineering decision may be to just not use WDF.
Okay, so let’s suppose you’ve decided to give WDF a spin. You’ll eventually notice that WDF overlaps somewhat with NDIS. For example, both frameworks have APIs for workitems (NdisQueueIoWorkItem versus WdfWorkItemEnqueue). Which API should you use? Again, in many cases, either framework’s APIs will work. Again, it’s an engineering decision that ought to consider several factors, including maintaining consistency with your other code, etc. But if you are new to NDIS and WDF, you can use this quick-reference table as a starting place for your decision-making process.
API family | Use NDIS APIs? | Use WDF APIs? | Use WDM APIs? |
---|---|---|---|
Work items | Avoid | Preferred | Do not use |
Timers | Avoid | Preferred | Do not use |
Memory allocation | Avoid | Preferred | Okay |
Locks & interlocks | Avoid (but RW locks are okay) | Preferred | Preferred |
Events | Avoid | Preferred | Preferred |
String handling | Avoid | Preferred | Preferred |
DMA | Preferred | Preferred | Avoid |
Interrupts | Preferred | Not permitted | Not permitted |
DPCs (for miniports) | Preferred for interrupts | Okay for non-interrupts | Avoid |
DPCs (for non-miniports) | Avoid | Preferred | Avoid |
Processor information | Avoid (except RSS APIs) | (no equivalent) | Preferred |
IRPs and IOCTLs (for miniports) | Required | Not permitted | Not permitted |
IRPs and IOCTLs (for non-miniports) | Avoid | Preferred | Avoid |
Direct bus/port access | Okay | Preferred | Preferred |
Reading configuration | Preferred for standard keywords | Preferred for other registry values | Okay for other registry values |
File I/O | Avoid | (no equivalent) | Preferred |
Remember, the above table only contains guidelines. It is still acceptable to ship a driver that uses an API marked "Avoid". You should use the table to help nudge your decision-making when you have no other compelling reasons to use a particular API family.