It says that I should use USB usage page 1 and usage 6 to get raw keyboard data, but what if I have a PS/2 keyboard?

Some time ago, I wrote about raw input and noted that if you want to receive raw keyboard input, you set the usage page to 1 and the usage to 6, noting that these numbers come from the USB HID specification. Commenter onodera asks, "Does this work for PS/2 input devices as well?"

Let's try it. I happen to have a laptop within easy reach, so I plugged in an external USB wireless keyboard, then went to Device Manager. It says

⌵ ⌨ Keyboards
⌨ Standard PS/2 Keyboard
⌨ Wireless Keyboard Filter Device

Then I ran the sample raw keyboard input program from the same article and pressed the space bar a few times on the external USB wireless keyboard, then the space bar a few times on the built-in PS/2 keyboard:

04E00817, msg=0100, vk=0020, scanCode=39, press
04E00817, msg=0101, vk=0020, scanCode=39, release
04E00817, msg=0100, vk=0020, scanCode=39, press
04E00817, msg=0101, vk=0020, scanCode=39, release
04E00817, msg=0100, vk=0020, scanCode=39, press
04E00817, msg=0101, vk=0020, scanCode=39, release
0001003D, msg=0100, vk=0020, scanCode=39, press
0001003D, msg=0101, vk=0020, scanCode=39, release
0001003D, msg=0100, vk=0020, scanCode=39, press
0001003D, msg=0101, vk=0020, scanCode=39, release
0001003D, msg=0100, vk=0020, scanCode=39, press
0001003D, msg=0101, vk=0020, scanCode=39, release

The entries from device 04E00817 are from the external USB wireless keyboard. The entries from device 0001003D are from the built-in PS/2 keyboard.

So there you go: It works even for PS/2 keyboards.

The fact that the input is described in terms of HID usage pages and HID usages is just a convenient way of expressing what kind of input you want, because it generalizes in the obvious way to to HID devices. Non-HID devices are mapped to the corresponding HID usages.

Bonus chatter: Raw input also captures synthesized input. Here's the result after performing a Send­Input to simulate pressing the space bar:

00000000, msg=0100, vk=0020, scanCode=39, press
00000000, msg=0101, vk=0020, scanCode=39, release

Notice that this was a completely synthetic keypress, but it showed up anyway, despite not having come from a USB device (or indeed any device at all). Note also that the device handle is null.

Comments (18)
  1. kantos says:

    This strikes me as a way to prevent cheating by tools or bots for games that are designed to pump input messages. Simply listen for a device handle of nullptr and bounce the cheaters.

    1. Mark (The other Mark) says:

      Ah, but then you end up blocking someone who uses an accessibility tool in order to play your game when they are unable to use the standard keyboard and mouse, and your bots start pretending to be a keyboard.

    2. Brian_EE says:

      Let me tell you how you make a cheap USB composite device that looks like HID keyboard and HID bulk device. The bulk endpoint gets messages from the bot and then sends them back to PC as keystrokes….

      Go ahead and make your wall taller. I have more lumber to extend my ladder.

      1. ErikF says:

        You can make your composite USB device: I’ll just spin up a VM and send it key commands via scripting :-)

        1. Koro says:

          Many games (or rather, their anti-cheat rootkit) will detect a VM and refuse to run.

          1. Zan Lynx' says:

            And then there are mods for the VM so it can’t be detected. Mostly for security researchers but it’d probably work on games too.

  2. Myria says:

    This is similar to how all CD-ROM drives from the past 15+ years all use the SCSI command set, even if they are connected via USB, IDE or SATA. You still use IOCTL_SCSI_PASS_THROUGH[_DIRECT] to send commands to all optical drives.

    However, in the case of optical drives, the drives all support SCSI commands. It’s the difference in bus interface that is abstracted by the OS.

    1. It’s gotten to the point where Linux even pretends that ATA hard drives are SCSI (using something called SAT). For example, the hard drive /dev/sda on my router (a small PC running Debian) is apparently implemented using the “sd” (sr_mod.ko) SCSI disk driver running atop the “ahci” AHCI SATA low-level driver; the latter uses “libata” to expose the AHCI as a SCSI host. One of the most obvious benefits is that optical drives are exposed as SCSI devices again — I still don’t understand why that went out of style in the first place, given that all the imaging/burning tools relied on the SCSI interfaces and that the drives basically just used SCSI command sets all along — but apparently the unification simplifies things even for PATA hard drives.

      Strangely, this effort began in 2003 and the bulk of the PATA drivers for it were approved for merging in 2006 , even though the first SCSI / ATA Translation standard didn’t reach it’s final draft until 2007 .

      (USB storage seems to be inherently SCSI-based.)

  3. Karellen says:

    “Non-HID devices are mapped to the corresponding HID usages.”

    Ah, this is obviously some strange usage of the word “raw” that I wasn’t previously aware of. :-)

    1. Ian Yates says:

      Well no one is cooking these, right? :)

      1. Karellen says:

        Sorry, I think my sarcasm meter is broken, as it’s not registering any in your comment?

        If so, well, my point is that they *are* cooking it. If you map PS/2 input data to USB HID input data, then that’s definitely a useful feature that allows devs to make sense of more data more easily than they would otherwise, but you can’t really claim that it’s the “raw” input data anymore.

        1. Brian_EE says:

          If I crack a chicken egg into your hand and you consume it I think we agree that it’s raw. If I crack that same egg onto a plate and hand it to you that way, is it still raw?

          1. Karellen says:

            I initially thought that your analogy just didn’t capture the scale of the issue as I was imagining it. I had thought that the difference between the PS/2 and the USB protocols would be so different that there would be very little in common at all between their “raw” data, and that a great deal of “cooking” would be required.

            However, I had forgotten the simplicity of physical PS/2 to USB (and vice-versa) adaptors when both were in common use a decade and a half ago. Given that, I have to conclude that it’s possible that there might not be a great deal of “cooking” needed to translate raw PS/2 data to the equivalent USB HID data, maybe to the point where it wouldn’t count as cooking any more.

            However, PS/2 devices are not the only non-USB HID input devices out there, and some will require more cooking of their raw input data than others to appear so. The point I was trying to make is that, on the face of it, the *general* case of “Non-HID devices are mapped to the corresponding HID usages.” seems contradictory to the idea that you’re looking at “raw” input data.

            (Note, I still think that such a mapping is a good and useful idea. I’m not trying to have a go at the feature. I’m just slightly amused at the naming of the API – only less so now that I’m actually dissecting the “joke”, such as it was.)

        2. You’re confusing the device with the data. The input data is still raw, it’s just the underlying device that’s been “cooked” to work with the HID interface.

          1. SimonRev says:

            But it makes the point the original customer asked make much more sense. I don’t find it surprising that a customer using the an API called “RawInput” would expect that USB and PS/2 devices would behave differently.

          2. Well, in this case the “original customer” was Raymond with his Little Programs™ and a commentator. :-)

        3. JDG says:

          It comes down to which definition of the word “raw” is meant. One way to interpret “raw” is “exactly as received from the device”, but another interpretation, equally valid, is “not yet processed from an indication of which physical key was pressed to the corresponding character and related commands”. E.g. the key marked “d” has a particular scan code, and that scan code is what will be returned regardless of the underlying connection or protocol. Thr non-raw data, though, might end up being a ‘d’, a ‘D’, a ^D, or maybe for some people (like myself) using the Dvorak layout, an ‘e’, ‘E’ or ^E. Other layouts may have other interpretations, or other processing layers might have entirely different kinds of meaning to assign (such as +strafe_right). It is this latter interpretation of “raw” that is intended, I’m pretty sure.

  4. Hmm, I wonder if any devices actually declare page 1 / usage 7 (“Keypad”), which is specified thus:

    CA – Any keyboard configuration that does not meet the minimum requirements of the Boot Keyboard. Keypad often refers to a supplementary calculator-style keyboard.

Comments are closed.

Skip to main content