diff --git a/ports/atmel-samd/boards/circuitplayground_express/mpconfigboard.mk b/ports/atmel-samd/boards/circuitplayground_express/mpconfigboard.mk index 3c43776f5b..e62bec85f3 100644 --- a/ports/atmel-samd/boards/circuitplayground_express/mpconfigboard.mk +++ b/ports/atmel-samd/boards/circuitplayground_express/mpconfigboard.mk @@ -3,6 +3,9 @@ USB_VID = 0x239A USB_PID = 0x8019 USB_PRODUCT = "CircuitPlayground Express" USB_MANUFACTURER = "Adafruit Industries LLC" +#USB_HID_DEVICES = KEYBOARD,XAC_COMPATIBLE_GAMEPAD +USB_HID_DEVICES = XAC_COMPATIBLE_GAMEPAD + CHIP_VARIANT = SAMD21G18A CHIP_FAMILY = samd21 diff --git a/ports/atmel-samd/boards/trinket_m0/mpconfigboard.mk b/ports/atmel-samd/boards/trinket_m0/mpconfigboard.mk index b4a00b654a..72fba7dd79 100644 --- a/ports/atmel-samd/boards/trinket_m0/mpconfigboard.mk +++ b/ports/atmel-samd/boards/trinket_m0/mpconfigboard.mk @@ -1,12 +1,16 @@ LD_FILE = boards/samd21x18-bootloader.ld USB_VID = 0x239A USB_PID = 0x801F + +USB_HID_DEVICES=MOUSE,KEYBOARD,XAC_COMPATIBLE_GAMEPAD + USB_PRODUCT = "Trinket M0" USB_MANUFACTURER = "Adafruit Industries LLC" CHIP_VARIANT = SAMD21E18A CHIP_FAMILY = samd21 + INTERNAL_FLASH_FILESYSTEM = 1 LONGINT_IMPL = NONE CIRCUITPY_SMALL_BUILD = 1 diff --git a/shared-module/usb_hid/Device.c b/shared-module/usb_hid/Device.c index 26d70cb822..bed7d163f9 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 f14fdd41e3..3ce26a1a17 100644 --- a/shared-module/usb_hid/__init__.c +++ b/shared-module/usb_hid/__init__.c @@ -53,6 +53,10 @@ static uint8_t sys_control_report_buffer[USB_HID_REPORT_LENGTH_SYS_CONTROL]; static uint8_t gamepad_report_buffer[USB_HID_REPORT_LENGTH_GAMEPAD]; #endif +#ifdef USB_HID_REPORT_ID_XAC_COMPATIBLE_GAMEPAD +static uint8_t xac_compatible_gamepad_report_buffer[USB_HID_REPORT_LENGTH_XAC_COMPATIBLE_GAMEPAD]; +#endif + #ifdef USB_HID_REPORT_ID_DIGITIZER static uint8_t digitizer_report_buffer[USB_HID_REPORT_LENGTH_DIGITIZER]; #endif @@ -113,6 +117,17 @@ usb_hid_device_obj_t usb_hid_devices[] = { }, #endif +#ifdef USB_HID_REPORT_ID_XAC_COMPATIBLE_GAMEPAD + { + .base = { .type = &usb_hid_device_type } , + .report_buffer = xac_compatible_gamepad_report_buffer , + .report_id = USB_HID_REPORT_ID_XAC_COMPATIBLE_GAMEPAD , + .report_length = USB_HID_REPORT_LENGTH_XAC_COMPATIBLE_GAMEPAD , + .usage_page = HID_USAGE_PAGE_DESKTOP , + .usage = HID_USAGE_DESKTOP_GAMEPAD , + }, +#endif + #ifdef USB_HID_REPORT_ID_DIGITIZER { .base = { .type = &usb_hid_device_type } , @@ -149,6 +164,9 @@ mp_obj_tuple_t common_hal_usb_hid_devices = { #endif #if USB_HID_NUM_DEVICES >= 6 (mp_obj_t) &usb_hid_devices[5], +#endif +#if USB_HID_NUM_DEVICES >= 7 +#error "Too many USB HID devices" #endif } }; diff --git a/supervisor/supervisor.mk b/supervisor/supervisor.mk index 2d50e7a8b1..016188dfd9 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 10bbf50663..f782f9afc5 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' +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 @@ parser.add_argument('--pid', type=lambda x: int(x, 16), help='product id') parser.add_argument('--serial_number_length', type=int, default=32, help='length needed for the serial number in digits') +parser.add_argument('--devices', type=lambda l: frozenset(l.split(',')), default=DEFAULT_DEVICES, + help='devices to include in descriptor (AUDIO includes MIDI support)') +parser.add_argument('--hid_devices', type=lambda l: frozenset(l.split(',')), default=DEFAULT_HID_DEVICES, + help='HID devices to include in HID report descriptor') parser.add_argument('--output_c_file', type=argparse.FileType('w'), required=True) parser.add_argument('--output_h_file', type=argparse.FileType('w'), required=True) args = parser.parse_args() +unknown_devices = list(args.devices - ALL_DEVICES_SET) +if unknown_devices: + raise ValueError("Unknown device(s)", unknown_devices) + +unknown_hid_devices = list(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,16 +160,27 @@ msc_interfaces = [ ) ] -# 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") +# Sort by Report ID for consistency. +hid_devices = sorted(args.hid_devices, key=lambda name: hid_report_descriptors.REPORT_IDS[name]) -combined_hid_report_descriptor = hid.ReportDescriptor( +# When there's only one hid_device, it can't be in a composite descriptor. +# It has no report id (indicated by 0), so remove the existing one. + +#if len(hid_devices) == 1: +if False: + name = hid_devices[0] + combined_hid_report_descriptor = hid.ReportDescriptor( + description=name, + report_descriptor=hid_report_descriptors.remove_report_id( + hid_report_descriptors.REPORT_DESCRIPTORS[name].report_descriptor)) + hid_report_ids_dict = { name : 0 } +else: + 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_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()) @@ -271,19 +304,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", diff --git a/tools/hid_report_descriptors.py b/tools/hid_report_descriptors.py index f3b28ebcf3..4902657af9 100644 --- a/tools/hid_report_descriptors.py +++ b/tools/hid_report_descriptors.py @@ -31,6 +31,11 @@ HID specific descriptors from adafruit_usb_descriptor import hid +def remove_report_id(report_descriptor_bytes): + #return report_descriptor_bytes + report_id_pos = report_descriptor_bytes.index(0x85) + return report_descriptor_bytes[:report_id_pos] + report_descriptor_bytes[report_id_pos + 2:] + REPORT_IDS = { "KEYBOARD" : 1, "MOUSE" : 2, @@ -38,6 +43,9 @@ REPORT_IDS = { "SYS_CONTROL" : 4, "GAMEPAD" : 5, "DIGITIZER" : 6, + "XAC_COMPATIBLE_GAMEPAD" : 7, + # RAW must not have a report ID, so it can't be used with other devices. + "RAW" : 0, } # Byte count for each kind of report. Length does not include report ID in first byte. @@ -48,6 +56,8 @@ REPORT_LENGTHS = { "SYS_CONTROL" : 1, "GAMEPAD" : 6, "DIGITIZER" : 5, + "XAC_COMPATIBLE_GAMEPAD" : 3, + "RAW" : 64, } KEYBOARD_WITH_ID = hid.ReportDescriptor( @@ -228,6 +238,54 @@ DIGITIZER_WITH_ID = hid.ReportDescriptor( 0xC0, # End Collection ])) +XAC_COMPATIBLE_GAMEPAD_WITH_ID = hid.ReportDescriptor( + description="XAC", + report_descriptor=bytes([ + # This descriptor mimics the simple joystick from PDP that the XBox likes + 0x05, 0x01, # Usage Page (Desktop), + 0x09, 0x05, # Usage (Gamepad), + 0xA1, 0x01, # Collection (Application), + 0x85, REPORT_IDS["XAC_COMPATIBLE_GAMEPAD"], # Report ID (n) + 0x15, 0x00, # Logical Minimum (0), + 0x25, 0x01, # Logical Maximum (1), + 0x35, 0x00, # Physical Minimum (0), + 0x45, 0x01, # Physical Maximum (1), + 0x75, 0x01, # Report Size (1), + 0x95, 0x08, # Report Count (8), + 0x05, 0x09, # Usage Page (Button), + 0x19, 0x01, # Usage Minimum (01h), + 0x29, 0x08, # Usage Maximum (08h), + 0x81, 0x02, # Input (Variable), + 0x05, 0x01, # Usage Page (Desktop), + 0x26, 0xFF, 0x00, # Logical Maximum (255), + 0x46, 0xFF, 0x00, # Physical Maximum (255), + 0x09, 0x30, # Usage (X), + 0x09, 0x31, # Usage (Y), + 0x75, 0x08, # Report Size (8), + 0x95, 0x02, # Report Count (2), + 0x81, 0x02, # Input (Variable), + 0xC0 # End Collection + ])) + +RAW = hid.ReportDescriptor( + description="RAW", + report_descriptor=bytes([ + # Provide vendor-defined + 0x06, 0xAF, 0xFF, # Usage Page (Vendor 0xFFAF "Adafruit"), + 0x09, 0xAF, # Usage (AF), + 0xA1, 0x01, # Collection (Application), + 0x75, 0x08, # Report Size (8), + 0x15, 0x00, # Logical Minimum (0), + 0x26, 0xFF, 0x00 # Logical Maximum (255), + 0x95, 0x08, # Report Count (8), + 0x09, 0x01 # Usage(xxx) + 0x81, 0x02 # Input (Variable) + 0x95, 0x08, # Report Count (8), + 0x09, 0x02, # Usage(xxx) + 0x91, 0x02, # Input (Variable) + 0xC0 # End Collection + ])) + # Byte count for each kind of report. Length does not include report ID in first byte. REPORT_DESCRIPTORS = { "KEYBOARD" : KEYBOARD_WITH_ID, @@ -236,4 +294,6 @@ REPORT_DESCRIPTORS = { "SYS_CONTROL" : SYS_CONTROL_WITH_ID, "GAMEPAD" : GAMEPAD_WITH_ID, "DIGITIZER" : DIGITIZER_WITH_ID, + "XAC_COMPATIBLE_GAMEPAD" : XAC_COMPATIBLE_GAMEPAD_WITH_ID, + "RAW" : RAW_WITH_ID, }