diff --git a/WEBUSB_README.md b/WEBUSB_README.md index a257d5259c..8250941eb0 100644 --- a/WEBUSB_README.md +++ b/WEBUSB_README.md @@ -62,33 +62,4 @@ The tinyusb examples already include a "WebUSB serial" example. Basically, this feature was ported into CircuitPython by pulling code snippets out of the tinyusb example, and putting them where they best belonged in the CircuitPython codebase. -There was one complication: - -tinyusb uses C preprocessor macros to define things like USB descriptors. - -CircuitPython uses a Python program (tools/gen_usb_descriptor.py) to create USB descriptors (etc.) -using "helper objects" from another repo (adafruit_usb_descriptor). This means some of the example -code had to be adapted to the new programing model, and gen_usb_descriptor gained new command-line -options to control the generated code. - -The generated files go into the "build" directory, look for autogen_usb_descriptor.c and -genhdr/autogen_usb_descriptor.h. - - -Also worth pointing out - the re-use of the CDC connect/disconnect mechanism is not actually part -of the WebUSB standard, it's more of "common idiom". We make use of it here because we need to know -when we should be paying attention to the WebUSB serial interface, and when we should ignore it.. - -## Possible future work areas - -The current code uses the existing Python infrastructure to create the Interface descriptor, but -simply outputs the code snippets from the original tinyusb demo code to create the WEBUSB_URL, -BOS, and MS_OS_20 descriptors. I suppose additional work could be done to add these to the -adafruit_usb_descriptor project, and then gen_usb_descriptor.py could be modified to make use -of them. - -Program gen_usb_descriptor.py creates objects for most interface types, regardless of whether or -not they are actually enabled. This increases the size of a generated string table. I made the -new vendor-interface-related code not do this (because some of the ARM platforms would no longer -build), but I did not go back and do this for the other interface types (CDC, MIDI, HID, etc.) -Some FLASH savings are probably possible if this is done. +### TODO: This needs to be reworked for dynamic USB descriptors. diff --git a/main.c b/main.c index 00c43275fb..21e7775a9f 100755 --- a/main.c +++ b/main.c @@ -518,7 +518,6 @@ STATIC void __attribute__ ((noinline)) run_boot_py(safe_mode_t safe_mode) { } #endif - // TODO(tannewt): Allocate temporary space to hold custom usb descriptors. filesystem_flush(); supervisor_allocation* heap = allocate_remaining_memory(); start_mp(heap); @@ -535,6 +534,12 @@ STATIC void __attribute__ ((noinline)) run_boot_py(safe_mode_t safe_mode) { boot_output_file = NULL; #endif + #if CIRCUITPY_USB + // Remember USB settings done during boot.py. + // Call this before the boot.py heap is destroyed. + usb_post_boot_py(); + #endif + cleanup_after_vm(heap); } } @@ -588,6 +593,7 @@ int __attribute__((used)) main(void) { // Port-independent devices, like CIRCUITPY_BLEIO_HCI. reset_devices(); reset_board(); + reset_usb(); // This is first time we are running CircuitPython after a reset or power-up. supervisor_set_run_reason(RUN_REASON_STARTUP); diff --git a/ports/atmel-samd/asf4_conf/samd21/hpl_usb_config.h b/ports/atmel-samd/asf4_conf/samd21/hpl_usb_config.h index d1bb42fe45..51c71cb823 100644 --- a/ports/atmel-samd/asf4_conf/samd21/hpl_usb_config.h +++ b/ports/atmel-samd/asf4_conf/samd21/hpl_usb_config.h @@ -2,60 +2,29 @@ #ifndef HPL_USB_CONFIG_H #define HPL_USB_CONFIG_H -// CIRCUITPY: +// CIRCUITPY: Since we have dynamic USB descriptors, we may end up using all endpoints. +// So provide cache space for all of them. -// Use 64-byte USB buffers for endpoint directions that are in use. They're set to 0 below otherwise. - -#include "genhdr/autogen_usb_descriptor.h" - -#if defined(USB_ENDPOINT_1_OUT_USED) && USB_ENDPOINT_1_OUT_USED #define CONF_USB_EP1_CACHE 64 -#endif -#if defined(USB_ENDPOINT_1_IN_USED) && USB_ENDPOINT_1_IN_USED #define CONF_USB_EP1_I_CACHE 64 -#endif -#if defined(USB_ENDPOINT_2_OUT_USED) && USB_ENDPOINT_2_OUT_USED #define CONF_USB_EP2_CACHE 64 -#endif -#if defined(USB_ENDPOINT_2_IN_USED) && USB_ENDPOINT_2_IN_USED #define CONF_USB_EP2_I_CACHE 64 -#endif -#if defined(USB_ENDPOINT_3_OUT_USED) && USB_ENDPOINT_3_OUT_USED #define CONF_USB_EP3_CACHE 64 -#endif -#if defined(USB_ENDPOINT_3_IN_USED) && USB_ENDPOINT_3_IN_USED #define CONF_USB_EP3_I_CACHE 64 -#endif -#if defined(USB_ENDPOINT_4_OUT_USED) && USB_ENDPOINT_4_OUT_USED #define CONF_USB_EP4_CACHE 64 -#endif -#if defined(USB_ENDPOINT_4_IN_USED) && USB_ENDPOINT_4_IN_USED #define CONF_USB_EP4_I_CACHE 64 -#endif -#if defined(USB_ENDPOINT_5_OUT_USED) && USB_ENDPOINT_5_OUT_USED #define CONF_USB_EP5_CACHE 64 -#endif -#if defined(USB_ENDPOINT_5_IN_USED) && USB_ENDPOINT_5_IN_USED #define CONF_USB_EP5_I_CACHE 64 -#endif -#if defined(USB_ENDPOINT_6_OUT_USED) && USB_ENDPOINT_6_OUT_USED #define CONF_USB_EP6_CACHE 64 -#endif -#if defined(USB_ENDPOINT_6_IN_USED) && USB_ENDPOINT_6_IN_USED #define CONF_USB_EP6_I_CACHE 64 -#endif -#if defined(USB_ENDPOINT_7_OUT_USED) && USB_ENDPOINT_7_OUT_USED #define CONF_USB_EP7_CACHE 64 -#endif -#if defined(USB_ENDPOINT_7_IN_USED) && USB_ENDPOINT_7_IN_USED #define CONF_USB_EP7_I_CACHE 64 -#endif // <<< Use Configuration Wizard in Context Menu >>> diff --git a/ports/atmel-samd/asf4_conf/samd51/hpl_usb_config.h b/ports/atmel-samd/asf4_conf/samd51/hpl_usb_config.h index d1bb42fe45..51c71cb823 100644 --- a/ports/atmel-samd/asf4_conf/samd51/hpl_usb_config.h +++ b/ports/atmel-samd/asf4_conf/samd51/hpl_usb_config.h @@ -2,60 +2,29 @@ #ifndef HPL_USB_CONFIG_H #define HPL_USB_CONFIG_H -// CIRCUITPY: +// CIRCUITPY: Since we have dynamic USB descriptors, we may end up using all endpoints. +// So provide cache space for all of them. -// Use 64-byte USB buffers for endpoint directions that are in use. They're set to 0 below otherwise. - -#include "genhdr/autogen_usb_descriptor.h" - -#if defined(USB_ENDPOINT_1_OUT_USED) && USB_ENDPOINT_1_OUT_USED #define CONF_USB_EP1_CACHE 64 -#endif -#if defined(USB_ENDPOINT_1_IN_USED) && USB_ENDPOINT_1_IN_USED #define CONF_USB_EP1_I_CACHE 64 -#endif -#if defined(USB_ENDPOINT_2_OUT_USED) && USB_ENDPOINT_2_OUT_USED #define CONF_USB_EP2_CACHE 64 -#endif -#if defined(USB_ENDPOINT_2_IN_USED) && USB_ENDPOINT_2_IN_USED #define CONF_USB_EP2_I_CACHE 64 -#endif -#if defined(USB_ENDPOINT_3_OUT_USED) && USB_ENDPOINT_3_OUT_USED #define CONF_USB_EP3_CACHE 64 -#endif -#if defined(USB_ENDPOINT_3_IN_USED) && USB_ENDPOINT_3_IN_USED #define CONF_USB_EP3_I_CACHE 64 -#endif -#if defined(USB_ENDPOINT_4_OUT_USED) && USB_ENDPOINT_4_OUT_USED #define CONF_USB_EP4_CACHE 64 -#endif -#if defined(USB_ENDPOINT_4_IN_USED) && USB_ENDPOINT_4_IN_USED #define CONF_USB_EP4_I_CACHE 64 -#endif -#if defined(USB_ENDPOINT_5_OUT_USED) && USB_ENDPOINT_5_OUT_USED #define CONF_USB_EP5_CACHE 64 -#endif -#if defined(USB_ENDPOINT_5_IN_USED) && USB_ENDPOINT_5_IN_USED #define CONF_USB_EP5_I_CACHE 64 -#endif -#if defined(USB_ENDPOINT_6_OUT_USED) && USB_ENDPOINT_6_OUT_USED #define CONF_USB_EP6_CACHE 64 -#endif -#if defined(USB_ENDPOINT_6_IN_USED) && USB_ENDPOINT_6_IN_USED #define CONF_USB_EP6_I_CACHE 64 -#endif -#if defined(USB_ENDPOINT_7_OUT_USED) && USB_ENDPOINT_7_OUT_USED #define CONF_USB_EP7_CACHE 64 -#endif -#if defined(USB_ENDPOINT_7_IN_USED) && USB_ENDPOINT_7_IN_USED #define CONF_USB_EP7_I_CACHE 64 -#endif // <<< Use Configuration Wizard in Context Menu >>> diff --git a/ports/atmel-samd/asf4_conf/same51/hpl_usb_config.h b/ports/atmel-samd/asf4_conf/same51/hpl_usb_config.h index d1bb42fe45..51c71cb823 100644 --- a/ports/atmel-samd/asf4_conf/same51/hpl_usb_config.h +++ b/ports/atmel-samd/asf4_conf/same51/hpl_usb_config.h @@ -2,60 +2,29 @@ #ifndef HPL_USB_CONFIG_H #define HPL_USB_CONFIG_H -// CIRCUITPY: +// CIRCUITPY: Since we have dynamic USB descriptors, we may end up using all endpoints. +// So provide cache space for all of them. -// Use 64-byte USB buffers for endpoint directions that are in use. They're set to 0 below otherwise. - -#include "genhdr/autogen_usb_descriptor.h" - -#if defined(USB_ENDPOINT_1_OUT_USED) && USB_ENDPOINT_1_OUT_USED #define CONF_USB_EP1_CACHE 64 -#endif -#if defined(USB_ENDPOINT_1_IN_USED) && USB_ENDPOINT_1_IN_USED #define CONF_USB_EP1_I_CACHE 64 -#endif -#if defined(USB_ENDPOINT_2_OUT_USED) && USB_ENDPOINT_2_OUT_USED #define CONF_USB_EP2_CACHE 64 -#endif -#if defined(USB_ENDPOINT_2_IN_USED) && USB_ENDPOINT_2_IN_USED #define CONF_USB_EP2_I_CACHE 64 -#endif -#if defined(USB_ENDPOINT_3_OUT_USED) && USB_ENDPOINT_3_OUT_USED #define CONF_USB_EP3_CACHE 64 -#endif -#if defined(USB_ENDPOINT_3_IN_USED) && USB_ENDPOINT_3_IN_USED #define CONF_USB_EP3_I_CACHE 64 -#endif -#if defined(USB_ENDPOINT_4_OUT_USED) && USB_ENDPOINT_4_OUT_USED #define CONF_USB_EP4_CACHE 64 -#endif -#if defined(USB_ENDPOINT_4_IN_USED) && USB_ENDPOINT_4_IN_USED #define CONF_USB_EP4_I_CACHE 64 -#endif -#if defined(USB_ENDPOINT_5_OUT_USED) && USB_ENDPOINT_5_OUT_USED #define CONF_USB_EP5_CACHE 64 -#endif -#if defined(USB_ENDPOINT_5_IN_USED) && USB_ENDPOINT_5_IN_USED #define CONF_USB_EP5_I_CACHE 64 -#endif -#if defined(USB_ENDPOINT_6_OUT_USED) && USB_ENDPOINT_6_OUT_USED #define CONF_USB_EP6_CACHE 64 -#endif -#if defined(USB_ENDPOINT_6_IN_USED) && USB_ENDPOINT_6_IN_USED #define CONF_USB_EP6_I_CACHE 64 -#endif -#if defined(USB_ENDPOINT_7_OUT_USED) && USB_ENDPOINT_7_OUT_USED #define CONF_USB_EP7_CACHE 64 -#endif -#if defined(USB_ENDPOINT_7_IN_USED) && USB_ENDPOINT_7_IN_USED #define CONF_USB_EP7_I_CACHE 64 -#endif // <<< Use Configuration Wizard in Context Menu >>> diff --git a/ports/atmel-samd/asf4_conf/same54/hpl_usb_config.h b/ports/atmel-samd/asf4_conf/same54/hpl_usb_config.h index d1bb42fe45..51c71cb823 100644 --- a/ports/atmel-samd/asf4_conf/same54/hpl_usb_config.h +++ b/ports/atmel-samd/asf4_conf/same54/hpl_usb_config.h @@ -2,60 +2,29 @@ #ifndef HPL_USB_CONFIG_H #define HPL_USB_CONFIG_H -// CIRCUITPY: +// CIRCUITPY: Since we have dynamic USB descriptors, we may end up using all endpoints. +// So provide cache space for all of them. -// Use 64-byte USB buffers for endpoint directions that are in use. They're set to 0 below otherwise. - -#include "genhdr/autogen_usb_descriptor.h" - -#if defined(USB_ENDPOINT_1_OUT_USED) && USB_ENDPOINT_1_OUT_USED #define CONF_USB_EP1_CACHE 64 -#endif -#if defined(USB_ENDPOINT_1_IN_USED) && USB_ENDPOINT_1_IN_USED #define CONF_USB_EP1_I_CACHE 64 -#endif -#if defined(USB_ENDPOINT_2_OUT_USED) && USB_ENDPOINT_2_OUT_USED #define CONF_USB_EP2_CACHE 64 -#endif -#if defined(USB_ENDPOINT_2_IN_USED) && USB_ENDPOINT_2_IN_USED #define CONF_USB_EP2_I_CACHE 64 -#endif -#if defined(USB_ENDPOINT_3_OUT_USED) && USB_ENDPOINT_3_OUT_USED #define CONF_USB_EP3_CACHE 64 -#endif -#if defined(USB_ENDPOINT_3_IN_USED) && USB_ENDPOINT_3_IN_USED #define CONF_USB_EP3_I_CACHE 64 -#endif -#if defined(USB_ENDPOINT_4_OUT_USED) && USB_ENDPOINT_4_OUT_USED #define CONF_USB_EP4_CACHE 64 -#endif -#if defined(USB_ENDPOINT_4_IN_USED) && USB_ENDPOINT_4_IN_USED #define CONF_USB_EP4_I_CACHE 64 -#endif -#if defined(USB_ENDPOINT_5_OUT_USED) && USB_ENDPOINT_5_OUT_USED #define CONF_USB_EP5_CACHE 64 -#endif -#if defined(USB_ENDPOINT_5_IN_USED) && USB_ENDPOINT_5_IN_USED #define CONF_USB_EP5_I_CACHE 64 -#endif -#if defined(USB_ENDPOINT_6_OUT_USED) && USB_ENDPOINT_6_OUT_USED #define CONF_USB_EP6_CACHE 64 -#endif -#if defined(USB_ENDPOINT_6_IN_USED) && USB_ENDPOINT_6_IN_USED #define CONF_USB_EP6_I_CACHE 64 -#endif -#if defined(USB_ENDPOINT_7_OUT_USED) && USB_ENDPOINT_7_OUT_USED #define CONF_USB_EP7_CACHE 64 -#endif -#if defined(USB_ENDPOINT_7_IN_USED) && USB_ENDPOINT_7_IN_USED #define CONF_USB_EP7_I_CACHE 64 -#endif // <<< Use Configuration Wizard in Context Menu >>> diff --git a/py/circuitpy_mpconfig.mk b/py/circuitpy_mpconfig.mk index 1758d8fe4c..622f991ffb 100644 --- a/py/circuitpy_mpconfig.mk +++ b/py/circuitpy_mpconfig.mk @@ -308,6 +308,9 @@ CFLAGS += -DCIRCUITPY_STRUCT=$(CIRCUITPY_STRUCT) CIRCUITPY_SUPERVISOR ?= 1 CFLAGS += -DCIRCUITPY_SUPERVISOR=$(CIRCUITPY_SUPERVISOR) +CIRCUITPY_SYNTHIO ?= $(CIRCUITPY_AUDIOCORE) +CFLAGS += -DCIRCUITPY_SYNTHIO=$(CIRCUITPY_SYNTHIO) + CIRCUITPY_TERMINALIO ?= $(CIRCUITPY_DISPLAYIO) CFLAGS += -DCIRCUITPY_TERMINALIO=$(CIRCUITPY_TERMINALIO) @@ -325,39 +328,34 @@ CFLAGS += -DCIRCUITPY_TOUCHIO=$(CIRCUITPY_TOUCHIO) CIRCUITPY_UHEAP ?= 0 CFLAGS += -DCIRCUITPY_UHEAP=$(CIRCUITPY_UHEAP) -# Secondary CDC is usually available if there are at least 8 endpoints. -CIRCUITPY_USB_CDC ?= $(shell expr $(USB_NUM_EP) '>=' 8) +CIRCUITPY_USB ?= 1 +CFLAGS += -DCIRCUITPY_USB=$(CIRCUITPY_USB) + +# If you need to count endpoints, do: +# $(shell expr $(USB_NUM_EP) '>=' 8) + +CIRCUITPY_USB_CDC ?= 1 CFLAGS += -DCIRCUITPY_USB_CDC=$(CIRCUITPY_USB_CDC) +CIRCUITPY_USB_CDC_REPL_ENABLED ?= 1 +CFLAGS += -DCIRCUITPY_USB_CDC_DATA_ENABLED_DEFAULT=$(CIRCUITPY_USB_CDC_DATA_ENABLED_DEFAULT) +CIRCUITPY_USB_CDC_DATA_ENABLED ?= 0 +CFLAGS += -DCIRCUITPY_USB_CDC_DATA_ENABLED_DEFAULT=$(CIRCUITPY_USB_CDC_DATA_ENABLED_DEFAULT) CIRCUITPY_USB_HID ?= 1 CFLAGS += -DCIRCUITPY_USB_HID=$(CIRCUITPY_USB_HID) +CIRCUITPY_USB_HID_ENABLED_DEFAULT = $(CIRCUITPY_USB_HID) +CFLAGS += -DCIRCUITPY_USB_HID_ENABLED_DEFAULT=$(CIRCUITPY_USB_HID_ENABLED_DEFAULT) -CIRCUITPY_USB_HID_CONSUMER ?= 1 -CFLAGS += -DCIRCUITPY_USB_HID_CONSUMER=$(CIRCUITPY_USB_HID_CONSUMER) - -CIRCUITPY_USB_HID_DIGITIZER ?= 0 -CFLAGS += -DCIRCUITPY_USB_HID_DIGITIZER=$(CIRCUITPY_USB_HID_DIGITIZER) - -CIRCUITPY_USB_HID_GAMEPAD ?= 1 -CFLAGS += -DCIRCUITPY_USB_HID_GAMEPAD=$(CIRCUITPY_USB_HID_GAMEPAD) - -CIRCUITPY_USB_HID_KEYBOARD ?= 1 -CFLAGS += -DCIRCUITPY_USB_HID_KEYBOARD=$(CIRCUITPY_USB_HID_KEYBOARD) - -CIRCUITPY_USB_HID_MOUSE ?= 1 -CFLAGS += -DCIRCUITPY_USB_HID_MOUSE=$(CIRCUITPY_USB_HID_MOUSE) - -CIRCUITPY_USB_HID_SYS_CONTROL ?= 0 -CFLAGS += -DCIRCUITPY_USB_HID_SYS_CONTROL=$(CIRCUITPY_USB_HID_SYS_CONTROL) - -CIRCUITPY_USB_HID_XAC_COMPATIBLE_GAMEPAD ?= 0 -CFLAGS += -DCIRCUITPY_USB_HID_XAC_COMPATIBLE_GAMEPAD=$(CIRCUITPY_USB_HID_XAC_COMPATIBLE_GAMEPAD) - -CIRCUITPY_USB_MIDI ?= 1 +# MIDI is usually available if there are at least 8 endpoints. +CIRCUITPY_USB_MIDI ?= $(shell expr $(USB_NUM_EP) '>=' 8) CFLAGS += -DCIRCUITPY_USB_MIDI=$(CIRCUITPY_USB_MIDI) +CIRCUITPY_USB_MIDI_ENABLED_DEFAULT = $(CIRCUITPY_USB_MIDI) +CFLAGS += -DCIRCUITPY_USB_MIDI_ENABLED_DEFAULT=$(CIRCUITPY_USB_MIDI_ENABLED_DEFAULT) CIRCUITPY_USB_MSC ?= 1 CFLAGS += -DCIRCUITPY_USB_MSC=$(CIRCUITPY_USB_MSC) +CIRCUITPY_USB_MSC_ENABLED_DEFAULT = $(CIRCUITPY_USB_MSC) +CFLAGS += -DCIRCUITPY_USB_MSC_ENABLED_DEFAULT=$(CIRCUITPY_USB_MSC_ENABLED_DEFAULT) # Defaulting this to OFF initially because it has only been tested on a # limited number of platforms, and the other platforms do not have this diff --git a/shared-bindings/usb_hid/__init__.c b/shared-bindings/usb_hid/__init__.c index fdecba53df..ccfaaaf96e 100644 --- a/shared-bindings/usb_hid/__init__.c +++ b/shared-bindings/usb_hid/__init__.c @@ -53,23 +53,19 @@ STATIC mp_obj_t usb_hid_configure_usb(mp_obj_t devices) { mp_obj_iter_buf_t iter_buf; - mp_obj_t iterable = mp_getiter(devices, &iter_buf); - mp_obj_t device; - while ((device = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) { + + const mp_int_t len = mp_obj_get_int(mp_obj_len(devices_seq)); + for (size_t i = 0; i < len; i++) { + mp_obj_t item = mp_obj_subscr(devices_seq, mp_obj_new_small_int(i), MP_OBJ_SENTINEL); if (!MP_OBJ_IS_TYPE(item, &usb_hid_device_type)) { mp_raise_ValueError_varg(translate("non-Device in %q", MP_QSTR_devices)); } } - switch (common_hal_usb_hid_configure_usb(descriptors)) { - case USB_CONFIG_TOO_LATE: - mp_raise_RuntimeError(translate("Cannot change USB devices now")); - break; - case USB_CONFIG_NON_DEVICE: - mp_raise_ValueError_varg(translate("non-Device in %q", MP_QSTR_devices)); - break; - default: + if (!common_hal_usb_hid_configure_usb(descriptors)) { + mp_raise_RuntimeError(translate("Cannot change USB devices now")); } + return mp_const_none; } MP_DEFINE_CONST_FUN_OBJ_1(usb_hid_configure_usb_obj, usb_hid_configure_usb); diff --git a/shared-bindings/usb_hid/__init__.h b/shared-bindings/usb_hid/__init__.h index 925b3d5e91..89779a84a7 100644 --- a/shared-bindings/usb_hid/__init__.h +++ b/shared-bindings/usb_hid/__init__.h @@ -32,10 +32,7 @@ extern mp_obj_tuple_t common_hal_usb_hid_devices; -typedef enum { - USB_CONFIG_OK = 0, - USB_CONFIG_TOO_LATE = 1, - USB_CONFIG_NON_DEVICE = 2, -} usb_hid_configure_status; +void common_hal_usb_hid_configure_usb_defaults(void); +usb_hid_configure_status common_hal_usb_hid_configure_usb(mp_obj_t devices_seq); -usb_hid_configure_status common_hal_usb_hid_configure_usb(mp_obj_t devices_seqf // SHARED_BINDINGS_USB_HID_H +#endif // SHARED_BINDINGS_USB_HID_H diff --git a/shared-module/usb_cdc/__init__.c b/shared-module/usb_cdc/__init__.c index 307f0a8e68..72eb1d8291 100644 --- a/shared-module/usb_cdc/__init__.c +++ b/shared-module/usb_cdc/__init__.c @@ -24,7 +24,6 @@ * THE SOFTWARE. */ -#include "genhdr/autogen_usb_descriptor.h" #include "py/gc.h" #include "py/obj.h" #include "py/mphal.h" diff --git a/shared-module/usb_hid/__init__.c b/shared-module/usb_hid/__init__.c index 8d8f5b2e08..7ab6b7592b 100644 --- a/shared-module/usb_hid/__init__.c +++ b/shared-module/usb_hid/__init__.c @@ -51,6 +51,9 @@ static const uint8_t usb_hid_descriptor_template[] = { 0x08, // 21 bInterval 8 (unit depends on device speed) }; +// Sequence of devices to configure. +static mp_obj_t hid_devices_seq; + // Is the HID device enabled? bool usb_hid_enabled; supervisor_allocation *hid_report_descriptor_allocation; @@ -78,23 +81,35 @@ size_t usb_hid_add_descriptor(uint8_t *descriptor_buf, uint8_t *current_interfac return sizeof(usb_hid_descriptor_template); } -usb_hid_configure_status common_hal_usb_hid_configure_usb(mp_obj_t devices_seq) { +static mp_obj_t default_hid_devices[] = { + MP_OBJ_FROM_PTR(usb_hid_device_keyboard_obj), + MP_OBJ_FROM_PTR(usb_hid_device_mouse_obj), +}; + +// Set the default list of devices that will be included. Called before boot.py runs, in the boot.py VM. +void common_hal_usb_hid_configure_usb_defaults(void) { + common_hal_usb_hid_configure_usb(mp_obj_new_tuple(sizeof(default_hid_devices), default_hid_devices)); +} + +bool common_hal_usb_hid_configure_usb(mp_obj_t devices_seq) { // We can't change the devices once we're connected. if (tud_connected()) { - return USB_CONFIG_TOO_LATE; + return false; } - // Assume no devices to start. - usb_hid_enabled = false; - if (devices_seq == mp_const_none) { - return USB_CONFIG_OK; - } + // Remember the devices for use in usb_hid_post_boot_py. + hid_devices_seq = devices_seq; + return true; +} +// Build the combined HID report descriptor and save the chosen devices. +// Both are saved in supervisor allocations. +void usb_hid_post_boot_py(void) { size_t total_report_descriptors_length = 0; // Build a combined report descriptor - mp_int_t len = mp_obj_get_int(mp_obj_len(devices_seq)); + mp_int_t len = mp_obj_get_int(mp_obj_len(hid_devices_seq)); // First get the total size. for (size_t i = 0; i < len; i++) { @@ -128,7 +143,7 @@ usb_hid_configure_status common_hal_usb_hid_configure_usb(mp_obj_t devices_seq) uint8_t *descriptor_start = (uint8_t *) hid_report_descriptor_allocation->ptr; for (size_t i = 0; i < len; i++) { - usb_hid_device_obj_t *device = MP_OBJ_TO_PTR(devices_seq, mp_obj_new_small_int(i), MP_OBJ_SENTINEL); + usb_hid_device_obj_t *device = MP_OBJ_TO_PTR(hid_devices_seq, mp_obj_new_small_int(i), MP_OBJ_SENTINEL); // Copy the report descriptor for this device. if (len == 1) { @@ -150,6 +165,8 @@ usb_hid_configure_status common_hal_usb_hid_configure_usb(mp_obj_t devices_seq) hid_devices[i].descriptor_obj = mp_const_none; } + // No longer keeping the Python object of devices to configure. + hid_devices_seq = MP_OBJ_NULL; } void usb_hid_gc_collect(void) { @@ -158,7 +175,8 @@ void usb_hid_gc_collect(void) { free_memory(hid_report_descriptor_allocation); free_memory(usb_hid_devices_allocation); } else { + gc_collect_ptr(hid_devices_seq); gc_collect_ptr(hid_report_descriptor_allocation->ptr); - gc_collect_ptr(usb_hid_devices_allocation); + gc_collect_ptr(usb_hid_devices_allocation_ptr); } } diff --git a/shared-module/usb_midi/__init__.c b/shared-module/usb_midi/__init__.c index d6abf7c9e5..14c3cfa5ae 100644 --- a/shared-module/usb_midi/__init__.c +++ b/shared-module/usb_midi/__init__.c @@ -26,7 +26,6 @@ #include "shared-bindings/usb_midi/__init__.h" -#include "genhdr/autogen_usb_descriptor.h" #include "py/obj.h" #include "py/mphal.h" #include "py/runtime.h" diff --git a/supervisor/shared/usb/tusb_config.h b/supervisor/shared/usb/tusb_config.h index 0b23d56b9b..16f8d13c5f 100644 --- a/supervisor/shared/usb/tusb_config.h +++ b/supervisor/shared/usb/tusb_config.h @@ -38,8 +38,6 @@ #ifndef _TUSB_CONFIG_H_ #define _TUSB_CONFIG_H_ -#include "genhdr/autogen_usb_descriptor.h" - #ifdef __cplusplus extern "C" { #endif diff --git a/supervisor/shared/usb/usb.c b/supervisor/shared/usb/usb.c index 5ce5083b17..983112f001 100644 --- a/supervisor/shared/usb/usb.c +++ b/supervisor/shared/usb/usb.c @@ -41,7 +41,6 @@ #include "tusb.h" #if CIRCUITPY_USB_VENDOR -#include "genhdr/autogen_usb_descriptor.h" // The WebUSB support being conditionally added to this file is based on the // tinyusb demo examples/device/webusb_serial. @@ -55,15 +54,17 @@ bool usb_enabled(void) { return tusb_inited(); } +// Initialization done only once, before boot.py is run. +void reset_usb(void) { + reset_usb_desc(); +} + + MP_WEAK void post_usb_init(void) { } void usb_init(void) { - usb_build_device_descriptor(); - usb_build_configuration_descriptor(); - usb_build_hid_descriptor(); - usb_build_string_descriptors(); - + usb_desc_init(); init_usb_hardware(); @@ -82,6 +83,13 @@ void usb_init(void) { #endif } +// Remember USB settings done during boot.py. +// The boot.py heap is still valid at this point. +void usb_post_boot_py(void) { + usb_desc_post_boot_py(); +} + + void usb_disconnect(void) { tud_disconnect(); } diff --git a/supervisor/shared/usb/usb_desc.c b/supervisor/shared/usb/usb_desc.c index ea67e3aaaf..b9c3e64951 100644 --- a/supervisor/shared/usb/usb_desc.c +++ b/supervisor/shared/usb/usb_desc.c @@ -44,10 +44,8 @@ #include "shared-module/usb_hid/Device.h" -#include "genhdr/autogen_usb_descriptor.h" - -supervisor_allocation *device_descriptor_allocation; -supervisor_allocation *config_descriptor_allocation; +uint8_t *device_descriptor; +uint8_t *config_descriptor; // Table for collecting interface strings (interface names) as descriptor is built. #define MAX_INTERFACE_STRINGS 16 @@ -99,31 +97,31 @@ static const uint8_t configuration_descriptor_template[] = { 0x32, // 8 bMaxPower 100mA }; -void usb_desc_init(void) { - uint8_t raw_id[COMMON_HAL_MCU_PROCESSOR_UID_LENGTH]; - common_hal_mcu_processor_get_uid(raw_id); +// Initialization done before boot.py is run. +// Turn on or off various USB devices. On devices with limited endpoints, +// some may be off by default. +void reset_usb_desc(void) { + // Set defaults for enabling/disabling of various USB devices. +#if CIRCUITPY_USB_CDC + common_hal_usb_cdc_configure_usb( + (bool) CIRCUITPY_USB_CDC_REPL_ENABLED_DEFAULT, + (bool) CIRCUITPY_USB_CDC_DATA_ENABLED_DEFAULT); +#endif - for (int i = 0; i < COMMON_HAL_MCU_PROCESSOR_UID_LENGTH; i++) { - for (int j = 0; j < 2; j++) { - uint8_t nibble = (raw_id[i] >> (j * 4)) & 0xf; - serial_number_hex_string[i * 2 + (1 - j)] = nibble_to_hex_upper[nibble]; - } - } +#if CIRCUITPY_USB_MSC + common_hal_storage_configure_usb((bool) CIRCUITPY_USB_MSC_ENABLED_DEFAULT); +#endif - // Null-terminate the string. - serial_number_hex_string[sizeof(serial_number_hex_string)] = '\0'; +#if CIRCUITPY_USB_MIDI + common_hal_usb_midi_configure_usb((bool) CIRCUITPY_USB_MIDI_ENABLED_DEFAULT); +#endif - // Memory is cleared to zero when allocated; we depend on that. - collected_interface_strings = m_malloc(MAX_INTERFACE_STRINGS + 1, false); - current_interface_string = 1; +#if CIRCUITPY_USB_HID + common_hal_usb_hid_configure_usb_default(); +#endif } - -void usb_build_device_descriptor(uint16_t vid, uint16_t pid, uint8_t *current_interface_string) { - device_descriptor_allocation = - allocate_memory(sizeof(device_descriptor_template), false /*highaddress*/, true /*movable*/); - uint8_t *device_descriptor = (uint8_t *) device_descriptor_allocation->ptr; - +static void usb_build_device_descriptor(uint16_t vid, uint16_t pid, uint8_t *current_interface_string) { memcpy(device_descriptor, device_descriptor_template, sizeof(device_descriptor_template)); device_descriptor[DEVICE_VID_LO_INDEX] = vid & 0xFF; @@ -144,7 +142,7 @@ void usb_build_device_descriptor(uint16_t vid, uint16_t pid, uint8_t *current_in (*current_interface_string)++; } -void usb_build_configuration_descriptor(uint16_t total_length, uint8_t num_interfaces) { +static void usb_build_configuration_descriptor(uint16_t total_length, uint8_t num_interfaces) { size_t total_descriptor_length = sizeof(configuration_descriptor_template); // CDC should be first, for compatibility with Adafruit Windows 7 drivers. @@ -178,11 +176,6 @@ void usb_build_configuration_descriptor(uint16_t total_length, uint8_t num_inter #endif // Now we now how big the configuration descriptor will be. - - configuration_descriptor_allocation = - allocate_memory(sizeof(configuration_descriptor_template), false /*highaddress*/, true /*movable*/); - uint8_t *configuration_descriptor = (uint8_t *) device_descriptor_allocation->ptr; - // Copy the top-level template, and fix up its length. memcpy(config_descriptor, configuration_descriptor_template, sizeof(configuration_descriptor_template)); configuration_descriptor[CONFIG_TOTAL_LENGTH_LO_INDEX] = total_descriptor_length & 0xFF; @@ -241,7 +234,7 @@ void usb_build_configuration_descriptor(uint16_t total_length, uint8_t num_inter } -void usb_add_interface_string(uint8_t interface_string_index, const char[] str) { +static void usb_add_interface_string(uint8_t interface_string_index, const char[] str) { if (interface_string_index > MAX_INTERFACE_STRINGS) { mp_raise_SystemError("Too many USB interface names"); } @@ -259,15 +252,45 @@ void usb_add_interface_string(uint8_t interface_string_index, const char[] str) } +// Remember USB information that must persist from the boot.py VM to the next VM. +// Some of this is already remembered in globals, for example, usb_midi_enabled and similar bools. +void usb_desc_post_boot_py(void) { + usb_hid_post_boot_py(); +} + +// Called in a the new VM created after boot.py is run. The USB devices to be used are now chosen. +static void usb_desc_init(void) { + uint8_t raw_id[COMMON_HAL_MCU_PROCESSOR_UID_LENGTH]; + common_hal_mcu_processor_get_uid(raw_id); + + for (int i = 0; i < COMMON_HAL_MCU_PROCESSOR_UID_LENGTH; i++) { + for (int j = 0; j < 2; j++) { + uint8_t nibble = (raw_id[i] >> (j * 4)) & 0xf; + serial_number_hex_string[i * 2 + (1 - j)] = nibble_to_hex_upper[nibble]; + } + } + + // Null-terminate the string. + serial_number_hex_string[sizeof(serial_number_hex_string)] = '\0'; + + // Memory is cleared to zero when allocated; we depend on that. + collected_interface_strings = m_malloc(MAX_INTERFACE_STRINGS + 1, false); + current_interface_string = 1; + + usb_build_device_descriptor(); + usb_build_configuration_descriptor(); + usb_build_hid_descriptor(); + usb_build_string_descriptors(); +} + void usb_desc_gc_collect(void) { // Once tud_mounted() is true, we're done with the constructed descriptors. if (tud_mounted()) { - // GC will pick up the inaccessible blocks. - free_memory(device_descriptor_allocation); - free_memory(configuration_descriptor_allocation); + gc_free(device_descriptor_allocation); + gc_free(configuration_descriptor); } else { - gc_collect_ptr(device_descriptor_allocation->ptr); - gc_collect_ptr(configuration_descriptor_allocation->ptr); + gc_collect_ptr(device_descriptor); + gc_collect_ptr(configuration_descriptor); } } diff --git a/tools/gen_separated_usb_descriptors.py b/tools/gen_separated_usb_descriptors.py deleted file mode 100644 index 1fad2fbeb3..0000000000 --- a/tools/gen_separated_usb_descriptors.py +++ /dev/null @@ -1,1064 +0,0 @@ -# SPDX-FileCopyrightText: 2014 MicroPython & CircuitPython contributors (https://github.com/adafruit/circuitpython/graphs/contributors) -# -# SPDX-License-Identifier: MIT - -import argparse - -import os -import sys - -sys.path.append("../../tools/usb_descriptor") - -from adafruit_usb_descriptor import audio, audio10, cdc, hid, midi, msc, standard, util -import hid_report_descriptors - -DEFAULT_INTERFACE_NAME = "CircuitPython" -ALL_DEVICES = "CDC CDC2 MSC AUDIO HID VENDOR" -ALL_DEVICES_SET = frozenset(ALL_DEVICES.split()) -DEFAULT_DEVICES = "CDC MSC AUDIO HID" - -# This list is in preferred order. MacOS does not like GAMEPAD coming before MOUSE. -ALL_HID_DEVICES = ( - "KEYBOARD MOUSE CONSUMER SYS_CONTROL GAMEPAD DIGITIZER XAC_COMPATIBLE_GAMEPAD RAW" -) -ALL_HID_DEVICES_ORDER = dict((name, idx) for (idx, name) in enumerate(ALL_HID_DEVICES.split())) -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", default=False, action="store_true", help="descriptor for highspeed device" -) -parser.add_argument("--manufacturer", type=str, help="manufacturer of the device") -parser.add_argument("--product", type=str, help="product name of the device") -parser.add_argument("--vid", type=lambda x: int(x, 16), help="vendor id") -parser.add_argument("--pid", type=lambda x: int(x, 16), help="product id") -parser.add_argument( - "--serial_number_length", - type=int, - default=32, - help="length needed for the serial number in digits", -) -parser.add_argument( - "--devices", - type=lambda l: tuple(l.split()), - default=DEFAULT_DEVICES, - help="devices to include in descriptor (AUDIO includes MIDI support)", -) -parser.add_argument( - "--hid_devices", - type=lambda l: tuple(l.split()), - default=DEFAULT_HID_DEVICES, - help="HID devices to include in HID report descriptor", -) -parser.add_argument( - "--interface_name", - type=str, - help="The name/prefix to use in the interface descriptions", - default=DEFAULT_INTERFACE_NAME, -) -parser.add_argument( - "--no-renumber_endpoints", - dest="renumber_endpoints", - action="store_false", - help="use to not renumber endpoint", -) -parser.add_argument( - "--cdc_ep_num_notification", type=int, default=0, help="endpoint number of CDC NOTIFICATION" -) -parser.add_argument( - "--cdc2_ep_num_notification", type=int, default=0, help="endpoint number of CDC2 NOTIFICATION" -) -parser.add_argument( - "--cdc_ep_num_data_out", type=int, default=0, help="endpoint number of CDC DATA OUT" -) -parser.add_argument( - "--cdc_ep_num_data_in", type=int, default=0, help="endpoint number of CDC DATA IN" -) -parser.add_argument( - "--cdc2_ep_num_data_out", type=int, default=0, help="endpoint number of CDC2 DATA OUT" -) -parser.add_argument( - "--cdc2_ep_num_data_in", type=int, default=0, help="endpoint number of CDC2 DATA IN" -) -parser.add_argument("--msc_ep_num_out", type=int, default=0, help="endpoint number of MSC OUT") -parser.add_argument("--msc_ep_num_in", type=int, default=0, help="endpoint number of MSC IN") -parser.add_argument("--hid_ep_num_out", type=int, default=0, help="endpoint number of HID OUT") -parser.add_argument("--hid_ep_num_in", type=int, default=0, help="endpoint number of HID IN") -parser.add_argument("--midi_ep_num_out", type=int, default=0, help="endpoint number of MIDI OUT") -parser.add_argument("--midi_ep_num_in", type=int, default=0, help="endpoint number of MIDI IN") -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( - "--output_h_file", type=argparse.FileType("w", encoding="UTF-8"), 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) - -include_cdc = "CDC" in args.devices -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") - -if not args.renumber_endpoints: - if include_cdc: - if args.cdc_ep_num_notification == 0: - raise ValueError("CDC notification endpoint number must not be 0") - if args.cdc_ep_num_data_out == 0: - raise ValueError("CDC data OUT endpoint number must not be 0") - if args.cdc_ep_num_data_in == 0: - raise ValueError("CDC data IN endpoint number must not be 0") - - if include_cdc2: - if args.cdc2_ep_num_notification == 0: - raise ValueError("CDC2 notification endpoint number must not be 0") - if args.cdc2_ep_num_data_out == 0: - raise ValueError("CDC2 data OUT endpoint number must not be 0") - if args.cdc2_ep_num_data_in == 0: - raise ValueError("CDC2 data IN endpoint number must not be 0") - - if include_msc: - if args.msc_ep_num_out == 0: - raise ValueError("MSC endpoint OUT number must not be 0") - if args.msc_ep_num_in == 0: - raise ValueError("MSC endpoint IN number must not be 0") - - if include_hid: - if args.args.hid_ep_num_out == 0: - raise ValueError("HID endpoint OUT number must not be 0") - if args.hid_ep_num_in == 0: - raise ValueError("HID endpoint IN number must not be 0") - - if include_audio: - if args.args.midi_ep_num_out == 0: - raise ValueError("MIDI endpoint OUT number must not be 0") - if 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") - if 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.""" - - string_to_index = {} - index_to_variable = {} - strings = [] - - @classmethod - def index(cls, string, *, variable_name=None): - if string in cls.string_to_index: - idx = cls.string_to_index[string] - if not cls.index_to_variable[idx]: - cls.index_to_variable[idx] = variable_name - return idx - else: - idx = len(cls.strings) - cls.string_to_index[string] = idx - cls.strings.append(string) - cls.index_to_variable[idx] = variable_name - return idx - - @classmethod - def strings_in_order(cls): - return cls.strings - - -# langid must be the 0th string descriptor -LANGID_INDEX = StringIndex.index("\u0409", variable_name="language_id") -assert LANGID_INDEX == 0 -SERIAL_NUMBER_INDEX = StringIndex.index( - "S" * args.serial_number_length, variable_name="usb_serial_number" -) - -device = standard.DeviceDescriptor( - description="top", - idVendor=args.vid, - idProduct=args.pid, - iManufacturer=StringIndex.index(args.manufacturer), - iProduct=StringIndex.index(args.product), - iSerialNumber=SERIAL_NUMBER_INDEX, -) - -# Interface numbers are interface-set local and endpoints are interface local -# until util.join_interfaces renumbers them. - - -def make_cdc_union(name): - return cdc.Union( - description="{} comm".format(name), - # Set bMasterInterface and bSlaveInterface_list to proper values after interfaces are renumbered. - bMasterInterface=0x00, - bSlaveInterface_list=[0x01], - ) - - -def make_cdc_call_management(name): - # Set bDataInterface to proper value after interfaces are renumbered. - return cdc.CallManagement( - description="{} comm".format(name), bmCapabilities=0x01, bDataInterface=0x01 - ) - - -def make_cdc_comm_interface(name, cdc_union, cdc_call_management, cdc_ep_num_notification): - return standard.InterfaceDescriptor( - description="{} comm".format(name), - bInterfaceClass=cdc.CDC_CLASS_COMM, # Communications Device Class - bInterfaceSubClass=cdc.CDC_SUBCLASS_ACM, # Abstract control model - bInterfaceProtocol=cdc.CDC_PROTOCOL_NONE, - iInterface=StringIndex.index("{} {} control".format(args.interface_name, name)), - subdescriptors=[ - cdc.Header(description="{} comm".format(name), bcdCDC=0x0110), - cdc_call_management, - cdc.AbstractControlManagement(description="{} comm".format(name), bmCapabilities=0x02), - cdc_union, - standard.EndpointDescriptor( - description="{} comm in".format(name), - bEndpointAddress=cdc_ep_num_notification - | standard.EndpointDescriptor.DIRECTION_IN, - bmAttributes=standard.EndpointDescriptor.TYPE_INTERRUPT, - wMaxPacketSize=0x0040, - bInterval=0x10, - ), - ], - ) - - -def make_cdc_data_interface(name, cdc_ep_num_data_in, cdc_ep_num_data_out): - return standard.InterfaceDescriptor( - description="{} data".format(name), - bInterfaceClass=cdc.CDC_CLASS_DATA, - iInterface=StringIndex.index("{} {} data".format(args.interface_name, name)), - subdescriptors=[ - standard.EndpointDescriptor( - description="{} data out".format(name), - bEndpointAddress=cdc_ep_num_data_out | standard.EndpointDescriptor.DIRECTION_OUT, - bmAttributes=standard.EndpointDescriptor.TYPE_BULK, - bInterval=0, - wMaxPacketSize=512 if args.highspeed else 64, - ), - standard.EndpointDescriptor( - description="{} data in".format(name), - bEndpointAddress=cdc_ep_num_data_in | standard.EndpointDescriptor.DIRECTION_IN, - bmAttributes=standard.EndpointDescriptor.TYPE_BULK, - bInterval=0, - wMaxPacketSize=512 if args.highspeed else 64, - ), - ], - ) - - -if include_cdc: - cdc_union = make_cdc_union("CDC") - cdc_call_management = make_cdc_call_management("CDC") - cdc_comm_interface = make_cdc_comm_interface( - "CDC", cdc_union, cdc_call_management, args.cdc_ep_num_notification - ) - cdc_data_interface = make_cdc_data_interface( - "CDC", args.cdc_ep_num_data_in, args.cdc_ep_num_data_out - ) - - cdc_interfaces = [cdc_comm_interface, cdc_data_interface] - -if include_cdc2: - cdc2_union = make_cdc_union("CDC2") - cdc2_call_management = make_cdc_call_management("CDC2") - cdc2_comm_interface = make_cdc_comm_interface( - "CDC2", cdc2_union, cdc2_call_management, args.cdc2_ep_num_notification - ) - cdc2_data_interface = make_cdc_data_interface( - "CDC2", args.cdc2_ep_num_data_in, args.cdc2_ep_num_data_out - ) - - cdc2_interfaces = [cdc2_comm_interface, cdc2_data_interface] - -if include_msc: - msc_interfaces = [ - standard.InterfaceDescriptor( - description="MSC", - bInterfaceClass=msc.MSC_CLASS, - bInterfaceSubClass=msc.MSC_SUBCLASS_TRANSPARENT, - bInterfaceProtocol=msc.MSC_PROTOCOL_BULK, - iInterface=StringIndex.index("{} Mass Storage".format(args.interface_name)), - subdescriptors=[ - standard.EndpointDescriptor( - description="MSC in", - bEndpointAddress=args.msc_ep_num_in | standard.EndpointDescriptor.DIRECTION_IN, - bmAttributes=standard.EndpointDescriptor.TYPE_BULK, - bInterval=0, - wMaxPacketSize=512 if args.highspeed else 64, - ), - standard.EndpointDescriptor( - description="MSC out", - bEndpointAddress=( - args.msc_ep_num_out | standard.EndpointDescriptor.DIRECTION_OUT - ), - bmAttributes=standard.EndpointDescriptor.TYPE_BULK, - bInterval=0, - wMaxPacketSize=512 if args.highspeed else 64, - ), - ], - ) - ] - - -if include_hid: - # 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. - - report_ids = {} - - 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)), - ) - report_ids[name] = 0 - else: - report_id = 1 - concatenated_descriptors = bytearray() - # Sort HID devices by preferred order. - for name in sorted(args.hid_devices, key=ALL_HID_DEVICES_ORDER.get): - concatenated_descriptors.extend( - bytes(hid_report_descriptors.REPORT_DESCRIPTOR_FUNCTIONS[name](report_id)) - ) - report_ids[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. - hid_endpoint_in_descriptor = standard.EndpointDescriptor( - description="HID in", - bEndpointAddress=args.hid_ep_num_in | standard.EndpointDescriptor.DIRECTION_IN, - bmAttributes=standard.EndpointDescriptor.TYPE_INTERRUPT, - bInterval=8, - ) - - hid_endpoint_out_descriptor = standard.EndpointDescriptor( - description="HID out", - bEndpointAddress=args.hid_ep_num_out | standard.EndpointDescriptor.DIRECTION_OUT, - bmAttributes=standard.EndpointDescriptor.TYPE_INTERRUPT, - bInterval=8, - ) - - hid_interfaces = [ - standard.InterfaceDescriptor( - description="HID Multiple Devices", - bInterfaceClass=hid.HID_CLASS, - bInterfaceSubClass=hid.HID_SUBCLASS_NOBOOT, - bInterfaceProtocol=hid.HID_PROTOCOL_NONE, - iInterface=StringIndex.index("{} HID".format(args.interface_name)), - subdescriptors=[ - hid.HIDDescriptor( - description="HID", wDescriptorLength=len(bytes(combined_hid_report_descriptor)) - ), - hid_endpoint_in_descriptor, - hid_endpoint_out_descriptor, - ], - ) - ] - -if include_audio: - # Audio! - # In and out here are relative to CircuitPython - - # USB OUT -> midi_in_jack_emb -> midi_out_jack_ext -> CircuitPython - midi_in_jack_emb = midi.InJackDescriptor( - description="MIDI PC -> {}".format(args.interface_name), - bJackType=midi.JACK_TYPE_EMBEDDED, - iJack=StringIndex.index("{} usb_midi.ports[0]".format(args.interface_name)), - ) - midi_out_jack_ext = midi.OutJackDescriptor( - description="MIDI data out to user code.", - bJackType=midi.JACK_TYPE_EXTERNAL, - input_pins=[(midi_in_jack_emb, 1)], - iJack=0, - ) - - # USB IN <- midi_out_jack_emb <- midi_in_jack_ext <- CircuitPython - midi_in_jack_ext = midi.InJackDescriptor( - description="MIDI data in from user code.", bJackType=midi.JACK_TYPE_EXTERNAL, iJack=0 - ) - midi_out_jack_emb = midi.OutJackDescriptor( - description="MIDI PC <- {}".format(args.interface_name), - bJackType=midi.JACK_TYPE_EMBEDDED, - input_pins=[(midi_in_jack_ext, 1)], - iJack=StringIndex.index("{} usb_midi.ports[1]".format(args.interface_name)), - ) - - audio_midi_interface = standard.InterfaceDescriptor( - description="Midi goodness", - bInterfaceClass=audio.AUDIO_CLASS_DEVICE, - bInterfaceSubClass=audio.AUDIO_SUBCLASS_MIDI_STREAMING, - bInterfaceProtocol=audio.AUDIO_PROTOCOL_V1, - iInterface=StringIndex.index("{} MIDI".format(args.interface_name)), - subdescriptors=[ - midi.Header( - jacks_and_elements=[ - midi_in_jack_emb, - midi_in_jack_ext, - midi_out_jack_emb, - midi_out_jack_ext, - ] - ), - standard.EndpointDescriptor( - description="MIDI data out to {}".format(args.interface_name), - bEndpointAddress=args.midi_ep_num_out | standard.EndpointDescriptor.DIRECTION_OUT, - bmAttributes=standard.EndpointDescriptor.TYPE_BULK, - bInterval=0, - wMaxPacketSize=512 if args.highspeed else 64, - ), - midi.DataEndpointDescriptor(baAssocJack=[midi_in_jack_emb]), - standard.EndpointDescriptor( - description="MIDI data in from {}".format(args.interface_name), - bEndpointAddress=args.midi_ep_num_in | standard.EndpointDescriptor.DIRECTION_IN, - bmAttributes=standard.EndpointDescriptor.TYPE_BULK, - bInterval=0x0, - wMaxPacketSize=512 if args.highspeed else 64, - ), - midi.DataEndpointDescriptor(baAssocJack=[midi_out_jack_emb]), - ], - ) - - cs_ac_interface = audio10.AudioControlInterface( - description="Empty audio control", - audio_streaming_interfaces=[], - midi_streaming_interfaces=[audio_midi_interface], - ) - - audio_control_interface = standard.InterfaceDescriptor( - description="All the audio", - bInterfaceClass=audio.AUDIO_CLASS_DEVICE, - bInterfaceSubClass=audio.AUDIO_SUBCLASS_CONTROL, - bInterfaceProtocol=audio.AUDIO_PROTOCOL_V1, - iInterface=StringIndex.index("{} Audio".format(args.interface_name)), - subdescriptors=[cs_ac_interface], - ) - - # Audio streaming interfaces must occur before MIDI ones. - audio_interfaces = ( - [audio_control_interface] - + cs_ac_interface.audio_streaming_interfaces - + 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: - interfaces_to_join.append(cdc_interfaces) - -if include_cdc2: - interfaces_to_join.append(cdc2_interfaces) - -if include_msc: - interfaces_to_join.append(msc_interfaces) - -if include_hid: - interfaces_to_join.append(hid_interfaces) - -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. -interfaces = util.join_interfaces(interfaces_to_join, renumber_endpoints=args.renumber_endpoints) - -if args.max_ep != 0: - for interface in interfaces: - for subdescriptor in interface.subdescriptors: - 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; you have probably run out of endpoints" - % (endpoint_address & 0x7F, interface.description, args.max_ep) - ) -else: - print("Unable to check whether maximum number of endpoints is respected", file=sys.stderr) - -# Now adjust the CDC interface cross-references. - -if include_cdc: - cdc_union.bMasterInterface = cdc_comm_interface.bInterfaceNumber - cdc_union.bSlaveInterface_list = [cdc_data_interface.bInterfaceNumber] - - cdc_call_management.bDataInterface = cdc_data_interface.bInterfaceNumber - -if include_cdc2: - cdc2_union.bMasterInterface = cdc2_comm_interface.bInterfaceNumber - cdc2_union.bSlaveInterface_list = [cdc2_data_interface.bInterfaceNumber] - - cdc2_call_management.bDataInterface = cdc2_data_interface.bInterfaceNumber - - -def make_cdc_iad(cdc_comm_interface, name): - return standard.InterfaceAssociationDescriptor( - description="{} IAD".format(name), - bFirstInterface=cdc_comm_interface.bInterfaceNumber, - bInterfaceCount=len(cdc_interfaces), - bFunctionClass=cdc.CDC_CLASS_COMM, # Communications Device Class - bFunctionSubClass=cdc.CDC_SUBCLASS_ACM, # Abstract control model - bFunctionProtocol=cdc.CDC_PROTOCOL_NONE, - ) - - -if include_cdc: - cdc_iad = make_cdc_iad(cdc_comm_interface, "CDC") -if include_cdc2: - cdc2_iad = make_cdc_iad(cdc2_comm_interface, "CDC2") - -descriptor_list = [] - -if include_cdc: - # 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 include_cdc2: - descriptor_list.append(cdc2_iad) - descriptor_list.extend(cdc2_interfaces) - -if include_msc: - descriptor_list.extend(msc_interfaces) - -if include_hid: - descriptor_list.extend(hid_interfaces) - -if include_audio: - # 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 include_vendor: - descriptor_list.extend(vendor_interfaces) - -# Finally, build the composite descriptor. - -configuration = standard.ConfigurationDescriptor( - description="Composite configuration", - wTotalLength=( - standard.ConfigurationDescriptor.bLength + sum([len(bytes(x)) for x in descriptor_list]) - ), - bNumInterfaces=len(interfaces), - # bus powered (bit 6), remote wakeup (bit 5), - # bit 7 is always 1 and 0-4 are always 0 - # Turn off remote wakeup until we handle it in CircuitPython. - bmAttributes=0x80, - -) -descriptor_list.insert(0, configuration) - -string_descriptors = [ - standard.StringDescriptor(string) for string in StringIndex.strings_in_order() -] -serial_number_descriptor = string_descriptors[SERIAL_NUMBER_INDEX] - -c_file = args.output_c_file -h_file = args.output_h_file - - -c_file.write( - """\ -#include - -#include "tusb.h" -#include "py/objtuple.h" -#include "shared-bindings/usb_hid/Device.h" -#include "{H_FILE_NAME}" - -""".format( - H_FILE_NAME=h_file.name - ) -) - -c_file.write( - """\ -// {DESCRIPTION} : {CLASS} -""".format( - DESCRIPTION=device.description, CLASS=device.__class__ - ) -) - -c_file.write( - """\ -const uint8_t usb_desc_dev[] = { -""" -) -for b in bytes(device): - c_file.write("0x{:02x}, ".format(b)) - -c_file.write( - """\ -}; -""" -) - -c_file.write( - """\ -const uint8_t usb_desc_cfg[] = { -""" -) - -# Write out all the regular descriptors as one long array (that's how ASF4 does it). -descriptor_length = 0 -for descriptor in descriptor_list: - c_file.write( - """\ -// {DESCRIPTION} : {CLASS} -""".format( - DESCRIPTION=descriptor.description, CLASS=descriptor.__class__ - ) - ) - - b = bytes(descriptor) - notes = descriptor.notes() - i = 0 - - # This prints each subdescriptor on a separate line. - n = 0 - while i < len(b): - length = b[i] - for j in range(length): - c_file.write("0x{:02x}, ".format(b[i + j])) - c_file.write("// " + notes[n]) - n += 1 - c_file.write("\n") - i += length - descriptor_length += len(b) - -c_file.write( - """\ -}; -""" -) - -pointers_to_strings = [] - -for idx, descriptor in enumerate(string_descriptors): - c_file.write( - """\ -// {DESCRIPTION} : {CLASS} -""".format( - DESCRIPTION=descriptor.description, CLASS=descriptor.__class__ - ) - ) - - b = bytes(descriptor) - notes = descriptor.notes() - i = 0 - - # This prints each subdescriptor on a separate line. - variable_name = StringIndex.index_to_variable[idx] - if not variable_name: - variable_name = "string_descriptor{}".format(idx) - pointers_to_strings.append("{name}".format(name=variable_name)) - - const = "const " - if variable_name == "usb_serial_number": - length = len(b) - c_file.write( - " uint16_t {NAME}[{length}];\n".format(NAME=variable_name, length=length // 2) - ) - else: - c_file.write( - """\ - const uint16_t {NAME}[] = {{ - """.format( - const=const, NAME=variable_name - ) - ) - n = 0 - while i < len(b): - length = b[i] - for j in range(length // 2): - c_file.write("0x{:04x}, ".format(b[i + 2 * j + 1] << 8 | b[i + 2 * j])) - n += 1 - c_file.write("\n") - i += length - c_file.write( - """\ - }; - """ - ) - -c_file.write( - """\ -// array of pointer to string descriptors -uint16_t const * const string_desc_arr [] = -{ -""" -) -c_file.write( - """,\ - -""".join( - pointers_to_strings - ) -) - -c_file.write( - """ -}; -""" -) - -c_file.write("\n") - -if include_hid: - hid_descriptor_length = len(bytes(combined_hid_report_descriptor)) -else: - hid_descriptor_length = 0 - -# Now the values we need for the .h file. -h_file.write( - """\ -#ifndef MICROPY_INCLUDED_AUTOGEN_USB_DESCRIPTOR_H -#define MICROPY_INCLUDED_AUTOGEN_USB_DESCRIPTOR_H - -#include - -extern const uint8_t usb_desc_dev[{device_length}]; -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}]; - -#define CFG_TUSB_RHPORT0_MODE ({rhport0_mode}) - -// Vendor name included in Inquiry response, max 8 bytes -#define CFG_TUD_MSC_VENDOR "{msc_vendor}" - -// Product name included in Inquiry response, max 16 bytes -#define CFG_TUD_MSC_PRODUCT "{msc_product}" - -""".format( - serial_number_length=len(bytes(serial_number_descriptor)) // 2, - device_length=len(bytes(device)), - configuration_length=descriptor_length, - max_configuration_length=max(hid_descriptor_length, descriptor_length), - string_descriptor_length=len(pointers_to_strings), - rhport0_mode="OPT_MODE_DEVICE | OPT_MODE_HIGH_SPEED" - if args.highspeed - else "OPT_MODE_DEVICE", - msc_vendor=args.manufacturer[:8], - msc_product=args.product[:16], - ) -) - -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_report_descriptor_length=len(bytes(combined_hid_report_descriptor)), - hid_num_devices=len(args.hid_devices), - ) - ) - -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 -""" -) - -if include_hid: - # Write out the report descriptor and info - c_file.write( - """\ -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( - """\ -}; - -""" - ) - - # Write out USB HID report buffer definitions. - for name in args.hid_devices: - 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, - ) - ) - - 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( - """\ - {{ - .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}, - .out_report_buffer = {out_report_buffer}, - .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, - ) - ) - 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( - """\ - }, -}; -""" - ) - -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 - ) - ) diff --git a/tools/gen_usb_descriptor.py b/tools/gen_usb_descriptor.py deleted file mode 100644 index b873e75cc7..0000000000 --- a/tools/gen_usb_descriptor.py +++ /dev/null @@ -1,1059 +0,0 @@ -# SPDX-FileCopyrightText: 2014 MicroPython & CircuitPython contributors (https://github.com/adafruit/circuitpython/graphs/contributors) -# -# SPDX-License-Identifier: MIT - -import argparse - -import os -import sys - -sys.path.append("../../tools/usb_descriptor") - -from adafruit_usb_descriptor import audio, audio10, cdc, hid, midi, msc, standard, util -import hid_report_descriptors - -DEFAULT_INTERFACE_NAME = "CircuitPython" -ALL_DEVICES = "CDC CDC2 MSC AUDIO HID VENDOR" -ALL_DEVICES_SET = frozenset(ALL_DEVICES.split()) -DEFAULT_DEVICES = "CDC MSC AUDIO HID" - -# This list is in preferred order. MacOS does not like GAMEPAD coming before MOUSE. -ALL_HID_DEVICES = ( - "KEYBOARD MOUSE CONSUMER SYS_CONTROL GAMEPAD DIGITIZER XAC_COMPATIBLE_GAMEPAD RAW" -) -ALL_HID_DEVICES_ORDER = dict((name, idx) for (idx, name) in enumerate(ALL_HID_DEVICES.split())) -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", default=False, action="store_true", help="descriptor for highspeed device" -) -parser.add_argument("--manufacturer", type=str, help="manufacturer of the device") -parser.add_argument("--product", type=str, help="product name of the device") -parser.add_argument("--vid", type=lambda x: int(x, 16), help="vendor id") -parser.add_argument("--pid", type=lambda x: int(x, 16), help="product id") -parser.add_argument( - "--serial_number_length", - type=int, - default=32, - help="length needed for the serial number in digits", -) -parser.add_argument( - "--devices", - type=lambda l: tuple(l.split()), - default=DEFAULT_DEVICES, - help="devices to include in descriptor (AUDIO includes MIDI support)", -) -parser.add_argument( - "--hid_devices", - type=lambda l: tuple(l.split()), - default=DEFAULT_HID_DEVICES, - help="HID devices to include in HID report descriptor", -) -parser.add_argument( - "--interface_name", - type=str, - help="The name/prefix to use in the interface descriptions", - default=DEFAULT_INTERFACE_NAME, -) -parser.add_argument( - "--no-renumber_endpoints", - dest="renumber_endpoints", - action="store_false", - help="use to not renumber endpoint", -) -parser.add_argument( - "--cdc_ep_num_notification", type=int, default=0, help="endpoint number of CDC NOTIFICATION" -) -parser.add_argument( - "--cdc2_ep_num_notification", type=int, default=0, help="endpoint number of CDC2 NOTIFICATION" -) -parser.add_argument( - "--cdc_ep_num_data_out", type=int, default=0, help="endpoint number of CDC DATA OUT" -) -parser.add_argument( - "--cdc_ep_num_data_in", type=int, default=0, help="endpoint number of CDC DATA IN" -) -parser.add_argument( - "--cdc2_ep_num_data_out", type=int, default=0, help="endpoint number of CDC2 DATA OUT" -) -parser.add_argument( - "--cdc2_ep_num_data_in", type=int, default=0, help="endpoint number of CDC2 DATA IN" -) -parser.add_argument("--msc_ep_num_out", type=int, default=0, help="endpoint number of MSC OUT") -parser.add_argument("--msc_ep_num_in", type=int, default=0, help="endpoint number of MSC IN") -parser.add_argument("--hid_ep_num_out", type=int, default=0, help="endpoint number of HID OUT") -parser.add_argument("--hid_ep_num_in", type=int, default=0, help="endpoint number of HID IN") -parser.add_argument("--midi_ep_num_out", type=int, default=0, help="endpoint number of MIDI OUT") -parser.add_argument("--midi_ep_num_in", type=int, default=0, help="endpoint number of MIDI IN") -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( - "--output_h_file", type=argparse.FileType("w", encoding="UTF-8"), 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) - -include_cdc = "CDC" in args.devices -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") - -if not args.renumber_endpoints: - if include_cdc: - if args.cdc_ep_num_notification == 0: - raise ValueError("CDC notification endpoint number must not be 0") - if args.cdc_ep_num_data_out == 0: - raise ValueError("CDC data OUT endpoint number must not be 0") - if args.cdc_ep_num_data_in == 0: - raise ValueError("CDC data IN endpoint number must not be 0") - - if include_cdc2: - if args.cdc2_ep_num_notification == 0: - raise ValueError("CDC2 notification endpoint number must not be 0") - if args.cdc2_ep_num_data_out == 0: - raise ValueError("CDC2 data OUT endpoint number must not be 0") - if args.cdc2_ep_num_data_in == 0: - raise ValueError("CDC2 data IN endpoint number must not be 0") - - if include_msc: - if args.msc_ep_num_out == 0: - raise ValueError("MSC endpoint OUT number must not be 0") - if args.msc_ep_num_in == 0: - raise ValueError("MSC endpoint IN number must not be 0") - - if include_hid: - if args.args.hid_ep_num_out == 0: - raise ValueError("HID endpoint OUT number must not be 0") - if args.hid_ep_num_in == 0: - raise ValueError("HID endpoint IN number must not be 0") - - if include_audio: - if args.args.midi_ep_num_out == 0: - raise ValueError("MIDI endpoint OUT number must not be 0") - if 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") - if 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.""" - - string_to_index = {} - index_to_variable = {} - strings = [] - - @classmethod - def index(cls, string, *, variable_name=None): - if string in cls.string_to_index: - idx = cls.string_to_index[string] - if not cls.index_to_variable[idx]: - cls.index_to_variable[idx] = variable_name - return idx - else: - idx = len(cls.strings) - cls.string_to_index[string] = idx - cls.strings.append(string) - cls.index_to_variable[idx] = variable_name - return idx - - @classmethod - def strings_in_order(cls): - return cls.strings - - -# langid must be the 0th string descriptor -LANGID_INDEX = StringIndex.index("\u0409", variable_name="language_id") -assert LANGID_INDEX == 0 -SERIAL_NUMBER_INDEX = StringIndex.index( - "S" * args.serial_number_length, variable_name="usb_serial_number" -) - -device = standard.DeviceDescriptor( - description="top", - idVendor=args.vid, - idProduct=args.pid, - iManufacturer=StringIndex.index(args.manufacturer), - iProduct=StringIndex.index(args.product), - iSerialNumber=SERIAL_NUMBER_INDEX, -) - -# Interface numbers are interface-set local and endpoints are interface local -# until util.join_interfaces renumbers them. - - -def make_cdc_union(name): - return cdc.Union( - description="{} comm".format(name), - # Set bMasterInterface and bSlaveInterface_list to proper values after interfaces are renumbered. - bMasterInterface=0x00, - bSlaveInterface_list=[0x01], - ) - - -def make_cdc_call_management(name): - # Set bDataInterface to proper value after interfaces are renumbered. - return cdc.CallManagement( - description="{} comm".format(name), bmCapabilities=0x01, bDataInterface=0x01 - ) - - -def make_cdc_comm_interface(name, cdc_union, cdc_call_management, cdc_ep_num_notification): - return standard.InterfaceDescriptor( - description="{} comm".format(name), - bInterfaceClass=cdc.CDC_CLASS_COMM, # Communications Device Class - bInterfaceSubClass=cdc.CDC_SUBCLASS_ACM, # Abstract control model - bInterfaceProtocol=cdc.CDC_PROTOCOL_NONE, - iInterface=StringIndex.index("{} {} control".format(args.interface_name, name)), - subdescriptors=[ - cdc.Header(description="{} comm".format(name), bcdCDC=0x0110), - cdc_call_management, - cdc.AbstractControlManagement(description="{} comm".format(name), bmCapabilities=0x02), - cdc_union, - standard.EndpointDescriptor( - description="{} comm in".format(name), - bEndpointAddress=cdc_ep_num_notification - | standard.EndpointDescriptor.DIRECTION_IN, - bmAttributes=standard.EndpointDescriptor.TYPE_INTERRUPT, - wMaxPacketSize=0x0040, - bInterval=0x10, - ), - ], - ) - - -def make_cdc_data_interface(name, cdc_ep_num_data_in, cdc_ep_num_data_out): - return standard.InterfaceDescriptor( - description="{} data".format(name), - bInterfaceClass=cdc.CDC_CLASS_DATA, - iInterface=StringIndex.index("{} {} data".format(args.interface_name, name)), - subdescriptors=[ - standard.EndpointDescriptor( - description="{} data out".format(name), - bEndpointAddress=cdc_ep_num_data_out | standard.EndpointDescriptor.DIRECTION_OUT, - bmAttributes=standard.EndpointDescriptor.TYPE_BULK, - bInterval=0, - wMaxPacketSize=512 if args.highspeed else 64, - ), - standard.EndpointDescriptor( - description="{} data in".format(name), - bEndpointAddress=cdc_ep_num_data_in | standard.EndpointDescriptor.DIRECTION_IN, - bmAttributes=standard.EndpointDescriptor.TYPE_BULK, - bInterval=0, - wMaxPacketSize=512 if args.highspeed else 64, - ), - ], - ) - - -if include_cdc: - cdc_union = make_cdc_union("CDC") - cdc_call_management = make_cdc_call_management("CDC") - cdc_comm_interface = make_cdc_comm_interface( - "CDC", cdc_union, cdc_call_management, args.cdc_ep_num_notification - ) - cdc_data_interface = make_cdc_data_interface( - "CDC", args.cdc_ep_num_data_in, args.cdc_ep_num_data_out - ) - - cdc_interfaces = [cdc_comm_interface, cdc_data_interface] - -if include_cdc2: - cdc2_union = make_cdc_union("CDC2") - cdc2_call_management = make_cdc_call_management("CDC2") - cdc2_comm_interface = make_cdc_comm_interface( - "CDC2", cdc2_union, cdc2_call_management, args.cdc2_ep_num_notification - ) - cdc2_data_interface = make_cdc_data_interface( - "CDC2", args.cdc2_ep_num_data_in, args.cdc2_ep_num_data_out - ) - - cdc2_interfaces = [cdc2_comm_interface, cdc2_data_interface] - -if include_msc: - msc_interfaces = [ - standard.InterfaceDescriptor( - description="MSC", - bInterfaceClass=msc.MSC_CLASS, - bInterfaceSubClass=msc.MSC_SUBCLASS_TRANSPARENT, - bInterfaceProtocol=msc.MSC_PROTOCOL_BULK, - iInterface=StringIndex.index("{} Mass Storage".format(args.interface_name)), - subdescriptors=[ - standard.EndpointDescriptor( - description="MSC in", - bEndpointAddress=args.msc_ep_num_in | standard.EndpointDescriptor.DIRECTION_IN, - bmAttributes=standard.EndpointDescriptor.TYPE_BULK, - bInterval=0, - wMaxPacketSize=512 if args.highspeed else 64, - ), - standard.EndpointDescriptor( - description="MSC out", - bEndpointAddress=( - args.msc_ep_num_out | standard.EndpointDescriptor.DIRECTION_OUT - ), - bmAttributes=standard.EndpointDescriptor.TYPE_BULK, - bInterval=0, - wMaxPacketSize=512 if args.highspeed else 64, - ), - ], - ) - ] - - -if include_hid: - # When there's only one hid_device, it shouldn't have a report id. - # Otherwise, report ids are assigned sequentially, starting at 1. - # args.hid_devices[0] has report_id 1 - # args.hid_devices[1] has report_id 2 - # etc. - - report_ids = {} - - if len(args.hid_devices) == 1: - name = args.hid_devices[0] - hid_descriptor = hid_report_descriptors.REPORT_DESCRIPTOR_FUNCTIONS[name](None) - concatenated_hid_report_descriptors = bytes( - hid_report_descriptors.REPORT_DESCRIPTOR_FUNCTIONS[name](report_id=0)) - report_ids[name] = 0 - else: - report_id = 1 - concatenated_hid_report_descriptors = bytearray() - # Sort HID devices by preferred order. - for name in sorted(args.hid_devices, key=ALL_HID_DEVICES_ORDER.get): - hid_report_descriptor = hid_report_descriptors.REPORT_DESCRIPTOR_FUNCTIONS[name](report_id) - concatenated_hid_report_descriptors.extend(bytes(hid_report_descriptor)) - report_ids[name] = report_id - report_id += 1 - - # ASF4 expects keyboard and generic devices to have both in and out endpoints, - # and will fail (possibly silently) if both are not supplied. - hid_endpoint_in_descriptor = standard.EndpointDescriptor( - description="HID in", - bEndpointAddress=args.hid_ep_num_in | standard.EndpointDescriptor.DIRECTION_IN, - bmAttributes=standard.EndpointDescriptor.TYPE_INTERRUPT, - bInterval=8, - ) - - hid_endpoint_out_descriptor = standard.EndpointDescriptor( - description="HID out", - bEndpointAddress=args.hid_ep_num_out | standard.EndpointDescriptor.DIRECTION_OUT, - bmAttributes=standard.EndpointDescriptor.TYPE_INTERRUPT, - bInterval=8, - ) - - hid_interfaces = [ - standard.InterfaceDescriptor( - description="HID Multiple Devices", - bInterfaceClass=hid.HID_CLASS, - bInterfaceSubClass=hid.HID_SUBCLASS_NOBOOT, - bInterfaceProtocol=hid.HID_PROTOCOL_NONE, - iInterface=StringIndex.index("{} HID".format(args.interface_name)), - subdescriptors=[ - hid.HIDDescriptor( - description="HID", wDescriptorLength=len(concatenated_hid_report_descriptors) - ), - hid_endpoint_in_descriptor, - hid_endpoint_out_descriptor, - ], - ) - ] - -if include_audio: - # Audio! - # In and out here are relative to CircuitPython - - # USB OUT -> midi_in_jack_emb -> midi_out_jack_ext -> CircuitPython - midi_in_jack_emb = midi.InJackDescriptor( - description="MIDI PC -> {}".format(args.interface_name), - bJackType=midi.JACK_TYPE_EMBEDDED, - iJack=StringIndex.index("{} usb_midi.ports[0]".format(args.interface_name)), - ) - midi_out_jack_ext = midi.OutJackDescriptor( - description="MIDI data out to user code.", - bJackType=midi.JACK_TYPE_EXTERNAL, - input_pins=[(midi_in_jack_emb, 1)], - iJack=0, - ) - - # USB IN <- midi_out_jack_emb <- midi_in_jack_ext <- CircuitPython - midi_in_jack_ext = midi.InJackDescriptor( - description="MIDI data in from user code.", bJackType=midi.JACK_TYPE_EXTERNAL, iJack=0 - ) - midi_out_jack_emb = midi.OutJackDescriptor( - description="MIDI PC <- {}".format(args.interface_name), - bJackType=midi.JACK_TYPE_EMBEDDED, - input_pins=[(midi_in_jack_ext, 1)], - iJack=StringIndex.index("{} usb_midi.ports[1]".format(args.interface_name)), - ) - - audio_midi_interface = standard.InterfaceDescriptor( - description="Midi goodness", - bInterfaceClass=audio.AUDIO_CLASS_DEVICE, - bInterfaceSubClass=audio.AUDIO_SUBCLASS_MIDI_STREAMING, - bInterfaceProtocol=audio.AUDIO_PROTOCOL_V1, - iInterface=StringIndex.index("{} MIDI".format(args.interface_name)), - subdescriptors=[ - midi.Header( - jacks_and_elements=[ - midi_in_jack_emb, - midi_in_jack_ext, - midi_out_jack_emb, - midi_out_jack_ext, - ] - ), - standard.EndpointDescriptor( - description="MIDI data out to {}".format(args.interface_name), - bEndpointAddress=args.midi_ep_num_out | standard.EndpointDescriptor.DIRECTION_OUT, - bmAttributes=standard.EndpointDescriptor.TYPE_BULK, - bInterval=0, - wMaxPacketSize=512 if args.highspeed else 64, - ), - midi.DataEndpointDescriptor(baAssocJack=[midi_in_jack_emb]), - standard.EndpointDescriptor( - description="MIDI data in from {}".format(args.interface_name), - bEndpointAddress=args.midi_ep_num_in | standard.EndpointDescriptor.DIRECTION_IN, - bmAttributes=standard.EndpointDescriptor.TYPE_BULK, - bInterval=0x0, - wMaxPacketSize=512 if args.highspeed else 64, - ), - midi.DataEndpointDescriptor(baAssocJack=[midi_out_jack_emb]), - ], - ) - - cs_ac_interface = audio10.AudioControlInterface( - description="Empty audio control", - audio_streaming_interfaces=[], - midi_streaming_interfaces=[audio_midi_interface], - ) - - audio_control_interface = standard.InterfaceDescriptor( - description="All the audio", - bInterfaceClass=audio.AUDIO_CLASS_DEVICE, - bInterfaceSubClass=audio.AUDIO_SUBCLASS_CONTROL, - bInterfaceProtocol=audio.AUDIO_PROTOCOL_V1, - iInterface=StringIndex.index("{} Audio".format(args.interface_name)), - subdescriptors=[cs_ac_interface], - ) - - # Audio streaming interfaces must occur before MIDI ones. - audio_interfaces = ( - [audio_control_interface] - + cs_ac_interface.audio_streaming_interfaces - + 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: - interfaces_to_join.append(cdc_interfaces) - -if include_cdc2: - interfaces_to_join.append(cdc2_interfaces) - -if include_msc: - interfaces_to_join.append(msc_interfaces) - -if include_hid: - interfaces_to_join.append(hid_interfaces) - -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. -interfaces = util.join_interfaces(interfaces_to_join, renumber_endpoints=args.renumber_endpoints) - -if args.max_ep != 0: - for interface in interfaces: - for subdescriptor in interface.subdescriptors: - 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; you have probably run out of endpoints" - % (endpoint_address & 0x7F, interface.description, args.max_ep) - ) -else: - print("Unable to check whether maximum number of endpoints is respected", file=sys.stderr) - -# Now adjust the CDC interface cross-references. - -if include_cdc: - cdc_union.bMasterInterface = cdc_comm_interface.bInterfaceNumber - cdc_union.bSlaveInterface_list = [cdc_data_interface.bInterfaceNumber] - - cdc_call_management.bDataInterface = cdc_data_interface.bInterfaceNumber - -if include_cdc2: - cdc2_union.bMasterInterface = cdc2_comm_interface.bInterfaceNumber - cdc2_union.bSlaveInterface_list = [cdc2_data_interface.bInterfaceNumber] - - cdc2_call_management.bDataInterface = cdc2_data_interface.bInterfaceNumber - - -def make_cdc_iad(cdc_comm_interface, name): - return standard.InterfaceAssociationDescriptor( - description="{} IAD".format(name), - bFirstInterface=cdc_comm_interface.bInterfaceNumber, - bInterfaceCount=len(cdc_interfaces), - bFunctionClass=cdc.CDC_CLASS_COMM, # Communications Device Class - bFunctionSubClass=cdc.CDC_SUBCLASS_ACM, # Abstract control model - bFunctionProtocol=cdc.CDC_PROTOCOL_NONE, - ) - - -if include_cdc: - cdc_iad = make_cdc_iad(cdc_comm_interface, "CDC") -if include_cdc2: - cdc2_iad = make_cdc_iad(cdc2_comm_interface, "CDC2") - -descriptor_list = [] - -if include_cdc: - # 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 include_cdc2: - descriptor_list.append(cdc2_iad) - descriptor_list.extend(cdc2_interfaces) - -if include_msc: - descriptor_list.extend(msc_interfaces) - -if include_hid: - descriptor_list.extend(hid_interfaces) - -if include_audio: - # 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 include_vendor: - descriptor_list.extend(vendor_interfaces) - -# Finally, build the composite descriptor. - -configuration = standard.ConfigurationDescriptor( - description="Composite configuration", - wTotalLength=( - standard.ConfigurationDescriptor.bLength + sum([len(bytes(x)) for x in descriptor_list]) - ), - bNumInterfaces=len(interfaces), - # bus powered (bit 6), remote wakeup (bit 5), - # bit 7 is always 1 and 0-4 are always 0 - # Turn off remote wakeup until we handle it in CircuitPython. - bmAttributes=0x80, - -) -descriptor_list.insert(0, configuration) - -string_descriptors = [ - standard.StringDescriptor(string) for string in StringIndex.strings_in_order() -] -serial_number_descriptor = string_descriptors[SERIAL_NUMBER_INDEX] - -c_file = args.output_c_file -h_file = args.output_h_file - - -c_file.write( - """\ -#include - -#include "tusb.h" -#include "py/objtuple.h" -#include "shared-bindings/usb_hid/Device.h" -#include "{H_FILE_NAME}" - -""".format( - H_FILE_NAME=h_file.name - ) -) - -c_file.write( - """\ -// {DESCRIPTION} : {CLASS} -""".format( - DESCRIPTION=device.description, CLASS=device.__class__ - ) -) - -c_file.write( - """\ -const uint8_t usb_desc_dev[] = { -""" -) -for b in bytes(device): - c_file.write("0x{:02x}, ".format(b)) - -c_file.write( - """\ -}; -""" -) - -c_file.write( - """\ -const uint8_t usb_desc_cfg[] = { -""" -) - -# Write out all the regular descriptors as one long array (that's how ASF4 does it). -descriptor_length = 0 -for descriptor in descriptor_list: - c_file.write( - """\ -// {DESCRIPTION} : {CLASS} -""".format( - DESCRIPTION=descriptor.description, CLASS=descriptor.__class__ - ) - ) - - b = bytes(descriptor) - notes = descriptor.notes() - i = 0 - - # This prints each subdescriptor on a separate line. - n = 0 - while i < len(b): - length = b[i] - for j in range(length): - c_file.write("0x{:02x}, ".format(b[i + j])) - c_file.write("// " + notes[n]) - n += 1 - c_file.write("\n") - i += length - descriptor_length += len(b) - -c_file.write( - """\ -}; -""" -) - -pointers_to_strings = [] - -for idx, descriptor in enumerate(string_descriptors): - c_file.write( - """\ -// {DESCRIPTION} : {CLASS} -""".format( - DESCRIPTION=descriptor.description, CLASS=descriptor.__class__ - ) - ) - - b = bytes(descriptor) - notes = descriptor.notes() - i = 0 - - # This prints each subdescriptor on a separate line. - variable_name = StringIndex.index_to_variable[idx] - if not variable_name: - variable_name = "string_descriptor{}".format(idx) - pointers_to_strings.append("{name}".format(name=variable_name)) - - const = "const " - if variable_name == "usb_serial_number": - length = len(b) - c_file.write( - " uint16_t {NAME}[{length}];\n".format(NAME=variable_name, length=length // 2) - ) - else: - c_file.write( - """\ - const uint16_t {NAME}[] = {{ - """.format( - const=const, NAME=variable_name - ) - ) - n = 0 - while i < len(b): - length = b[i] - for j in range(length // 2): - c_file.write("0x{:04x}, ".format(b[i + 2 * j + 1] << 8 | b[i + 2 * j])) - n += 1 - c_file.write("\n") - i += length - c_file.write( - """\ - }; - """ - ) - -c_file.write( - """\ -// array of pointer to string descriptors -uint16_t const * const string_desc_arr [] = -{ -""" -) -c_file.write( - """,\ - -""".join( - pointers_to_strings - ) -) - -c_file.write( - """ -}; -""" -) - -c_file.write("\n") - -if include_hid: - hid_report_descriptors_length = len(concatenated_hid_report_descriptors) -else: - hid_report_descriptors_length = 0 - -# Now the values we need for the .h file. -h_file.write( - """\ -#ifndef MICROPY_INCLUDED_AUTOGEN_USB_DESCRIPTOR_H -#define MICROPY_INCLUDED_AUTOGEN_USB_DESCRIPTOR_H - -#include - -extern const uint8_t usb_desc_dev[{device_length}]; -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}]; - -#define CFG_TUSB_RHPORT0_MODE ({rhport0_mode}) - -// Vendor name included in Inquiry response, max 8 bytes -#define CFG_TUD_MSC_VENDOR "{msc_vendor}" - -// Product name included in Inquiry response, max 16 bytes -#define CFG_TUD_MSC_PRODUCT "{msc_product}" - -""".format( - serial_number_length=len(bytes(serial_number_descriptor)) // 2, - device_length=len(bytes(device)), - configuration_length=descriptor_length, - max_configuration_length=max(hid_report_descriptors_length, descriptor_length), - string_descriptor_length=len(pointers_to_strings), - rhport0_mode="OPT_MODE_DEVICE | OPT_MODE_HIGH_SPEED" - if args.highspeed - else "OPT_MODE_DEVICE", - msc_vendor=args.manufacturer[:8], - msc_product=args.product[:16], - ) -) - -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_report_descriptor_length=len(concatenated_hid_report_descriptors), - hid_num_devices=len(args.hid_devices), - ) - ) - -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 -""" -) - -if include_hid: - # Write out the report descriptor and info - c_file.write( - """\ -const uint8_t hid_report_descriptor[{HID_REPORT_DESCRIPTORS_LENGTH}] = {{ -""".format( - HID_REPORT_DESCRIPTORS_LENGTH=hid_report_descriptors_length - ) - ) - - for b in bytes(concatenated_hid_report_descriptors): - c_file.write("0x{:02x}, ".format(b)) - - c_file.write( - """\ -}; - -""" - ) - - # Write out USB HID report buffer definitions. - for name in args.hid_devices: - 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, - ) - ) - - 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( - """\ - {{ - .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}, - .out_report_buffer = {out_report_buffer}, - .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, - ) - ) - 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( - """\ - }, -}; -""" - ) - -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 - ) - )