diff --git a/shared-module/usb_hid/Device.c b/shared-module/usb_hid/Device.c index 26d70cb822909..bed7d163f958c 100644 --- a/shared-module/usb_hid/Device.c +++ b/shared-module/usb_hid/Device.c @@ -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 uint64_t end_ticks = ticks_ms + 2000; while ( (ticks_ms < end_ticks) && !tud_hid_ready() ) { -#ifdef MICROPY_VM_HOOK_LOOP - MICROPY_VM_HOOK_LOOP; -#endif + RUN_BACKGROUND_TASKS; } if ( !tud_hid_ready() ) { diff --git a/shared-module/usb_hid/__init__.c b/shared-module/usb_hid/__init__.c index f14fdd41e3ba3..d88f9787acf14 100644 --- a/shared-module/usb_hid/__init__.c +++ b/shared-module/usb_hid/__init__.c @@ -24,131 +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_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 - } -}; +// Nothing needed here. Tables of HID devices are generated in autogen_usb_descriptor.c at compile-time. diff --git a/supervisor/supervisor.mk b/supervisor/supervisor.mk index 2d50e7a8b1660..016188dfd9cad 100644 --- a/supervisor/supervisor.mk +++ b/supervisor/supervisor.mk @@ -81,6 +81,14 @@ else CFLAGS += -DUSB_AVAILABLE endif +ifndef USB_DEVICES +USB_DEVICES = "CDC,MSC,AUDIO,HID" +endif + +ifndef USB_HID_DEVICES +USB_HID_DEVICES = "KEYBOARD,MOUSE,CONSUMER,GAMEPAD" +endif + SUPERVISOR_O = $(addprefix $(BUILD)/, $(SRC_SUPERVISOR:.c=.o)) $(BUILD)/autogen_display_resources.o $(BUILD)/supervisor/shared/translate.o: $(HEADER_BUILD)/qstrdefs.generated.h @@ -98,6 +106,8 @@ autogen_usb_descriptor.intermediate: ../../tools/gen_usb_descriptor.py Makefile --vid $(USB_VID)\ --pid $(USB_PID)\ --serial_number_length $(USB_SERIAL_NUMBER_LENGTH)\ + --devices $(USB_DEVICES) \ + --hid_devices $(USB_HID_DEVICES) \ --output_c_file $(BUILD)/autogen_usb_descriptor.c\ --output_h_file $(BUILD)/genhdr/autogen_usb_descriptor.h diff --git a/tools/gen_usb_descriptor.py b/tools/gen_usb_descriptor.py index 10bbf5066343f..82d2daf0f78c6 100644 --- a/tools/gen_usb_descriptor.py +++ b/tools/gen_usb_descriptor.py @@ -8,6 +8,15 @@ 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,24 @@ 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('--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) + + class StringIndex: """Assign a monotonically increasing index to each unique string. Start with 0.""" string_to_index = {} @@ -138,18 +160,27 @@ def strings_in_order(cls): ) ] -# 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 +302,27 @@ def strings_in_order(cls): 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 +341,8 @@ def strings_in_order(cls): c_file.write("""\ #include +#include "py/objtuple.h" +#include "shared-bindings/usb_hid/Device.h" #include "{H_FILE_NAME}" """.format(H_FILE_NAME=h_file.name)) @@ -420,7 +461,9 @@ def strings_in_order(cls): 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 +477,11 @@ def strings_in_order(cls): 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 +489,55 @@ def strings_in_order(cls): 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 f3b28ebcf38e2..92c962d59b52b 100644 --- a/tools/hid_report_descriptors.py +++ b/tools/hid_report_descriptors.py @@ -29,211 +29,284 @@ * 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, +}