At first glance, one would think that the reporting of a keypress on a power related (sleep, off, wake) button on your keyboard would be simple. I would have guessed that it worked something like this
  • The key is pressed
  • The key press is reported to the raw input thread (RIT) as a scan code
  • The RIT then takes the appropriate power related action
Well, that guess would be totally wrong and far from what actually happens :). Intead, the keyboard stacks support the power manager interface for reporting power buttons. This KB article was written to show how to filter out power button reporting from a keyboard. To further complicate matters, USB keyboards and PS2 keyboards detect and expose the keys in two totally different ways.

USB Keyboards

Let's start with the simple implementation. HIDClass.sys has a HIDP_PREPARSED_DATA for each top level collection. When the preparsed data is created when the collection is parsed, the parser looks for 3 usages on the generic usage page (0x01). These 3 usages are (as defined in hidusage.h)
  • HID_USAGE_GENERIC_SYSCTL_POWER (0x81)
  • HID_USAGE_GENERIC_SYSCTL_SLEEP (0x82)
  • HID_USAGE_GENERIC_SYSCTL_WAKE (0x83)
The parser stores the presence of any of these usages in the preparsed data. HIDClass will then report the presence of any of these keys in response to IOCTL_GET_SYS_BUTTON_CAPS. If there are no caps to report, the power manager will not send a IOCTL_GET_SYS_BUTTON_EVENT and close the device. Otherise, a IOCTL_GET_SYS_BUTTON_EVENT is sent and pended and when HIDClass detects that the key has been pressed (it checks whenever an I/O from the device is sent), the pended IRP is completed.

PS2 Keyboards

PS2 (i8042prt.sys) is drastically different then HID (nearly all source for handling power buttons is in sysbtn.c). PS2 does not have a method for discovering the presence of different keys, so i8042prt must take a different approach. i8042prt must react to the keypress when it occurs. This means the first time a power button is pressed the following happens
  1. The ISR (I8042KeyboardInterruptService) is called and the scan code is read from the hardware. If the scancode is any of the 3 power keys (as defined in i8042prt.h and the industry spec) a DPC is queued after the keypress has been cached in the device extension
    • KEYBOARD_POWER_CODE (0x5E)
    • KEYBOARD_SLEEP_CODE (0x5F)
    • KEYBOARD_WAKE_CODE (0x63)
  2. In the DPC (I8xKeyboardSysButtonEventDpc), a work item is queued (registering a device interface requires PASSIVE_LEVEL)
  3. In the work item (I8xUpdateSysButtonCaps), register the GUID_DEVICE_SYS_BUTTON device interface
  4. Complete the IOCTL_GET_SYS_BUTTON_CAPS irp (I8xKeyboardGetSysButtonCaps) with the appropriate capability flags set. It is important to note that i8042prt only reports the specific capability which was discovered. If it reports extra button capabilities, the user may be presented with a choice of a power action that does not exist.
  5. Complete the IOCTL_GET_SYS_BUTTON_EVENT irp with the appropriate event.

OK, that was relatively simple :). The next time the same button is pressed, things are much simpler. A DPC is queued and the already pended IOCTL_GET_SYS_BUTTON_EVENT is completed. But happens if we know about the presence of one button and we detect a second button for the first time?

Again, a DPC is queued and a work item is queued from it. Instead of registering for the device interface, i8042prt fails the pended IOCTL_GET_SYS_BUTTON_EVENT, turns off the device interface and then turns it back on. i8042prt does this so that the power manager will requery the power capabilities. Once requeried, i8042prt will report the power button event. The power manager power button interface was not designed with capabilities changing after being reported the first time.

Finally, i8042prt will store the presence of these buttons in the keyboard's devnode. This value is read when the keyboard is initially started and updated every time i8042prt detects a new power button (in the same work item that is used to register the device interface). This allows i8042prt to simplify the power button path once the button has been detected.