diff options
Diffstat (limited to 'tools')
| -rw-r--r-- | tools/gen_display_resources.py | 4 | ||||
| -rw-r--r-- | tools/gen_usb_descriptor.py | 332 |
2 files changed, 262 insertions, 74 deletions
diff --git a/tools/gen_display_resources.py b/tools/gen_display_resources.py index 6657b6a27..141e6ee2a 100644 --- a/tools/gen_display_resources.py +++ b/tools/gen_display_resources.py @@ -8,8 +8,8 @@ import os import struct import sys -sys.path.append("bitmap_font") -sys.path.append("../../tools/bitmap_font") +sys.path.insert(0, "bitmap_font") +sys.path.insert(0, "../../tools/bitmap_font") from adafruit_bitmap_font import bitmap_font diff --git a/tools/gen_usb_descriptor.py b/tools/gen_usb_descriptor.py index 199cdf9cf..b056f853a 100644 --- a/tools/gen_usb_descriptor.py +++ b/tools/gen_usb_descriptor.py @@ -13,7 +13,7 @@ from adafruit_usb_descriptor import audio, audio10, cdc, hid, midi, msc, standar import hid_report_descriptors DEFAULT_INTERFACE_NAME = "CircuitPython" -ALL_DEVICES = "CDC CDC2 MSC AUDIO HID" +ALL_DEVICES = "CDC CDC2 MSC AUDIO HID VENDOR" ALL_DEVICES_SET = frozenset(ALL_DEVICES.split()) DEFAULT_DEVICES = "CDC MSC AUDIO HID" @@ -24,6 +24,11 @@ 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" +# In the following URL, don't include the https:// because that prefix gets added automatically +DEFAULT_WEBUSB_URL = ( + "circuitpython.org" # In the future, this may become a specific landing page +) + parser = argparse.ArgumentParser(description="Generate USB descriptors.") parser.add_argument( "--highspeed", @@ -99,6 +104,18 @@ parser.add_argument( "--max_ep", type=int, default=0, help="total number of endpoints available" ) parser.add_argument( + "--webusb_url", + type=str, + help="The URL to include in the WebUSB URL Descriptor", + default=DEFAULT_WEBUSB_URL, +) +parser.add_argument( + "--vendor_ep_num_out", type=int, default=0, help="endpoint number of VENDOR OUT" +) +parser.add_argument( + "--vendor_ep_num_in", type=int, default=0, help="endpoint number of VENDOR IN" +) +parser.add_argument( "--output_c_file", type=argparse.FileType("w", encoding="UTF-8"), required=True ) parser.add_argument( @@ -120,6 +137,7 @@ include_cdc2 = "CDC2" in args.devices include_msc = "MSC" in args.devices include_hid = "HID" in args.devices include_audio = "AUDIO" in args.devices +include_vendor = "VENDOR" in args.devices if not include_cdc and include_cdc2: raise ValueError("CDC2 requested without CDC") @@ -154,6 +172,12 @@ if not args.renumber_endpoints: elif args.midi_ep_num_in == 0: raise ValueError("MIDI endpoint IN number must not be 0") + if include_vendor: + if args.vendor_ep_num_out == 0: + raise ValueError("VENDOR endpoint OUT number must not be 0") + elif args.vendor_ep_num_in == 0: + raise ValueError("VENDOR endpoint IN number must not be 0") + class StringIndex: """Assign a monotonically increasing index to each unique string. Start with 0.""" @@ -273,7 +297,7 @@ if include_cdc: cdc_comm_interface = make_cdc_comm_interface("CDC", cdc_union) cdc_data_interface = make_cdc_data_interface("CDC") - cdc_interfaces = [cdc_comm_interface, cdc_data_interface] +cdc_interfaces = [cdc_comm_interface, cdc_data_interface] if include_cdc2: cdc2_union = make_cdc_union("CDC2") @@ -472,6 +496,38 @@ if include_audio: + cs_ac_interface.midi_streaming_interfaces ) +if include_vendor: + # Vendor-specific interface, for example WebUSB + vendor_endpoint_in_descriptor = standard.EndpointDescriptor( + description="VENDOR in", + bEndpointAddress=args.vendor_ep_num_in + | standard.EndpointDescriptor.DIRECTION_IN, + bmAttributes=standard.EndpointDescriptor.TYPE_BULK, + bInterval=16, + ) + + vendor_endpoint_out_descriptor = standard.EndpointDescriptor( + description="VENDOR out", + bEndpointAddress=args.vendor_ep_num_out + | standard.EndpointDescriptor.DIRECTION_OUT, + bmAttributes=standard.EndpointDescriptor.TYPE_BULK, + bInterval=16, + ) + + vendor_interface = standard.InterfaceDescriptor( + description="VENDOR", + bInterfaceClass=0xFF, # Vendor-specific + bInterfaceSubClass=0x00, + bInterfaceProtocol=0x00, + iInterface=StringIndex.index("{} VENDOR".format(args.interface_name)), + subdescriptors=[ + vendor_endpoint_in_descriptor, + vendor_endpoint_out_descriptor, + ], + ) + + vendor_interfaces = [vendor_interface] + interfaces_to_join = [] if include_cdc: @@ -489,6 +545,9 @@ if include_hid: if include_audio: interfaces_to_join.append(audio_interfaces) +if include_vendor: + interfaces_to_join.append(vendor_interfaces) + # util.join_interfaces() will renumber the endpoints to make them unique across descriptors, # and renumber the interfaces in order. But we still need to fix up certain # interface cross-references. @@ -502,7 +561,7 @@ if args.max_ep != 0: endpoint_address = getattr(subdescriptor, "bEndpointAddress", 0) & 0x7F if endpoint_address >= args.max_ep: raise ValueError( - "Endpoint address %d of %s must be less than %d" + "Endpoint address %d of '%s' must be less than %d; you have probably run out of endpoints" % (endpoint_address & 0x7F, interface.description, args.max_ep) ) else: @@ -569,6 +628,9 @@ if include_audio: # correct ordering. descriptor_list.append(audio_control_interface) +if include_vendor: + descriptor_list.extend(vendor_interfaces) + # Finally, build the composite descriptor. configuration = standard.ConfigurationDescriptor( @@ -594,6 +656,7 @@ c_file.write( """\ #include <stdint.h> +#include "tusb.h" #include "py/objtuple.h" #include "shared-bindings/usb_hid/Device.h" #include "{H_FILE_NAME}" @@ -732,9 +795,12 @@ c_file.write( c_file.write("\n") -hid_descriptor_length = len(bytes(combined_hid_report_descriptor)) +if include_hid: + hid_descriptor_length = len(bytes(combined_hid_report_descriptor)) +else: + hid_descriptor_length = 0 -# Now we values we need for the .h file. +# Now the values we need for the .h file. h_file.write( """\ #ifndef MICROPY_INCLUDED_AUTOGEN_USB_DESCRIPTOR_H @@ -747,12 +813,8 @@ extern const uint8_t usb_desc_cfg[{configuration_length}]; extern uint16_t usb_serial_number[{serial_number_length}]; extern uint16_t const * const string_desc_arr [{string_descriptor_length}]; -extern const uint8_t hid_report_descriptor[{hid_report_descriptor_length}]; - #define CFG_TUSB_RHPORT0_MODE ({rhport0_mode}) -#define USB_HID_NUM_DEVICES {hid_num_devices} - // Vendor name included in Inquiry response, max 8 bytes #define CFG_TUD_MSC_VENDOR "{msc_vendor}" @@ -765,7 +827,6 @@ extern 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)), rhport0_mode="OPT_MODE_DEVICE | OPT_MODE_HIGH_SPEED" if args.highspeed else "OPT_MODE_DEVICE", @@ -775,62 +836,96 @@ extern const uint8_t hid_report_descriptor[{hid_report_descriptor_length}]; ) ) -# Write out the report descriptor and info -c_file.write( - """\ -const uint8_t hid_report_descriptor[{HID_DESCRIPTOR_LENGTH}] = {{ +if include_hid: + h_file.write( + """\ +extern const uint8_t hid_report_descriptor[{hid_report_descriptor_length}]; + +#define USB_HID_NUM_DEVICES {hid_num_devices} """.format( - HID_DESCRIPTOR_LENGTH=hid_descriptor_length + hid_report_descriptor_length=len(bytes(combined_hid_report_descriptor)), ) ) -for b in bytes(combined_hid_report_descriptor): - c_file.write("0x{:02x}, ".format(b)) -c_file.write( - """\ +if include_vendor: + h_file.write( + """\ +enum +{ + VENDOR_REQUEST_WEBUSB = 1, + VENDOR_REQUEST_MICROSOFT = 2 }; +extern uint8_t const desc_ms_os_20[]; + +// Currently getting compile-time errors in files like tusb_fifo.c +// if we try do define this here (TODO figure this out!) +//extern const tusb_desc_webusb_url_t desc_webusb_url; + +""" + ) + +h_file.write( + """\ +#endif // MICROPY_INCLUDED_AUTOGEN_USB_DESCRIPTOR_H """ ) -# Write out USB HID report buffer definitions. -for name in args.hid_devices: +if include_hid: + # Write out the report descriptor and info 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, - ) +const uint8_t hid_report_descriptor[{HID_DESCRIPTOR_LENGTH}] = {{ +""".format(HID_DESCRIPTOR_LENGTH=hid_descriptor_length)) + + for b in bytes(combined_hid_report_descriptor): + c_file.write("0x{:02x}, ".format(b)) + + c_file.write( + """\ +}; + +""" ) - if hid_report_descriptors.HID_DEVICE_DATA[name].out_report_length > 0: + # Write out USB HID report buffer definitions. + for name in args.hid_devices: c_file.write( """\ -static uint8_t {name}_out_report_buffer[{report_length}]; +static uint8_t {name}_report_buffer[{report_length}]; """.format( name=name.lower(), - report_length=hid_report_descriptors.HID_DEVICE_DATA[ - name - ].out_report_length, + report_length=hid_report_descriptors.HID_DEVICE_DATA[name].report_length, ) ) -# Write out table of device objects. -c_file.write( - """ + if hid_report_descriptors.HID_DEVICE_DATA[name].out_report_length > 0: + c_file.write( + """\ +static uint8_t {name}_out_report_buffer[{report_length}]; +""".format( + name=name.lower(), + report_length=hid_report_descriptors.HID_DEVICE_DATA[ + name + ].out_report_length, + ) + ) + + # Write out table of device objects. + c_file.write( + """\ usb_hid_device_obj_t usb_hid_devices[] = { """ -) -for name in args.hid_devices: - device_data = hid_report_descriptors.HID_DEVICE_DATA[name] - out_report_buffer = ( - "{}_out_report_buffer".format(name.lower()) - if device_data.out_report_length > 0 - else "NULL" ) - c_file.write( - """\ + for name in args.hid_devices: + device_data = hid_report_descriptors.HID_DEVICE_DATA[name] + out_report_buffer = ( + "{}_out_report_buffer".format(name.lower()) + if device_data.out_report_length > 0 + else "NULL" + ) + c_file.write( + """\ {{ .base = {{ .type = &usb_hid_device_type }}, .report_buffer = {name}_report_buffer, @@ -842,24 +937,24 @@ for name in args.hid_devices: .out_report_length = {out_report_length}, }}, """.format( - name=name.lower(), - report_id=report_ids[name], - report_length=device_data.report_length, - usage_page=device_data.usage_page, - usage=device_data.usage, - out_report_buffer=out_report_buffer, - out_report_length=device_data.out_report_length, + name=name.lower(), + report_id=report_ids[name], + report_length=device_data.report_length, + usage_page=device_data.usage_page, + usage=device_data.usage, + out_report_buffer=out_report_buffer, + out_report_length=device_data.out_report_length, + ) ) - ) -c_file.write( - """\ + c_file.write( + """\ }; """ -) + ) -# Write out tuple of device objects. -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, @@ -867,26 +962,119 @@ mp_obj_tuple_t common_hal_usb_hid_devices = {{ .len = {num_devices}, .items = {{ """.format( - num_devices=len(args.hid_devices) + num_devices=len(args.hid_devices) + ) ) -) -for idx in range(len(args.hid_devices)): - c_file.write( - """\ + for idx in range(len(args.hid_devices)): + c_file.write( + """\ (mp_obj_t) &usb_hid_devices[{idx}], """.format( - idx=idx + idx=idx + ) ) - ) -c_file.write( - """\ + c_file.write( + """\ }, }; """ -) + ) -h_file.write( - """\ -#endif // MICROPY_INCLUDED_AUTOGEN_USB_DESCRIPTOR_H -""" -) +if include_vendor: + # Mimic what the tinyusb webusb demo does in its main.c file + c_file.write( + """ +#define URL "{webusb_url}" + +const tusb_desc_webusb_url_t desc_webusb_url = +{{ + .bLength = 3 + sizeof(URL) - 1, + .bDescriptorType = 3, // WEBUSB URL type + .bScheme = 1, // 0: http, 1: https, 255: "" + .url = URL +}}; + +// These next two hardcoded descriptors were pulled from the usb_descriptor.c file +// of the tinyusb webusb_serial demo. TODO - this is probably something else to +// integrate into the adafruit_usb_descriptors project... + +//--------------------------------------------------------------------+ +// BOS Descriptor +//--------------------------------------------------------------------+ + +/* Microsoft OS 2.0 registry property descriptor +Per MS requirements https://msdn.microsoft.com/en-us/library/windows/hardware/hh450799(v=vs.85).aspx +device should create DeviceInterfaceGUIDs. It can be done by driver and +in case of real PnP solution device should expose MS "Microsoft OS 2.0 +registry property descriptor". Such descriptor can insert any record +into Windows registry per device/configuration/interface. In our case it +will insert "DeviceInterfaceGUIDs" multistring property. + +GUID is freshly generated and should be OK to use. + +https://developers.google.com/web/fundamentals/native-hardware/build-for-webusb/ +(Section Microsoft OS compatibility descriptors) +*/ + +#define BOS_TOTAL_LEN (TUD_BOS_DESC_LEN + TUD_BOS_WEBUSB_DESC_LEN + TUD_BOS_MICROSOFT_OS_DESC_LEN) + +#define MS_OS_20_DESC_LEN 0xB2 + +// BOS Descriptor is required for webUSB +uint8_t const desc_bos[] = +{{ + // total length, number of device caps + TUD_BOS_DESCRIPTOR(BOS_TOTAL_LEN, 2), + + // Vendor Code, iLandingPage + TUD_BOS_WEBUSB_DESCRIPTOR(VENDOR_REQUEST_WEBUSB, 1), + + // Microsoft OS 2.0 descriptor + TUD_BOS_MS_OS_20_DESCRIPTOR(MS_OS_20_DESC_LEN, VENDOR_REQUEST_MICROSOFT) +}}; + +uint8_t const * tud_descriptor_bos_cb(void) +{{ + return desc_bos; +}} + + +#define ITF_NUM_VENDOR {webusb_interface} // used in this next descriptor + +uint8_t const desc_ms_os_20[] = +{{ + // Set header: length, type, windows version, total length + U16_TO_U8S_LE(0x000A), U16_TO_U8S_LE(MS_OS_20_SET_HEADER_DESCRIPTOR), U32_TO_U8S_LE(0x06030000), U16_TO_U8S_LE(MS_OS_20_DESC_LEN), + + // Configuration subset header: length, type, configuration index, reserved, configuration total length + U16_TO_U8S_LE(0x0008), U16_TO_U8S_LE(MS_OS_20_SUBSET_HEADER_CONFIGURATION), 0, 0, U16_TO_U8S_LE(MS_OS_20_DESC_LEN-0x0A), + + // Function Subset header: length, type, first interface, reserved, subset length + U16_TO_U8S_LE(0x0008), U16_TO_U8S_LE(MS_OS_20_SUBSET_HEADER_FUNCTION), ITF_NUM_VENDOR, 0, U16_TO_U8S_LE(MS_OS_20_DESC_LEN-0x0A-0x08), + + // MS OS 2.0 Compatible ID descriptor: length, type, compatible ID, sub compatible ID + U16_TO_U8S_LE(0x0014), U16_TO_U8S_LE(MS_OS_20_FEATURE_COMPATBLE_ID), 'W', 'I', 'N', 'U', 'S', 'B', 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // sub-compatible + + // MS OS 2.0 Registry property descriptor: length, type + U16_TO_U8S_LE(MS_OS_20_DESC_LEN-0x0A-0x08-0x08-0x14), U16_TO_U8S_LE(MS_OS_20_FEATURE_REG_PROPERTY), + U16_TO_U8S_LE(0x0007), U16_TO_U8S_LE(0x002A), // wPropertyDataType, wPropertyNameLength and PropertyName "DeviceInterfaceGUIDs\0" in UTF-16 + 'D', 0x00, 'e', 0x00, 'v', 0x00, 'i', 0x00, 'c', 0x00, 'e', 0x00, 'I', 0x00, 'n', 0x00, 't', 0x00, 'e', 0x00, + 'r', 0x00, 'f', 0x00, 'a', 0x00, 'c', 0x00, 'e', 0x00, 'G', 0x00, 'U', 0x00, 'I', 0x00, 'D', 0x00, 's', 0x00, 0x00, 0x00, + U16_TO_U8S_LE(0x0050), // wPropertyDataLength + //bPropertyData: “{{975F44D9-0D08-43FD-8B3E-127CA8AFFF9D}}”. + '{{', 0x00, '9', 0x00, '7', 0x00, '5', 0x00, 'F', 0x00, '4', 0x00, '4', 0x00, 'D', 0x00, '9', 0x00, '-', 0x00, + '0', 0x00, 'D', 0x00, '0', 0x00, '8', 0x00, '-', 0x00, '4', 0x00, '3', 0x00, 'F', 0x00, 'D', 0x00, '-', 0x00, + '8', 0x00, 'B', 0x00, '3', 0x00, 'E', 0x00, '-', 0x00, '1', 0x00, '2', 0x00, '7', 0x00, 'C', 0x00, 'A', 0x00, + '8', 0x00, 'A', 0x00, 'F', 0x00, 'F', 0x00, 'F', 0x00, '9', 0x00, 'D', 0x00, '}}', 0x00, 0x00, 0x00, 0x00, 0x00 +}}; + +TU_VERIFY_STATIC(sizeof(desc_ms_os_20) == MS_OS_20_DESC_LEN, "Incorrect size"); + +// End of section about desc_ms_os_20 + +""".format( + webusb_url=args.webusb_url, + webusb_interface=vendor_interface.bInterfaceNumber, + ) + ) |
