The following suggests a general set of design criteria that any NT driver writer can use to start designing a driver.
Before writing any code, see the Kernel-mode Driver Reference to determine which IRP major function codes your driver must handle:
When you have identified the IRP_MJ_XXX your driver must handle, you can determine the upper limit on the number of Dispatch routines your driver might have. You can also start considering whether to combine particular IRP_MJ_XXX into convenient subsets to be handled by particular Dispatch routines.
For example, most lowest-level and intermediate NT drivers need do little or no device or IoCompletion processing for an IRP_MJ_CREATE (equivalent to an “open target device” request for these types of drivers) or IRP_MJ_CLOSE request. Except for underlying disk devices and NT drivers with pageable-image sections, IRPs containing IRP_MJ_CREATE usually just establish the existence of the target device object for higher-level drivers and for user-mode protected subsystems via the I/O Manager’s system services. For lower-level drivers, IRPs containing IRP_MJ_CLOSE usually indicate that a user-mode subsystem on behalf of an application has closed the file object handle for the device to which it was sending I/O requests.
For create/close requests, many NT device and intermediate drivers simply set the IRP’s I/O status block with STATUS_SUCCESS in the Status member and zero in the Information member and, then, call IoCompleteRequest with the IRP from their Dispatch routines. Consequently, many lowest-level and intermediate NT drivers have a combined Dispatch routine for create/close requests.
However, a serial device driver usually resets its device for a create request; it can lock down a pageable-image section when it processes create requests and unlock its pageable-image section when it processes close requests. Disk device drivers are called with create requests only when a higher-level driver calls IoGetDeviceObjectPointer or IoAttachDevice; because any disk driver might control the device that holds the system page file, they are not called with close requests. On the other hand, NT file system drivers do considerably more processing for create/close requests.
If you are designing a higher-level NT driver, consider the set of IRP major function codes your driver must handle and determine the set of requests for which you might need to implement an IoCompletion routine.
As a general rule, a higher-level NT driver need not have an IoCompletion routine for each kind of request. It must have an IoCompletion routine to dispose of the IRPs the driver allocates, if any. Otherwise, it needs IoCompletion routines only for those IRP_MJ_XXX that could require further processing in the higher-level driver, depending on how lower drivers handle a given IRP_MJ_XXX request.
Next, consider how many named device objects your driver must create:
You can develop your driver in stages, as outlined in Section 4.4.4, so an under-development driver need not create every device object that the fully developed driver will have. Nevertheless, determining how many device objects the driver must eventually create helps to identify any synchronization problems the driver writer must solve.
Determining the number of necessary or possible device objects also can help a driver writer in defining the driver-determined contents and structure of the device extension(s) for the driver’s device object(s). For example, if the device extension will contain context shared between a device driver’s ISR and its other routines, consider setting up the device extension to isolate all the shared context in a part of the device extension. The driver’s nonISR routines can then access other areas in the device extension without having to call KeSynchronizeExecution with the SynchCritSection routine, discussed in Chapter 10.
As another example, if one nonISR routine shares an area with another, the driver’s device extension would likely contain an executive spin lock, used to protect that area from simultaneous access by both routines. For more information about excecutive spin locks, see Chapter 16.
If you are writing a driver for a physical device, determine whether setting up the device for I/O requires the driver to wait for longer than around 50 microseconds for device state changes: