diff --git a/ports/atmel-samd/mpconfigport.mk b/ports/atmel-samd/mpconfigport.mk index fb9cdf2e66..9839a2aa44 100644 --- a/ports/atmel-samd/mpconfigport.mk +++ b/ports/atmel-samd/mpconfigport.mk @@ -18,7 +18,16 @@ ifeq ($(LONGINT_IMPL),LONGLONG) MPY_TOOL_LONGINT_IMPL = -mlongint-impl=longlong endif +INTERNAL_LIBM = 1 + +USB_SERIAL_NUMBER_LENGTH = 32 + +# Number of USB endpoint pairs. +USB_NUM_EP = 8 + +###################################################################### # Put samd21-only choices here. + ifeq ($(CHIP_FAMILY),samd21) # The ?='s allow overriding in mpconfigboard.mk. @@ -40,7 +49,9 @@ CIRCUITPY_SDCARDIO ?= 0 CIRCUITPY_FRAMEBUFFERIO ?= 0 # SAMD21 needs separate endpoint pairs for MSC BULK IN and BULK OUT, otherwise it's erratic. +# Because of that, there aren't enough endpoints for serial2. USB_MSC_EP_NUM_OUT = 1 +CIRCUITPY_USB_SERIAL2 = 0 CIRCUITPY_ULAB = 0 @@ -55,9 +66,13 @@ CIRCUITPY_TERMINALIO = 0 endif endif # samd21 +###################################################################### +###################################################################### # Put samd51-only choices here. + ifeq ($(CHIP_FAMILY),samd51) + # No native touchio on SAMD51. CIRCUITPY_TOUCHIO_USE_NATIVE = 0 @@ -70,9 +85,4 @@ CIRCUITPY_RGBMATRIX ?= $(CIRCUITPY_FULL_BUILD) CIRCUITPY_FRAMEBUFFERIO ?= $(CIRCUITPY_FULL_BUILD) endif # samd51 - -INTERNAL_LIBM = 1 - -USB_SERIAL_NUMBER_LENGTH = 32 - -USB_NUM_EP = 8 +###################################################################### diff --git a/ports/esp32s2/boards/lilygo_ttgo_t8_s2_st7789/mpconfigboard.mk b/ports/esp32s2/boards/lilygo_ttgo_t8_s2_st7789/mpconfigboard.mk index 38a7ed1fb7..79b45fd92c 100644 --- a/ports/esp32s2/boards/lilygo_ttgo_t8_s2_st7789/mpconfigboard.mk +++ b/ports/esp32s2/boards/lilygo_ttgo_t8_s2_st7789/mpconfigboard.mk @@ -2,7 +2,6 @@ USB_VID = 0x303a USB_PID = 0x8007 USB_PRODUCT = "TTGO T8 ESP32-S2" USB_MANUFACTURER = "LILYGO" -USB_DEVICES = "CDC,MSC,HID" INTERNAL_FLASH_FILESYSTEM = 1 LONGINT_IMPL = MPZ diff --git a/ports/esp32s2/boards/microdev_micro_s2/mpconfigboard.mk b/ports/esp32s2/boards/microdev_micro_s2/mpconfigboard.mk index 5156170957..783e7ad4c7 100644 --- a/ports/esp32s2/boards/microdev_micro_s2/mpconfigboard.mk +++ b/ports/esp32s2/boards/microdev_micro_s2/mpconfigboard.mk @@ -2,7 +2,6 @@ USB_VID = 0x239A USB_PID = 0x80C6 USB_PRODUCT = "microS2" USB_MANUFACTURER = "microDev" -USB_DEVICES = "CDC,MSC,HID" INTERNAL_FLASH_FILESYSTEM = 1 LONGINT_IMPL = MPZ diff --git a/ports/esp32s2/boards/unexpectedmaker_feathers2/mpconfigboard.mk b/ports/esp32s2/boards/unexpectedmaker_feathers2/mpconfigboard.mk index 9857c07617..d045cc2149 100644 --- a/ports/esp32s2/boards/unexpectedmaker_feathers2/mpconfigboard.mk +++ b/ports/esp32s2/boards/unexpectedmaker_feathers2/mpconfigboard.mk @@ -2,7 +2,6 @@ USB_VID = 0x239A USB_PID = 0x80AC USB_PRODUCT = "FeatherS2" USB_MANUFACTURER = "UnexpectedMaker" -USB_DEVICES = "CDC,MSC,HID" INTERNAL_FLASH_FILESYSTEM = 1 LONGINT_IMPL = MPZ diff --git a/ports/esp32s2/boards/unexpectedmaker_feathers2_prerelease/mpconfigboard.mk b/ports/esp32s2/boards/unexpectedmaker_feathers2_prerelease/mpconfigboard.mk index 9857c07617..d045cc2149 100644 --- a/ports/esp32s2/boards/unexpectedmaker_feathers2_prerelease/mpconfigboard.mk +++ b/ports/esp32s2/boards/unexpectedmaker_feathers2_prerelease/mpconfigboard.mk @@ -2,7 +2,6 @@ USB_VID = 0x239A USB_PID = 0x80AC USB_PRODUCT = "FeatherS2" USB_MANUFACTURER = "UnexpectedMaker" -USB_DEVICES = "CDC,MSC,HID" INTERNAL_FLASH_FILESYSTEM = 1 LONGINT_IMPL = MPZ diff --git a/ports/litex/mpconfigport.mk b/ports/litex/mpconfigport.mk index 003fb5c2c3..af6a94e64a 100644 --- a/ports/litex/mpconfigport.mk +++ b/ports/litex/mpconfigport.mk @@ -6,6 +6,9 @@ MPY_TOOL_LONGINT_IMPL = -mlongint-impl=mpz # Internal math library is substantially smaller than toolchain one INTERNAL_LIBM = 1 +# Number of USB endpoint pairs. +USB_NUM_EP = 16 + # Chip supplied serial number, in bytes USB_SERIAL_NUMBER_LENGTH = 30 diff --git a/ports/mimxrt10xx/mpconfigport.mk b/ports/mimxrt10xx/mpconfigport.mk index b4cc9586ac..de594b14be 100644 --- a/ports/mimxrt10xx/mpconfigport.mk +++ b/ports/mimxrt10xx/mpconfigport.mk @@ -17,6 +17,9 @@ INTERNAL_LIBM = 1 USB_SERIAL_NUMBER_LENGTH = 32 USB_HIGHSPEED = 1 +# Number of USB endpoint pairs. +USB_NUM_EP = 8 + INTERNAL_FLASH_FILESYSTEM = 1 CIRCUITPY_AUDIOIO = 0 diff --git a/ports/nrf/mpconfigport.mk b/ports/nrf/mpconfigport.mk index 9560064fbc..83924ff795 100644 --- a/ports/nrf/mpconfigport.mk +++ b/ports/nrf/mpconfigport.mk @@ -11,6 +11,9 @@ INTERNAL_LIBM = 1 USB_SERIAL_NUMBER_LENGTH = 16 +# Number of USB endpoint pairs. +USB_NUM_EP = 8 + # All nRF ports have longints. LONGINT_IMPL = MPZ diff --git a/ports/raspberrypi/mpconfigport.mk b/ports/raspberrypi/mpconfigport.mk index d0a21a7b41..6fb4ffaae3 100644 --- a/ports/raspberrypi/mpconfigport.mk +++ b/ports/raspberrypi/mpconfigport.mk @@ -44,4 +44,5 @@ INTERNAL_LIBM = 1 USB_SERIAL_NUMBER_LENGTH = 32 +# Number of USB endpoint pairs. USB_NUM_EP = 8 diff --git a/ports/stm/boards/thunderpack_v12/mpconfigboard.mk b/ports/stm/boards/thunderpack_v12/mpconfigboard.mk index a2e1da1011..4436f04b77 100644 --- a/ports/stm/boards/thunderpack_v12/mpconfigboard.mk +++ b/ports/stm/boards/thunderpack_v12/mpconfigboard.mk @@ -2,7 +2,9 @@ USB_VID = 0x239A USB_PID = 0x8071 USB_PRODUCT = "Thunderpack STM32F411" USB_MANUFACTURER = "Jeremy Gillick" -USB_DEVICES = "CDC,MSC" + +# Turn off HID devices +CIRCUITPY_USB_HID = 0 LONGINT_IMPL = NONE diff --git a/ports/stm/mpconfigport.mk b/ports/stm/mpconfigport.mk index bcecaa5170..fe257fc505 100644 --- a/ports/stm/mpconfigport.mk +++ b/ports/stm/mpconfigport.mk @@ -8,6 +8,7 @@ ifeq ($(MCU_VARIANT),STM32F405xx) CIRCUITPY_FRAMEBUFFERIO ?= 1 CIRCUITPY_RGBMATRIX ?= 1 CIRCUITPY_SDIOIO ?= 1 + # Number of USB endpoint pairs. USB_NUM_EP = 4 endif @@ -23,6 +24,8 @@ ifeq ($(MCU_SERIES),F4) CIRCUITPY_RTC ?= 0 CIRCUITPY_USB_MIDI ?= 0 CIRCUITPY_USB_HID ?= 0 + + USB_NUM_EP = 4 endif ifeq ($(MCU_SERIES),H7) @@ -40,6 +43,8 @@ ifeq ($(MCU_SERIES),H7) CIRCUITPY_RTC ?= 0 CIRCUITPY_USB_HID ?= 0 CIRCUITPY_USB_MIDI ?= 0 + + USB_NUM_EP = 9 endif ifeq ($(MCU_SERIES),F7) @@ -56,4 +61,6 @@ ifeq ($(MCU_SERIES),F7) CIRCUITPY_RTC ?= 0 CIRCUITPY_USB_HID ?= 0 CIRCUITPY_USB_MIDI ?= 0 + + USB_NUM_EP = 6 endif diff --git a/py/circuitpy_mpconfig.mk b/py/circuitpy_mpconfig.mk index b8fe73eea9..e13eca6efe 100644 --- a/py/circuitpy_mpconfig.mk +++ b/py/circuitpy_mpconfig.mk @@ -89,6 +89,9 @@ CFLAGS += -DCIRCUITPY_AUDIOMP3=$(CIRCUITPY_AUDIOMP3) CIRCUITPY_BINASCII ?= $(CIRCUITPY_FULL_BUILD) CFLAGS += -DCIRCUITPY_BINASCII=$(CIRCUITPY_BINASCII) +CIRCUITPY_BITBANG_APA102 ?= 0 +CFLAGS += -DCIRCUITPY_BITBANG_APA102=$(CIRCUITPY_BITBANG_APA102) + CIRCUITPY_BITBANGIO ?= $(CIRCUITPY_FULL_BUILD) CFLAGS += -DCIRCUITPY_BITBANGIO=$(CIRCUITPY_BITBANGIO) @@ -100,6 +103,9 @@ CFLAGS += -DCIRCUITPY_BLEIO_HCI=$(CIRCUITPY_BLEIO_HCI) CIRCUITPY_BLEIO ?= $(CIRCUITPY_BLEIO_HCI) CFLAGS += -DCIRCUITPY_BLEIO=$(CIRCUITPY_BLEIO) +CIRCUITPY_BLE_FILE_SERVICE ?= 0 +CFLAGS += -DCIRCUITPY_BLE_FILE_SERVICE=$(CIRCUITPY_BLE_FILE_SERVICE) + CIRCUITPY_BOARD ?= 1 CFLAGS += -DCIRCUITPY_BOARD=$(CIRCUITPY_BOARD) @@ -127,6 +133,13 @@ CFLAGS += -DCIRCUITPY_COUNTIO=$(CIRCUITPY_COUNTIO) CIRCUITPY_DISPLAYIO ?= $(CIRCUITPY_FULL_BUILD) CFLAGS += -DCIRCUITPY_DISPLAYIO=$(CIRCUITPY_DISPLAYIO) +CIRCUITPY_DUALBANK ?= 0 +CFLAGS += -DCIRCUITPY_DUALBANK=$(CIRCUITPY_DUALBANK) + +# Enabled micropython.native decorator (experimental) +CIRCUITPY_ENABLE_MPY_NATIVE ?= 0 +CFLAGS += -DCIRCUITPY_ENABLE_MPY_NATIVE=$(CIRCUITPY_ENABLE_MPY_NATIVE) + CIRCUITPY_ERRNO ?= $(CIRCUITPY_FULL_BUILD) CFLAGS += -DCIRCUITPY_ERRNO=$(CIRCUITPY_ERRNO) @@ -179,6 +192,9 @@ CFLAGS += -DCIRCUITPY_MEMORYMONITOR=$(CIRCUITPY_MEMORYMONITOR) CIRCUITPY_MICROCONTROLLER ?= 1 CFLAGS += -DCIRCUITPY_MICROCONTROLLER=$(CIRCUITPY_MICROCONTROLLER) +CIRCUITPY_MSGPACK ?= $(CIRCUITPY_FULL_BUILD) +CFLAGS += -DCIRCUITPY_MSGPACK=$(CIRCUITPY_MSGPACK) + CIRCUITPY_NEOPIXEL_WRITE ?= 1 CFLAGS += -DCIRCUITPY_NEOPIXEL_WRITE=$(CIRCUITPY_NEOPIXEL_WRITE) @@ -192,8 +208,8 @@ CFLAGS += -DCIRCUITPY_NVM=$(CIRCUITPY_NVM) CIRCUITPY_OS ?= 1 CFLAGS += -DCIRCUITPY_OS=$(CIRCUITPY_OS) -CIRCUITPY_DUALBANK ?= 0 -CFLAGS += -DCIRCUITPY_DUALBANK=$(CIRCUITPY_DUALBANK) +CIRCUITPY_PEW ?= 0 +CFLAGS += -DCIRCUITPY_PEW=$(CIRCUITPY_PEW) CIRCUITPY_PIXELBUF ?= $(CIRCUITPY_FULL_BUILD) CFLAGS += -DCIRCUITPY_PIXELBUF=$(CIRCUITPY_PIXELBUF) @@ -216,6 +232,11 @@ CFLAGS += -DCIRCUITPY_RANDOM=$(CIRCUITPY_RANDOM) CIRCUITPY_RE ?= $(CIRCUITPY_FULL_BUILD) CFLAGS += -DCIRCUITPY_RE=$(CIRCUITPY_RE) +# Should busio.I2C() check for pullups? +# Some boards in combination with certain peripherals may not want this. +CIRCUITPY_REQUIRE_I2C_PULLUPS ?= 1 +CFLAGS += -DCIRCUITPY_REQUIRE_I2C_PULLUPS=$(CIRCUITPY_REQUIRE_I2C_PULLUPS) + # CIRCUITPY_RP2PIO is handled in the raspberrypi tree. # Only for rp2 chips. # Assume not a rp2 build. @@ -243,6 +264,18 @@ CFLAGS += -DCIRCUITPY_SDCARDIO=$(CIRCUITPY_SDCARDIO) CIRCUITPY_SDIOIO ?= 0 CFLAGS += -DCIRCUITPY_SDIOIO=$(CIRCUITPY_SDIOIO) +# Second USB CDC serial channel. +CIRCUITPY_SERIAL2 ?= +CFLAGS += -DCIRCUITPY_SERIAL2=$(CIRCUITPY_SERIAL2) + +# REPL over BLE +CIRCUITPY_SERIAL_BLE ?= 0 +CFLAGS += -DCIRCUITPY_SERIAL_BLE=$(CIRCUITPY_SERIAL_BLE) + +# REPL over UART +CIRCUITPY_SERIAL_UART ?= 0 +CFLAGS += -DCIRCUITPY_SERIAL_UART=$(CIRCUITPY_SERIAL_UART) + CIRCUITPY_SHARPDISPLAY ?= $(CIRCUITPY_FRAMEBUFFERIO) CFLAGS += -DCIRCUITPY_SHARPDISPLAY=$(CIRCUITPY_SHARPDISPLAY) @@ -285,37 +318,48 @@ CFLAGS += -DCIRCUITPY_UHEAP=$(CIRCUITPY_UHEAP) CIRCUITPY_USB_HID ?= 1 CFLAGS += -DCIRCUITPY_USB_HID=$(CIRCUITPY_USB_HID) +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_CONTROL=$(CIRCUITPY_USB_HID_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 CFLAGS += -DCIRCUITPY_USB_MIDI=$(CIRCUITPY_USB_MIDI) -CIRCUITPY_PEW ?= 0 -CFLAGS += -DCIRCUITPY_PEW=$(CIRCUITPY_PEW) +CIRCUITPY_USB_MSC ?= 1 +CFLAGS += -DCIRCUITPY_USB_MSC=$(CIRCUITPY_USB_MSC) + +CIRCUITPY_USB_SERIAL ?= 1 +CFLAGS += -DCIRCUITPY_USB_SERIAL=$(CIRCUITPY_USB_MSC) + +ifndef USB_NUM_EP +$(error "USB_NUM_EP (number of USB endpoint pairs)must be defined") +endif + +# Secondary CDC is usually available if there are at least 8 endpoints. +CIRCUITPY_USB_SERIAL2 ?= $(shell expr $(USB_NUM_EP) '>=' 8) +CFLAGS += -DCIRCUITPY_USB_SERIAL2=$(CIRCUITPY_USB_SERIAL2) # For debugging. CIRCUITPY_USTACK ?= 0 CFLAGS += -DCIRCUITPY_USTACK=$(CIRCUITPY_USTACK) -# Non-module conditionals - -CIRCUITPY_BITBANG_APA102 ?= 0 -CFLAGS += -DCIRCUITPY_BITBANG_APA102=$(CIRCUITPY_BITBANG_APA102) - -# Should busio.I2C() check for pullups? -# Some boards in combination with certain peripherals may not want this. -CIRCUITPY_REQUIRE_I2C_PULLUPS ?= 1 -CFLAGS += -DCIRCUITPY_REQUIRE_I2C_PULLUPS=$(CIRCUITPY_REQUIRE_I2C_PULLUPS) - -# REPL over BLE -CIRCUITPY_SERIAL_BLE ?= 0 -CFLAGS += -DCIRCUITPY_SERIAL_BLE=$(CIRCUITPY_SERIAL_BLE) - -CIRCUITPY_BLE_FILE_SERVICE ?= 0 -CFLAGS += -DCIRCUITPY_BLE_FILE_SERVICE=$(CIRCUITPY_BLE_FILE_SERVICE) - -# REPL over UART -CIRCUITPY_SERIAL_UART ?= 0 -CFLAGS += -DCIRCUITPY_SERIAL_UART=$(CIRCUITPY_SERIAL_UART) - # ulab numerics library CIRCUITPY_ULAB ?= $(CIRCUITPY_FULL_BUILD) CFLAGS += -DCIRCUITPY_ULAB=$(CIRCUITPY_ULAB) @@ -326,10 +370,3 @@ CFLAGS += -DCIRCUITPY_WATCHDOG=$(CIRCUITPY_WATCHDOG) CIRCUITPY_WIFI ?= 0 CFLAGS += -DCIRCUITPY_WIFI=$(CIRCUITPY_WIFI) - -# Enabled micropython.native decorator (experimental) -CIRCUITPY_ENABLE_MPY_NATIVE ?= 0 -CFLAGS += -DCIRCUITPY_ENABLE_MPY_NATIVE=$(CIRCUITPY_ENABLE_MPY_NATIVE) - -CIRCUITPY_MSGPACK ?= $(CIRCUITPY_FULL_BUILD) -CFLAGS += -DCIRCUITPY_MSGPACK=$(CIRCUITPY_MSGPACK) diff --git a/supervisor/shared/usb/tusb_config.h b/supervisor/shared/usb/tusb_config.h index 15d9fabafe..0e70367d0c 100644 --- a/supervisor/shared/usb/tusb_config.h +++ b/supervisor/shared/usb/tusb_config.h @@ -61,10 +61,15 @@ // DEVICE CONFIGURATION //--------------------------------------------------------------------+ -#define CFG_TUD_ENDOINT0_SIZE 64 +#define CFG_TUD_ENDPOINT0_SIZE 64 //------------- CLASS -------------// + +// Could be 2 if secondary CDC channel requested. +#ifndef CFG_TUD_CDC #define CFG_TUD_CDC 1 +#endif + #define CFG_TUD_MSC 1 #define CFG_TUD_HID CIRCUITPY_USB_HID #define CFG_TUD_MIDI CIRCUITPY_USB_MIDI diff --git a/supervisor/supervisor.mk b/supervisor/supervisor.mk index a59e99e3de..8fd32fa0b5 100644 --- a/supervisor/supervisor.mk +++ b/supervisor/supervisor.mk @@ -14,23 +14,15 @@ SRC_SUPERVISOR = \ supervisor/shared/tick.c \ supervisor/shared/translate.c -ifndef $(NO_USB) - NO_USB = $(wildcard supervisor/usb.c) -endif +NO_USB ?= $(wildcard supervisor/usb.c) -ifndef INTERNAL_FLASH_FILESYSTEM -INTERNAL_FLASH_FILESYSTEM = 0 -endif +INTERNAL_FLASH_FILESYSTEM ?= 0 CFLAGS += -DINTERNAL_FLASH_FILESYSTEM=$(INTERNAL_FLASH_FILESYSTEM) -ifndef QSPI_FLASH_FILESYSTEM -QSPI_FLASH_FILESYSTEM = 0 -endif +QSPI_FLASH_FILESYSTEM ?= 0 CFLAGS += -DQSPI_FLASH_FILESYSTEM=$(QSPI_FLASH_FILESYSTEM) -ifndef SPI_FLASH_FILESYSTEM SPI_FLASH_FILESYSTEM = 0 -endif CFLAGS += -DSPI_FLASH_FILESYSTEM=$(SPI_FLASH_FILESYSTEM) ifeq ($(CIRCUITPY_BLEIO),1) @@ -115,66 +107,78 @@ ifeq ($(CIRCUITPY_DISPLAYIO), 1) SUPERVISOR_O += $(BUILD)/autogen_display_resources.o endif endif -ifndef USB_INTERFACE_NAME -USB_INTERFACE_NAME = "CircuitPython" + +USB_INTERFACE_NAME ?= "CircuitPython" + +ifneq ($(USB_VID),) +CFLAGS += -DUSB_VID=$(USB_VID) +CFLAGS += -DSUB_PID=$(USB_PID) +CFLAGS += -DUSB_MANUFACTURER=$(USB_MANUFACTURER) +CFLAGS += -DUSB_PRODUCT=$(USB_PRODUCT) endif -USB_DEVICES_COMPUTED := CDC,MSC -ifeq ($(CIRCUITPY_USB_MIDI),1) -USB_DEVICES_COMPUTED := $(USB_DEVICES_COMPUTED),AUDIO -endif +USB_DEVICES = ifeq ($(CIRCUITPY_USB_HID),1) -USB_DEVICES_COMPUTED := $(USB_DEVICES_COMPUTED),HID +USB_DEVICES += HID endif -USB_DEVICES ?= "$(USB_DEVICES_COMPUTED)" - -ifndef USB_HID_DEVICES -USB_HID_DEVICES = "KEYBOARD,MOUSE,CONSUMER,GAMEPAD" +ifeq ($(CIRCUITPY_USB_MIDI),1) +USB_DEVICES += AUDIO +endif +ifeq ($(CIRCUITPY_USB_MSC),1) +USB_DEVICES += MSC +endif +ifeq ($(CIRCUITPY_USB_SERIAL),1) +USB_DEVICES += CDC +endif +ifeq ($(CIRCUITPY_USB_SERIAL2),1) +# Inform TinyUSB there are two CDC devices. +CFLAGS += -DCFG_TUD_CDC=2 +USB_DEVICES += CDC2 endif -ifndef USB_HIGHSPEED -USB_HIGHSPEED = 0 +USB_HID_DEVICES = +ifeq ($(CIRCUITPY_USB_HID_CONSUMER),1) +USB_HID_DEVICES += CONSUMER +endif +ifeq ($(CIRCUITPY_USB_HID_DIGITIZER),1) +USB_HID_DEVICES += DIGITIZER +endif +ifeq ($(CIRCUITPY_USB_HID_GAMEPAD),1) +USB_HID_DEVICES += GAMEPAD +endif +ifeq ($(CIRCUITPY_USB_HID_KEYBOARD),1) +USB_HID_DEVICES += KEYBOARD +endif +ifeq ($(CIRCUITPY_USB_HID_MOUSE),1) +USB_HID_DEVICES += MOUSE +endif +ifeq ($(CIRCUITPY_USB_HID_SYS_CONTROL),1) +USB_HID_DEVICES += SYS_CONTROL +endif +ifeq ($(CIRCUITPY_USB_HID_XAC_COMPATIBLE_GAMEPAD),1) +USB_HID_DEVICES += XAC_COMPATIBLE_GAMEPAD endif -ifndef USB_CDC_EP_NUM_NOTIFICATION -USB_CDC_EP_NUM_NOTIFICATION = 0 +# RAW is not compatible with other HID devices. +ifeq ($(CIRCUITPY_USB_HID_RAW),1) + ifneq ($(CIRCUITPY_USB_HID_DEVICES,) + $(error HID RAW must not be combined with other HID devices) + endif +USB_HID_DEVICES += MOUSE endif -ifndef USB_CDC_EP_NUM_DATA_OUT -USB_CDC_EP_NUM_DATA_OUT = 0 -endif +USB_HIGHSPEED ?= 0 -ifndef USB_CDC_EP_NUM_DATA_IN -USB_CDC_EP_NUM_DATA_IN = 0 -endif - -ifndef USB_MSC_EP_NUM_OUT -USB_MSC_EP_NUM_OUT = 0 -endif - -ifndef USB_MSC_EP_NUM_IN -USB_MSC_EP_NUM_IN = 0 -endif - -ifndef USB_HID_EP_NUM_OUT -USB_HID_EP_NUM_OUT = 0 -endif - -ifndef USB_HID_EP_NUM_IN -USB_HID_EP_NUM_IN = 0 -endif - -ifndef USB_MIDI_EP_NUM_OUT -USB_MIDI_EP_NUM_OUT = 0 -endif - -ifndef USB_MIDI_EP_NUM_IN -USB_MIDI_EP_NUM_IN = 0 -endif - -ifndef USB_NUM_EP -USB_NUM_EP = 0 -endif +USB_CDC_EP_NUM_NOTIFICATION ?= 0 +USB_CDC_EP_NUM_DATA_OUT ?= 0 +USB_CDC_EP_NUM_DATA_IN ?= 0 +USB_MSC_EP_NUM_OUT ?= 0 +USB_MSC_EP_NUM_IN ?= 0 +USB_HID_EP_NUM_OUT ?= 0 +USB_HID_EP_NUM_IN ?= 0 +USB_MIDI_EP_NUM_OUT ?= 0 +USB_MIDI_EP_NUM_IN ?= 0 +USB_NUM_EP ?= 0 USB_DESCRIPTOR_ARGS = \ --manufacturer $(USB_MANUFACTURER)\ @@ -183,8 +187,8 @@ USB_DESCRIPTOR_ARGS = \ --pid $(USB_PID)\ --serial_number_length $(USB_SERIAL_NUMBER_LENGTH)\ --interface_name $(USB_INTERFACE_NAME)\ - --devices $(USB_DEVICES)\ - --hid_devices $(USB_HID_DEVICES)\ + --devices "$(USB_DEVICES)"\ + --hid_devices "$(USB_HID_DEVICES)"\ --max_ep $(USB_NUM_EP) \ --cdc_ep_num_notification $(USB_CDC_EP_NUM_NOTIFICATION)\ --cdc_ep_num_data_out $(USB_CDC_EP_NUM_DATA_OUT)\ diff --git a/tools/gen_usb_descriptor.py b/tools/gen_usb_descriptor.py index 672f09c889..199cdf9cf6 100644 --- a/tools/gen_usb_descriptor.py +++ b/tools/gen_usb_descriptor.py @@ -12,60 +12,98 @@ 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,MSC,AUDIO,HID' -ALL_DEVICES_SET=frozenset(ALL_DEVICES.split(',')) -DEFAULT_DEVICES='CDC,MSC,AUDIO,HID' +DEFAULT_INTERFACE_NAME = "CircuitPython" +ALL_DEVICES = "CDC CDC2 MSC AUDIO HID" +ALL_DEVICES_SET = frozenset(ALL_DEVICES.split()) +DEFAULT_DEVICES = "CDC MSC AUDIO HID" -ALL_HID_DEVICES='KEYBOARD,MOUSE,CONSUMER,SYS_CONTROL,GAMEPAD,DIGITIZER,XAC_COMPATIBLE_GAMEPAD,RAW' -ALL_HID_DEVICES_SET=frozenset(ALL_HID_DEVICES.split(',')) +ALL_HID_DEVICES = ( + "KEYBOARD MOUSE CONSUMER SYS_CONTROL GAMEPAD DIGITIZER XAC_COMPATIBLE_GAMEPAD RAW" +) +ALL_HID_DEVICES_SET = frozenset(ALL_HID_DEVICES.split()) # Digitizer works on Linux but conflicts with mouse, so omit it. -DEFAULT_HID_DEVICES='KEYBOARD,MOUSE,CONSUMER,GAMEPAD' +DEFAULT_HID_DEVICES = "KEYBOARD MOUSE CONSUMER GAMEPAD" -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('--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('--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('--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) +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( + "--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( + "--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( + "--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() @@ -77,8 +115,17 @@ 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 + +if not include_cdc and include_cdc2: + raise ValueError("CDC2 requested without CDC") + if not args.renumber_endpoints: - if 'CDC' in args.devices: + if include_cdc: if args.cdc_ep_num_notification == 0: raise ValueError("CDC notification endpoint number must not be 0") elif args.cdc_ep_num_data_out == 0: @@ -86,32 +133,37 @@ if not args.renumber_endpoints: elif args.cdc_ep_num_data_in == 0: raise ValueError("CDC data IN endpoint number must not be 0") - if 'MSC' in args.devices: + if include_cdc2: + raise ValueError("Second CDC not supported without renumbering endpoints") + + if include_msc: if args.msc_ep_num_out == 0: raise ValueError("MSC endpoint OUT number must not be 0") - elif args.msc_ep_num_in == 0: + elif args.msc_ep_num_in == 0: raise ValueError("MSC endpoint IN number must not be 0") - if 'HID' in args.devices: + if include_hid: if args.args.hid_ep_num_out == 0: raise ValueError("HID endpoint OUT number must not be 0") - elif args.hid_ep_num_in == 0: + elif args.hid_ep_num_in == 0: raise ValueError("HID endpoint IN number must not be 0") - if 'AUDIO' in args.devices: + if include_audio: if args.args.midi_ep_num_out == 0: raise ValueError("MIDI endpoint OUT number must not be 0") - elif args.midi_ep_num_in == 0: + elif args.midi_ep_num_in == 0: raise ValueError("MIDI 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): + 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]: @@ -129,11 +181,12 @@ class StringIndex: 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") +SERIAL_NUMBER_INDEX = StringIndex.index( + "S" * args.serial_number_length, variable_name="usb_serial_number" +) device = standard.DeviceDescriptor( description="top", @@ -141,212 +194,267 @@ device = standard.DeviceDescriptor( idProduct=args.pid, iManufacturer=StringIndex.index(args.manufacturer), iProduct=StringIndex.index(args.product), - iSerialNumber=SERIAL_NUMBER_INDEX) + iSerialNumber=SERIAL_NUMBER_INDEX, +) # Interface numbers are interface-set local and endpoints are interface local # until util.join_interfaces renumbers them. -cdc_union = cdc.Union( - description="CDC comm", - bMasterInterface=0x00, # Adjust this after interfaces are renumbered. - bSlaveInterface_list=[0x01]) # Adjust this after interfaces are renumbered. -cdc_call_management = cdc.CallManagement( - description="CDC comm", - bmCapabilities=0x01, - bDataInterface=0x01) # Adjust this after interfaces are renumbered. +def make_cdc_union(name): + return cdc.Union( + description="{} comm".format(name), + bMasterInterface=0x00, # Adjust this after interfaces are renumbered. + bSlaveInterface_list=[0x01], + ) # Adjust this after interfaces are renumbered. -cdc_comm_interface = standard.InterfaceDescriptor( - description="CDC comm", - bInterfaceClass=cdc.CDC_CLASS_COMM, # Communications Device Class - bInterfaceSubClass=cdc.CDC_SUBCLASS_ACM, # Abstract control model - bInterfaceProtocol=cdc.CDC_PROTOCOL_NONE, - iInterface=StringIndex.index("{} CDC control".format(args.interface_name)), - subdescriptors=[ - cdc.Header( - description="CDC comm", - bcdCDC=0x0110), - cdc_call_management, - cdc.AbstractControlManagement( - description="CDC comm", - bmCapabilities=0x02), - cdc_union, - standard.EndpointDescriptor( - description="CDC comm in", - bEndpointAddress=args.cdc_ep_num_notification | standard.EndpointDescriptor.DIRECTION_IN, - bmAttributes=standard.EndpointDescriptor.TYPE_INTERRUPT, - wMaxPacketSize=0x0040, - bInterval=0x10) - ]) -cdc_data_interface = standard.InterfaceDescriptor( - description="CDC data", - bInterfaceClass=cdc.CDC_CLASS_DATA, - iInterface=StringIndex.index("{} CDC data".format(args.interface_name)), - subdescriptors=[ - standard.EndpointDescriptor( - description="CDC data out", - bEndpointAddress=args.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="CDC data in", - bEndpointAddress=args.cdc_ep_num_data_in | standard.EndpointDescriptor.DIRECTION_IN, - bmAttributes=standard.EndpointDescriptor.TYPE_BULK, - bInterval=0, - wMaxPacketSize=512 if args.highspeed else 64), - ]) +def make_cdc_call_management(name): + return cdc.CallManagement( + description="{} comm".format(name), bmCapabilities=0x01, bDataInterface=0x01 + ) # Adjust this after interfaces are renumbered. -cdc_interfaces = [cdc_comm_interface, cdc_data_interface] -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)), +def make_cdc_comm_interface(name, cdc_union): + 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="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), - ] + description="{} comm in".format(name), + bEndpointAddress=args.cdc_ep_num_notification + | standard.EndpointDescriptor.DIRECTION_IN, + bmAttributes=standard.EndpointDescriptor.TYPE_INTERRUPT, + wMaxPacketSize=0x0040, + bInterval=0x10, + ), + ], ) -] -# 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() - for name in args.hid_devices: - 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)), +def make_cdc_data_interface(name): + return standard.InterfaceDescriptor( + description="{} data".format(name), + bInterfaceClass=cdc.CDC_CLASS_DATA, + iInterface=StringIndex.index("{} {} data".format(args.interface_name, name)), subdescriptors=[ - hid.HIDDescriptor( - description="HID", - wDescriptorLength=len(bytes(combined_hid_report_descriptor))), - hid_endpoint_in_descriptor, - hid_endpoint_out_descriptor, - ] + standard.EndpointDescriptor( + description="{} data out".format(name), + bEndpointAddress=args.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=args.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_data_interface = make_cdc_data_interface("CDC") + + 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_data_interface = make_cdc_data_interface("CDC2") + + 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() + for name in args.hid_devices: + 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, + ], ), ] -# Audio! -# In and out here are relative to CircuitPython +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 - ] + # 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, ) -audio_control_interface = standard.InterfaceDescriptor( + # 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, @@ -354,57 +462,89 @@ audio_control_interface = standard.InterfaceDescriptor( 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 + # 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 + ) interfaces_to_join = [] -if 'CDC' in args.devices: +if include_cdc: interfaces_to_join.append(cdc_interfaces) -if 'MSC' in args.devices: +if include_cdc2: + interfaces_to_join.append(cdc2_interfaces) + +if include_msc: interfaces_to_join.append(msc_interfaces) -if 'HID' in args.devices: +if include_hid: interfaces_to_join.append(hid_interfaces) -if 'AUDIO' in args.devices: +if include_audio: interfaces_to_join.append(audio_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) +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 + endpoint_address = getattr(subdescriptor, "bEndpointAddress", 0) & 0x7F if endpoint_address >= args.max_ep: - raise ValueError("Endpoint address %d of %s must be less than %d" % (endpoint_address & 0x7f, interface.description, args.max_ep)) + raise ValueError( + "Endpoint address %d of %s must be less than %d" + % (endpoint_address & 0x7F, interface.description, args.max_ep) + ) else: - print("Unable to check whether maximum number of endpoints is respected", file=sys.stderr) + print( + "Unable to check whether maximum number of endpoints is respected", + file=sys.stderr, + ) # Now adjust the CDC interface cross-references. -cdc_union.bMasterInterface = cdc_comm_interface.bInterfaceNumber -cdc_union.bSlaveInterface_list = [cdc_data_interface.bInterfaceNumber] +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 + cdc_call_management.bDataInterface = cdc_data_interface.bInterfaceNumber -cdc_iad = standard.InterfaceAssociationDescriptor( - description="CDC IAD", - 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_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 'CDC' in args.devices: +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 @@ -414,13 +554,17 @@ if 'CDC' in args.devices: descriptor_list.append(cdc_iad) descriptor_list.extend(cdc_interfaces) -if 'MSC' in args.devices: +if include_cdc2: + descriptor_list.append(cdc2_iad) + descriptor_list.extend(cdc2_interfaces) + +if include_msc: descriptor_list.extend(msc_interfaces) -if 'HID' in args.devices: +if include_hid: descriptor_list.extend(hid_interfaces) -if 'AUDIO' in args.devices: +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) @@ -429,53 +573,74 @@ if 'AUDIO' in args.devices: configuration = standard.ConfigurationDescriptor( description="Composite configuration", - wTotalLength=(standard.ConfigurationDescriptor.bLength + - sum([len(bytes(x)) for x in descriptor_list])), - bNumInterfaces=len(interfaces)) + wTotalLength=( + standard.ConfigurationDescriptor.bLength + + sum([len(bytes(x)) for x in descriptor_list]) + ), + bNumInterfaces=len(interfaces), +) descriptor_list.insert(0, configuration) -string_descriptors = [standard.StringDescriptor(string) for string in StringIndex.strings_in_order()] +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("""\ +c_file.write( + """\ #include #include "py/objtuple.h" #include "shared-bindings/usb_hid/Device.h" #include "{H_FILE_NAME}" -""".format(H_FILE_NAME=h_file.name)) +""".format( + H_FILE_NAME=h_file.name + ) +) -c_file.write("""\ +c_file.write( + """\ // {DESCRIPTION} : {CLASS} -""".format(DESCRIPTION=device.description, - CLASS=device.__class__)) +""".format( + DESCRIPTION=device.description, CLASS=device.__class__ + ) +) -c_file.write("""\ +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( + """\ }; -""") +""" +) -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("""\ + c_file.write( + """\ // {DESCRIPTION} : {CLASS} -""".format(DESCRIPTION=descriptor.description, - CLASS=descriptor.__class__)) +""".format( + DESCRIPTION=descriptor.description, CLASS=descriptor.__class__ + ) + ) b = bytes(descriptor) notes = descriptor.notes() @@ -493,17 +658,22 @@ for descriptor in descriptor_list: i += length descriptor_length += len(b) -c_file.write("""\ +c_file.write( + """\ }; -""") +""" +) pointers_to_strings = [] for idx, descriptor in enumerate(string_descriptors): - c_file.write("""\ + c_file.write( + """\ // {DESCRIPTION} : {CLASS} -""".format(DESCRIPTION=descriptor.description, - CLASS=descriptor.__class__)) +""".format( + DESCRIPTION=descriptor.description, CLASS=descriptor.__class__ + ) + ) b = bytes(descriptor) notes = descriptor.notes() @@ -517,41 +687,56 @@ for idx, descriptor in enumerate(string_descriptors): const = "const " if variable_name == "usb_serial_number": const = "" - c_file.write("""\ + c_file.write( + """\ {const}uint16_t {NAME}[] = {{ -""".format(const=const, NAME=variable_name)) +""".format( + const=const, NAME=variable_name + ) + ) pointers_to_strings.append("{name}".format(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])) + 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( + """\ }; -""") +""" + ) -c_file.write("""\ +c_file.write( + """\ // array of pointer to string descriptors uint16_t const * const string_desc_arr [] = { -""") -c_file.write(""",\ +""" +) +c_file.write( + """,\ -""".join(pointers_to_strings)) +""".join( + pointers_to_strings + ) +) -c_file.write(""" +c_file.write( + """ }; -""") +""" +) c_file.write("\n") hid_descriptor_length = len(bytes(combined_hid_report_descriptor)) # Now we values we need for the .h file. -h_file.write("""\ +h_file.write( + """\ #ifndef MICROPY_INCLUDED_AUTOGEN_USB_DESCRIPTOR_H #define MICROPY_INCLUDED_AUTOGEN_USB_DESCRIPTOR_H @@ -574,49 +759,78 @@ extern const uint8_t hid_report_descriptor[{hid_report_descriptor_length}]; // 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, +""".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), hid_report_descriptor_length=len(bytes(combined_hid_report_descriptor)), - rhport0_mode='OPT_MODE_DEVICE | OPT_MODE_HIGH_SPEED' if args.highspeed else 'OPT_MODE_DEVICE', + rhport0_mode="OPT_MODE_DEVICE | OPT_MODE_HIGH_SPEED" + if args.highspeed + else "OPT_MODE_DEVICE", hid_num_devices=len(args.hid_devices), msc_vendor=args.manufacturer[:8], - msc_product=args.product[:16])) + msc_product=args.product[:16], + ) +) # Write out the report descriptor and info -c_file.write("""\ +c_file.write( + """\ const uint8_t hid_report_descriptor[{HID_DESCRIPTOR_LENGTH}] = {{ -""".format(HID_DESCRIPTOR_LENGTH=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("""\ +c_file.write( + """\ }; -""") +""" +) # Write out USB HID report buffer definitions. for name in args.hid_devices: - c_file.write("""\ + 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)) +""".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("""\ + 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)) +""".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(""" +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("""\ + 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, @@ -627,34 +841,52 @@ for name in args.hid_devices: .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("""\ +""".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(""" +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))) +""".format( + num_devices=len(args.hid_devices) + ) +) for idx in range(len(args.hid_devices)): - c_file.write("""\ + c_file.write( + """\ (mp_obj_t) &usb_hid_devices[{idx}], -""".format(idx=idx)) -c_file.write("""\ +""".format( + idx=idx + ) + ) +c_file.write( + """\ }, }; -""") +""" +) -h_file.write("""\ +h_file.write( + """\ #endif // MICROPY_INCLUDED_AUTOGEN_USB_DESCRIPTOR_H -""") +""" +) diff --git a/tools/usb_descriptor b/tools/usb_descriptor index 701cafc50e..2eaa6114b2 160000 --- a/tools/usb_descriptor +++ b/tools/usb_descriptor @@ -1 +1 @@ -Subproject commit 701cafc50e2e574dccaf7a340eedbd64a0b41a42 +Subproject commit 2eaa6114b209fe7f0a795eda8d6a7b3b93d76d2e