There are a few different possible types of power related buttons on a PC. There is the button on the case itself. If you have a laptop, there is a lid switch to detect state change in the lid. There are also power keys on a keyboard. All of these buttons have different underylying physical connections to the PC, but each reports the button press in the same way.

The definitions for all of the constants mentioned in this post can be found in poclass.h in the DDK. There are 2 IOCTLs that the power manager uses for detecting power buttons and knowing when they are pressed.

IOCTL_GET_SYS_BUTTON_CAPS

This is used to find out what buttons are present. The buffer is a ULONG and is treated as a bitfield. You can report the following flags: SYS_BUTTON_POWER, SYS_BUTTON_SLEEP, SYS_BUTTON_WAKE (which is 0x0, so really it is a nop), and SYS_BUTTON_LID. If you are responding to this IOCTL, you should only set the flags for the buttons that are present, otherwise the UI will present the user with an option to map a button to a power action, but that button will not exist! This IOCTL is completed immediately. If completed with an error NTSTATUS, the power manager will close your device and not send the second IOCTL.

IOCTL_GET_SYS_BUTTON_EVENT

This is used to report the actual button press. It's buffer is also a ULONG and is also treated as a bit field. It uses the same flag values as the first IOCTL. This PIRP is pended until the actual power button has been pressed. When completing this PIRP, you set the appropriate flag in the buffer indicating which button was pressed. The power manager will then process the power button event and do the right thing according to the system and the user's policy.

If you report SYS_BUTTON_LID, then there are additional flags that you can specify in the ULONG: SYS_BUTTON_LID_OPEN, SYS_BUTTON_LID_CLOSED, SYS_BUTTON_LID_INITIAL, SYS_BUTTON_LID_CHANGED. The open and closed flags are obvious in how they are used. You set SYS_BUTTON_LID_INITIAL when first reporting the lid state or coming out of low power so that the OS can know what the initial lid state is. For instance, this allows you to report the lid as closed and still boot up without immediately shutting down again. SYS_BUTTON_LID_CHANGED is used to report a change in the previous lid state.

Opening a power button source

So now we know how the buttons are reported, but how does the power manager even know to ask your driver if it has power buttons? Well, it registers for notification arrival for two different device interface GUIDs and when either appears, it opens up the interface and sends the IOCTLs. The first device interface the power manager listens for, GUID_DEVICE_SYS_BUTTON, is dedicated to reporting power butttons. The second interface GUID is GUID_CLASS_INPUT, the HID interface. This means that every HID device, whether or not it has power buttons on it, will be opened and queried by the power manager! If you want to expose power buttons, I would suggest registering the GUID_DEVICE_SYS_BUTTON interface GUID and keep things simple.

My next entry will explain how the two different types of keyboards (PS2 and HID) detect and report power buttons.