…
0004:00000492 ?PnpDefaultHandler@@YGJPAU_DEVICE_OBJECT@@PAU_IRP@@@Z 00012512 f pnp.obj
0004:00000591 ?ForwardAndWait@@YGJPAU_DEVICE_OBJECT@@PAU_IRP@@@Z 00012611 f pnp.obj
Conclusion
Please test your driver well before releasing it. You must check that your driver works on a variety of computers. It must cope with user mode program aborts.
Debugging drivers is harder than user mode applications. In the worst case, a bugcheck occurs that tells you roughly what went wrong. Resource leaks and timing problems are more difficult to sort out.
The
The next chapter looks at the dispatch routines in the Wdm1 device driver.
Chapter 7
Dispatch Routines
This chapter looks at how to write driver dispatch routines that process I/O Request Packets (IRPs). Dispatch routines are used to handle requests from Win32 applications. It is crucial that you understand everything in this chapter clearly. If necessary, refer to Chapter 3 where IRPs are first introduced.
The dispatch routines for the Wdm1 driver are explained in full. These handle open, close, read, write, and IOCTL requests. The Wdm1 driver implements a global memory buffer that is shared by all Wdm1 devices.
This chapter takes a good hard look at I/O Request Packets (IRPs). It is worth reading this chapter carefully, as a good understanding of IRPs will ease your passage through the rest of the book.
Dispatch Routine IRPs
Table 7.1 lists the most common Win32 functions that are used to access devices. A CreateFile call to your device ends up as a Create IRP, an I/O Request Packet with a major function code of IRP_MJ_CREATE. The driver routine to handle this IRP can have any name. However, I use a generic name for the Create IRP handler of Create. In your driver, you would usually put a short name or acronym in front of this base name. The Wdm1 device driver's Create IRP handler is called
Table 7.1 is not an exhaustive list of Win32 functions and their matching IRPs. For example,
Table 7.1 Common dispatch routines
Win32 Function | IRP Major Code | Base Driver routine name |
---|---|---|
CreateFile | IRP_MJ_CREATE | Create |
CloseHandle | IRP_MJ_CLOSE | Close |
ReadFile | IRP_MJ_READ | Read |
WriteFile | IRP_MJ_WRITE | Write |
DeviceIoControl | IRP_MJ_DEVICE_CONTROL | DeviceControl |
A driver need not handle all these IRPs, though handling Create and Close IRPs is an obvious minimum. Its DriverEntry routine sets up the entry points that are valid. If an entry point is not set, then the I/O Manager fails the Win32 request and
The following line in DriverEntry sets the Wdm1Read routine as the handler for Read IRPs.
DriverObject->MajorFunction[IRP_MJ_READ] = Wdm1Read;
I/O Request Packets
All dispatch routines have the same function prototype. The function is passed a pointer to your device object and the IRP. The function must return a suitable NTSTATUS value (e.g., STATUS_SUCCESS).
NTSTATUS Wdm1Read( IN PDEVICE_OBJECT fdo. IN PIRP Irp)
A dispatch routine is usually called at PASSIVE_LEVEL IRQL. (This is a bit unexpected, as you might expect dispatch routines to run at DISPATCH_LEVEL IRQL.) Running at PASSIVE_ LEVEL means that a dispatch routine can very easily be interrupted by other parts of the kernel or even your own driver. However, a routine that runs at PASSIVE_LEVEL can issue most kernel calls, including writing to other files.
A dispatch routine can be called at DISPATCH_LEVEL if a higher level driver calls you at this IRQL level. This might happen if it calls you from a completion routine, as described in Chapter 9. If you think this might be happening, then assume that your driver dispatch routines are running at DISPATCH_LEVEL
Dispatch routines run in an arbitrary thread context. As well as standard user mode threads, the kernel has its own threads that run only in kernel mode. Although your IRP will have originated in one of these threads, you cannot guarantee that you arc running in its context. This means that a valid address in the originating thread may not be valid when your driver sees it. A later section in this chapter shows how to access user buffers correctly.
A dispatch routine must be reentrant. This means that it may be called 'simultaneously' to process two separate IRPs. In a multiprocessor Windows 2000 system, one processor could call Wdm1Read with one IRP, and a second process on another CPU could also call