summaryrefslogtreecommitdiff
path: root/tools
diff options
context:
space:
mode:
authorDan Halbert <halbert@adafruit.com>2019-09-05 13:25:03 -0400
committerGitHub <noreply@github.com>2019-09-05 13:25:03 -0400
commit296e8589b0bedca0f01db8d5002ed9f85cb04c4f (patch)
tree7656cc49325cb37f878fcdd77faf142b76b739dd /tools
parent28e17feb93a097bbbf8fa033be088ba009793280 (diff)
parentf3af2a6fb7a9b67dc81ab471f30e212453af4627 (diff)
Merge pull request #2116 from dhalbert/choose-usb-devices-xac
Choose which USB and USB HID devices to include at compile-time
Diffstat (limited to 'tools')
-rw-r--r--tools/gen_usb_descriptor.py181
-rw-r--r--tools/hid_report_descriptors.py467
2 files changed, 397 insertions, 251 deletions
diff --git a/tools/gen_usb_descriptor.py b/tools/gen_usb_descriptor.py
index 10bbf5066..87219194b 100644
--- a/tools/gen_usb_descriptor.py
+++ b/tools/gen_usb_descriptor.py
@@ -8,6 +8,15 @@ sys.path.append("../../tools/usb_descriptor")
from adafruit_usb_descriptor import audio, audio10, cdc, hid, midi, msc, standard, util
import hid_report_descriptors
+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,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'
+
parser = argparse.ArgumentParser(description='Generate USB descriptors.')
parser.add_argument('--manufacturer', type=str,
help='manufacturer of the device')
@@ -19,11 +28,29 @@ 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: tuple(l.split(',')), default=DEFAULT_DEVICES,
+ help='devices to include in descriptor (AUDIO includes MIDI support)')
+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('--msc_num_endpoint_pairs', type=int, default=1,
+ help='Use 1 or 2 endpoint pairs for MSC (1 bidirectional, or 1 input + 1 output (required by SAMD21))')
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(frozenset(args.devices) - ALL_DEVICES_SET)
+if unknown_devices:
+ raise ValueError("Unknown device(s)", unknown_devices)
+
+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)
+
+if args.msc_num_endpoint_pairs not in (1, 2):
+ raise ValueError("--msc_num_endpoint_pairs must be 1 or 2")
+
+
class StringIndex:
"""Assign a monotonically increasing index to each unique string. Start with 0."""
string_to_index = {}
@@ -131,25 +158,36 @@ msc_interfaces = [
bInterval=0),
standard.EndpointDescriptor(
description="MSC out",
- bEndpointAddress=0x1 | standard.EndpointDescriptor.DIRECTION_OUT,
+ # SAMD21 needs to use a separate pair of endpoints for MSC.
+ bEndpointAddress=((0x1 if args.msc_num_endpoint_pairs == 2 else 0x0) |
+ standard.EndpointDescriptor.DIRECTION_OUT),
bmAttributes=standard.EndpointDescriptor.TYPE_BULK,
bInterval=0)
]
)
]
-# Include only these HID devices.
-# DIGITIZER works on Linux but conflicts with MOUSE, so leave it out for now.
-hid_devices = ("KEYBOARD", "MOUSE", "CONSUMER", "GAMEPAD")
-
-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())
+# 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.
+
+if len(args.hid_devices) == 1:
+ name = args.hid_devices[0]
+ combined_hid_report_descriptor = hid.ReportDescriptor(
+ description=name,
+ 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=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.
@@ -271,19 +309,27 @@ cdc_iad = standard.InterfaceAssociationDescriptor(
bFunctionProtocol=cdc.CDC_PROTOCOL_NONE)
descriptor_list = []
-descriptor_list.append(cdc_iad)
-descriptor_list.extend(cdc_interfaces)
-descriptor_list.extend(msc_interfaces)
-# Only add the control interface because other audio interfaces are managed by it to ensure the
-# correct ordering.
-descriptor_list.append(audio_control_interface)
-# Put the CDC IAD just before the CDC interfaces.
-# There appears to be a bug in the Windows composite USB driver that requests the
-# HID report descriptor with the wrong interface number if the HID interface is not given
-# first. However, it still fetches the descriptor anyway. We could reorder the interfaces but
-# the Windows 7 Adafruit_usbser.inf file thinks CDC is at Interface 0, so we'll leave it
-# there for backwards compatibility.
-descriptor_list.extend(hid_interfaces)
+
+if 'CDC' in args.devices:
+ # Put the CDC IAD just before the CDC interfaces.
+ # There appears to be a bug in the Windows composite USB driver that requests the
+ # HID report descriptor with the wrong interface number if the HID interface is not given
+ # first. However, it still fetches the descriptor anyway. We could reorder the interfaces but
+ # the Windows 7 Adafruit_usbser.inf file thinks CDC is at Interface 0, so we'll leave it
+ # there for backwards compatibility.
+ descriptor_list.append(cdc_iad)
+ descriptor_list.extend(cdc_interfaces)
+
+if 'MSC' in args.devices:
+ descriptor_list.extend(msc_interfaces)
+
+if 'AUDIO' in args.devices:
+ # Only add the control interface because other audio interfaces are managed by it to ensure the
+ # correct ordering.
+ descriptor_list.append(audio_control_interface)
+
+if 'HID' in args.devices:
+ descriptor_list.extend(hid_interfaces)
configuration = standard.ConfigurationDescriptor(
description="Composite configuration",
@@ -302,6 +348,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))
@@ -420,7 +468,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}"
@@ -434,36 +484,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}] = {{
@@ -471,7 +496,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("""\
+ },
};
""")
diff --git a/tools/hid_report_descriptors.py b/tools/hid_report_descriptors.py
index f3b28ebcf..92c962d59 100644
--- a/tools/hid_report_descriptors.py
+++ b/tools/hid_report_descriptors.py
@@ -29,211 +29,284 @@ HID specific descriptors
* Author(s): Dan Halbert
"""
+from collections import namedtuple
+
from adafruit_usb_descriptor import hid
-REPORT_IDS = {
- "KEYBOARD" : 1,
- "MOUSE" : 2,
- "CONSUMER" : 3,
- "SYS_CONTROL" : 4,
- "GAMEPAD" : 5,
- "DIGITIZER" : 6,
+# 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,
- }
+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
+ )))
-# 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,
- }
+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
+ )))
+
+# 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,
+}