Plug and Play and power management handle the activities that are required to bring a newly inserted device to full operation and to remove an operational device from the system. When the user plugs in a new device, the system must determine the type and capabilities of the device, assign resources to the device, work with the device's drivers to power up and initialize the device, and do whatever else is required to ready the device for operation. When the user unplugs the device, the system reverses this process. The core activities related to device arrival and startup follow a fixed sequence, as do the core activities related to shutdown and removal.
At each point in the sequence, the device stack is in a well-defined state. The WDF state machines track the device stack through the transitions from one state to another, and WDF defines callbacks that correspond to many of the state transitions. When such a state change occurs, the framework invokes the callback, if any, that applies to the new state.
For example, a key activity in the startup sequence is to initialize the device and driver when the device powers up. In most device stacks, the bus driver is responsible for ensuring that the device has power, but the function driver handles initialization. Depending on the type of device, the driver might perform initialization before power is applied, after power is applied, or both. WDF defines callbacks for each of these states. If the device generates interrupts, the driver can further request a callback after power is applied but before the interrupt is connected or immediately after the interrupt is connected.
A device stack can change state for numerous reasons. The following are among the most common:
Common state changes that can trigger callbacks The device is added to the system.
The device is removed from the system, either in an orderly way or by surprise.
The system is powering up.
The system is either hibernating or standing by.
The system is shutting down.
An idle device is being powered down to conserve energy.
An idle device is being re-powered because I/O has arrived for it or because an external wake signal was triggered.
Another device has been added to the system, requiring Windows to rebalance resources.
A driver is being upgraded or reinstalled.
The reason for the change determines exactly which callbacks the framework calls. For example, if the system is shutting down, the framework calls all of the driver's callbacks involved in stopping all device I/O and powering down the device. However, if an idle device is powering down, only the callbacks to stop some device I/O and power down the device are required. The framework retains the device object and the device's resources for use when the device resumes operation.
From the perspective of the driver, however, the reason for the state change is not important. The driver's callbacks perform discrete tasks at clearly defined times-such as initializing the device after the hardware is powered up. Whether the device is powering up as part of initial installation or because it triggered a wake signal is irrelevant to the driver. For the driver, the important point is that the framework always invokes the callbacks in the same sequence and according to the contract for that state.
The framework invokes the relevant callbacks in a fixed sequence as the device is inserted and made fully operational and in the reverse order as the device is powered down and removed. However, devices are not often installed and removed. More typically, the system transitions to an Sx state when the user closes the lid of a laptop or the driver for a network card powers down its device when the cable is unplugged. In these situations, the entire sequence of callbacks is unnecessary. Instead, the framework calls only the part of the sequence that is required to put the device in the desired state.
During power-down, the framework stops the sequence at one of four points, depending on whether the device is:
Transitioning to a lower-power state.
Stopping to rebalance resources.
Disabled or removed but still physically present-for example, if the user disabled the device in Device Manager.
With each subsequent callback, and at each subsequent stopping point, less device and driver functionality is available. When the framework returns the device to the working state, it starts the sequence of callbacks at the same point at which it stopped, but in the reverse order. If the device is removed unexpectedly (that is, "surprise removed"), the sequence differs somewhat, as described in "Surprise Removal" later in this chapter.
After the device has left the working state and transitioned to a lower-powered state, the framework always returns the device to the working state before it changes the device power state again. For example, assume that a device is idling in state D2 when the user shuts down the system. The framework resumes the shutdown callback sequence at the stopping point and calls the callbacks in the reverse order to return the device to D0. Then it starts the sequence again, starting with the device in the operational state, to perform the activities that are required for shutdown. Although it might seem counterintuitive, this approach ensures that the drivers for the device can perform any additional tasks that are required before the system shuts down. A driver might save less state information when it transitions to D2 than it requires for D3, so the interim transition to D0 lets the driver recover whatever additional information it requires before the device enters D3.
To ensure that your driver implements the appropriate callbacks and that each one performs the right tasks, you must understand the order in which the framework invokes the callbacks.
Figure 7-1 lists the steps in the framework's startup and shutdown sequences. The startup actions in the left column correspond to the shutdown actions in the right column. Not all steps apply to every type of driver or device object, and whether some steps apply depends on the reason for the startup or shutdown. For example, the framework does not call the driver to enable the device wake signal when the device is stopping so that the PnP manager can rebalance system resources. A surprise removal triggers the shutdown and removal sequence even though the hardware has already been physically removed.
Figure 7-1: Steps in framework startup and shutdown sequences
The specific callbacks that the framework invokes to perform each of these actions are listed later in this chapter.
If the callback at any of the steps returns a failure status, the framework tears down the device stack.
If the failure occurs during power-up, the framework calls the driver's release-hardware callback-if the driver implements one-but does not call any other callbacks.
If the failure occurs during power-down, the framework continues to call the driver's callbacks. Therefore, the callback methods in the power-down sequence must be able to tolerate failures that are caused by unresponsive hardware.
Whenever Windows boots, the loader, the PnP manager, and the drivers cooperatively enumerate all the devices on the system and build devnodes to represent them. The same procedure also occurs when a device is added to a running system, although on a smaller scale.
During device enumeration, the PnP manager loads the drivers and builds the device stack for each device that is attached to the system. To prepare a device for operation, the PnP manager sends a sequence of requests to each device stack to get the information about the resources that the device requires, the capabilities of the device, and so on. The drivers in the device stack respond to these requests one at a time from the bottom up, starting with the driver that is closest to the hardware. Thus, the PDO powers on the device before the FDO receives a request to perform its power-on tasks. Power-down occurs in the opposite order. In short, the higher level drivers depend on the functionality of the drivers below them, so they start later and stop earlier.
The frameworks participate in this sequence on behalf of their drivers. The frameworks respond immediately to the PnP manager's requests when they can and invoke driver callback functions to supply information and perform tasks for which driver input or actions are required. For framework drivers:
All UMDF drivers can implement the same set of callback interfaces.
The IDriverEntry interface is implemented on the driver callback object. The IPnpCallback, IPnpCallbackHardware, and IPnpSelfManagedIo interfaces are implemented on the device callback object. The IQueueCallbackXxx interfaces are implemented on the queue callback objects.
For KMDF drivers, most of the event callbacks apply to any type of device object.
However, the framework invokes some callbacks only for PDOs and other callbacks only for FDOs and filter DOs.
Figures 7-2 and 7-3 show the sequence of callbacks for UMDF drivers and for KMDF FDO and filter DOs that are involved in bringing a device to the fully operational state, starting from the Device Arrived state at the bottom of the figure.
Figure 7-2: Device enumeration and startup sequence for a UMDF driver
Figure 7-3: Device enumeration and startup sequence for KMDF FDO or filter DO
In these figures, the broad horizontal lines mark the steps for starting a device. The column on the left side of the figure describes the step, and the column on the right lists the event callbacks that accomplish the step. If the device is stopped because the PnP manager is rebalancing system resources or if the device is physically present but not in the working state, not all of the steps are required, as the figures show.
UMDF At the bottom of Figure 7-2, the device is not present on the system. When the user plugs in the device, the framework begins by calling the driver's IDriverEntry::OnDeviceAdd callback so that the driver can create a device callback object and a framework device object to represent the device. The framework continues calling the driver's callback routines by progressing up through the sequence until the device is operational.
Figure 7-3 shows the callbacks for a KMDF FDO or filter DO that is involved in bringing a device to the fully operational state.
KMDF At the bottom of Figure 7-3, the device is not present on the system. When the user inserts the device, KMDF begins by calling the driver's EvtDriverDeviceAdd callback so that the driver can create a device object to represent the device. KMDF continues calling the driver's callback routines by progressing up through the sequence until the device is operational. Remember that the event callbacks are listed in bottom-up order as shown in Figure 7-3, so EvtDeviceFilterRemoveResourceRequirements is called before EvtDeviceFilterAddResourceRequirements and so forth.
Figure 7-4 shows the callbacks for a bus driver (PDO) that is involved in bringing a device to the fully operational state.
Figure 7-4: Device addition/startup sequence for PDO
The framework retains the PDO until the corresponding device is physically removed from the system or the bus that enumerated the device is disabled. For example, if a user disables the device in Device Manager but does not physically remove it, KMDF retains the PDO. This requirement is imposed by the underlying WDM model. Thus, the three steps at the bottom of Figure 7-4 occur only during Plug and Play enumeration-that is, during initial boot, when the user plugs in a new device, and when the bus to which the device is attached enumerates its child devices.
During device power-down, the sequence of callbacks depends on the role of the device object just as it does during device startup. In general, the power-down and removal sequence involves calling the corresponding "undo" callbacks in the reverse order from which the framework called the methods that it invoked to make the device operational. Drivers perform power-down operations from the top down, so the driver at the top of the device stack performs its power-down tasks first, and the PDO performs its power-down tasks last.
UMDF Figure 7-5 shows the sequence of UMDF callbacks in power-down and removal. The sequence starts at the top of the figure with a device that is in the working power state (D0).
Figure 7-5: Device power-down and orderly removal sequence for a UMDF driver
KMDF Figure 7-6 shows the sequence of callbacks in power-down and removal for a KMDF FDO or filter DO.
Figure 7-6: Device power-down and orderly removal sequence for KMDF FDO and filter DO
Figure 7-7 shows the callbacks in the power-down and removal sequence for a PDO.
Figure 7-7: Device power-down and orderly removal sequence for PDO
As previously mentioned in "Device Enumeration and Startup" earlier in this chapter, the framework does not delete the PDO until the device is physically removed from the system or the bus that enumerated the device is disabled. For example, if a user disables the device in Device Manager or uses the Safely Remove Hardware utility to stop the device but does not physically remove it, KMDF retains the PDO. If the device is later reenabled, KMDF uses the same PDO and begins the startup sequence by calling the EvtDevicePrepareHardware callback, as previously shown in Figure 7-4.
If the user removes a device without warning, by simply unplugging it without using Device Manager or the Safely Remove Hardware utility, the device is considered "surprise removed." When surprise removal occurs, WDF follows a slightly different removal sequence from that used with orderly removal and shutdown. WDF also follows the surprise-removal sequence if any driver in the kernel-mode stack invalidates the device state even if the device is still physically present. A KMDF driver can invalidate the device state by calling WdfDeviceSetDeviceState.
Drivers for all removable devices must ensure that the callbacks in both the shutdown and startup paths can handle failure, particularly failures that are caused by hardware removal.
In the surprise-removal sequence, UMDF calls the IPnpCallback::OnSurpriseRemoval callback to notify the driver that the device has been unexpectedly removed. This callback is not guaranteed to occur in any particular order with the other callbacks in the removal sequence.
Generally, the driver should avoid accessing the hardware in the remove path. The reflector times out the driver if an attempt to access the hardware waits indefinitely. Figure 7-8 shows the surprise-removal sequence for a UMDF driver.
Figure 7-8: Surprise-removal sequence for a UMDF driver
The framework can call the EvtDeviceSurpriseRemoval callback at any time before, during, or even after the power-down sequence. For example, if the user unplugs the device during an idle power-down, the framework can call the EvtDeviceSurpriseRemoval callback in the middle of the sequence. There is no guarantee on the order in which EvtDeviceSurpriseRemoval is called in relation to the other power-down callbacks.
KMDF destroys the device object after the EvtDeviceSurpriseRemoval callback has returned and the last handle to the WDF device object has been closed.
Any attempts to access the hardware should not block indefinitely, but should be subject to time-outs or a watchdog timer.
Figure 7-9 shows the surprise-removal sequence for a KMDF driver.
Figure 7-9: Surprise-removal sequence for a KMDF driver