BlocNotes

Notepad of a tinker, maker, hacker or whatever you call it :)

Custom USB HID device descriptor : consumer device (media) + keyboard

For a personal project I need media keys (play, volume, mute, ...) and a way to lock a computer under Windows (Windows+L keys).
There is a lot of HID keyboard descriptors on the Internet, some are for keyboards + media but all contains at least one unused byte... Not a good thing for embedded systems with constraint memory. Why this ? The descriptor can be found in USB-IF example (Appendix B) and describe a boot compatible keyboard. So unless you need your keyboard in boot menus, it can be dropped. Also, the example define 6 simultaneous keys, I decreased it to three as I only need one key.

Consumer device allows lot of actions, which can be found in HID usage tables document, in §15 consumer page. I selected some relevant to my application (media keys), but you can add more if required, just increase the report count and add the new key. Note that each started byte must be padded with constant bits, so if your report count is not a multiple of 8, add a constant section.

How to use it ?

Prepare an uint8_t table with a size matching the amount of data you have to send plus one for the descriptor (first byte).
For media keys, each bit of the second byte will correspond to one key, LSB is the first in the descriptor.
For keyboard, the second byte is for modifiers, and third, fourth, fifth are for keys.

Those examples are related to the following descriptor.

uint8_t bufferHID[5] = {0, 0, 0, 0, 0};  
// Send media mute
bufferHID[0] = 2;      // Start with the report ID  
bufferHID[1] = 0x20;   // Switch mute bit to 1  
USB_HID_SendReport(bufferHID, 2); // Only 2 bytes required : report ID + media keys byte.  
// Send Windows+L
bufferHID[0] = 1;      // Start with the report ID  
bufferHID[1] = 0x08;   // Switch left GUI bit to 1 - Windows or Apple/Cmd key  
bufferHID[2] = 0x0F;   // 'l' key  
USB_HID_SendReport(bufferHID, 5); // 5 bytes required : report ID + keyboard[4]  
delay(30);             // Leave some time for the USB stack  
bufferHID[1] = 0;      // Release keys  
bufferHID[2] = 0;  
USB_HID_SendReport(bufferHID, 5); // Send released keys  

Descriptor

  • Keyboard
    • Modifiers (MSB-LSB) : RIGHT_GUI-RIGHT_ALT-RIGHT_SHIFT-RIGHT_CTRL-LEFT_GUI-LEFT_ALT-LEFT_SHIFT-LEFT_CTRL
  • Media keys
    • Keys (MSB-LSB) : Next-Previous-Stop-Eject-Play/Pause-Mute-Vol inc-Vol dec

Descriptor

// 78 bytes
0x05, 0x01,        // Usage Page (Generic Desktop Ctrls)  
0x09, 0x06,        // Usage (Keyboard)  
0xA1, 0x01,        // Collection (Application)  
0x85, 0x01,        //   Report ID (1)  
0x05, 0x07,        //   Usage Page (Kbrd/Keypad)  
0x75, 0x01,        //   Report Size (1)  
0x95, 0x08,        //   Report Count (8)  
0x19, 0xE0,        //   Usage Minimum (0xE0)  
0x29, 0xE7,        //   Usage Maximum (0xE7)  
0x15, 0x00,        //   Logical Minimum (0)  
0x25, 0x01,        //   Logical Maximum (1)  
0x81, 0x02,        //   Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)  
0x95, 0x03,        //   Report Count (3)  
0x75, 0x08,        //   Report Size (8)  
0x15, 0x00,        //   Logical Minimum (0)  
0x25, 0x64,        //   Logical Maximum (100)  
0x05, 0x07,        //   Usage Page (Kbrd/Keypad)  
0x19, 0x00,        //   Usage Minimum (0x00)  
0x29, 0x65,        //   Usage Maximum (0x65)  
0x81, 0x00,        //   Input (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)  
0xC0,              // End Collection  
0x05, 0x0C,        // Usage Page (Consumer)  
0x09, 0x01,        // Usage (Consumer Control)  
0xA1, 0x01,        // Collection (Application)  
0x85, 0x02,        //   Report ID (2)  
0x05, 0x0C,        //   Usage Page (Consumer)  
0x15, 0x00,        //   Logical Minimum (0)  
0x25, 0x01,        //   Logical Maximum (1)  
0x75, 0x01,        //   Report Size (1)  
0x95, 0x07,        //   Report Count (7)  
0x09, 0xB5,        //   Usage (Scan Next Track)  
0x09, 0xB6,        //   Usage (Scan Previous Track)  
0x09, 0xB7,        //   Usage (Stop)  
0x09, 0xB8,        //   Usage (Eject)  
0x09, 0xCD,        //   Usage (Play/Pause)  
0x09, 0xE2,        //   Usage (Mute)  
0x09, 0xE9,        //   Usage (Volume Increment)  
0x09, 0xEA,        //   Usage (Volume Decrement)  
0x81, 0x02,        //   Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)  
0xC0,              // End Collection  

Helpful defines

// USB media codes
#define USB_HID_SCAN_NEXT 0x01
#define USB_HID_SCAN_PREV 0x02
#define USB_HID_STOP      0x04
#define USB_HID_EJECT     0x08
#define USB_HID_PAUSE     0x10
#define USB_HID_MUTE      0x20
#define USB_HID_VOL_UP    0x40
#define USB_HID_VOL_DEC   0x80

// USB keyboard codes
#define USB_HID_MODIFIER_LEFT_CTRL   0x01
#define USB_HID_MODIFIER_LEFT_SHIFT  0x02
#define USB_HID_MODIFIER_LEFT_ALT    0x04
#define USB_HID_MODIFIER_LEFT_GUI    0x08 // (Win/Apple/Meta)
#define USB_HID_MODIFIER_RIGHT_CTRL  0x10
#define USB_HID_MODIFIER_RIGHT_SHIFT 0x20
#define USB_HID_MODIFIER_RIGHT_ALT   0x40
#define USB_HID_MODIFIER_RIGHT_GUI   0x80
#define USB_HID_KEY_L     0x0F

Documentation