geekhack

geekhack Community => Keyboards => Topic started by: NessDan on Sat, 26 December 2020, 18:22:58

Title: Making a Keyboard-to-Controller adapter, USB-level questions (Interfaces, NKRO)
Post by: NessDan on Sat, 26 December 2020, 18:22:58
Hi everyone, need USB-level protocol advice / knowledge!

I've been trying to make a keyboard adapter for the Nintendo Switch (https://keyboard.gg/) for over a year now. Right now, I've been doing some USB-level sniffing of my NKRO keyboard (Corsair K65) and found out it has 3 interfaces:


I've made a video of me diving into all three of these points (around 1:46): https://photos.app.goo.gl/7j98istA1GyRfbjE6

My questions are these:

Attaching some USB properties and pasting them below:

Code: [Select]
Connection Status Device connected
Current Configuration 1
Speed Full (12 Mbit/s)
Device Address 5
Number Of Open Pipes 3

Device Descriptor Corsair K65 Gaming Keyboard
Offset Field Size Value Description
0 bLength 1 12h 
1 bDescriptorType 1 01h Device
2 bcdUSB 2 0200h USB Spec 2.0
4 bDeviceClass 1 00h Class info in Ifc Descriptors
5 bDeviceSubClass 1 00h 
6 bDeviceProtocol 1 00h 
7 bMaxPacketSize0 1 08h 8 bytes
8 idVendor 2 1B1Ch 
10 idProduct 2 1B07h 
12 bcdDevice 2 0101h 1.01
14 iManufacturer 1 01h "Corsair"
15 iProduct 1 02h "Corsair K65 Gaming Keyboard"
16 iSerialNumber 1 00h 
17 bNumConfigurations 1 01h 

Configuration Descriptor 1 Bus Powered, 100 mA
Offset Field Size Value Description
0 bLength 1 09h 
1 bDescriptorType 1 02h Configuration
2 wTotalLength 2 0054h 
4 bNumInterfaces 1 03h 
5 bConfigurationValue 1 01h 
6 iConfiguration 1 00h 
7 bmAttributes 1 A0h Bus Powered, Remote Wakeup
 4..0: Reserved  ...00000   
 5: Remote Wakeup  ..1.....  Yes
 6: Self Powered  .0......  No, Bus Powered
 7: Reserved (set to one)
(bus-powered for 1.0)  1.......   
8 bMaxPower 1 32h 100 mA

Interface Descriptor 0/0 HID, 1 Endpoint
Offset Field Size Value Description
0 bLength 1 09h 
1 bDescriptorType 1 04h Interface
2 bInterfaceNumber 1 00h 
3 bAlternateSetting 1 00h 
4 bNumEndpoints 1 01h 
5 bInterfaceClass 1 03h HID
6 bInterfaceSubClass 1 01h Boot Interface
7 bInterfaceProtocol 1 01h Keyboard
8 iInterface 1 00h 

HID Descriptor
Offset Field Size Value Description
0 bLength 1 09h 
1 bDescriptorType 1 21h HID
2 bcdHID 2 0111h 1.11
4 bCountryCode 1 00h 
5 bNumDescriptors 1 01h 
6 bDescriptorType 1 22h Report
7 wDescriptorLength 2 004Fh 79 bytes

Endpoint Descriptor 81 1 In, Interrupt, 8 ms
Offset Field Size Value Description
0 bLength 1 07h 
1 bDescriptorType 1 05h Endpoint
2 bEndpointAddress 1 81h 1 In
3 bmAttributes 1 03h Interrupt
 1..0: Transfer Type  ......11  Interrupt
 7..2: Reserved  000000..   
4 wMaxPacketSize 2 0008h 8 bytes
6 bInterval 1 08h 8 ms

Interface Descriptor 1/0 HID, 1 Endpoint
Offset Field Size Value Description
0 bLength 1 09h 
1 bDescriptorType 1 04h Interface
2 bInterfaceNumber 1 01h 
3 bAlternateSetting 1 00h 
4 bNumEndpoints 1 01h 
5 bInterfaceClass 1 03h HID
6 bInterfaceSubClass 1 00h 
7 bInterfaceProtocol 1 00h 
8 iInterface 1 00h 

HID Descriptor
Offset Field Size Value Description
0 bLength 1 09h 
1 bDescriptorType 1 21h HID
2 bcdHID 2 0111h 1.11
4 bCountryCode 1 00h 
5 bNumDescriptors 1 01h 
6 bDescriptorType 1 22h Report
7 wDescriptorLength 2 0017h 23 bytes

Endpoint Descriptor 82 2 In, Interrupt, 8 ms
Offset Field Size Value Description
0 bLength 1 07h 
1 bDescriptorType 1 05h Endpoint
2 bEndpointAddress 1 82h 2 In
3 bmAttributes 1 03h Interrupt
 1..0: Transfer Type  ......11  Interrupt
 7..2: Reserved  000000..   
4 wMaxPacketSize 2 0002h 2 bytes
6 bInterval 1 08h 8 ms

Interface Descriptor 2/0 HID, 1 Endpoint
Offset Field Size Value Description
0 bLength 1 09h 
1 bDescriptorType 1 04h Interface
2 bInterfaceNumber 1 02h 
3 bAlternateSetting 1 00h 
4 bNumEndpoints 1 01h 
5 bInterfaceClass 1 03h HID
6 bInterfaceSubClass 1 00h 
7 bInterfaceProtocol 1 00h 
8 iInterface 1 00h 

HID Descriptor
Offset Field Size Value Description
0 bLength 1 09h 
1 bDescriptorType 1 21h HID
2 bcdHID 2 0111h 1.11
4 bCountryCode 1 00h 
5 bNumDescriptors 1 01h 
6 bDescriptorType 1 22h Report
7 wDescriptorLength 2 0039h 57 bytes

Endpoint Descriptor 83 3 In, Interrupt, 1 ms
Offset Field Size Value Description
0 bLength 1 07h 
1 bDescriptorType 1 05h Endpoint
2 bEndpointAddress 1 83h 3 In
3 bmAttributes 1 03h Interrupt
 1..0: Transfer Type  ......11  Interrupt
 7..2: Reserved  000000..   
4 wMaxPacketSize 2 000Fh 15 bytes
6 bInterval 1 01h 1 ms

Interface 0 HID Report Descriptor Keyboard
Item Tag (Value) Raw Data
Usage Page (Generic Desktop) 05 01 
Usage (Keyboard) 09 06 
Collection (Application) A1 01 
    Usage Page (Keyboard/Keypad) 05 07 
    Usage Minimum (Keyboard Left Control) 19 E0 
    Usage Maximum (Keyboard Right GUI) 29 E7 
    Logical Minimum (0) 15 00 
    Logical Maximum (1) 25 01 
    Report Size (1) 75 01 
    Report Count (8) 95 08 
    Input (Data,Var,Abs,NWrp,Lin,Pref,NNul,Bit) 81 02 
    Report Count (1) 95 01 
    Report Size (8) 75 08 
    Input (Cnst,Ary,Abs) 81 01 
    Report Count (5) 95 05 
    Report Size (1) 75 01 
    Usage Page (LEDs) 05 08 
    Usage Minimum (Num Lock) 19 01 
    Usage Maximum (Kana) 29 05 
    Output (Data,Var,Abs,NWrp,Lin,Pref,NNul,NVol,Bit) 91 02 
    Report Count (1) 95 01 
    Report Size (3) 75 03 
    Output (Cnst,Ary,Abs,NWrp,Lin,Pref,NNul,NVol,Bit) 91 01 
    Report Count (6) 95 06 
    Report Size (8) 75 08 
    Logical Minimum (0) 15 00 
    Logical Maximum (255) 26 FF 00 
    Usage Page (Keyboard/Keypad) 05 07 
    Usage Minimum (Undefined) 19 00 
    Usage Maximum 2A FF 00 
    Input (Data,Ary,Abs) 81 00 
    Usage Page (Consumer Devices) 05 0C 
    Usage (Undefined) 09 00 
    Logical Minimum (-128) 15 80 
    Logical Maximum (127) 25 7F 
    Report Size (8) 75 08 
    Report Count (8) 95 08 
    Feature (Data,Var,Abs,NWrp,Lin,Pref,NNul,NVol,Bit) B1 02 
End Collection C0 

Interface 1 HID Report Descriptor Consumer Control
Item Tag (Value) Raw Data
Usage Page (Consumer Devices) 05 0C 
Usage (Consumer Control) 09 01 
Collection (Application) A1 01 
    Usage Minimum (Undefined) 19 00 
    Usage Maximum 2A FF 0F 
    Logical Minimum (0) 15 00 
    Logical Maximum (4095) 26 FF 0F 
    Report Size (16) 75 10 
    Report Count (1) 95 01 
    Input (Data,Ary,Abs) 81 00 
End Collection C0 

Interface 2 HID Report Descriptor Keyboard
Item Tag (Value) Raw Data
Usage Page (Generic Desktop) 05 01 
Usage (Keyboard) 09 06 
Collection (Application) A1 01 
    Usage Page (Keyboard/Keypad) 05 07 
    Usage Minimum (Keyboard Left Control) 19 E0 
    Usage Maximum (Keyboard Right GUI) 29 E7 
    Logical Minimum (0) 15 00 
    Logical Maximum (1) 25 01 
    Report Size (1) 75 01 
    Report Count (8) 95 08 
    Input (Data,Var,Abs,NWrp,Lin,Pref,NNul,Bit) 81 02 
    Usage Minimum (Undefined) 19 00 
    Usage Maximum (Keypad =) 29 67 
    Logical Minimum (0) 15 00 
    Logical Maximum (1) 25 01 
    Report Size (1) 75 01 
    Report Count (104) 95 68 
    Input (Data,Var,Abs,NWrp,Lin,Pref,NNul,Bit) 81 02 
    Usage (Keypad Comma) 09 85 
    Usage Minimum (Keyboard International 1) 19 87 
    Usage Maximum (Keyboard International 5) 29 8B 
    Usage (Keyboard LANG1) 09 90 
    Usage (Keyboard LANG2) 09 91 
    Logical Minimum (0) 15 00 
    Logical Maximum (1) 25 01 
    Report Size (1) 75 01 
    Report Count (8) 95 08 
    Input (Data,Var,Abs,NWrp,Lin,Pref,NNul,Bit) 81 02 
End Collection C0 
Title: Re: Making a Keyboard-to-Controller adapter, USB-level questions (Interfaces, NKRO)
Post by: ethereal on Wed, 30 December 2020, 04:35:04
1. You need to parse the HID descriptors.

2. There are 3 usual methods for implementing NKRO (https://github.com/qmk/qmk_firmware/blob/master/docs/usb_nkro.txt (https://github.com/qmk/qmk_firmware/blob/master/docs/usb_nkro.txt)). But each keyboard/microcontroller tends to implement USB stuff in its particular way.

3. You have to parse the HID descriptors of all the USB interfaces. Then you find which reports belong to the Keyboard class and listen to all of them. Some keyboards present themselves as multiple virtual keyboards and you have to get the events of all of them.

4. I guess that would be named "HID parsing and decoding".
Title: Re: Making a Keyboard-to-Controller adapter, USB-level questions (Interfaces, NKRO)
Post by: NessDan on Tue, 05 January 2021, 20:47:59
Thank you so much ethereal!

1. You need to parse the HID descriptors.

Awesome! So I'd need to write logic to parse messages like the below so I can interpret the data I'm getting from my reports?:

Code: [Select]
Interface 0 HID Report Descriptor Keyboard
Item Tag (Value) Raw Data
Usage Page (Generic Desktop) 05 01
Usage (Keyboard) 09 06
Collection (Application) A1 01
    Usage Page (Keyboard/Keypad) 05 07
    Usage Minimum (Keyboard Left Control) 19 E0
    Usage Maximum (Keyboard Right GUI) 29 E7
    Logical Minimum (0) 15 00
    Logical Maximum (1) 25 01
    Report Size (1) 75 01
    Report Count (8) 95 08
    Input (Data,Var,Abs,NWrp,Lin,Pref,NNul,Bit) 81 02
    Input (Cnst,Ary,Abs) 81 01
    Usage Minimum (Undefined) 19 00
    Usage Maximum 2A FF 00
    Logical Minimum (0) 15 00
    Logical Maximum (255) 26 FF 00
    Report Size (8) 75 08
    Report Count (6) 95 06
    Input (Data,Ary,Abs) 81 00
    Usage Page (LEDs) 05 08
    Usage Minimum (Num Lock) 19 01
    Usage Maximum (Scroll Lock) 29 03
    Logical Minimum (0) 15 00
    Logical Maximum (1) 25 01
    Report Size (1) 75 01
    Report Count (3) 95 03
    Output (Data,Var,Abs,NWrp,Lin,Pref,NNul,NVol,Bit) 91 02
    Report Count (5) 95 05
    Output (Cnst,Ary,Abs,NWrp,Lin,Pref,NNul,NVol,Bit) 91 01
End Collection C0

May be an odd question, but this must have already been done before in C right? Is it possible there exists a library that handles this? Or should I write it from scratch? (I'm using a FTDI / Bridgetek FT90X chip)
Title: Re: Making a Keyboard-to-Controller adapter, USB-level questions (Interfaces, NKRO)
Post by: ethereal on Wed, 06 January 2021, 06:51:24
You're welcome!

I see the FT9xx comes with a toolchain (https://www.ftdichip.com/Firmware/FT90xToolchain.htm (https://www.ftdichip.com/Firmware/FT90xToolchain.htm)). I guess it doesn't have a HID library...

The Linux kernel has a generic HID parser/decoder (in C like all kernel code). Maybe you can use some part of it: https://elixir.bootlin.com/linux/v5.10/source/drivers/hid (https://elixir.bootlin.com/linux/v5.10/source/drivers/hid)

There is also another project that implements a generic HID parser and decoder: hid-tools (https://gitlab.freedesktop.org/libevdev/hid-tools (https://gitlab.freedesktop.org/libevdev/hid-tools)). This one is written in Python (so maybe it's of no use to you if the FT9xx comes with no Python interpreter).

Do you really need a generic HID parser and decoder? Maybe a simple implementation, capable of interacting only with keyboards, would do the trick. I think some BIOS projects like coreboot (https://coreboot.org/ (https://coreboot.org/)) implement code to interact with USB keyboards. These would use, most probably, the boot protocol, so they would be limited to 6KRO mode only. But their code would be much simpler to follow and maybe it's all you need.
Title: Re: Making a Keyboard-to-Controller adapter, USB-level questions (Interfaces, NKRO)
Post by: NessDan on Wed, 06 January 2021, 17:31:33
You're welcome!

I see the FT9xx comes with a toolchain (https://www.ftdichip.com/Firmware/FT90xToolchain.htm (https://www.ftdichip.com/Firmware/FT90xToolchain.htm)). I guess it doesn't have a HID library...

That's what I'm using right now! They actually do have a lot of USB related functions but nothing around parsing the HID Report Descriptor - they always assume the shape of the data (e.g. if it's a keyboard, they always treat the report as a BOOT report.)

Quote
Do you really need a generic HID parser and decoder? Maybe a simple implementation, capable of interacting only with keyboards, would do the trick. I think some BIOS projects like coreboot (https://coreboot.org/ (https://coreboot.org/)) implement code to interact with USB keyboards. These would use, most probably, the boot protocol, so they would be limited to 6KRO mode only. But their code would be much simpler to follow and maybe it's all you need.

Honestly, a simple implementation would probably work just fine too. Coreboot unfortunately handles only the BOOT keyboard report (https://github.com/coreboot/coreboot/blob/master/payloads/libpayload/drivers/usb/usbhid.c) but I appreciate you passing that to me.

I think I'm going to get a really naive parser made myself - something small that only handles keyboard reports.

I really appreciate all the help ethereal!