simpler generation of HID device tables

This commit is contained in:
Dan Halbert 2019-09-03 21:16:14 -04:00
parent 42f5edbd33
commit 94ba027544
3 changed files with 346 additions and 453 deletions

View File

@ -24,149 +24,4 @@
* THE SOFTWARE.
*/
#include "py/obj.h"
#include "py/mphal.h"
#include "py/runtime.h"
#include "genhdr/autogen_usb_descriptor.h"
#include "shared-module/usb_hid/Device.h"
#include "shared-bindings/usb_hid/Device.h"
#include "tusb.h"
#ifdef USB_HID_REPORT_ID_KEYBOARD
static uint8_t keyboard_report_buffer[USB_HID_REPORT_LENGTH_KEYBOARD];
#endif
#ifdef USB_HID_REPORT_ID_MOUSE
static uint8_t mouse_report_buffer[USB_HID_REPORT_LENGTH_MOUSE];
#endif
#ifdef USB_HID_REPORT_ID_CONSUMER
static uint8_t consumer_report_buffer[USB_HID_REPORT_LENGTH_CONSUMER];
#endif
#ifdef USB_HID_REPORT_ID_SYS_CONTROL
static uint8_t sys_control_report_buffer[USB_HID_REPORT_LENGTH_SYS_CONTROL];
#endif
#ifdef USB_HID_REPORT_ID_GAMEPAD
static uint8_t gamepad_report_buffer[USB_HID_REPORT_LENGTH_GAMEPAD];
#endif
#ifdef USB_HID_REPORT_ID_XAC_COMPATIBLE_GAMEPAD
static uint8_t xac_compatible_gamepad_report_buffer[USB_HID_REPORT_LENGTH_XAC_COMPATIBLE_GAMEPAD];
#endif
#ifdef USB_HID_REPORT_ID_DIGITIZER
static uint8_t digitizer_report_buffer[USB_HID_REPORT_LENGTH_DIGITIZER];
#endif
usb_hid_device_obj_t usb_hid_devices[] = {
#ifdef USB_HID_REPORT_ID_KEYBOARD
{
.base = { .type = &usb_hid_device_type } ,
.report_buffer = keyboard_report_buffer ,
.report_id = USB_HID_REPORT_ID_KEYBOARD ,
.report_length = USB_HID_REPORT_LENGTH_KEYBOARD ,
.usage_page = HID_USAGE_PAGE_DESKTOP ,
.usage = HID_USAGE_DESKTOP_KEYBOARD ,
},
#endif
#ifdef USB_HID_REPORT_ID_MOUSE
{
.base = { .type = &usb_hid_device_type } ,
.report_buffer = mouse_report_buffer ,
.report_id = USB_HID_REPORT_ID_MOUSE ,
.report_length = USB_HID_REPORT_LENGTH_MOUSE ,
.usage_page = HID_USAGE_PAGE_DESKTOP ,
.usage = HID_USAGE_DESKTOP_MOUSE ,
},
#endif
#ifdef USB_HID_REPORT_ID_CONSUMER
{
.base = { .type = &usb_hid_device_type } ,
.report_buffer = consumer_report_buffer ,
.report_id = USB_HID_REPORT_ID_CONSUMER ,
.report_length = USB_HID_REPORT_LENGTH_CONSUMER ,
.usage_page = HID_USAGE_PAGE_CONSUMER ,
.usage = HID_USAGE_CONSUMER_CONTROL ,
},
#endif
#ifdef USB_HID_REPORT_ID_SYS_CONTROL
{
.base = { .type = &usb_hid_device_type } ,
.report_buffer = sys_control_report_buffer ,
.report_id = USB_HID_REPORT_ID_SYS_CONTROL ,
.report_length = USB_HID_REPORT_LENGTH_SYS_CONTROL ,
.usage_page = HID_USAGE_PAGE_DESKTOP ,
.usage = HID_USAGE_DESKTOP_SYSTEM_CONTROL ,
},
#endif
#ifdef USB_HID_REPORT_ID_GAMEPAD
{
.base = { .type = &usb_hid_device_type } ,
.report_buffer = gamepad_report_buffer ,
.report_id = USB_HID_REPORT_ID_GAMEPAD ,
.report_length = USB_HID_REPORT_LENGTH_GAMEPAD ,
.usage_page = HID_USAGE_PAGE_DESKTOP ,
.usage = HID_USAGE_DESKTOP_GAMEPAD ,
},
#endif
#ifdef USB_HID_REPORT_ID_XAC_COMPATIBLE_GAMEPAD
{
.base = { .type = &usb_hid_device_type } ,
.report_buffer = xac_compatible_gamepad_report_buffer ,
.report_id = USB_HID_REPORT_ID_XAC_COMPATIBLE_GAMEPAD ,
.report_length = USB_HID_REPORT_LENGTH_XAC_COMPATIBLE_GAMEPAD ,
.usage_page = HID_USAGE_PAGE_DESKTOP ,
.usage = HID_USAGE_DESKTOP_GAMEPAD ,
},
#endif
#ifdef USB_HID_REPORT_ID_DIGITIZER
{
.base = { .type = &usb_hid_device_type } ,
.report_buffer = digitizer_report_buffer ,
.report_id = USB_HID_REPORT_ID_DIGITIZER ,
.report_length = USB_HID_REPORT_LENGTH_DIGITIZER ,
.usage_page = 0x0D ,
.usage = 0x02 ,
},
#endif
};
mp_obj_tuple_t common_hal_usb_hid_devices = {
.base = {
.type = &mp_type_tuple,
},
.len = USB_HID_NUM_DEVICES,
.items = {
#if USB_HID_NUM_DEVICES >= 1
(mp_obj_t) &usb_hid_devices[0],
#endif
#if USB_HID_NUM_DEVICES >= 2
(mp_obj_t) &usb_hid_devices[1],
#endif
#if USB_HID_NUM_DEVICES >= 3
(mp_obj_t) &usb_hid_devices[2],
#endif
#if USB_HID_NUM_DEVICES >= 4
(mp_obj_t) &usb_hid_devices[3],
#endif
#if USB_HID_NUM_DEVICES >= 5
(mp_obj_t) &usb_hid_devices[4],
#endif
#if USB_HID_NUM_DEVICES >= 6
(mp_obj_t) &usb_hid_devices[5],
#endif
#if USB_HID_NUM_DEVICES >= 7
#error "Too many USB HID devices"
#endif
}
};
// Nothing needed here. Tables of HID devices are in autogen_usb_descriptor.c.

View File

@ -12,7 +12,7 @@ ALL_DEVICES='CDC,MSC,AUDIO,HID'
ALL_DEVICES_SET=frozenset(ALL_DEVICES.split(','))
DEFAULT_DEVICES='CDC,MSC,AUDIO,HID'
ALL_HID_DEVICES='KEYBOARD,MOUSE,CONSUMER,SYS_CONTROL,GAMEPAD,DIGITIZER,XAC_COMPATIBLE_GAMEPAD'
ALL_HID_DEVICES='KEYBOARD,MOUSE,CONSUMER,SYS_CONTROL,GAMEPAD,DIGITIZER,XAC_COMPATIBLE_GAMEPAD,RAW'
ALL_HID_DEVICES_SET=frozenset(ALL_HID_DEVICES.split(','))
# Digitizer works on Linux but conflicts with mouse, so omit it.
DEFAULT_HID_DEVICES='KEYBOARD,MOUSE,CONSUMER,GAMEPAD'
@ -28,20 +28,20 @@ parser.add_argument('--pid', type=lambda x: int(x, 16),
help='product id')
parser.add_argument('--serial_number_length', type=int, default=32,
help='length needed for the serial number in digits')
parser.add_argument('--devices', type=lambda l: frozenset(l.split(',')), default=DEFAULT_DEVICES,
parser.add_argument('--devices', type=lambda l: tuple(l.split(',')), default=DEFAULT_DEVICES,
help='devices to include in descriptor (AUDIO includes MIDI support)')
parser.add_argument('--hid_devices', type=lambda l: frozenset(l.split(',')), default=DEFAULT_HID_DEVICES,
parser.add_argument('--hid_devices', type=lambda l: tuple(l.split(',')), default=DEFAULT_HID_DEVICES,
help='HID devices to include in HID report descriptor')
parser.add_argument('--output_c_file', type=argparse.FileType('w'), required=True)
parser.add_argument('--output_h_file', type=argparse.FileType('w'), required=True)
args = parser.parse_args()
unknown_devices = list(args.devices - ALL_DEVICES_SET)
unknown_devices = list(frozenset(args.devices) - ALL_DEVICES_SET)
if unknown_devices:
raise ValueError("Unknown device(s)", unknown_devices)
unknown_hid_devices = list(args.hid_devices - ALL_HID_DEVICES_SET)
unknown_hid_devices = list(frozenset(args.hid_devices) - ALL_HID_DEVICES_SET)
if unknown_hid_devices:
raise ValueError("Unknown HID devices(s)", unknown_hid_devices)
@ -160,29 +160,27 @@ msc_interfaces = [
)
]
# Sort by Report ID for consistency.
hid_devices = sorted(args.hid_devices, key=lambda name: hid_report_descriptors.REPORT_IDS[name])
# When there's only one hid_device, it shouldn't have a report id.
# Otherwise, report ids are assigned sequentially:
# args.hid_devices[0] has report_id 1
# args.hid_devices[1] has report_id 2
# etc.
# When there's only one hid_device, it can't be in a composite descriptor.
# It has no report id (indicated by 0), so remove the existing one.
#if len(hid_devices) == 1:
if False:
name = hid_devices[0]
if len(args.hid_devices) == 1:
name = args.hid_devices[0]
combined_hid_report_descriptor = hid.ReportDescriptor(
description=name,
report_descriptor=hid_report_descriptors.remove_report_id(
hid_report_descriptors.REPORT_DESCRIPTORS[name].report_descriptor))
hid_report_ids_dict = { name : 0 }
report_descriptor=bytes(hid_report_descriptors.REPORT_DESCRIPTOR_FUNCTIONS[name](0)))
else:
report_id = 1
concatenated_descriptors = bytearray()
for name in args.hid_devices:
concatenated_descriptors.extend(
bytes(hid_report_descriptors.REPORT_DESCRIPTOR_FUNCTIONS[name](report_id)))
report_id += 1
combined_hid_report_descriptor = hid.ReportDescriptor(
description="MULTIDEVICE",
report_descriptor=b''.join(
hid_report_descriptors.REPORT_DESCRIPTORS[name].report_descriptor for name in hid_devices ))
hid_report_ids_dict = { name: hid_report_descriptors.REPORT_IDS[name] for name in hid_devices }
hid_report_lengths_dict = { name: hid_report_descriptors.REPORT_LENGTHS[name] for name in hid_devices }
hid_max_report_length = max(hid_report_lengths_dict.values())
description="MULTIDEVICE",
report_descriptor=bytes(concatenated_descriptors))
# ASF4 expects keyboard and generic devices to have both in and out endpoints,
# and will fail (possibly silently) if both are not supplied.
@ -343,6 +341,8 @@ h_file = args.output_h_file
c_file.write("""\
#include <stdint.h>
#include "py/objtuple.h"
#include "shared-bindings/usb_hid/Device.h"
#include "{H_FILE_NAME}"
""".format(H_FILE_NAME=h_file.name))
@ -461,7 +461,9 @@ const uint8_t usb_desc_cfg[{configuration_length}];
uint16_t usb_serial_number[{serial_number_length}];
uint16_t const * const string_desc_arr [{string_descriptor_length}];
const uint8_t hid_report_descriptor[{HID_REPORT_DESCRIPTOR_LENGTH}];
const uint8_t hid_report_descriptor[{hid_report_descriptor_length}];
#define USB_HID_NUM_DEVICES {hid_num_devices}
// Vendor name included in Inquiry response, max 8 bytes
#define CFG_TUD_MSC_VENDOR "{msc_vendor}"
@ -475,36 +477,11 @@ const uint8_t hid_report_descriptor[{HID_REPORT_DESCRIPTOR_LENGTH}];
configuration_length=descriptor_length,
max_configuration_length=max(hid_descriptor_length, descriptor_length),
string_descriptor_length=len(pointers_to_strings),
HID_REPORT_DESCRIPTOR_LENGTH=len(bytes(combined_hid_report_descriptor)),
hid_report_descriptor_length=len(bytes(combined_hid_report_descriptor)),
hid_num_devices=len(args.hid_devices),
msc_vendor=args.manufacturer[:8],
msc_product=args.product[:16]))
# #define the report ID's used in the combined HID descriptor
for name, id in hid_report_ids_dict.items():
h_file.write("""\
#define USB_HID_REPORT_ID_{name} {id}
""".format(name=name,
id=id))
h_file.write("\n")
# #define the report sizes used in the combined HID descriptor
for name, length in hid_report_lengths_dict.items():
h_file.write("""\
#define USB_HID_REPORT_LENGTH_{name} {length}
""".format(name=name,
length=length))
h_file.write("\n")
h_file.write("""\
#define USB_HID_NUM_DEVICES {num_devices}
#define USB_HID_MAX_REPORT_LENGTH {max_length}
""".format(num_devices=len(hid_report_lengths_dict),
max_length=hid_max_report_length))
# Write out the report descriptor and info
c_file.write("""\
const uint8_t hid_report_descriptor[{HID_DESCRIPTOR_LENGTH}] = {{
@ -512,7 +489,55 @@ const uint8_t hid_report_descriptor[{HID_DESCRIPTOR_LENGTH}] = {{
for b in bytes(combined_hid_report_descriptor):
c_file.write("0x{:02x}, ".format(b))
c_file.write("""\
};
""")
# Write out USB HID report buffer definitions.
for report_id, name in enumerate(args.hid_devices, start=1):
c_file.write("""\
static uint8_t {name}_report_buffer[{report_length}];
""".format(name=name.lower(), report_length=hid_report_descriptors.HID_DEVICE_DATA[name].report_length))
# Write out table of device objects.
c_file.write("""
usb_hid_device_obj_t usb_hid_devices[] = {
""");
for report_id, name in enumerate(args.hid_devices, start=1):
device_data = hid_report_descriptors.HID_DEVICE_DATA[name]
c_file.write("""\
{{
.base = {{ .type = &usb_hid_device_type }},
.report_buffer = {name}_report_buffer,
.report_id = {report_id:},
.report_length = {report_length},
.usage_page = {usage_page:#04x},
.usage = {usage:#04x},
}},
""".format(name=name.lower(), report_id=report_id,
report_length=device_data.report_length,
usage_page=device_data.usage_page,
usage=device_data.usage))
c_file.write("""\
};
""")
# Write out tuple of device objects.
c_file.write("""
mp_obj_tuple_t common_hal_usb_hid_devices = {{
.base = {{
.type = &mp_type_tuple,
}},
.len = {num_devices},
.items = {{
""".format(num_devices=len(args.hid_devices)))
for idx in range(len(args.hid_devices)):
c_file.write("""\
(mp_obj_t) &usb_hid_devices[{idx}],
""".format(idx=idx))
c_file.write("""\
},
};
""")

View File

@ -29,271 +29,284 @@ HID specific descriptors
* Author(s): Dan Halbert
"""
from collections import namedtuple
from adafruit_usb_descriptor import hid
def remove_report_id(report_descriptor_bytes):
#return report_descriptor_bytes
report_id_pos = report_descriptor_bytes.index(0x85)
return report_descriptor_bytes[:report_id_pos] + report_descriptor_bytes[report_id_pos + 2:]
REPORT_IDS = {
"KEYBOARD" : 1,
"MOUSE" : 2,
"CONSUMER" : 3,
"SYS_CONTROL" : 4,
"GAMEPAD" : 5,
"DIGITIZER" : 6,
"XAC_COMPATIBLE_GAMEPAD" : 7,
# RAW must not have a report ID, so it can't be used with other devices.
"RAW" : 0,
# Information about each kind of device
# report_length does not include report ID in first byte, if present when sent.
DeviceData = namedtuple('DeviceData', ('report_length', 'usage_page', 'usage'))
HID_DEVICE_DATA = {
"KEYBOARD" : DeviceData(report_length=8, usage_page=0x01, usage=0x06), # Generic Desktop, Keyboard
"MOUSE" : DeviceData(report_length=4, usage_page=0x01, usage=0x02), # Generic Desktop, Mouse
"CONSUMER" : DeviceData(report_length=2, usage_page=0x0C, usage=0x01), # Consumer, Consumer Control
"SYS_CONTROL" : DeviceData(report_length=1, usage_page=0x01, usage=0x80), # Generic Desktop, Sys Control
"GAMEPAD" : DeviceData(report_length=6, usage_page=0x01, usage=0x05), # Generic Desktop, Game Pad
"DIGITIZER" : DeviceData(report_length=5, usage_page=0x0D, usage=0x02), # Digitizers, Pen
"XAC_COMPATIBLE_GAMEPAD" : DeviceData(report_length=3, usage_page=0x01, usage=0x05), # Generic Desktop, Game Pad
"RAW" : DeviceData(report_length=64, usage_page=0xFFAF, usage=0xAF), # Vendor 0xFFAF "Adafruit", 0xAF
}
# Byte count for each kind of report. Length does not include report ID in first byte.
REPORT_LENGTHS = {
"KEYBOARD" : 8,
"MOUSE" : 4,
"CONSUMER" : 2,
"SYS_CONTROL" : 1,
"GAMEPAD" : 6,
"DIGITIZER" : 5,
"XAC_COMPATIBLE_GAMEPAD" : 3,
"RAW" : 64,
}
def keyboard_hid_descriptor(report_id):
data = HID_DEVICE_DATA["KEYBOARD"]
return hid.ReportDescriptor(
description="KEYBOARD",
report_descriptor=bytes(
# Regular keyboard
(0x05, data.usage_page, # Usage Page (Generic Desktop)
0x09, data.usage, # Usage (Keyboard)
0xA1, 0x01, # Collection (Application)
) +
((0x85, report_id) if report_id != 0 else ()) +
(0x05, 0x07, # Usage Page (Keyboard)
0x19, 224, # Usage Minimum (224)
0x29, 231, # Usage Maximum (231)
0x15, 0x00, # Logical Minimum (0)
0x25, 0x01, # Logical Maximum (1)
0x75, 0x01, # Report Size (1)
0x95, 0x08, # Report Count (8)
0x81, 0x02, # Input (Data, Variable, Absolute)
0x81, 0x01, # Input (Constant)
0x19, 0x00, # Usage Minimum (0)
0x29, 101, # Usage Maximum (101)
0x15, 0x00, # Logical Minimum (0)
0x25, 101, # Logical Maximum (101)
0x75, 0x08, # Report Size (8)
0x95, 0x06, # Report Count (6)
0x81, 0x00, # Input (Data, Array)
0x05, 0x08, # Usage Page (LED)
0x19, 0x01, # Usage Minimum (1)
0x29, 0x05, # Usage Maximum (5)
0x15, 0x00, # Logical Minimum (0)
0x25, 0x01, # Logical Maximum (1)
0x75, 0x01, # Report Size (1)
0x95, 0x05, # Report Count (5)
0x91, 0x02, # Output (Data, Variable, Absolute)
0x95, 0x03, # Report Count (3)
0x91, 0x01, # Output (Constant)
0xC0, # End Collection
)))
KEYBOARD_WITH_ID = hid.ReportDescriptor(
description="KEYBOARD",
report_descriptor=bytes([
# Regular keyboard
0x05, 0x01, # Usage Page (Generic Desktop)
0x09, 0x06, # Usage (Keyboard)
0xA1, 0x01, # Collection (Application)
0x85, REPORT_IDS["KEYBOARD"], # Report ID (1)
0x05, 0x07, # Usage Page (Keyboard)
0x19, 224, # Usage Minimum (224)
0x29, 231, # Usage Maximum (231)
0x15, 0x00, # Logical Minimum (0)
0x25, 0x01, # Logical Maximum (1)
0x75, 0x01, # Report Size (1)
0x95, 0x08, # Report Count (8)
0x81, 0x02, # Input (Data, Variable, Absolute)
0x81, 0x01, # Input (Constant)
0x19, 0x00, # Usage Minimum (0)
0x29, 101, # Usage Maximum (101)
0x15, 0x00, # Logical Minimum (0)
0x25, 101, # Logical Maximum (101)
0x75, 0x08, # Report Size (8)
0x95, 0x06, # Report Count (6)
0x81, 0x00, # Input (Data, Array)
0x05, 0x08, # Usage Page (LED)
0x19, 0x01, # Usage Minimum (1)
0x29, 0x05, # Usage Maximum (5)
0x15, 0x00, # Logical Minimum (0)
0x25, 0x01, # Logical Maximum (1)
0x75, 0x01, # Report Size (1)
0x95, 0x05, # Report Count (5)
0x91, 0x02, # Output (Data, Variable, Absolute)
0x95, 0x03, # Report Count (3)
0x91, 0x01, # Output (Constant)
0xC0, # End Collection
]))
def mouse_hid_descriptor(report_id):
data = HID_DEVICE_DATA["MOUSE"]
return hid.ReportDescriptor(
description="MOUSE",
report_descriptor=bytes(
# Regular mouse
(0x05, data.usage_page, # Usage Page (Generic Desktop)
0x09, data.usage, # Usage (Mouse)
0xA1, 0x01, # Collection (Application)
0x09, 0x01, # Usage (Pointer)
0xA1, 0x00, # Collection (Physical)
) +
((0x85, report_id) if report_id != 0 else ()) +
(0x05, 0x09, # Usage Page (Button)
0x19, 0x01, # Usage Minimum (0x01)
0x29, 0x05, # Usage Maximum (0x05)
0x15, 0x00, # Logical Minimum (0)
0x25, 0x01, # Logical Maximum (1)
0x95, 0x05, # Report Count (5)
0x75, 0x01, # Report Size (1)
0x81, 0x02, # Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x95, 0x01, # Report Count (1)
0x75, 0x03, # Report Size (3)
0x81, 0x01, # Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x05, 0x01, # Usage Page (Generic Desktop Ctrls)
0x09, 0x30, # Usage (X)
0x09, 0x31, # Usage (Y)
0x15, 0x81, # Logical Minimum (-127)
0x25, 0x7F, # Logical Maximum (127)
0x75, 0x08, # Report Size (8)
0x95, 0x02, # Report Count (2)
0x81, 0x06, # Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position)
0x09, 0x38, # Usage (Wheel)
0x15, 0x81, # Logical Minimum (-127)
0x25, 0x7F, # Logical Maximum (127)
0x75, 0x08, # Report Size (8)
0x95, 0x01, # Report Count (1)
0x81, 0x06, # Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position)
0xC0, # End Collection
0xC0, # End Collection
)))
MOUSE_WITH_ID = hid.ReportDescriptor(
description="MOUSE",
report_descriptor=bytes([
# Regular mouse
0x05, 0x01, # Usage Page (Generic Desktop)
0x09, 0x02, # Usage (Mouse)
0xA1, 0x01, # Collection (Application)
0x09, 0x01, # Usage (Pointer)
0xA1, 0x00, # Collection (Physical)
0x85, REPORT_IDS["MOUSE"], # Report ID (n)
0x05, 0x09, # Usage Page (Button)
0x19, 0x01, # Usage Minimum (0x01)
0x29, 0x05, # Usage Maximum (0x05)
0x15, 0x00, # Logical Minimum (0)
0x25, 0x01, # Logical Maximum (1)
0x95, 0x05, # Report Count (5)
0x75, 0x01, # Report Size (1)
0x81, 0x02, # Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x95, 0x01, # Report Count (1)
0x75, 0x03, # Report Size (3)
0x81, 0x01, # Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x05, 0x01, # Usage Page (Generic Desktop Ctrls)
0x09, 0x30, # Usage (X)
0x09, 0x31, # Usage (Y)
0x15, 0x81, # Logical Minimum (-127)
0x25, 0x7F, # Logical Maximum (127)
0x75, 0x08, # Report Size (8)
0x95, 0x02, # Report Count (2)
0x81, 0x06, # Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position)
0x09, 0x38, # Usage (Wheel)
0x15, 0x81, # Logical Minimum (-127)
0x25, 0x7F, # Logical Maximum (127)
0x75, 0x08, # Report Size (8)
0x95, 0x01, # Report Count (1)
0x81, 0x06, # Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position)
0xC0, # End Collection
0xC0, # End Collection
]))
def consumer_hid_descriptor(report_id):
data = HID_DEVICE_DATA["CONSUMER"]
return hid.ReportDescriptor(
description="CONSUMER",
report_descriptor=bytes(
# Consumer ("multimedia") keys
(0x05, data.usage_page, # Usage Page (Consumer)
0x09, data.usage, # Usage (Consumer Control)
0xA1, 0x01, # Collection (Application)
) +
((0x85, report_id) if report_id != 0 else ()) +
(0x75, 0x10, # Report Size (16)
0x95, 0x01, # Report Count (1)
0x15, 0x01, # Logical Minimum (1)
0x26, 0x8C, 0x02, # Logical Maximum (652)
0x19, 0x01, # Usage Minimum (Consumer Control)
0x2A, 0x8C, 0x02, # Usage Maximum (AC Send)
0x81, 0x00, # Input (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
0xC0, # End Collection
)))
CONSUMER_WITH_ID = hid.ReportDescriptor(
description="CONSUMER",
report_descriptor=bytes([
# Consumer ("multimedia") keys
0x05, 0x0C, # Usage Page (Consumer)
0x09, 0x01, # Usage (Consumer Control)
0xA1, 0x01, # Collection (Application)
0x85, REPORT_IDS["CONSUMER"], # Report ID (n)
0x75, 0x10, # Report Size (16)
0x95, 0x01, # Report Count (1)
0x15, 0x01, # Logical Minimum (1)
0x26, 0x8C, 0x02, # Logical Maximum (652)
0x19, 0x01, # Usage Minimum (Consumer Control)
0x2A, 0x8C, 0x02, # Usage Maximum (AC Send)
0x81, 0x00, # Input (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
0xC0, # End Collection
]))
def sys_control_hid_descriptor(report_id):
data = HID_DEVICE_DATA["SYS_CONTROL"]
return hid.ReportDescriptor(
description="SYS_CONTROL",
report_descriptor=bytes(
# Power controls
(0x05, data.usage_page, # Usage Page (Generic Desktop Ctrls)
0x09, data.usage, # Usage (Sys Control)
0xA1, 0x01, # Collection (Application)
) +
((0x85, report_id) if report_id != 0 else ()) +
(0x75, 0x02, # Report Size (2)
0x95, 0x01, # Report Count (1)
0x15, 0x01, # Logical Minimum (1)
0x25, 0x03, # Logical Maximum (3)
0x09, 0x82, # Usage (Sys Sleep)
0x09, 0x81, # Usage (Sys Power Down)
0x09, 0x83, # Usage (Sys Wake Up)
0x81, 0x60, # Input (Data,Array,Abs,No Wrap,Linear,No Preferred State,Null State)
0x75, 0x06, # Report Size (6)
0x81, 0x03, # Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0xC0, # End Collection
)))
SYS_CONTROL_WITH_ID = hid.ReportDescriptor(
description="SYS_CONTROL",
report_descriptor=bytes([
# Power controls
0x05, 0x01, # Usage Page (Generic Desktop Ctrls)
0x09, 0x80, # Usage (Sys Control)
0xA1, 0x01, # Collection (Application)
0x85, REPORT_IDS["SYS_CONTROL"], # Report ID (n)
0x75, 0x02, # Report Size (2)
0x95, 0x01, # Report Count (1)
0x15, 0x01, # Logical Minimum (1)
0x25, 0x03, # Logical Maximum (3)
0x09, 0x82, # Usage (Sys Sleep)
0x09, 0x81, # Usage (Sys Power Down)
0x09, 0x83, # Usage (Sys Wake Up)
0x81, 0x60, # Input (Data,Array,Abs,No Wrap,Linear,No Preferred State,Null State)
0x75, 0x06, # Report Size (6)
0x81, 0x03, # Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0xC0, # End Collection
]))
def gamepad_hid_descriptor(report_id):
data = HID_DEVICE_DATA["GAMEPAD"]
return hid.ReportDescriptor(
description="GAMEPAD",
report_descriptor=bytes(
# Gamepad with 16 buttons and two joysticks
(0x05, data.usage_page, # Usage Page (Generic Desktop Ctrls)
0x09, data.usage, # Usage (Game Pad)
0xA1, 0x01, # Collection (Application)
) +
((0x85, report_id) if report_id != 0 else ()) +
(0x05, 0x09, # Usage Page (Button)
0x19, 0x01, # Usage Minimum (Button 1)
0x29, 0x10, # Usage Maximum (Button 16)
0x15, 0x00, # Logical Minimum (0)
0x25, 0x01, # Logical Maximum (1)
0x75, 0x01, # Report Size (1)
0x95, 0x10, # Report Count (16)
0x81, 0x02, # Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x05, 0x01, # Usage Page (Generic Desktop Ctrls)
0x15, 0x81, # Logical Minimum (-127)
0x25, 0x7F, # Logical Maximum (127)
0x09, 0x30, # Usage (X)
0x09, 0x31, # Usage (Y)
0x09, 0x32, # Usage (Z)
0x09, 0x35, # Usage (Rz)
0x75, 0x08, # Report Size (8)
0x95, 0x04, # Report Count (4)
0x81, 0x02, # Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0xC0, # End Collection
)))
GAMEPAD_WITH_ID = hid.ReportDescriptor(
description="GAMEPAD",
report_descriptor=bytes([
# Gamepad with 16 buttons and two joysticks
0x05, 0x01, # Usage Page (Generic Desktop Ctrls)
0x09, 0x05, # Usage (Game Pad)
0xA1, 0x01, # Collection (Application)
0x85, REPORT_IDS["GAMEPAD"], # Report ID (n)
0x05, 0x09, # Usage Page (Button)
0x19, 0x01, # Usage Minimum (Button 1)
0x29, 0x10, # Usage Maximum (Button 16)
0x15, 0x00, # Logical Minimum (0)
0x25, 0x01, # Logical Maximum (1)
0x75, 0x01, # Report Size (1)
0x95, 0x10, # Report Count (16)
0x81, 0x02, # Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x05, 0x01, # Usage Page (Generic Desktop Ctrls)
0x15, 0x81, # Logical Minimum (-127)
0x25, 0x7F, # Logical Maximum (127)
0x09, 0x30, # Usage (X)
0x09, 0x31, # Usage (Y)
0x09, 0x32, # Usage (Z)
0x09, 0x35, # Usage (Rz)
0x75, 0x08, # Report Size (8)
0x95, 0x04, # Report Count (4)
0x81, 0x02, # Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0xC0, # End Collection
]))
def digitizer_hid_descriptor(report_id):
data = HID_DEVICE_DATA["DIGITIZER"]
return hid.ReportDescriptor(
description="DIGITIZER",
report_descriptor=bytes(
# Digitizer (used as an absolute pointer)
(0x05, data.usage_page, # Usage Page (Digitizers)
0x09, data.usage, # Usage (Pen)
0xA1, 0x01, # Collection (Application)
) +
((0x85, report_id) if report_id != 0 else ()) +
(0x09, 0x01, # Usage (Stylus)
0xA1, 0x00, # Collection (Physical)
0x09, 0x32, # Usage (In-Range)
0x09, 0x42, # Usage (Tip Switch)
0x09, 0x44, # Usage (Barrel Switch)
0x09, 0x45, # Usage (Eraser Switch)
0x15, 0x00, # Logical Minimum (0)
0x25, 0x01, # Logical Maximum (1)
0x75, 0x01, # Report Size (1)
0x95, 0x04, # Report Count (4)
0x81, 0x02, # Input (Data,Var,Abs)
0x75, 0x04, # Report Size (4) -- Filler
0x95, 0x01, # Report Count (1) -- Filler
0x81, 0x01, # Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x05, 0x01, # Usage Page (Generic Desktop Ctrls)
0x15, 0x00, # Logical Minimum (0)
0x26, 0xff, 0x7f, # Logical Maximum (32767)
0x09, 0x30, # Usage (X)
0x09, 0x31, # Usage (Y)
0x75, 0x10, # Report Size (16)
0x95, 0x02, # Report Count (2)
0x81, 0x02, # Input (Data,Var,Abs)
0xC0, # End Collection
0xC0, # End Collection
)))
DIGITIZER_WITH_ID = hid.ReportDescriptor(
description="DIGITIZER",
report_descriptor=bytes([
# Digitizer (used as an absolute pointer)
0x05, 0x0D, # Usage Page (Digitizers)
0x09, 0x02, # Usage (Pen)
0xA1, 0x01, # Collection (Application)
0x85, REPORT_IDS["DIGITIZER"], # Report ID (n)
0x09, 0x01, # Usage (Stylus)
0xA1, 0x00, # Collection (Physical)
0x09, 0x32, # Usage (In-Range)
0x09, 0x42, # Usage (Tip Switch)
0x09, 0x44, # Usage (Barrel Switch)
0x09, 0x45, # Usage (Eraser Switch)
0x15, 0x00, # Logical Minimum (0)
0x25, 0x01, # Logical Maximum (1)
0x75, 0x01, # Report Size (1)
0x95, 0x04, # Report Count (4)
0x81, 0x02, # Input (Data,Var,Abs)
0x75, 0x04, # Report Size (4) -- Filler
0x95, 0x01, # Report Count (1) -- Filler
0x81, 0x01, # Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x05, 0x01, # Usage Page (Generic Desktop Ctrls)
0x15, 0x00, # Logical Minimum (0)
0x26, 0xff, 0x7f, # Logical Maximum (32767)
0x09, 0x30, # Usage (X)
0x09, 0x31, # Usage (Y)
0x75, 0x10, # Report Size (16)
0x95, 0x02, # Report Count (2)
0x81, 0x02, # Input (Data,Var,Abs)
0xC0, # End Collection
0xC0, # End Collection
]))
def xac_compatible_gamepad_hid_descriptor(report_id):
data = HID_DEVICE_DATA["XAC_COMPATIBLE_GAMEPAD"]
return hid.ReportDescriptor(
description="XAC",
report_descriptor=bytes(
# This descriptor mimics the simple joystick from PDP that the XBox likes
(0x05, data.usage_page, # Usage Page (Desktop),
0x09, data.usage, # Usage (Gamepad),
0xA1, 0x01, # Collection (Application),
) +
((0x85, report_id) if report_id != 0 else ()) +
(0x15, 0x00, # Logical Minimum (0),
0x25, 0x01, # Logical Maximum (1),
0x35, 0x00, # Physical Minimum (0),
0x45, 0x01, # Physical Maximum (1),
0x75, 0x01, # Report Size (1),
0x95, 0x08, # Report Count (8),
0x05, 0x09, # Usage Page (Button),
0x19, 0x01, # Usage Minimum (01h),
0x29, 0x08, # Usage Maximum (08h),
0x81, 0x02, # Input (Variable),
0x05, 0x01, # Usage Page (Desktop),
0x26, 0xFF, 0x00, # Logical Maximum (255),
0x46, 0xFF, 0x00, # Physical Maximum (255),
0x09, 0x30, # Usage (X),
0x09, 0x31, # Usage (Y),
0x75, 0x08, # Report Size (8),
0x95, 0x02, # Report Count (2),
0x81, 0x02, # Input (Variable),
0xC0 # End Collection
)))
XAC_COMPATIBLE_GAMEPAD_WITH_ID = hid.ReportDescriptor(
description="XAC",
report_descriptor=bytes([
# This descriptor mimics the simple joystick from PDP that the XBox likes
0x05, 0x01, # Usage Page (Desktop),
0x09, 0x05, # Usage (Gamepad),
0xA1, 0x01, # Collection (Application),
0x85, REPORT_IDS["XAC_COMPATIBLE_GAMEPAD"], # Report ID (n)
0x15, 0x00, # Logical Minimum (0),
0x25, 0x01, # Logical Maximum (1),
0x35, 0x00, # Physical Minimum (0),
0x45, 0x01, # Physical Maximum (1),
0x75, 0x01, # Report Size (1),
0x95, 0x08, # Report Count (8),
0x05, 0x09, # Usage Page (Button),
0x19, 0x01, # Usage Minimum (01h),
0x29, 0x08, # Usage Maximum (08h),
0x81, 0x02, # Input (Variable),
0x05, 0x01, # Usage Page (Desktop),
0x26, 0xFF, 0x00, # Logical Maximum (255),
0x46, 0xFF, 0x00, # Physical Maximum (255),
0x09, 0x30, # Usage (X),
0x09, 0x31, # Usage (Y),
0x75, 0x08, # Report Size (8),
0x95, 0x02, # Report Count (2),
0x81, 0x02, # Input (Variable),
0xC0 # End Collection
]))
def raw_hid_descriptor(report_id):
if report_id != 0:
raise ValueError("raw hid must not have a report id")
data = HID_DEVICE_DATA["RAW"]
return hid.ReportDescriptor(
description="RAW",
report_descriptor=bytes(
# Provide vendor-defined
# This is a two-byte page value.
(0x06, data.usage_page & 0xff, (data.usage_page >> 8) & 0xff, # Usage Page (Vendor 0xFFAF "Adafruit"),
0x09, data.usage, # Usage (AF),
0xA1, 0x01, # Collection (Application),
0x75, 0x08, # Report Size (8),
0x15, 0x00, # Logical Minimum (0),
0x26, 0xFF, 0x00, # Logical Maximum (255),
0x95, 0x08, # Report Count (8),
0x09, 0x01, # Usage(xxx)
0x81, 0x02, # Input (Variable)
0x95, 0x08, # Report Count (8),
0x09, 0x02, # Usage(xxx)
0x91, 0x02, # Input (Variable)
0xC0 # End Collection
)))
RAW = hid.ReportDescriptor(
description="RAW",
report_descriptor=bytes([
# Provide vendor-defined
0x06, 0xAF, 0xFF, # Usage Page (Vendor 0xFFAF "Adafruit"),
0x09, 0xAF, # Usage (AF),
0xA1, 0x01, # Collection (Application),
0x75, 0x08, # Report Size (8),
0x15, 0x00, # Logical Minimum (0),
0x26, 0xFF, 0x00 # Logical Maximum (255),
0x95, 0x08, # Report Count (8),
0x09, 0x01 # Usage(xxx)
0x81, 0x02 # Input (Variable)
0x95, 0x08, # Report Count (8),
0x09, 0x02, # Usage(xxx)
0x91, 0x02, # Input (Variable)
0xC0 # End Collection
]))
# Byte count for each kind of report. Length does not include report ID in first byte.
REPORT_DESCRIPTORS = {
"KEYBOARD" : KEYBOARD_WITH_ID,
"MOUSE" : MOUSE_WITH_ID,
"CONSUMER" : CONSUMER_WITH_ID,
"SYS_CONTROL" : SYS_CONTROL_WITH_ID,
"GAMEPAD" : GAMEPAD_WITH_ID,
"DIGITIZER" : DIGITIZER_WITH_ID,
"XAC_COMPATIBLE_GAMEPAD" : XAC_COMPATIBLE_GAMEPAD_WITH_ID,
"RAW" : RAW_WITH_ID,
}
# Function to call for each kind of HID descriptor.
REPORT_DESCRIPTOR_FUNCTIONS = {
"KEYBOARD" : keyboard_hid_descriptor,
"MOUSE" : mouse_hid_descriptor,
"CONSUMER" : consumer_hid_descriptor,
"SYS_CONTROL" : sys_control_hid_descriptor,
"GAMEPAD" : gamepad_hid_descriptor,
"DIGITIZER" : digitizer_hid_descriptor,
"XAC_COMPATIBLE_GAMEPAD" : xac_compatible_gamepad_hid_descriptor,
"RAW" : raw_hid_descriptor,
}