Merge pull request #2116 from dhalbert/choose-usb-devices-xac

Choose which USB and USB HID devices to include at compile-time
This commit is contained in:
Dan Halbert 2019-09-05 13:25:03 -04:00 committed by GitHub
commit 296e8589b0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 417 additions and 381 deletions

View File

@ -20,6 +20,9 @@ ifeq ($(CHIP_FAMILY),samd21)
CIRCUITPY_AUDIOMIXER = 0 CIRCUITPY_AUDIOMIXER = 0
CIRCUITPY_FREQUENCYIO = 0 CIRCUITPY_FREQUENCYIO = 0
CIRCUITPY_TOUCHIO_USE_NATIVE = 1 CIRCUITPY_TOUCHIO_USE_NATIVE = 1
# SAMD21 needs separate endpoint pairs for MSC BULK IN and BULK OUT, otherwise it's erratic.
USB_MSC_NUM_ENDPOINT_PAIRS = 2
endif endif
# Put samd51-only choices here. # Put samd51-only choices here.

View File

@ -48,9 +48,7 @@ void common_hal_usb_hid_device_send_report(usb_hid_device_obj_t *self, uint8_t*
// Wait until interface is ready, timeout = 2 seconds // Wait until interface is ready, timeout = 2 seconds
uint64_t end_ticks = ticks_ms + 2000; uint64_t end_ticks = ticks_ms + 2000;
while ( (ticks_ms < end_ticks) && !tud_hid_ready() ) { while ( (ticks_ms < end_ticks) && !tud_hid_ready() ) {
#ifdef MICROPY_VM_HOOK_LOOP RUN_BACKGROUND_TASKS;
MICROPY_VM_HOOK_LOOP;
#endif
} }
if ( !tud_hid_ready() ) { if ( !tud_hid_ready() ) {

View File

@ -24,131 +24,4 @@
* THE SOFTWARE. * THE SOFTWARE.
*/ */
#include "py/obj.h" // Nothing needed here. Tables of HID devices are generated in autogen_usb_descriptor.c at compile-time.
#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_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_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
}
};

View File

@ -86,6 +86,19 @@ else
CFLAGS += -DUSB_AVAILABLE CFLAGS += -DUSB_AVAILABLE
endif endif
ifndef USB_DEVICES
USB_DEVICES = "CDC,MSC,AUDIO,HID"
endif
ifndef USB_HID_DEVICES
USB_HID_DEVICES = "KEYBOARD,MOUSE,CONSUMER,GAMEPAD"
endif
# SAMD21 needs separate endpoint pairs for MSC BULK IN and BULK OUT, otherwise it's erratic.
ifndef USB_MSC_NUM_ENDPOINT_PAIRS
USB_MSC_NUM_ENDPOINT_PAIRS = 1
endif
SUPERVISOR_O = $(addprefix $(BUILD)/, $(SRC_SUPERVISOR:.c=.o)) $(BUILD)/autogen_display_resources.o SUPERVISOR_O = $(addprefix $(BUILD)/, $(SRC_SUPERVISOR:.c=.o)) $(BUILD)/autogen_display_resources.o
$(BUILD)/supervisor/shared/translate.o: $(HEADER_BUILD)/qstrdefs.generated.h $(BUILD)/supervisor/shared/translate.o: $(HEADER_BUILD)/qstrdefs.generated.h
@ -103,6 +116,9 @@ autogen_usb_descriptor.intermediate: ../../tools/gen_usb_descriptor.py Makefile
--vid $(USB_VID)\ --vid $(USB_VID)\
--pid $(USB_PID)\ --pid $(USB_PID)\
--serial_number_length $(USB_SERIAL_NUMBER_LENGTH)\ --serial_number_length $(USB_SERIAL_NUMBER_LENGTH)\
--devices $(USB_DEVICES)\
--hid_devices $(USB_HID_DEVICES)\
--msc_num_endpoint_pairs $(USB_MSC_NUM_ENDPOINT_PAIRS)\
--output_c_file $(BUILD)/autogen_usb_descriptor.c\ --output_c_file $(BUILD)/autogen_usb_descriptor.c\
--output_h_file $(BUILD)/genhdr/autogen_usb_descriptor.h --output_h_file $(BUILD)/genhdr/autogen_usb_descriptor.h

View File

@ -8,6 +8,15 @@ sys.path.append("../../tools/usb_descriptor")
from adafruit_usb_descriptor import audio, audio10, cdc, hid, midi, msc, standard, util from adafruit_usb_descriptor import audio, audio10, cdc, hid, midi, msc, standard, util
import hid_report_descriptors 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 = argparse.ArgumentParser(description='Generate USB descriptors.')
parser.add_argument('--manufacturer', type=str, parser.add_argument('--manufacturer', type=str,
help='manufacturer of the device') help='manufacturer of the device')
@ -19,11 +28,29 @@ parser.add_argument('--pid', type=lambda x: int(x, 16),
help='product id') help='product id')
parser.add_argument('--serial_number_length', type=int, default=32, parser.add_argument('--serial_number_length', type=int, default=32,
help='length needed for the serial number in digits') 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_c_file', type=argparse.FileType('w'), required=True)
parser.add_argument('--output_h_file', type=argparse.FileType('w'), required=True) parser.add_argument('--output_h_file', type=argparse.FileType('w'), required=True)
args = parser.parse_args() 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: class StringIndex:
"""Assign a monotonically increasing index to each unique string. Start with 0.""" """Assign a monotonically increasing index to each unique string. Start with 0."""
string_to_index = {} string_to_index = {}
@ -131,25 +158,36 @@ msc_interfaces = [
bInterval=0), bInterval=0),
standard.EndpointDescriptor( standard.EndpointDescriptor(
description="MSC out", 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, bmAttributes=standard.EndpointDescriptor.TYPE_BULK,
bInterval=0) bInterval=0)
] ]
) )
] ]
# Include only these HID devices. # When there's only one hid_device, it shouldn't have a report id.
# DIGITIZER works on Linux but conflicts with MOUSE, so leave it out for now. # Otherwise, report ids are assigned sequentially:
hid_devices = ("KEYBOARD", "MOUSE", "CONSUMER", "GAMEPAD") # args.hid_devices[0] has report_id 1
# args.hid_devices[1] has report_id 2
# etc.
combined_hid_report_descriptor = hid.ReportDescriptor( if len(args.hid_devices) == 1:
description="MULTIDEVICE", name = args.hid_devices[0]
report_descriptor=b''.join( combined_hid_report_descriptor = hid.ReportDescriptor(
hid_report_descriptors.REPORT_DESCRIPTORS[name].report_descriptor for name in hid_devices )) description=name,
report_descriptor=bytes(hid_report_descriptors.REPORT_DESCRIPTOR_FUNCTIONS[name](0)))
hid_report_ids_dict = { name: hid_report_descriptors.REPORT_IDS[name] for name in hid_devices } else:
hid_report_lengths_dict = { name: hid_report_descriptors.REPORT_LENGTHS[name] for name in hid_devices } report_id = 1
hid_max_report_length = max(hid_report_lengths_dict.values()) 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, # ASF4 expects keyboard and generic devices to have both in and out endpoints,
# and will fail (possibly silently) if both are not supplied. # and will fail (possibly silently) if both are not supplied.
@ -271,19 +309,27 @@ cdc_iad = standard.InterfaceAssociationDescriptor(
bFunctionProtocol=cdc.CDC_PROTOCOL_NONE) bFunctionProtocol=cdc.CDC_PROTOCOL_NONE)
descriptor_list = [] descriptor_list = []
descriptor_list.append(cdc_iad)
descriptor_list.extend(cdc_interfaces) if 'CDC' in args.devices:
descriptor_list.extend(msc_interfaces) # Put the CDC IAD just before the CDC interfaces.
# Only add the control interface because other audio interfaces are managed by it to ensure the # There appears to be a bug in the Windows composite USB driver that requests the
# correct ordering. # HID report descriptor with the wrong interface number if the HID interface is not given
descriptor_list.append(audio_control_interface) # first. However, it still fetches the descriptor anyway. We could reorder the interfaces but
# Put the CDC IAD just before the CDC interfaces. # the Windows 7 Adafruit_usbser.inf file thinks CDC is at Interface 0, so we'll leave it
# There appears to be a bug in the Windows composite USB driver that requests the # there for backwards compatibility.
# HID report descriptor with the wrong interface number if the HID interface is not given descriptor_list.append(cdc_iad)
# first. However, it still fetches the descriptor anyway. We could reorder the interfaces but descriptor_list.extend(cdc_interfaces)
# the Windows 7 Adafruit_usbser.inf file thinks CDC is at Interface 0, so we'll leave it
# there for backwards compatibility. if 'MSC' in args.devices:
descriptor_list.extend(hid_interfaces) 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( configuration = standard.ConfigurationDescriptor(
description="Composite configuration", description="Composite configuration",
@ -302,6 +348,8 @@ h_file = args.output_h_file
c_file.write("""\ c_file.write("""\
#include <stdint.h> #include <stdint.h>
#include "py/objtuple.h"
#include "shared-bindings/usb_hid/Device.h"
#include "{H_FILE_NAME}" #include "{H_FILE_NAME}"
""".format(H_FILE_NAME=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 usb_serial_number[{serial_number_length}];
uint16_t const * const string_desc_arr [{string_descriptor_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 // Vendor name included in Inquiry response, max 8 bytes
#define CFG_TUD_MSC_VENDOR "{msc_vendor}" #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, configuration_length=descriptor_length,
max_configuration_length=max(hid_descriptor_length, descriptor_length), max_configuration_length=max(hid_descriptor_length, descriptor_length),
string_descriptor_length=len(pointers_to_strings), 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_vendor=args.manufacturer[:8],
msc_product=args.product[:16])) 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 # Write out the report descriptor and info
c_file.write("""\ c_file.write("""\
const uint8_t hid_report_descriptor[{HID_DESCRIPTOR_LENGTH}] = {{ 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): for b in bytes(combined_hid_report_descriptor):
c_file.write("0x{:02x}, ".format(b)) 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(""" 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,211 +29,284 @@ HID specific descriptors
* Author(s): Dan Halbert * Author(s): Dan Halbert
""" """
from collections import namedtuple
from adafruit_usb_descriptor import hid from adafruit_usb_descriptor import hid
REPORT_IDS = { # Information about each kind of device
"KEYBOARD" : 1, # report_length does not include report ID in first byte, if present when sent.
"MOUSE" : 2, DeviceData = namedtuple('DeviceData', ('report_length', 'usage_page', 'usage'))
"CONSUMER" : 3, HID_DEVICE_DATA = {
"SYS_CONTROL" : 4, "KEYBOARD" : DeviceData(report_length=8, usage_page=0x01, usage=0x06), # Generic Desktop, Keyboard
"GAMEPAD" : 5, "MOUSE" : DeviceData(report_length=4, usage_page=0x01, usage=0x02), # Generic Desktop, Mouse
"DIGITIZER" : 6, "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. def keyboard_hid_descriptor(report_id):
REPORT_LENGTHS = { data = HID_DEVICE_DATA["KEYBOARD"]
"KEYBOARD" : 8, return hid.ReportDescriptor(
"MOUSE" : 4, description="KEYBOARD",
"CONSUMER" : 2, report_descriptor=bytes(
"SYS_CONTROL" : 1, # Regular keyboard
"GAMEPAD" : 6, (0x05, data.usage_page, # Usage Page (Generic Desktop)
"DIGITIZER" : 5, 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( def mouse_hid_descriptor(report_id):
description="KEYBOARD", data = HID_DEVICE_DATA["MOUSE"]
report_descriptor=bytes([ return hid.ReportDescriptor(
# Regular keyboard description="MOUSE",
0x05, 0x01, # Usage Page (Generic Desktop) report_descriptor=bytes(
0x09, 0x06, # Usage (Keyboard) # Regular mouse
0xA1, 0x01, # Collection (Application) (0x05, data.usage_page, # Usage Page (Generic Desktop)
0x85, REPORT_IDS["KEYBOARD"], # Report ID (1) 0x09, data.usage, # Usage (Mouse)
0x05, 0x07, # Usage Page (Keyboard) 0xA1, 0x01, # Collection (Application)
0x19, 224, # Usage Minimum (224) 0x09, 0x01, # Usage (Pointer)
0x29, 231, # Usage Maximum (231) 0xA1, 0x00, # Collection (Physical)
0x15, 0x00, # Logical Minimum (0) ) +
0x25, 0x01, # Logical Maximum (1) ((0x85, report_id) if report_id != 0 else ()) +
0x75, 0x01, # Report Size (1) (0x05, 0x09, # Usage Page (Button)
0x95, 0x08, # Report Count (8) 0x19, 0x01, # Usage Minimum (0x01)
0x81, 0x02, # Input (Data, Variable, Absolute) 0x29, 0x05, # Usage Maximum (0x05)
0x81, 0x01, # Input (Constant) 0x15, 0x00, # Logical Minimum (0)
0x19, 0x00, # Usage Minimum (0) 0x25, 0x01, # Logical Maximum (1)
0x29, 101, # Usage Maximum (101) 0x95, 0x05, # Report Count (5)
0x15, 0x00, # Logical Minimum (0) 0x75, 0x01, # Report Size (1)
0x25, 101, # Logical Maximum (101) 0x81, 0x02, # Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x75, 0x08, # Report Size (8) 0x95, 0x01, # Report Count (1)
0x95, 0x06, # Report Count (6) 0x75, 0x03, # Report Size (3)
0x81, 0x00, # Input (Data, Array) 0x81, 0x01, # Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x05, 0x08, # Usage Page (LED) 0x05, 0x01, # Usage Page (Generic Desktop Ctrls)
0x19, 0x01, # Usage Minimum (1) 0x09, 0x30, # Usage (X)
0x29, 0x05, # Usage Maximum (5) 0x09, 0x31, # Usage (Y)
0x15, 0x00, # Logical Minimum (0) 0x15, 0x81, # Logical Minimum (-127)
0x25, 0x01, # Logical Maximum (1) 0x25, 0x7F, # Logical Maximum (127)
0x75, 0x01, # Report Size (1) 0x75, 0x08, # Report Size (8)
0x95, 0x05, # Report Count (5) 0x95, 0x02, # Report Count (2)
0x91, 0x02, # Output (Data, Variable, Absolute) 0x81, 0x06, # Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position)
0x95, 0x03, # Report Count (3) 0x09, 0x38, # Usage (Wheel)
0x91, 0x01, # Output (Constant) 0x15, 0x81, # Logical Minimum (-127)
0xC0, # End Collection 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( def consumer_hid_descriptor(report_id):
description="MOUSE", data = HID_DEVICE_DATA["CONSUMER"]
report_descriptor=bytes([ return hid.ReportDescriptor(
# Regular mouse description="CONSUMER",
0x05, 0x01, # Usage Page (Generic Desktop) report_descriptor=bytes(
0x09, 0x02, # Usage (Mouse) # Consumer ("multimedia") keys
0xA1, 0x01, # Collection (Application) (0x05, data.usage_page, # Usage Page (Consumer)
0x09, 0x01, # Usage (Pointer) 0x09, data.usage, # Usage (Consumer Control)
0xA1, 0x00, # Collection (Physical) 0xA1, 0x01, # Collection (Application)
0x85, REPORT_IDS["MOUSE"], # Report ID (n) ) +
0x05, 0x09, # Usage Page (Button) ((0x85, report_id) if report_id != 0 else ()) +
0x19, 0x01, # Usage Minimum (0x01) (0x75, 0x10, # Report Size (16)
0x29, 0x05, # Usage Maximum (0x05) 0x95, 0x01, # Report Count (1)
0x15, 0x00, # Logical Minimum (0) 0x15, 0x01, # Logical Minimum (1)
0x25, 0x01, # Logical Maximum (1) 0x26, 0x8C, 0x02, # Logical Maximum (652)
0x95, 0x05, # Report Count (5) 0x19, 0x01, # Usage Minimum (Consumer Control)
0x75, 0x01, # Report Size (1) 0x2A, 0x8C, 0x02, # Usage Maximum (AC Send)
0x81, 0x02, # Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) 0x81, 0x00, # Input (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x95, 0x01, # Report Count (1) 0xC0, # End Collection
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
]))
CONSUMER_WITH_ID = hid.ReportDescriptor( def sys_control_hid_descriptor(report_id):
description="CONSUMER", data = HID_DEVICE_DATA["SYS_CONTROL"]
report_descriptor=bytes([ return hid.ReportDescriptor(
# Consumer ("multimedia") keys description="SYS_CONTROL",
0x05, 0x0C, # Usage Page (Consumer) report_descriptor=bytes(
0x09, 0x01, # Usage (Consumer Control) # Power controls
0xA1, 0x01, # Collection (Application) (0x05, data.usage_page, # Usage Page (Generic Desktop Ctrls)
0x85, REPORT_IDS["CONSUMER"], # Report ID (n) 0x09, data.usage, # Usage (Sys Control)
0x75, 0x10, # Report Size (16) 0xA1, 0x01, # Collection (Application)
0x95, 0x01, # Report Count (1) ) +
0x15, 0x01, # Logical Minimum (1) ((0x85, report_id) if report_id != 0 else ()) +
0x26, 0x8C, 0x02, # Logical Maximum (652) (0x75, 0x02, # Report Size (2)
0x19, 0x01, # Usage Minimum (Consumer Control) 0x95, 0x01, # Report Count (1)
0x2A, 0x8C, 0x02, # Usage Maximum (AC Send) 0x15, 0x01, # Logical Minimum (1)
0x81, 0x00, # Input (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position) 0x25, 0x03, # Logical Maximum (3)
0xC0, # End Collection 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( def gamepad_hid_descriptor(report_id):
description="SYS_CONTROL", data = HID_DEVICE_DATA["GAMEPAD"]
report_descriptor=bytes([ return hid.ReportDescriptor(
# Power controls description="GAMEPAD",
0x05, 0x01, # Usage Page (Generic Desktop Ctrls) report_descriptor=bytes(
0x09, 0x80, # Usage (Sys Control) # Gamepad with 16 buttons and two joysticks
0xA1, 0x01, # Collection (Application) (0x05, data.usage_page, # Usage Page (Generic Desktop Ctrls)
0x85, REPORT_IDS["SYS_CONTROL"], # Report ID (n) 0x09, data.usage, # Usage (Game Pad)
0x75, 0x02, # Report Size (2) 0xA1, 0x01, # Collection (Application)
0x95, 0x01, # Report Count (1) ) +
0x15, 0x01, # Logical Minimum (1) ((0x85, report_id) if report_id != 0 else ()) +
0x25, 0x03, # Logical Maximum (3) (0x05, 0x09, # Usage Page (Button)
0x09, 0x82, # Usage (Sys Sleep) 0x19, 0x01, # Usage Minimum (Button 1)
0x09, 0x81, # Usage (Sys Power Down) 0x29, 0x10, # Usage Maximum (Button 16)
0x09, 0x83, # Usage (Sys Wake Up) 0x15, 0x00, # Logical Minimum (0)
0x81, 0x60, # Input (Data,Array,Abs,No Wrap,Linear,No Preferred State,Null State) 0x25, 0x01, # Logical Maximum (1)
0x75, 0x06, # Report Size (6) 0x75, 0x01, # Report Size (1)
0x81, 0x03, # Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) 0x95, 0x10, # Report Count (16)
0xC0, # End Collection 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( def digitizer_hid_descriptor(report_id):
description="GAMEPAD", data = HID_DEVICE_DATA["DIGITIZER"]
report_descriptor=bytes([ return hid.ReportDescriptor(
# Gamepad with 16 buttons and two joysticks description="DIGITIZER",
0x05, 0x01, # Usage Page (Generic Desktop Ctrls) report_descriptor=bytes(
0x09, 0x05, # Usage (Game Pad) # Digitizer (used as an absolute pointer)
0xA1, 0x01, # Collection (Application) (0x05, data.usage_page, # Usage Page (Digitizers)
0x85, REPORT_IDS["GAMEPAD"], # Report ID (n) 0x09, data.usage, # Usage (Pen)
0x05, 0x09, # Usage Page (Button) 0xA1, 0x01, # Collection (Application)
0x19, 0x01, # Usage Minimum (Button 1) ) +
0x29, 0x10, # Usage Maximum (Button 16) ((0x85, report_id) if report_id != 0 else ()) +
0x15, 0x00, # Logical Minimum (0) (0x09, 0x01, # Usage (Stylus)
0x25, 0x01, # Logical Maximum (1) 0xA1, 0x00, # Collection (Physical)
0x75, 0x01, # Report Size (1) 0x09, 0x32, # Usage (In-Range)
0x95, 0x10, # Report Count (16) 0x09, 0x42, # Usage (Tip Switch)
0x81, 0x02, # Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) 0x09, 0x44, # Usage (Barrel Switch)
0x05, 0x01, # Usage Page (Generic Desktop Ctrls) 0x09, 0x45, # Usage (Eraser Switch)
0x15, 0x81, # Logical Minimum (-127) 0x15, 0x00, # Logical Minimum (0)
0x25, 0x7F, # Logical Maximum (127) 0x25, 0x01, # Logical Maximum (1)
0x09, 0x30, # Usage (X) 0x75, 0x01, # Report Size (1)
0x09, 0x31, # Usage (Y) 0x95, 0x04, # Report Count (4)
0x09, 0x32, # Usage (Z) 0x81, 0x02, # Input (Data,Var,Abs)
0x09, 0x35, # Usage (Rz) 0x75, 0x04, # Report Size (4) -- Filler
0x75, 0x08, # Report Size (8) 0x95, 0x01, # Report Count (1) -- Filler
0x95, 0x04, # Report Count (4) 0x81, 0x01, # Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x81, 0x02, # Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) 0x05, 0x01, # Usage Page (Generic Desktop Ctrls)
0xC0, # End Collection 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( def xac_compatible_gamepad_hid_descriptor(report_id):
description="DIGITIZER", data = HID_DEVICE_DATA["XAC_COMPATIBLE_GAMEPAD"]
report_descriptor=bytes([ return hid.ReportDescriptor(
# Digitizer (used as an absolute pointer) description="XAC",
0x05, 0x0D, # Usage Page (Digitizers) report_descriptor=bytes(
0x09, 0x02, # Usage (Pen) # This descriptor mimics the simple joystick from PDP that the XBox likes
0xA1, 0x01, # Collection (Application) (0x05, data.usage_page, # Usage Page (Desktop),
0x85, REPORT_IDS["DIGITIZER"], # Report ID (n) 0x09, data.usage, # Usage (Gamepad),
0x09, 0x01, # Usage (Stylus) 0xA1, 0x01, # Collection (Application),
0xA1, 0x00, # Collection (Physical) ) +
0x09, 0x32, # Usage (In-Range) ((0x85, report_id) if report_id != 0 else ()) +
0x09, 0x42, # Usage (Tip Switch) (0x15, 0x00, # Logical Minimum (0),
0x09, 0x44, # Usage (Barrel Switch) 0x25, 0x01, # Logical Maximum (1),
0x09, 0x45, # Usage (Eraser Switch) 0x35, 0x00, # Physical Minimum (0),
0x15, 0x00, # Logical Minimum (0) 0x45, 0x01, # Physical Maximum (1),
0x25, 0x01, # Logical Maximum (1) 0x75, 0x01, # Report Size (1),
0x75, 0x01, # Report Size (1) 0x95, 0x08, # Report Count (8),
0x95, 0x04, # Report Count (4) 0x05, 0x09, # Usage Page (Button),
0x81, 0x02, # Input (Data,Var,Abs) 0x19, 0x01, # Usage Minimum (01h),
0x75, 0x04, # Report Size (4) -- Filler 0x29, 0x08, # Usage Maximum (08h),
0x95, 0x01, # Report Count (1) -- Filler 0x81, 0x02, # Input (Variable),
0x81, 0x01, # Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position) 0x05, 0x01, # Usage Page (Desktop),
0x05, 0x01, # Usage Page (Generic Desktop Ctrls) 0x26, 0xFF, 0x00, # Logical Maximum (255),
0x15, 0x00, # Logical Minimum (0) 0x46, 0xFF, 0x00, # Physical Maximum (255),
0x26, 0xff, 0x7f, # Logical Maximum (32767) 0x09, 0x30, # Usage (X),
0x09, 0x30, # Usage (X) 0x09, 0x31, # Usage (Y),
0x09, 0x31, # Usage (Y) 0x75, 0x08, # Report Size (8),
0x75, 0x10, # Report Size (16) 0x95, 0x02, # Report Count (2),
0x95, 0x02, # Report Count (2) 0x81, 0x02, # Input (Variable),
0x81, 0x02, # Input (Data,Var,Abs) 0xC0 # End Collection
0xC0, # End Collection )))
0xC0, # End Collection
]))
# Byte count for each kind of report. Length does not include report ID in first byte. def raw_hid_descriptor(report_id):
REPORT_DESCRIPTORS = { if report_id != 0:
"KEYBOARD" : KEYBOARD_WITH_ID, raise ValueError("raw hid must not have a report id")
"MOUSE" : MOUSE_WITH_ID, data = HID_DEVICE_DATA["RAW"]
"CONSUMER" : CONSUMER_WITH_ID, return hid.ReportDescriptor(
"SYS_CONTROL" : SYS_CONTROL_WITH_ID, description="RAW",
"GAMEPAD" : GAMEPAD_WITH_ID, report_descriptor=bytes(
"DIGITIZER" : DIGITIZER_WITH_ID, # 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,
}