15.2 Unload Routine Functionality
The input parameter to an Unload routine is a DriverObject pointer, as shown by the declaration, to the driver object that the NT I/O Manager uses to represent that driver's load image.
Every NT driver's calls to IoCreateDevice require the DriverObject pointer that was input to its DriverEntry routine. Each successful call to IoCreateDevice associates the newly created device object with the given driver object.
Consequently, an NT driver's Unload routine can walk the chain of device objects associated with the input DriverObject to release the resources and objects that driver was using.
Consider the following implementation guidelines for the Unload routines of NT drivers
·An NT device driver must disable interrupts from its device if it is possible to do so before its Unload routine releases the system objects and resources the DriverEntry routine set up for processing I/O requests and before its Unload routine frees any hardware resources that the DriverEntry routine claimed for its physical device in the registry.
·Any NT driver's Unload routine must ensure that no other driver routine is currently using or might shortly be using a particular resource before it releases that resource.
While the NT I/O Manager does not continue to send IRPs to any driver after its Unload routine has been called, disabling interrupts from the device should be the first thing done by any device driver's Unload routine if possible. Otherwise, its ISR might be called to handle a device interrupt while the Unload routine is executing and releasing resources in the device extension that the ISR needs to handle the interrupt. Note that even if its ISR runs successfully in these circumstances, the DpcForIsr or CustomDpc routine that the ISR queues, and possibly other driver routines that run at IRQL >= DISPATCH_LEVEL, will execute before the Unload routine regains control, thereby increasing the likelihood that the Unload routine has deleted a resource that another driver routine references. For more information about managing IRQLs in NT drivers, see Chapter 16.
Any Unload routine must release the resources an NT driver is using in stages. However, the specifics of how any particular NT driver uses the registry, sets up system objects and resources in its device extension(s), in any controller extension, or in driver-allocated nonpaged pool varies from driver to driver.
A DriverEntry and Unload routine can sometimes share internal driver routines that set up necessary parameters to allocate and release resources. For example, a driver that sets up symbolic links between Win32®-visible names and the corresponding NT device object names for its devices might have a common routine to construct Unicode string paths to the registry keys in the \Registry..\DeviceMap tree where it stores configuration information about these aliases.
In general, an NT driver's Unload routine releases all driver-allocated resources in the following stages:
1.First, disable interrupts on any physical device(s), if possible, and then call IoDisconnectInterrupt as soon as interrupts are disabled.
2.Ensure that no other driver routine can reference the resources that the Unload routine intends to release, such as calling IoStopTimer one or more times to disable the driver's IoTimer routine(s) for all associated device objects, and so forth.
3.Release certain system objects and resources that the DriverEntry routine set up in the device extension of the device object(s) or in the controller extension of the controller object, if any.
·In particular, a device driver's Unload routine must call IoDisconnectInterrupt before it calls IoDeleteDevice or IoDeleteController to free the interrupt object(s) pointer stored in the corresponding device/controller extension by its DriverEntry routine.
·In particular, a higher-level driver's Unload routine must call ObDereferenceObject with the pointer to the next-lower driver's file object if it called IoGetDeviceObjectPointer and stored this pointer in a device or controller extension before it calls IoDeleteDevice or IoDeleteController with its own device or controller object.
·In particular, a higher-level driver's Unload routine must call IoDetachDevice with the pointer to the lower driver's device object if it called IoAttachDevice and stored this pointer in a device or controller extension before it calls IoDeleteDevice or IoDeleteController with its own device or controller object.
4.Free any hardware resources that the DriverEntry routine claimed for the driver's physical device(s), if any, in the registry under the \Registry\Machine\Hardware\ResourceMap tree.
For more information about releasing hardware resource claims in the registry, see Chapter 16. For specific information about IoAssignResources and IoReportResourceUsage, see the Kernel-Mode Driver Reference.
5.Deassign any symbolic links that the driver created, such as links between the NT name for its device objects and the Win32-visible names for the same devices and any other symbolic links set up with IoCreateSymbolicLink, IoCreateUnprotectedSymbolicLink and/or IoAssignArcName in the DriverEntry routine.
The Unload routine should remove any names for its devices that the DriverEntry routine stored in the registry under the \Registry..\DeviceMap tree, as well.
6.Delete each device object with IoDeleteDevice and any controller object with IoDeleteController after freeing, as necessary, any pointers to external objects or other resources that were stored in the device and controller extensions.
If the DriverEntry routine called IoGetConfigurationInformation to increment the count for a particular type of device, the Unload routine also must call IoGetConfigurationInformation and decrements the count for its devices in the I/O Manager's global configuration information structure as it deletes the corresponding device objects.
In effect, an Unload routine must undo whatever the corresponding DriverEntry and Reinitialize, if any, routine(s) did to initialize its physical device(s), if any. An Unload routine must undo whatever the corresponding DriverEntry and Reinitialize, if any, routine(s) did to allocate the resources the driver needed to remain loaded and process IRPs.
Before it returns control, an Unload routine also is responsible for freeing any other driver-allocated resources that have not yet been freed by other driver routines.