Windows 98/Me Compatibility Notes
Windows 98/Me incompletely implements many power-management features. Consequently, the Windows 98/Me environment will forgive your mistakes more readily than Windows XP will, facilitating the initial development of a driver. But since Windows 98/Me tolerates mistakes that Windows XP won t tolerate, you must be sure to test all of your driver s power functionality under Windows XP.
The Importance of DO_POWER_PAGABLE
The DO_POWER_PAGABLE flag has additional and unexpected significance in Windows 98/Me. Unless every device object, including the PDO and all filter devices, in your particular stack has this flag set, the I/O Manager tells the Windows 98/Me Configuration Manager that the device supports only the D0 power state and is incapable of waking the system. Thus, an additional consequence of not setting the DO_POWER_PAGABLE flag is that any idle notification request you make by calling PoRegisterDeviceForIdleDetection is effectively ignored that is, you ll never receive a power IRP as a result of being idle too long. Another consequence is that your device s wake-up feature, if any, won t be used.
Completing Power IRPs
You must complete power set and query requests in Windows 98/Me at PASSIVE_LEVEL only. If you look carefully at the power-management code for GENERIC in the companion content, you ll find it scheduling a work item instead of completing the IRP at DISPATCH_LEVEL.
If you copy my code, or if you use GENERIC.SYS, you may have a bit of explaining to do when you submit your driver to Windows Hardware Quality Lab (WHQL). Windows 98/Me does not support the IoXxxWorkItem functions that Windows 2000 and later systems provide, so it s necessary to use the older ExXxxWorkItem functions. Unfortunately, the WHQL tests spot the symbol import from your driver instead of performing a run-time test to see which function you actually call. My sample code has a run-time test and therefore completely meets the spirit of the test, just not the letter of the law. If enough of my readers ask for exceptions, maybe WHQL will change the tests. See Chapter 14 for information about work items and Chapter 15 for information about WHQL.
Requesting Device Power IRPs
As previously discussed, Windows 98/Me has a bug whereby PoRequestPowerIrp can appear to succeed that is, it returns STATUS_PENDING without actually causing you to receive a device set-power IRP. The problem arises when you ask for a set-power IRP that specifies the same device state that your device is already in the Windows 98/Me Configuration Manager knows that there s no news to report by sending a configuration event to the configuration function that NTKERN operates on your behalf. Mind you, if you re waiting for a device IRP to complete, your device will simply stop responding at this point.
I used an obvious workaround to overcome this problem: if we detect that we re about to request a device power IRP for the same power state that the device already occupies, I simply pretend that the device IRP succeeded. In terms of the state transitions that HandlePowerEvent goes through, I jump from SendDeviceIrp directly to whichever action (SubPowerUpComplete or Sub Power DownComplete) is appropriate.
PoCallDriver
PoCallDriver just calls IoCallDriver in Windows 98/Me. Consequently, it would be easy for you to make the mistake of using IoCallDriver to forward power IRPs. There is, however, an even worse problem in Windows 98/Me.
The Windows XP version of PoCallDriver makes sure that it sends power IRPs to DO_POWER_PAGABLE drivers at PASSIVE_LEVEL and to INRUSH or nonpaged drivers at DISPATCH_LEVEL. I took advantage of that fact in GENERIC to forward power IRPs in situations in which HandlePowerEvent is called at DISPATCH_LEVEL from an I/O completion routine. The Windows 98/Me version, since it s just IoCallDriver under a different name, doesn t switch IRQL. As it happens, all power IRPs in Windows 98/Me should be sent at PASSIVE_LEVEL. So I wrote a helper routine named SafePoCallDriver for use in GENERIC that queues an executive work item to send the IRP at PASSIVE_LEVEL. The implications of using a work item in this situation are the same as discussed just above in connection with completing Power IRPs.
Other Differences
You should know about a few other differences between the way Windows 98/Me and Windows XP handle power-management features. I ll describe them briefly and indicate how they might affect the development of your drivers.
When you call PoRegisterDeviceForIdleDetection, you must supply the address of the PDO rather than your own device object. That s because, internally, the system needs to find the address of the DEVNODE that the Windows 98/Me Configuration Manager works with, and that s accessible only from the PDO. You can also use the PDO as the argument in Windows XP, so you might as well write your code that way in the first place.
The PoSetPowerState support routine is a no-operation in Windows 98/Me. Furthermore, although it s documented as returning the previous device or system power state, the Windows 98/Me version returns whatever state argument you happen to supply. This is the new state rather than the old state or maybe just a random number that occupies an uninitialized variable that you happened to use as an argument to the function: no one checks.
PoStartNextPowerIrp is a no-operation in Windows 98/Me, so it s easy for you to forget to call it if you do your development in Windows 98/Me.
The service routines having to do with device power relations (PoRegisterDeviceNotify and PoCancelDeviceNotify) aren t defined in Windows 98/Me. As far as I can tell, Windows 98/Me also doesn t issue a PowerRelations query to gather the information needed to support the callbacks in the first place. The service routines PoRegisterSystemState, PoSetSystemState, and PoUnregisterSystemState are also not implemented in Windows 98/Me. To load a driver in Windows 98/Me that calls these or other undefined service functions, you ll need to employ a technique, like the WDMSTUB.SYS filter driver described in Appendix A, for defining the missing functions.