From 0802b22ed6229a981d26e5d89737877cfb044f61 Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Thu, 4 Feb 2021 19:23:40 -0500 Subject: [PATCH 01/11] usb descriptors all set; rework of enabling for USB devices --- ports/atmel-samd/mpconfigport.mk | 22 +- .../lilygo_ttgo_t8_s2_st7789/mpconfigboard.mk | 1 - .../boards/microdev_micro_s2/mpconfigboard.mk | 1 - .../mpconfigboard.mk | 1 - .../mpconfigboard.mk | 1 - ports/litex/mpconfigport.mk | 3 + ports/mimxrt10xx/mpconfigport.mk | 3 + ports/nrf/mpconfigport.mk | 3 + ports/raspberrypi/mpconfigport.mk | 1 + .../boards/thunderpack_v12/mpconfigboard.mk | 4 +- ports/stm/mpconfigport.mk | 7 + py/circuitpy_mpconfig.mk | 99 +- supervisor/shared/usb/tusb_config.h | 7 +- supervisor/supervisor.mk | 112 +-- tools/gen_usb_descriptor.py | 912 +++++++++++------- tools/usb_descriptor | 2 +- 16 files changed, 741 insertions(+), 438 deletions(-) diff --git a/ports/atmel-samd/mpconfigport.mk b/ports/atmel-samd/mpconfigport.mk index fb9cdf2e66c67..9839a2aa44987 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 38a7ed1fb7bda..79b45fd92c11f 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 515617095775d..783e7ad4c7ac8 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 9857c07617370..d045cc2149e88 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 9857c07617370..d045cc2149e88 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 003fb5c2c3718..af6a94e64adc5 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 b4cc9586ac073..de594b14be38c 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 9560064fbc5c4..83924ff7958cd 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 d0a21a7b41d27..6fb4ffaae333a 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 a2e1da1011253..4436f04b77c72 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 bcecaa517023f..fe257fc505de4 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 b8fe73eea999a..e13eca6efedf2 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,36 +318,47 @@ CFLAGS += -DCIRCUITPY_UHEAP=$(CIRCUITPY_UHEAP) CIRCUITPY_USB_HID ?= 1 CFLAGS += -DCIRCUITPY_USB_HID=$(CIRCUITPY_USB_HID) -CIRCUITPY_USB_MIDI ?= 1 -CFLAGS += -DCIRCUITPY_USB_MIDI=$(CIRCUITPY_USB_MIDI) +CIRCUITPY_USB_HID_CONSUMER ?= 1 +CFLAGS += -DCIRCUITPY_USB_HID_CONSUMER=$(CIRCUITPY_USB_HID_CONSUMER) -CIRCUITPY_PEW ?= 0 -CFLAGS += -DCIRCUITPY_PEW=$(CIRCUITPY_PEW) +CIRCUITPY_USB_HID_DIGITIZER ?= 0 +CFLAGS += -DCIRCUITPY_USB_HID_DIGITIZER=$(CIRCUITPY_USB_HID_DIGITIZER) -# For debugging. -CIRCUITPY_USTACK ?= 0 -CFLAGS += -DCIRCUITPY_USTACK=$(CIRCUITPY_USTACK) +CIRCUITPY_USB_HID_GAMEPAD ?= 1 +CFLAGS += -DCIRCUITPY_USB_HID_GAMEPAD=$(CIRCUITPY_USB_HID_GAMEPAD) -# Non-module conditionals +CIRCUITPY_USB_HID_KEYBOARD ?= 1 +CFLAGS += -DCIRCUITPY_USB_HID_KEYBOARD=$(CIRCUITPY_USB_HID_KEYBOARD) -CIRCUITPY_BITBANG_APA102 ?= 0 -CFLAGS += -DCIRCUITPY_BITBANG_APA102=$(CIRCUITPY_BITBANG_APA102) +CIRCUITPY_USB_HID_MOUSE ?= 1 +CFLAGS += -DCIRCUITPY_USB_HID_MOUSE=$(CIRCUITPY_USB_HID_MOUSE) -# 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_USB_HID_SYS_CONTROL ?= 0 +CFLAGS += -DCIRCUITPY_USB_HID_CONTROL=$(CIRCUITPY_USB_HID_CONTROL) -# REPL over BLE -CIRCUITPY_SERIAL_BLE ?= 0 -CFLAGS += -DCIRCUITPY_SERIAL_BLE=$(CIRCUITPY_SERIAL_BLE) +CIRCUITPY_USB_HID_XAC_COMPATIBLE_GAMEPAD ?= 0 +CFLAGS += -DCIRCUITPY_USB_HID_XAC_COMPATIBLE_GAMEPAD=$(CIRCUITPY_USB_HID_XAC_COMPATIBLE_GAMEPAD) -CIRCUITPY_BLE_FILE_SERVICE ?= 0 -CFLAGS += -DCIRCUITPY_BLE_FILE_SERVICE=$(CIRCUITPY_BLE_FILE_SERVICE) +CIRCUITPY_USB_MIDI ?= 1 +CFLAGS += -DCIRCUITPY_USB_MIDI=$(CIRCUITPY_USB_MIDI) -# REPL over UART -CIRCUITPY_SERIAL_UART ?= 0 -CFLAGS += -DCIRCUITPY_SERIAL_UART=$(CIRCUITPY_SERIAL_UART) +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) # ulab numerics library CIRCUITPY_ULAB ?= $(CIRCUITPY_FULL_BUILD) @@ -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 15d9fabafed04..0e70367d0ce31 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 a59e99e3def2b..8fd32fa0b5d50 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 +USB_DEVICES = +ifeq ($(CIRCUITPY_USB_HID),1) +USB_DEVICES += HID +endif ifeq ($(CIRCUITPY_USB_MIDI),1) -USB_DEVICES_COMPUTED := $(USB_DEVICES_COMPUTED),AUDIO +USB_DEVICES += AUDIO endif -ifeq ($(CIRCUITPY_USB_HID),1) -USB_DEVICES_COMPUTED := $(USB_DEVICES_COMPUTED),HID +ifeq ($(CIRCUITPY_USB_MSC),1) +USB_DEVICES += MSC endif -USB_DEVICES ?= "$(USB_DEVICES_COMPUTED)" - -ifndef USB_HID_DEVICES -USB_HID_DEVICES = "KEYBOARD,MOUSE,CONSUMER,GAMEPAD" +ifeq ($(CIRCUITPY_USB_SERIAL),1) +USB_DEVICES += CDC endif - -ifndef USB_HIGHSPEED -USB_HIGHSPEED = 0 +ifeq ($(CIRCUITPY_USB_SERIAL2),1) +# Inform TinyUSB there are two CDC devices. +CFLAGS += -DCFG_TUD_CDC=2 +USB_DEVICES += CDC2 endif -ifndef USB_CDC_EP_NUM_NOTIFICATION -USB_CDC_EP_NUM_NOTIFICATION = 0 +USB_HID_DEVICES = +ifeq ($(CIRCUITPY_USB_HID_CONSUMER),1) +USB_HID_DEVICES += CONSUMER endif - -ifndef USB_CDC_EP_NUM_DATA_OUT -USB_CDC_EP_NUM_DATA_OUT = 0 +ifeq ($(CIRCUITPY_USB_HID_DIGITIZER),1) +USB_HID_DEVICES += DIGITIZER endif - -ifndef USB_CDC_EP_NUM_DATA_IN -USB_CDC_EP_NUM_DATA_IN = 0 +ifeq ($(CIRCUITPY_USB_HID_GAMEPAD),1) +USB_HID_DEVICES += GAMEPAD endif - -ifndef USB_MSC_EP_NUM_OUT -USB_MSC_EP_NUM_OUT = 0 +ifeq ($(CIRCUITPY_USB_HID_KEYBOARD),1) +USB_HID_DEVICES += KEYBOARD endif - -ifndef USB_MSC_EP_NUM_IN -USB_MSC_EP_NUM_IN = 0 +ifeq ($(CIRCUITPY_USB_HID_MOUSE),1) +USB_HID_DEVICES += MOUSE endif - -ifndef USB_HID_EP_NUM_OUT -USB_HID_EP_NUM_OUT = 0 +ifeq ($(CIRCUITPY_USB_HID_SYS_CONTROL),1) +USB_HID_DEVICES += SYS_CONTROL endif - -ifndef USB_HID_EP_NUM_IN -USB_HID_EP_NUM_IN = 0 +ifeq ($(CIRCUITPY_USB_HID_XAC_COMPATIBLE_GAMEPAD),1) +USB_HID_DEVICES += XAC_COMPATIBLE_GAMEPAD endif -ifndef USB_MIDI_EP_NUM_OUT -USB_MIDI_EP_NUM_OUT = 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_MIDI_EP_NUM_IN -USB_MIDI_EP_NUM_IN = 0 -endif +USB_HIGHSPEED ?= 0 -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 672f09c889485..199cdf9cf626b 100644 --- a/tools/gen_usb_descriptor.py +++ b/tools/gen_usb_descriptor.py @@ -12,60 +12,98 @@ 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' - -ALL_HID_DEVICES='KEYBOARD,MOUSE,CONSUMER,SYS_CONTROL,GAMEPAD,DIGITIZER,XAC_COMPATIBLE_GAMEPAD,RAW' -ALL_HID_DEVICES_SET=frozenset(ALL_HID_DEVICES.split(',')) +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()) # Digitizer works on Linux but conflicts with mouse, so omit it. -DEFAULT_HID_DEVICES='KEYBOARD,MOUSE,CONSUMER,GAMEPAD' - -parser = argparse.ArgumentParser(description='Generate USB descriptors.') -parser.add_argument('--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) +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 +) args = parser.parse_args() @@ -77,8 +115,17 @@ 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 @@ 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 @@ 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") +SERIAL_NUMBER_INDEX = StringIndex.index( + "S" * args.serial_number_length, variable_name="usb_serial_number" +) device = standard.DeviceDescriptor( description="top", @@ -141,212 +194,267 @@ def strings_in_order(cls): 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. - -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), - ]) - -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_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. + + +def make_cdc_call_management(name): + return cdc.CallManagement( + description="{} comm".format(name), bmCapabilities=0x01, bDataInterface=0x01 + ) # Adjust this after interfaces are renumbered. + + +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="{} comm in".format(name), + bEndpointAddress=args.cdc_ep_num_notification + | standard.EndpointDescriptor.DIRECTION_IN, + bmAttributes=standard.EndpointDescriptor.TYPE_INTERRUPT, + wMaxPacketSize=0x0040, + bInterval=0x10, + ), + ], + ) + + +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=[ standard.EndpointDescriptor( - description="MSC in", - bEndpointAddress=args.msc_ep_num_in | standard.EndpointDescriptor.DIRECTION_IN, + 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), + wMaxPacketSize=512 if args.highspeed else 64, + ), standard.EndpointDescriptor( - description="MSC out", - bEndpointAddress=(args.msc_ep_num_out | standard.EndpointDescriptor.DIRECTION_OUT), + 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), - ] + wMaxPacketSize=512 if args.highspeed else 64, + ), + ], ) -] - -# 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, - ] - ), +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, + ), + ], + ) ] -# 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 + +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, ], ), - 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( + ] + +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_streaming_interfaces=[], + midi_streaming_interfaces=[audio_midi_interface], ) -audio_control_interface = standard.InterfaceDescriptor( + audio_control_interface = standard.InterfaceDescriptor( description="All the audio", bInterfaceClass=audio.AUDIO_CLASS_DEVICE, bInterfaceSubClass=audio.AUDIO_SUBCLASS_CONTROL, @@ -354,57 +462,89 @@ def strings_in_order(cls): 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 + +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 -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) +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 @@ def strings_in_order(cls): 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 @@ def strings_in_order(cls): 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 @@ def strings_in_order(cls): 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 @@ def strings_in_order(cls): 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 @@ def strings_in_order(cls): // 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 @@ def strings_in_order(cls): .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 701cafc50e2e5..2eaa6114b209f 160000 --- a/tools/usb_descriptor +++ b/tools/usb_descriptor @@ -1 +1 @@ -Subproject commit 701cafc50e2e574dccaf7a340eedbd64a0b41a42 +Subproject commit 2eaa6114b209fe7f0a795eda8d6a7b3b93d76d2e From 1b7f3d11e73be5cb0f7a97a528fedad87624b97f Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Mon, 8 Feb 2021 10:57:41 -0500 Subject: [PATCH 02/11] wip --- ports/nrf/common-hal/supervisor/Runtime.c | 18 +++- shared-bindings/supervisor/Runtime.c | 111 ++++++++++++++++------ shared-bindings/supervisor/Runtime.h | 8 +- supervisor/shared/serial.c | 11 ++- 4 files changed, 113 insertions(+), 35 deletions(-) diff --git a/ports/nrf/common-hal/supervisor/Runtime.c b/ports/nrf/common-hal/supervisor/Runtime.c index ea663f897d54a..a8f50f70edb47 100755 --- a/ports/nrf/common-hal/supervisor/Runtime.c +++ b/ports/nrf/common-hal/supervisor/Runtime.c @@ -28,10 +28,24 @@ #include "shared-bindings/supervisor/Runtime.h" #include "supervisor/serial.h" -bool common_hal_get_serial_connected(void) { +bool common_hal_supervisor_runtime_get_serial_connected(void) { return (bool) serial_connected(); } -bool common_hal_get_serial_bytes_available(void) { +bool common_hal_get_supervisor_runtime_serial_bytes_available(void) { return (bool) serial_bytes_available(); } + +#if CIRCUITPY_USB_SERIAL2 +mp_obj_t common_hal_supervisor_runtime_get_serial2(void) { + return (bool) serial_connected(); +} + +bool common_hal_supervisor_runtime_get_serial2_connected(void) { + return (bool) serial_connected(); +} + +bool common_hal_get_supervisor_runtime_serial2_bytes_available(void) { + return (bool) serial_bytes_available(); +} +#endif diff --git a/shared-bindings/supervisor/Runtime.c b/shared-bindings/supervisor/Runtime.c index 8e0259a3b38f4..cfda620bc0d2d 100755 --- a/shared-bindings/supervisor/Runtime.c +++ b/shared-bindings/supervisor/Runtime.c @@ -55,43 +55,96 @@ STATIC supervisor_run_reason_t _run_reason; //| serial_connected: bool //| """Returns the USB serial communication status (read-only).""" //| +STATIC mp_obj_t supervisor_runtime_get_serial_connected(mp_obj_t self){ + return mp_obj_new_bool(common_hal_supervisor_runtime_get_serial_connected()); +} +MP_DEFINE_CONST_FUN_OBJ_1(supervisor_runtime_get_serial_connected_obj, supervisor_runtime_get_serial_connected); -STATIC mp_obj_t supervisor_get_serial_connected(mp_obj_t self){ - if (!common_hal_get_serial_connected()) { - return mp_const_false; - } - else { - return mp_const_true; - } +//| serial2: io.BytesIO +//| """Returns the USB secondary serial communication channel. +//| Raises `NotImplementedError` if it does not exist. +//| """ +//| +STATIC mp_obj_t supervisor_runtime_get_serial2(mp_obj_t self){ +#if CIRCUITPY_USB_SERIAL2 + return mp_obj_new_bool(common_hal_supervisor_runtime_get_serial2()); +#else + mp_raise_NotImplementedError(translate("serial2 not available")); +#endif } -MP_DEFINE_CONST_FUN_OBJ_1(supervisor_get_serial_connected_obj, supervisor_get_serial_connected); +MP_DEFINE_CONST_FUN_OBJ_1(supervisor_runtime_get_serial2_obj, supervisor_runtime_get_serial2); + +const mp_obj_property_t supervisor_runtime_serial2_connected_obj = { + .base.type = &mp_type_property, + .proxy = {(mp_obj_t)&supervisor_runtime_get_serial2_connected_obj, + (mp_obj_t)&mp_const_none_obj, + (mp_obj_t)&mp_const_none_obj}, +}; -const mp_obj_property_t supervisor_serial_connected_obj = { +const mp_obj_property_t supervisor_runtime_serial_connected_obj = { .base.type = &mp_type_property, - .proxy = {(mp_obj_t)&supervisor_get_serial_connected_obj, + .proxy = {(mp_obj_t)&supervisor_runtime_get_serial_connected_obj, (mp_obj_t)&mp_const_none_obj, (mp_obj_t)&mp_const_none_obj}, }; +//| serial2_connected: bool +//| """Returns the USB secondary serial communication status (read-only). +//| Raises `NotImplementedError` if there is no secondary serial channel. +//| """ +//| +STATIC mp_obj_t supervisor_runtime_get_serial2_connected(mp_obj_t self){ +#if CIRCUITPY_USB_SERIAL2 + return mp_obj_new_bool(common_hal_supervisor_runtime_get_serial2_connected()); +#else + mp_raise_NotImplementedError(translate("serial2 not available")); +#endif +} +MP_DEFINE_CONST_FUN_OBJ_1(supervisor_runtime_get_serial2_connected_obj, supervisor_runtime_get_serial2_connected); + +const mp_obj_property_t supervisor_runtime_serial2_connected_obj = { + .base.type = &mp_type_property, + .proxy = {(mp_obj_t)&supervisor_runtime_get_serial2_connected_obj, + (mp_obj_t)&mp_const_none_obj, + (mp_obj_t)&mp_const_none_obj}, +}; //| serial_bytes_available: int //| """Returns the whether any bytes are available to read //| on the USB serial input. Allows for polling to see whether //| to call the built-in input() or wait. (read-only)""" //| -STATIC mp_obj_t supervisor_get_serial_bytes_available(mp_obj_t self){ - if (!common_hal_get_serial_bytes_available()) { - return mp_const_false; - } - else { - return mp_const_true; - } +STATIC mp_obj_t supervisor_runtime_get_serial_bytes_available(mp_obj_t self){ + return mp_obj_new_bool(common_hal_supervisor_runtime_get_serial_bytes_available()); +} +MP_DEFINE_CONST_FUN_OBJ_1(supervisor_runtime_get_serial_bytes_available_obj, supervisor_runtime_get_serial_bytes_available); + +const mp_obj_property_t supervisor_runtime_serial_bytes_available_obj = { + .base.type = &mp_type_property, + .proxy = {(mp_obj_t)&supervisor_runtime_get_serial_bytes_available_obj, + (mp_obj_t)&mp_const_none_obj, + (mp_obj_t)&mp_const_none_obj}, +}; + + +//| serial2_bytes_available: int +//| """Returns the whether any bytes are available to read +//| on the secondary USB serial input (read-only). +//| Raises `NotImplementedError` if there is no secondary serial input. +//| """ +//| +STATIC mp_obj_t supervisor_runtime_get_serial2_bytes_available(mp_obj_t self){ +#if CIRCUITPY_USB_SERIAL2 + return mp_obj_new_bool(common_hal_supervisor_runtime_get_serial2_bytes_available()); +#else + mp_raise_NotImplementedError(translate("serial2 not available")); +#endif } -MP_DEFINE_CONST_FUN_OBJ_1(supervisor_get_serial_bytes_available_obj, supervisor_get_serial_bytes_available); +MP_DEFINE_CONST_FUN_OBJ_1(supervisor_runtime_get_serial2_bytes_available_obj, supervisor_runtime_get_serial2_bytes_available); -const mp_obj_property_t supervisor_serial_bytes_available_obj = { +const mp_obj_property_t supervisor_runtime_serial2_bytes_available_obj = { .base.type = &mp_type_property, - .proxy = {(mp_obj_t)&supervisor_get_serial_bytes_available_obj, + .proxy = {(mp_obj_t)&supervisor_runtime_get_serial2_bytes_available_obj, (mp_obj_t)&mp_const_none_obj, (mp_obj_t)&mp_const_none_obj}, }; @@ -100,26 +153,28 @@ const mp_obj_property_t supervisor_serial_bytes_available_obj = { //| run_reason: RunReason //| """Returns why CircuitPython started running this particular time.""" //| -STATIC mp_obj_t supervisor_get_run_reason(mp_obj_t self) { +STATIC mp_obj_t supervisor_runtime_get_run_reason(mp_obj_t self) { return cp_enum_find(&supervisor_run_reason_type, _run_reason); } -MP_DEFINE_CONST_FUN_OBJ_1(supervisor_get_run_reason_obj, supervisor_get_run_reason); +MP_DEFINE_CONST_FUN_OBJ_1(supervisor_runtime_get_run_reason_obj, supervisor_runtime_get_run_reason); -const mp_obj_property_t supervisor_run_reason_obj = { +const mp_obj_property_t supervisor_runtime_run_reason_obj = { .base.type = &mp_type_property, - .proxy = {(mp_obj_t)&supervisor_get_run_reason_obj, + .proxy = {(mp_obj_t)&supervisor_runtime_get_run_reason_obj, (mp_obj_t)&mp_const_none_obj, (mp_obj_t)&mp_const_none_obj}, }; -void supervisor_set_run_reason(supervisor_run_reason_t run_reason) { +void supervisor_runtime_set_run_reason(supervisor_runtime_run_reason_t run_reason) { _run_reason = run_reason; } STATIC const mp_rom_map_elem_t supervisor_runtime_locals_dict_table[] = { - { MP_ROM_QSTR(MP_QSTR_serial_connected), MP_ROM_PTR(&supervisor_serial_connected_obj) }, - { MP_ROM_QSTR(MP_QSTR_serial_bytes_available), MP_ROM_PTR(&supervisor_serial_bytes_available_obj) }, - { MP_ROM_QSTR(MP_QSTR_run_reason), MP_ROM_PTR(&supervisor_run_reason_obj) }, + { MP_ROM_QSTR(MP_QSTR_serial_connected), MP_ROM_PTR(&supervisor_runtime_serial_connected_obj) }, + { MP_ROM_QSTR(MP_QSTR_serial2_connected), MP_ROM_PTR(&supervisor_runtime_serial2_connected_obj) }, + { MP_ROM_QSTR(MP_QSTR_serial_bytes_available), MP_ROM_PTR(&supervisor_runtime_serial_bytes_available_obj) }, + { MP_ROM_QSTR(MP_QSTR_serial2_bytes_available), MP_ROM_PTR(&supervisor_runtime_serial2_bytes_available_obj) }, + { MP_ROM_QSTR(MP_QSTR_run_reason), MP_ROM_PTR(&supervisor_runtime_run_reason_obj) }, }; STATIC MP_DEFINE_CONST_DICT(supervisor_runtime_locals_dict, supervisor_runtime_locals_dict_table); diff --git a/shared-bindings/supervisor/Runtime.h b/shared-bindings/supervisor/Runtime.h index 51ed7604df7b8..6874ac744a74b 100755 --- a/shared-bindings/supervisor/Runtime.h +++ b/shared-bindings/supervisor/Runtime.h @@ -36,12 +36,12 @@ extern const mp_obj_type_t supervisor_runtime_type; void supervisor_set_run_reason(supervisor_run_reason_t run_reason); -bool common_hal_get_serial_connected(void); +bool common_hal_supervisor_runtime_get_serial_connected(void); -bool common_hal_get_serial_bytes_available(void); +bool common_hal_supervisor_runtime_get_serial_bytes_available(void); //TODO: placeholders for future functions -//bool common_hal_get_repl_active(void); -//bool common_hal_get_usb_enumerated(void); +//bool common_hal_get_supervisor_runtime_repl_active(void); +//bool common_hal_get_supervisor_runtime_usb_enumerated(void); #endif // MICROPY_INCLUDED_SHARED_BINDINGS_SUPERVISOR_RUNTIME_H diff --git a/supervisor/shared/serial.c b/supervisor/shared/serial.c index 303f89e7521ab..6199af379e1f7 100644 --- a/supervisor/shared/serial.c +++ b/supervisor/shared/serial.c @@ -94,7 +94,6 @@ bool serial_bytes_available(void) { return tud_cdc_available() > 0; #endif } - void serial_write_substring(const char* text, uint32_t length) { if (length == 0) { return; @@ -119,3 +118,13 @@ void serial_write_substring(const char* text, uint32_t length) { void serial_write(const char* text) { serial_write_substring(text, strlen(text)); } + +#if CIRCUITPY_USB_SERIAL2 +bool serial2_bytes_available(void) { + return tud_cdc_n_available(1) > 0; +} + +bool serial2_connected(void) { + return tud_cdc_n_connected(1); +} +#endif From d54b5861a30b1b1a352e1c22fc67e729ec95390d Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Fri, 12 Feb 2021 19:01:14 -0500 Subject: [PATCH 03/11] wip --- ports/atmel-samd/mpconfigport.mk | 4 +- ports/nrf/bluetooth/ble_uart.c | 4 +- ports/nrf/common-hal/supervisor/Runtime.c | 14 -- ports/nrf/supervisor/serial.c | 10 +- ports/raspberrypi/common-hal/pwmio/PWMOut.c | 2 + py/circuitpy_defns.mk | 3 + py/circuitpy_mpconfig.h | 8 + py/circuitpy_mpconfig.mk | 32 ++-- shared-bindings/usb_cdc/Serial.c | 139 +++++++++++++++++ shared-bindings/usb_cdc/Serial.h | 40 +++++ shared-bindings/usb_cdc/__init__.c | 57 +++++++ shared-bindings/usb_cdc/__init__.h | 34 ++++ shared-module/usb_cdc/Serial.c | 44 ++++++ shared-module/usb_cdc/Serial.h | 38 +++++ shared-module/usb_cdc/__init__.c | 55 +++++++ shared-module/usb_cdc/__init__.h | 32 ++++ supervisor/shared/serial.c | 10 -- supervisor/supervisor.mk | 162 +++++++++++--------- tools/gen_usb_descriptor.py | 2 +- 19 files changed, 566 insertions(+), 124 deletions(-) create mode 100644 shared-bindings/usb_cdc/Serial.c create mode 100644 shared-bindings/usb_cdc/Serial.h create mode 100644 shared-bindings/usb_cdc/__init__.c create mode 100644 shared-bindings/usb_cdc/__init__.h create mode 100644 shared-module/usb_cdc/Serial.c create mode 100644 shared-module/usb_cdc/Serial.h create mode 100644 shared-module/usb_cdc/__init__.c create mode 100644 shared-module/usb_cdc/__init__.h diff --git a/ports/atmel-samd/mpconfigport.mk b/ports/atmel-samd/mpconfigport.mk index 9839a2aa44987..e47acfb3674da 100644 --- a/ports/atmel-samd/mpconfigport.mk +++ b/ports/atmel-samd/mpconfigport.mk @@ -49,9 +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. +# Because of that, there aren't enough endpoints for a secondary CDC serial connection. USB_MSC_EP_NUM_OUT = 1 -CIRCUITPY_USB_SERIAL2 = 0 +CIRCUITPY_USB_CDC = 0 CIRCUITPY_ULAB = 0 diff --git a/ports/nrf/bluetooth/ble_uart.c b/ports/nrf/bluetooth/ble_uart.c index 1e7a319bdd5f8..1b87b82d11b47 100644 --- a/ports/nrf/bluetooth/ble_uart.c +++ b/ports/nrf/bluetooth/ble_uart.c @@ -40,7 +40,7 @@ #include "shared-bindings/_bleio/Service.h" #include "shared-bindings/_bleio/UUID.h" -#if CIRCUITPY_SERIAL_BLE +#if CIRCUITPY_REPL_BLE static const char default_name[] = "CP-REPL"; // max 8 chars or uuid won't fit in adv data static const char NUS_UUID[] = "6e400001-b5a3-f393-e0a9-e50e24dcca9e"; @@ -190,4 +190,4 @@ void mp_hal_stdout_tx_strn(const char *str, size_t len) { } } -#endif // CIRCUITPY_SERIAL_BLE +#endif // CIRCUITPY_REPL_BLE diff --git a/ports/nrf/common-hal/supervisor/Runtime.c b/ports/nrf/common-hal/supervisor/Runtime.c index a8f50f70edb47..def609cda0c6a 100755 --- a/ports/nrf/common-hal/supervisor/Runtime.c +++ b/ports/nrf/common-hal/supervisor/Runtime.c @@ -35,17 +35,3 @@ bool common_hal_supervisor_runtime_get_serial_connected(void) { bool common_hal_get_supervisor_runtime_serial_bytes_available(void) { return (bool) serial_bytes_available(); } - -#if CIRCUITPY_USB_SERIAL2 -mp_obj_t common_hal_supervisor_runtime_get_serial2(void) { - return (bool) serial_connected(); -} - -bool common_hal_supervisor_runtime_get_serial2_connected(void) { - return (bool) serial_connected(); -} - -bool common_hal_get_supervisor_runtime_serial2_bytes_available(void) { - return (bool) serial_bytes_available(); -} -#endif diff --git a/ports/nrf/supervisor/serial.c b/ports/nrf/supervisor/serial.c index b19e9267ccdb3..2c2c0116fb093 100644 --- a/ports/nrf/supervisor/serial.c +++ b/ports/nrf/supervisor/serial.c @@ -28,15 +28,15 @@ #include "supervisor/serial.h" -#if CIRCUITPY_SERIAL_BLE +#if CIRCUITPY_REPL_BLE #include "ble_uart.h" -#elif CIRCUITPY_SERIAL_UART +#elif CIRCUITPY_REPL_UART #include #include "nrf_gpio.h" #include "nrfx_uarte.h" #endif -#if CIRCUITPY_SERIAL_BLE +#if CIRCUITPY_REPL_BLE void serial_init(void) { ble_uart_init(); @@ -58,7 +58,7 @@ void serial_write(const char *text) { ble_uart_stdout_tx_str(text); } -#elif CIRCUITPY_SERIAL_UART +#elif CIRCUITPY_REPL_UART uint8_t serial_received_char; nrfx_uarte_t serial_instance = NRFX_UARTE_INSTANCE(0); @@ -124,4 +124,4 @@ void serial_write_substring(const char *text, uint32_t len) { } } -#endif // CIRCUITPY_SERIAL_UART +#endif // CIRCUITPY_REPL_UART diff --git a/ports/raspberrypi/common-hal/pwmio/PWMOut.c b/ports/raspberrypi/common-hal/pwmio/PWMOut.c index 3d8979a036bd0..799ee887f6fe9 100644 --- a/ports/raspberrypi/common-hal/pwmio/PWMOut.c +++ b/ports/raspberrypi/common-hal/pwmio/PWMOut.c @@ -164,6 +164,7 @@ void common_hal_pwmio_pwmout_deinit(pwmio_pwmout_obj_t* self) { extern void common_hal_pwmio_pwmout_set_duty_cycle(pwmio_pwmout_obj_t* self, uint16_t duty) { self->duty_cycle = duty; uint16_t actual_duty = duty * self->top / ((1 << 16) - 1); + mp_printf(&mp_plat_print, "actual_duty: %d, self->top: %d\n", actual_duty, top); /// *** pwm_set_chan_level(self->slice, self->channel, actual_duty); } @@ -200,6 +201,7 @@ void common_hal_pwmio_pwmout_set_frequency(pwmio_pwmout_obj_t* self, uint32_t fr } else { uint32_t top = common_hal_mcu_processor_get_frequency() / frequency; self->actual_frequency = common_hal_mcu_processor_get_frequency() / top; + mp_printf(&mp_plat_print, "high speed self->top: %d\n", top); /// *** self->top = top; pwm_set_clkdiv_int_frac(self->slice, 1, 0); pwm_set_wrap(self->slice, self->top - 1); diff --git a/py/circuitpy_defns.mk b/py/circuitpy_defns.mk index f907bf7ae6326..c6dfbb5f2b006 100644 --- a/py/circuitpy_defns.mk +++ b/py/circuitpy_defns.mk @@ -286,6 +286,9 @@ endif ifeq ($(CIRCUITPY_UHEAP),1) SRC_PATTERNS += uheap/% endif +ifeq ($(CIRCUITPY_USB_CDC),1) +SRC_PATTERNS += usb_cdc/% +endif ifeq ($(CIRCUITPY_USB_HID),1) SRC_PATTERNS += usb_hid/% endif diff --git a/py/circuitpy_mpconfig.h b/py/circuitpy_mpconfig.h index ee23c715698e5..7db864a42bed5 100644 --- a/py/circuitpy_mpconfig.h +++ b/py/circuitpy_mpconfig.h @@ -736,6 +736,13 @@ extern const struct _mp_obj_module_t uheap_module; #define UHEAP_MODULE #endif +#if CIRCUITPY_USB_CDC +extern const struct _mp_obj_module_t usb_cdc_module; +#define USB_CDC_MODULE { MP_OBJ_NEW_QSTR(MP_QSTR_usb_cdc),(mp_obj_t)&usb_cdc_module }, +#else +#define USB_CDC_MODULE +#endif + #if CIRCUITPY_USB_HID extern const struct _mp_obj_module_t usb_hid_module; #define USB_HID_MODULE { MP_OBJ_NEW_QSTR(MP_QSTR_usb_hid),(mp_obj_t)&usb_hid_module }, @@ -875,6 +882,7 @@ extern const struct _mp_obj_module_t msgpack_module; SUPERVISOR_MODULE \ TOUCHIO_MODULE \ UHEAP_MODULE \ + USB_CDC_MODULE \ USB_HID_MODULE \ USB_MIDI_MODULE \ USTACK_MODULE \ diff --git a/py/circuitpy_mpconfig.mk b/py/circuitpy_mpconfig.mk index b63bbcb3eaa6e..1a12bc50be67e 100644 --- a/py/circuitpy_mpconfig.mk +++ b/py/circuitpy_mpconfig.mk @@ -232,6 +232,15 @@ CFLAGS += -DCIRCUITPY_RANDOM=$(CIRCUITPY_RANDOM) CIRCUITPY_RE ?= $(CIRCUITPY_FULL_BUILD) CFLAGS += -DCIRCUITPY_RE=$(CIRCUITPY_RE) +CIRCUITPY_REPL_BLE ?= 0 +CFLAGS += -DCIRCUITPY_REPL_BLE=$(CIRCUITPY_REPL_BLE) + +CIRCUITPY_REPL_UART ?= 0 +CFLAGS += -DCIRCUITPY_REPL_UART=$(CIRCUITPY_REPL_UART) + +CIRCUITPY_REPL_USB ?= 1 +CFLAGS += -DCIRCUITPY_REPL_USB=$(CIRCUITPY_REPL_USB) + # Should busio.I2C() check for pullups? # Some boards in combination with certain peripherals may not want this. CIRCUITPY_REQUIRE_I2C_PULLUPS ?= 1 @@ -264,18 +273,6 @@ 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) @@ -315,6 +312,10 @@ 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) +CFLAGS += -DCIRCUITPY_USB_CDC=$(CIRCUITPY_USB_CDC) + CIRCUITPY_USB_HID ?= 1 CFLAGS += -DCIRCUITPY_USB_HID=$(CIRCUITPY_USB_HID) @@ -345,9 +346,6 @@ CFLAGS += -DCIRCUITPY_USB_MIDI=$(CIRCUITPY_USB_MIDI) CIRCUITPY_USB_MSC ?= 1 CFLAGS += -DCIRCUITPY_USB_MSC=$(CIRCUITPY_USB_MSC) -CIRCUITPY_USB_SERIAL ?= 1 -CFLAGS += -DCIRCUITPY_USB_SERIAL=$(CIRCUITPY_USB_SERIAL) - CIRCUITPY_USB_VENDOR ?= 0 CFLAGS += -DCIRCUITPY_USB_VENDOR=$(CIRCUITPY_USB_VENDOR) @@ -355,10 +353,6 @@ 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) diff --git a/shared-bindings/usb_cdc/Serial.c b/shared-bindings/usb_cdc/Serial.c new file mode 100644 index 0000000000000..72e9482a257c4 --- /dev/null +++ b/shared-bindings/usb_cdc/Serial.c @@ -0,0 +1,139 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Dan Halbert for Adafruit Industries + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include + +#include "shared-bindings/usb_cdc/Serial.h" +#include "shared-bindings/util.h" + +#include "py/ioctl.h" +#include "py/objproperty.h" +#include "py/runtime.h" +#include "py/stream.h" +#include "supervisor/shared/translate.h" + +//| class Serial: +//| """Receives cdc commands over USB""" +//| +//| def __init__(self) -> None: +//| """You cannot create an instance of `usb_cdc.Serial`. +//| +//| Serial objects are constructed for every corresponding entry in the USB +//| descriptor and added to the ``usb_cdc.ports`` tuple.""" +//| ... +//| + +// These are standard stream methods. Code is in py/stream.c. +// +//| def read(self, nbytes: Optional[int] = None) -> Optional[bytes]: +//| """Read characters. If ``nbytes`` is specified then read at most that many +//| bytes. Otherwise, read everything that arrives until the connection +//| times out. Providing the number of bytes expected is highly recommended +//| because it will be faster. +//| +//| :return: Data read +//| :rtype: bytes or None""" +//| ... +//| +//| def readinto(self, buf: WriteableBuffer, nbytes: Optional[int] = None) -> Optional[bytes]: +//| """Read bytes into the ``buf``. If ``nbytes`` is specified then read at most +//| that many bytes. Otherwise, read at most ``len(buf)`` bytes. +//| +//| :return: number of bytes read and stored into ``buf`` +//| :rtype: bytes or None""" +//| ... +//| +//| def write(self, buf: ReadableBuffer) -> Optional[int]: +//| """Write the buffer of bytes to the bus. +//| +//| :return: the number of bytes written +//| :rtype: int or None""" +//| ... +//| + +// These three methods are used by the shared stream methods. +STATIC mp_uint_t usb_cdc_serial_read(mp_obj_t self_in, void *buf_in, mp_uint_t size, int *errcode) { + usb_cdc_serial_obj_t *self = MP_OBJ_TO_PTR(self_in); + byte *buf = buf_in; + + // make sure we want at least 1 char + if (size == 0) { + return 0; + } + + return common_hal_usb_cdc_serial_read(self, buf, size, errcode); +} + +STATIC mp_uint_t usb_cdc_serial_write(mp_obj_t self_in, const void *buf_in, mp_uint_t size, int *errcode) { + usb_cdc_serial_obj_t *self = MP_OBJ_TO_PTR(self_in); + const byte *buf = buf_in; + + return common_hal_usb_cdc_serial_write(self, buf, size, errcode); +} + +STATIC mp_uint_t usb_cdc_serial_ioctl(mp_obj_t self_in, mp_uint_t request, mp_uint_t arg, int *errcode) { + usb_cdc_serial_obj_t *self = MP_OBJ_TO_PTR(self_in); + mp_uint_t ret; + if (request == MP_IOCTL_POLL) { + mp_uint_t flags = arg; + ret = 0; + if ((flags & MP_IOCTL_POLL_RD) && common_hal_usb_cdc_serial_bytes_available(self) > 0) { + ret |= MP_IOCTL_POLL_RD; + } + if ((flags & MP_IOCTL_POLL_WR) && common_hal_usb_cdc_serial_ready_to_tx(self)) { + ret |= MP_IOCTL_POLL_WR; + } + } else { + *errcode = MP_EINVAL; + ret = MP_STREAM_ERROR; + } + return ret; +} + +STATIC const mp_rom_map_elem_t usb_cdc_serial_locals_dict_table[] = { + // Standard stream methods. + { MP_OBJ_NEW_QSTR(MP_QSTR_read), MP_ROM_PTR(&mp_stream_read_obj) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_readinto), MP_ROM_PTR(&mp_stream_readinto_obj) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_write), MP_ROM_PTR(&mp_stream_write_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(usb_cdc_serial_locals_dict, usb_cdc_serial_locals_dict_table); + +STATIC const mp_stream_p_t usb_cdc_serial_stream_p = { + MP_PROTO_IMPLEMENT(MP_QSTR_protocol_stream) + .read = usb_cdc_serial_read, + .write = usb_cdc_serial_write, + .ioctl = usb_cdc_serial_ioctl, + .is_text = false, +}; + +const mp_obj_type_t usb_cdc_serial_type = { + { &mp_type_type }, + .name = MP_QSTR_Serial, + .getiter = mp_identity_getiter, + .iternext = mp_stream_unbuffered_iter, + .protocol = &usb_cdc_serial_stream_p, + .locals_dict = (mp_obj_dict_t*)&usb_cdc_serial_locals_dict, +}; diff --git a/shared-bindings/usb_cdc/Serial.h b/shared-bindings/usb_cdc/Serial.h new file mode 100644 index 0000000000000..1ef4167d5e32d --- /dev/null +++ b/shared-bindings/usb_cdc/Serial.h @@ -0,0 +1,40 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Dan Halbert for Adafruit Industries + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef MICROPY_INCLUDED_SHARED_BINDINGS_USB_CDC_SERIAL_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_USB_CDC_SERIAL_H + +#include "shared-module/usb_cdc/Serial.h" + +extern const mp_obj_type_t usb_cdc_serial_type; + +extern size_t common_hal_usb_cdc_serial_read(usb_cdc_serial_obj_t *self, uint8_t *data, size_t len, int *errcode); +extern uint32_t common_hal_usb_cdc_serial_bytes_available(usb_cdc_serial_obj_t *self); +extern void common_hal_usb_cdc_serial_clear_buffer(usb_cdc_serial_obj_t *self); +extern size_t common_hal_usb_cdc_serial_write(usb_cdc_serial_obj_t *self, const uint8_t *data, size_t len, int *errcode); +extern bool common_hal_usb_cdc_serial_ready_to_tx(usb_cdc_serial_obj_t *self); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_USB_CDC_SERIAL_H diff --git a/shared-bindings/usb_cdc/__init__.c b/shared-bindings/usb_cdc/__init__.c new file mode 100644 index 0000000000000..d6a70b177a35d --- /dev/null +++ b/shared-bindings/usb_cdc/__init__.c @@ -0,0 +1,57 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Dan Halbertfor Adafruit Industries + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include + +#include "py/obj.h" +#include "py/runtime.h" + +#include "shared-bindings/usb_cdc/__init__.h" +#include "shared-bindings/usb_cdc/Serial.h" + +#include "py/runtime.h" + +//| """USB CDC Serial streams +//| +//| The `usb_cdc` module allows access to USB CDC (serial) communications. +//| +//| serial: Tuple[Serial, ...] +//| """Tuple of all CDC streams. Each item is a `Serial`.""" +//| + +mp_map_elem_t usb_cdc_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_usb_cdc) }, + { MP_ROM_QSTR(MP_QSTR_Serial), MP_OBJ_FROM_PTR(&usb_cdc_serial_type) }, + { MP_ROM_QSTR(MP_QSTR_serial), mp_const_empty_tuple }, +}; + +// This isn't const so we can set the streams dynamically. +MP_DEFINE_MUTABLE_DICT(usb_cdc_module_globals, usb_cdc_module_globals_table); + +const mp_obj_module_t usb_cdc_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t*)&usb_cdc_module_globals, +}; diff --git a/shared-bindings/usb_cdc/__init__.h b/shared-bindings/usb_cdc/__init__.h new file mode 100644 index 0000000000000..7938e91722209 --- /dev/null +++ b/shared-bindings/usb_cdc/__init__.h @@ -0,0 +1,34 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Dan Halbert for Adafruit Industries + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef MICROPY_INCLUDED_SHARED_BINDINGS_USB_CDC___INIT___H +#define MICROPY_INCLUDED_SHARED_BINDINGS_USB_CDC___INIT___H + +#include "py/obj.h" + +extern mp_obj_dict_t usb_cdc_module_globals; + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_USB_CDC___INIT___H diff --git a/shared-module/usb_cdc/Serial.c b/shared-module/usb_cdc/Serial.c new file mode 100644 index 0000000000000..663b23c259cad --- /dev/null +++ b/shared-module/usb_cdc/Serial.c @@ -0,0 +1,44 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Dan Halbert for Adafruit Industries + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "shared-module/usb_cdc/Serial.h" +#include "tusb.h" + +size_t common_hal_usb_cdc_serial_read(usb_cdc_serial_obj_t *self, uint8_t *data, size_t len, int *errcode) { + return tud_cdc_n_read(self->idx, data, len); +} + +uint32_t common_hal_usb_cdc_serial_bytes_available(usb_cdc_serial_obj_t *self) { + return tud_cdc_n_available(self->idx); +} + +size_t common_hal_usb_cdc_serial_write(usb_cdc_serial_obj_t *self, const uint8_t *data, size_t len, int *errcode) { + return tud_cdc_n_write(self->idx, data, len); +} + +bool common_hal_usb_cdc_serial_ready_to_tx(usb_cdc_serial_obj_t *self) { + return tud_cdc_n_connected(self->idx); +} diff --git a/shared-module/usb_cdc/Serial.h b/shared-module/usb_cdc/Serial.h new file mode 100644 index 0000000000000..7e3c53e8a4660 --- /dev/null +++ b/shared-module/usb_cdc/Serial.h @@ -0,0 +1,38 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Dan Halbert for Adafruit Industries + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef SHARED_MODULE_USB_CDC_SERIAL_H +#define SHARED_MODULE_USB_CDC_SERIAL_H + +#include "py/obj.h" + +typedef struct { + mp_obj_base_t base; + // Which CDC device? + uint8_t idx; +} usb_cdc_serial_obj_t; + +#endif // SHARED_MODULE_USB_CDC_SERIAL_H diff --git a/shared-module/usb_cdc/__init__.c b/shared-module/usb_cdc/__init__.c new file mode 100644 index 0000000000000..ae2412cddb06e --- /dev/null +++ b/shared-module/usb_cdc/__init__.c @@ -0,0 +1,55 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 hathach for Adafruit Industries + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "shared-bindings/usb_cdc/__init__.h" + +#include "genhdr/autogen_usb_descriptor.h" +#include "py/obj.h" +#include "py/mphal.h" +#include "py/runtime.h" +#include "py/objtuple.h" +#include "shared-bindings/usb_cdc/Serial.h" +#include "tusb.h" + +supervisor_allocation* usb_cdc_allocation; + +static usb_cdc_serial_obj_t serial_objs[CFG_TUD_CDC]; + +void usb_cdc_init(void) { + + serial_obj_ptrs *usb_cdc_serial_obj_t[CFG_TUD_CDC]; + + for (size_t i = 0; i < CFG_TUD_CDC; i++) { + serial_objs[i].base.type = &usb_cdc_serial_type; + serial_objs[i].idx = i; + serial_obj_ptrs[i] = &serial_objs[i]; + } + + serials_tuple->base.type = mp_obj_new_tuple(CFG_TUD_CDC, serials); + + repl->base.type = + repl->idx = 0; mp_map_lookup(&usb_cdc_module_globals.map, MP_ROM_QSTR(MP_QSTR_serial), MP_MAP_LOOKUP)->value = MP_OBJ_FROM_PTR(serials_tuple); +} diff --git a/shared-module/usb_cdc/__init__.h b/shared-module/usb_cdc/__init__.h new file mode 100644 index 0000000000000..02b48e1165537 --- /dev/null +++ b/shared-module/usb_cdc/__init__.h @@ -0,0 +1,32 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Dan Halbert for Adafruit Industries + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef SHARED_MODULE_USB_CDC___INIT___H +#define SHARED_MODULE_USB_CDC___INIT___H + +void usb_cdc_init(void); + +#endif /* SHARED_MODULE_USB_CDC___INIT___H */ diff --git a/supervisor/shared/serial.c b/supervisor/shared/serial.c index 40151cbf68319..9cc12de512ff3 100644 --- a/supervisor/shared/serial.c +++ b/supervisor/shared/serial.c @@ -148,13 +148,3 @@ void serial_write_substring(const char* text, uint32_t length) { void serial_write(const char* text) { serial_write_substring(text, strlen(text)); } - -#if CIRCUITPY_USB_SERIAL2 -bool serial2_bytes_available(void) { - return tud_cdc_n_available(1) > 0; -} - -bool serial2_connected(void) { - return tud_cdc_n_connected(1); -} -#endif diff --git a/supervisor/supervisor.mk b/supervisor/supervisor.mk index b36b9bc00b2d3..374b7a72bd238 100644 --- a/supervisor/supervisor.mk +++ b/supervisor/supervisor.mk @@ -33,84 +33,100 @@ endif # (Right now INTERNAL_FLASH_FILESYSTEM and (Q)SPI_FLASH_FILESYSTEM are mutually exclusive. # But that might not be true in the future.) ifdef EXTERNAL_FLASH_DEVICES - CFLAGS += -DEXTERNAL_FLASH_DEVICES=$(EXTERNAL_FLASH_DEVICES) \ - -DEXTERNAL_FLASH_DEVICE_COUNT=$(EXTERNAL_FLASH_DEVICE_COUNT) - - SRC_SUPERVISOR += supervisor/shared/external_flash/external_flash.c - ifeq ($(SPI_FLASH_FILESYSTEM),1) - SRC_SUPERVISOR += supervisor/shared/external_flash/spi_flash.c - else - endif - ifeq ($(QSPI_FLASH_FILESYSTEM),1) - SRC_SUPERVISOR += supervisor/qspi_flash.c supervisor/shared/external_flash/qspi_flash.c - endif + CFLAGS += -DEXTERNAL_FLASH_DEVICES=$(EXTERNAL_FLASH_DEVICES) \ + -DEXTERNAL_FLASH_DEVICE_COUNT=$(EXTERNAL_FLASH_DEVICE_COUNT) + + SRC_SUPERVISOR += supervisor/shared/external_flash/external_flash.c + ifeq ($(SPI_FLASH_FILESYSTEM),1) + SRC_SUPERVISOR += supervisor/shared/external_flash/spi_flash.c + endif + ifeq ($(QSPI_FLASH_FILESYSTEM),1) + SRC_SUPERVISOR += supervisor/qspi_flash.c supervisor/shared/external_flash/qspi_flash.c + endif else - ifeq ($(DISABLE_FILESYSTEM),1) - SRC_SUPERVISOR += supervisor/stub/internal_flash.c - else - SRC_SUPERVISOR += supervisor/internal_flash.c - endif + ifeq ($(DISABLE_FILESYSTEM),1) + SRC_SUPERVISOR += supervisor/stub/internal_flash.c + else + SRC_SUPERVISOR += supervisor/internal_flash.c + endif endif ifeq ($(USB),FALSE) - ifeq ($(wildcard supervisor/serial.c),) - SRC_SUPERVISOR += supervisor/stub/serial.c - else - SRC_SUPERVISOR += supervisor/serial.c - endif + ifeq ($(wildcard supervisor/serial.c),) + SRC_SUPERVISOR += supervisor/stub/serial.c + else + SRC_SUPERVISOR += supervisor/serial.c + endif else - SRC_SUPERVISOR += \ - lib/tinyusb/src/common/tusb_fifo.c \ - lib/tinyusb/src/device/usbd.c \ - lib/tinyusb/src/device/usbd_control.c \ - lib/tinyusb/src/class/msc/msc_device.c \ - lib/tinyusb/src/class/cdc/cdc_device.c \ - lib/tinyusb/src/tusb.c \ - supervisor/shared/serial.c \ - supervisor/shared/workflow.c \ - supervisor/usb.c \ - supervisor/shared/usb/usb_desc.c \ - supervisor/shared/usb/usb.c \ - supervisor/shared/usb/usb_msc_flash.c \ - $(BUILD)/autogen_usb_descriptor.c - - ifeq ($(CIRCUITPY_USB_HID), 1) - SRC_SUPERVISOR += \ - lib/tinyusb/src/class/hid/hid_device.c \ - shared-bindings/usb_hid/__init__.c \ - shared-bindings/usb_hid/Device.c \ - shared-module/usb_hid/__init__.c \ - shared-module/usb_hid/Device.c - endif - - ifeq ($(CIRCUITPY_USB_MIDI), 1) - SRC_SUPERVISOR += \ - lib/tinyusb/src/class/midi/midi_device.c \ - shared-bindings/usb_midi/__init__.c \ - shared-bindings/usb_midi/PortIn.c \ - shared-bindings/usb_midi/PortOut.c \ - shared-module/usb_midi/__init__.c \ - shared-module/usb_midi/PortIn.c \ - shared-module/usb_midi/PortOut.c - endif - - ifeq ($(CIRCUITPY_USB_VENDOR), 1) - SRC_SUPERVISOR += \ - lib/tinyusb/src/class/vendor/vendor_device.c - endif - - CFLAGS += -DUSB_AVAILABLE + SRC_SUPERVISOR += \ + lib/tinyusb/src/class/cdc/cdc_device.c \ + lib/tinyusb/src/common/tusb_fifo.c \ + lib/tinyusb/src/device/usbd.c \ + lib/tinyusb/src/device/usbd_control.c \ + lib/tinyusb/src/tusb.c \ + supervisor/shared/serial.c \ + supervisor/shared/workflow.c \ + supervisor/usb.c \ + supervisor/shared/usb/usb_desc.c \ + supervisor/shared/usb/usb.c \ + $(BUILD)/autogen_usb_descriptor.c \ + + ifeq ($(CIRCUITPY_USB_CDC), 1) + SRC_SUPERVISOR += \ + shared-bindings/usb_cdc/__init__.c \ + shared-bindings/usb_cdc/Serial.c \ + shared-module/usb_cdc/__init__.c \ + shared-module/usb_cdc/Serial.c \ + + endif + + ifeq ($(CIRCUITPY_USB_HID), 1) + SRC_SUPERVISOR += \ + lib/tinyusb/src/class/hid/hid_device.c \ + shared-bindings/usb_hid/__init__.c \ + shared-bindings/usb_hid/Device.c \ + shared-module/usb_hid/__init__.c \ + shared-module/usb_hid/Device.c \ + + endif + + ifeq ($(CIRCUITPY_USB_MIDI), 1) + SRC_SUPERVISOR += \ + lib/tinyusb/src/class/midi/midi_device.c \ + shared-bindings/usb_midi/__init__.c \ + shared-bindings/usb_midi/PortIn.c \ + shared-bindings/usb_midi/PortOut.c \ + shared-module/usb_midi/__init__.c \ + shared-module/usb_midi/PortIn.c \ + shared-module/usb_midi/PortOut.c \ + + endif + + ifeq ($(CIRCUITPY_USB_MSC), 1) + SRC_SUPERVISOR += \ + lib/tinyusb/src/class/msc/msc_device.c \ + supervisor/shared/usb/usb_msc_flash.c \ + + endif + + ifeq ($(CIRCUITPY_USB_VENDOR), 1) + SRC_SUPERVISOR += \ + lib/tinyusb/src/class/vendor/vendor_device.c \ + + endif + + CFLAGS += -DUSB_AVAILABLE endif SUPERVISOR_O = $(addprefix $(BUILD)/, $(SRC_SUPERVISOR:.c=.o)) ifeq ($(CIRCUITPY_DISPLAYIO), 1) - SRC_SUPERVISOR += \ - supervisor/shared/display.c + SRC_SUPERVISOR += \ + supervisor/shared/display.c - ifeq ($(CIRCUITPY_TERMINALIO), 1) - SUPERVISOR_O += $(BUILD)/autogen_display_resources.o - endif + ifeq ($(CIRCUITPY_TERMINALIO), 1) + SUPERVISOR_O += $(BUILD)/autogen_display_resources.o + endif endif USB_INTERFACE_NAME ?= "CircuitPython" @@ -126,16 +142,20 @@ endif # It gets added automatically. USB_WEBUSB_URL ?= "circuitpython.org" +ifeq ($(CIRCUITPY_REPL_USB),1) +USB_DEVICES += CDC +endif + +ifeq ($(CIRCUITPY_USB_HID),1) +USB_DEVICES += HID +endif 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) +ifeq ($(CIRCUITPY_USB_CDC),1) # Inform TinyUSB there are two CDC devices. CFLAGS += -DCFG_TUD_CDC=2 USB_DEVICES += CDC2 diff --git a/tools/gen_usb_descriptor.py b/tools/gen_usb_descriptor.py index b056f853a75e7..3f4249f0a6165 100644 --- a/tools/gen_usb_descriptor.py +++ b/tools/gen_usb_descriptor.py @@ -830,7 +830,6 @@ def make_cdc_iad(cdc_comm_interface, name): 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], ) @@ -844,6 +843,7 @@ def make_cdc_iad(cdc_comm_interface, name): #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), ) ) From 0b8f1b9a9039ab2ad4d43cc1d9bc388518ab0d38 Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Mon, 15 Feb 2021 20:06:18 -0500 Subject: [PATCH 04/11] wip: usb_cdc.serials --- main.c | 26 +++++++------ ports/atmel-samd/Makefile | 2 +- ports/raspberrypi/common-hal/pwmio/PWMOut.c | 2 - shared-bindings/usb_cdc/__init__.c | 9 ++--- shared-bindings/usb_cdc/__init__.h | 4 +- shared-module/usb_cdc/Serial.h | 2 +- shared-module/usb_cdc/__init__.c | 42 +++++++++++---------- shared-module/usb_cdc/__init__.h | 4 +- supervisor/shared/usb/usb.c | 5 ++- 9 files changed, 51 insertions(+), 45 deletions(-) diff --git a/main.c b/main.c index 7bfa565df43c4..7df6e293aadcd 100755 --- a/main.c +++ b/main.c @@ -69,6 +69,19 @@ #include "shared-bindings/alarm/__init__.h" #endif +#if CIRCUITPY_BLEIO +#include "shared-bindings/_bleio/__init__.h" +#include "supervisor/shared/bluetooth.h" +#endif + +#if CIRCUITPY_BOARD +#include "shared-module/board/__init__.h" +#endif + +#if CIRCUITPY_CANIO +#include "common-hal/canio/CAN.h" +#endif + #if CIRCUITPY_DISPLAYIO #include "shared-module/displayio/__init__.h" #endif @@ -81,17 +94,8 @@ #include "shared-module/network/__init__.h" #endif -#if CIRCUITPY_BOARD -#include "shared-module/board/__init__.h" -#endif - -#if CIRCUITPY_BLEIO -#include "shared-bindings/_bleio/__init__.h" -#include "supervisor/shared/bluetooth.h" -#endif - -#if CIRCUITPY_CANIO -#include "common-hal/canio/CAN.h" +#if CIRCUITPY_USB_CDC +#include "shared-module/usb_cdc/__init__.h" #endif #if CIRCUITPY_WIFI diff --git a/ports/atmel-samd/Makefile b/ports/atmel-samd/Makefile index 58c1c0d60ebd6..48724af0c875d 100644 --- a/ports/atmel-samd/Makefile +++ b/ports/atmel-samd/Makefile @@ -122,7 +122,7 @@ CFLAGS += -ftree-vrp $(echo PERIPHERALS_CHIP_FAMILY=$(PERIPHERALS_CHIP_FAMILY)) #Debugging/Optimization ifeq ($(DEBUG), 1) - CFLAGS += -ggdb3 -Og + CFLAGS += -ggdb3 -Og -Os # You may want to disable -flto if it interferes with debugging. CFLAGS += -flto -flto-partition=none # You may want to enable these flags to make setting breakpoints easier. diff --git a/ports/raspberrypi/common-hal/pwmio/PWMOut.c b/ports/raspberrypi/common-hal/pwmio/PWMOut.c index 6e23e236befc8..e861bab5a93d0 100644 --- a/ports/raspberrypi/common-hal/pwmio/PWMOut.c +++ b/ports/raspberrypi/common-hal/pwmio/PWMOut.c @@ -165,7 +165,6 @@ void common_hal_pwmio_pwmout_deinit(pwmio_pwmout_obj_t* self) { extern void common_hal_pwmio_pwmout_set_duty_cycle(pwmio_pwmout_obj_t* self, uint16_t duty) { self->duty_cycle = duty; uint16_t actual_duty = duty * self->top / ((1 << 16) - 1); - mp_printf(&mp_plat_print, "actual_duty: %d, self->top: %d\n", actual_duty, top); /// *** pwm_set_chan_level(self->slice, self->channel, actual_duty); } @@ -209,7 +208,6 @@ void common_hal_pwmio_pwmout_set_frequency(pwmio_pwmout_obj_t* self, uint32_t fr } else { uint32_t top = common_hal_mcu_processor_get_frequency() / frequency; self->actual_frequency = common_hal_mcu_processor_get_frequency() / top; - mp_printf(&mp_plat_print, "high speed self->top: %d\n", top); /// *** self->top = top; pwm_set_clkdiv_int_frac(self->slice, 1, 0); pwm_set_wrap(self->slice, self->top - 1); diff --git a/shared-bindings/usb_cdc/__init__.c b/shared-bindings/usb_cdc/__init__.c index d6a70b177a35d..6ded44c4f74ed 100644 --- a/shared-bindings/usb_cdc/__init__.c +++ b/shared-bindings/usb_cdc/__init__.c @@ -38,18 +38,17 @@ //| //| The `usb_cdc` module allows access to USB CDC (serial) communications. //| -//| serial: Tuple[Serial, ...] +//| serials: Tuple[Serial, ...] //| """Tuple of all CDC streams. Each item is a `Serial`.""" //| -mp_map_elem_t usb_cdc_module_globals_table[] = { +static const mp_map_elem_t usb_cdc_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_usb_cdc) }, { MP_ROM_QSTR(MP_QSTR_Serial), MP_OBJ_FROM_PTR(&usb_cdc_serial_type) }, - { MP_ROM_QSTR(MP_QSTR_serial), mp_const_empty_tuple }, + { MP_ROM_QSTR(MP_QSTR_serials), MP_OBJ_FROM_PTR(&usb_cdc_serials_tuple) }, }; -// This isn't const so we can set the streams dynamically. -MP_DEFINE_MUTABLE_DICT(usb_cdc_module_globals, usb_cdc_module_globals_table); +static MP_DEFINE_CONST_DICT(usb_cdc_module_globals, usb_cdc_module_globals_table); const mp_obj_module_t usb_cdc_module = { .base = { &mp_type_module }, diff --git a/shared-bindings/usb_cdc/__init__.h b/shared-bindings/usb_cdc/__init__.h index 7938e91722209..e81d243e6ccd8 100644 --- a/shared-bindings/usb_cdc/__init__.h +++ b/shared-bindings/usb_cdc/__init__.h @@ -27,8 +27,6 @@ #ifndef MICROPY_INCLUDED_SHARED_BINDINGS_USB_CDC___INIT___H #define MICROPY_INCLUDED_SHARED_BINDINGS_USB_CDC___INIT___H -#include "py/obj.h" - -extern mp_obj_dict_t usb_cdc_module_globals; +#include "shared-module/usb_cdc/__init__.h" #endif // MICROPY_INCLUDED_SHARED_BINDINGS_USB_CDC___INIT___H diff --git a/shared-module/usb_cdc/Serial.h b/shared-module/usb_cdc/Serial.h index 7e3c53e8a4660..60a1d1922ccbe 100644 --- a/shared-module/usb_cdc/Serial.h +++ b/shared-module/usb_cdc/Serial.h @@ -3,7 +3,7 @@ * * The MIT License (MIT) * - * Copyright (c) 2020 Dan Halbert for Adafruit Industries + * Copyright (c) 2021 Dan Halbert for Adafruit Industries * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/shared-module/usb_cdc/__init__.c b/shared-module/usb_cdc/__init__.c index ae2412cddb06e..ecb5777b26f13 100644 --- a/shared-module/usb_cdc/__init__.c +++ b/shared-module/usb_cdc/__init__.c @@ -3,7 +3,7 @@ * * The MIT License (MIT) * - * Copyright (c) 2018 hathach for Adafruit Industries + * Copyright (c) 2021 Dan Halbert for Adafruit Industries * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -24,32 +24,34 @@ * THE SOFTWARE. */ -#include "shared-bindings/usb_cdc/__init__.h" - #include "genhdr/autogen_usb_descriptor.h" +#include "py/gc.h" #include "py/obj.h" #include "py/mphal.h" #include "py/runtime.h" #include "py/objtuple.h" +#include "shared-bindings/usb_cdc/__init__.h" #include "shared-bindings/usb_cdc/Serial.h" #include "tusb.h" -supervisor_allocation* usb_cdc_allocation; - -static usb_cdc_serial_obj_t serial_objs[CFG_TUD_CDC]; +#if CFG_TUD_CDC != 2 +#error CFG_TUD_CDC must be exactly 2 +#endif -void usb_cdc_init(void) { - - serial_obj_ptrs *usb_cdc_serial_obj_t[CFG_TUD_CDC]; - - for (size_t i = 0; i < CFG_TUD_CDC; i++) { - serial_objs[i].base.type = &usb_cdc_serial_type; - serial_objs[i].idx = i; - serial_obj_ptrs[i] = &serial_objs[i]; +static const usb_cdc_serial_obj_t serial_objs[CFG_TUD_CDC] = { + { .base.type = &usb_cdc_serial_type, + .idx = 0, + }, { + .base.type = &usb_cdc_serial_type, + .idx = 1, } - - serials_tuple->base.type = mp_obj_new_tuple(CFG_TUD_CDC, serials); - - repl->base.type = - repl->idx = 0; mp_map_lookup(&usb_cdc_module_globals.map, MP_ROM_QSTR(MP_QSTR_serial), MP_MAP_LOOKUP)->value = MP_OBJ_FROM_PTR(serials_tuple); -} +}; + +const mp_rom_obj_tuple_t usb_cdc_serials_tuple = { + .base.type = &mp_type_tuple, + .len = CFG_TUD_CDC, + .items = { + &serial_objs[0], + &serial_objs[1], + }, +}; diff --git a/shared-module/usb_cdc/__init__.h b/shared-module/usb_cdc/__init__.h index 02b48e1165537..9de3eb2faab9e 100644 --- a/shared-module/usb_cdc/__init__.h +++ b/shared-module/usb_cdc/__init__.h @@ -27,6 +27,8 @@ #ifndef SHARED_MODULE_USB_CDC___INIT___H #define SHARED_MODULE_USB_CDC___INIT___H -void usb_cdc_init(void); +#include "py/objtuple.h" + +extern const mp_rom_obj_tuple_t usb_cdc_serials_tuple; #endif /* SHARED_MODULE_USB_CDC___INIT___H */ diff --git a/supervisor/shared/usb/usb.c b/supervisor/shared/usb/usb.c index 43564d5d5cb0a..af48f46737959 100644 --- a/supervisor/shared/usb/usb.c +++ b/supervisor/shared/usb/usb.c @@ -26,7 +26,6 @@ #include "py/objstr.h" #include "shared-bindings/microcontroller/Processor.h" -#include "shared-module/usb_midi/__init__.h" #include "supervisor/background_callback.h" #include "supervisor/port.h" #include "supervisor/serial.h" @@ -35,6 +34,10 @@ #include "lib/utils/interrupt_char.h" #include "lib/mp-readline/readline.h" +#if CIRCUITPY_USB_MIDI +#include "shared-module/usb_midi/__init__.h" +#endif + #include "tusb.h" #if CIRCUITPY_USB_VENDOR From c26de0136a0ad746bf0ace600a1a9d3ff428c4c6 Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Tue, 16 Feb 2021 17:11:37 -0500 Subject: [PATCH 05/11] works! no timeouts --- ports/cxd56/mpconfigport.mk | 3 + shared-bindings/usb_cdc/Serial.c | 122 ++++++++++++++++++++++++++--- shared-bindings/usb_cdc/Serial.h | 13 ++- shared-bindings/usb_cdc/__init__.c | 7 +- shared-module/usb_cdc/Serial.c | 27 ++++++- tools/gen_usb_descriptor.py | 87 ++++++++++++++------ 6 files changed, 213 insertions(+), 46 deletions(-) diff --git a/ports/cxd56/mpconfigport.mk b/ports/cxd56/mpconfigport.mk index e1ffc79d089fd..ab39e3bb7258d 100644 --- a/ports/cxd56/mpconfigport.mk +++ b/ports/cxd56/mpconfigport.mk @@ -7,6 +7,9 @@ USB_CDC_EP_NUM_DATA_IN = 1 USB_MSC_EP_NUM_OUT = 5 USB_MSC_EP_NUM_IN = 4 +# Number of USB endpoint pairs. +USB_NUM_EP = 5 + MPY_TOOL_LONGINT_IMPL = -mlongint-impl=mpz CIRCUITPY_AUDIOBUSIO = 0 diff --git a/shared-bindings/usb_cdc/Serial.c b/shared-bindings/usb_cdc/Serial.c index 72e9482a257c4..03fb94f313f71 100644 --- a/shared-bindings/usb_cdc/Serial.c +++ b/shared-bindings/usb_cdc/Serial.c @@ -67,12 +67,16 @@ //| ... //| //| def write(self, buf: ReadableBuffer) -> Optional[int]: -//| """Write the buffer of bytes to the bus. +//| """Write as many bytes as possible from the buffer of bytes. //| //| :return: the number of bytes written //| :rtype: int or None""" //| ... //| +//| def flush(self) -> None: +//| """Force out any unwritten bytes, waiting until they are written.""" +//| ... +//| // These three methods are used by the shared stream methods. STATIC mp_uint_t usb_cdc_serial_read(mp_obj_t self_in, void *buf_in, mp_uint_t size, int *errcode) { @@ -97,27 +101,121 @@ STATIC mp_uint_t usb_cdc_serial_write(mp_obj_t self_in, const void *buf_in, mp_u STATIC mp_uint_t usb_cdc_serial_ioctl(mp_obj_t self_in, mp_uint_t request, mp_uint_t arg, int *errcode) { usb_cdc_serial_obj_t *self = MP_OBJ_TO_PTR(self_in); mp_uint_t ret; - if (request == MP_IOCTL_POLL) { - mp_uint_t flags = arg; - ret = 0; - if ((flags & MP_IOCTL_POLL_RD) && common_hal_usb_cdc_serial_bytes_available(self) > 0) { - ret |= MP_IOCTL_POLL_RD; - } - if ((flags & MP_IOCTL_POLL_WR) && common_hal_usb_cdc_serial_ready_to_tx(self)) { - ret |= MP_IOCTL_POLL_WR; + switch (request) { + case MP_IOCTL_POLL: { + mp_uint_t flags = arg; + ret = 0; + if ((flags & MP_IOCTL_POLL_RD) && common_hal_usb_cdc_serial_get_in_waiting(self) > 0) { + ret |= MP_IOCTL_POLL_RD; + } + if ((flags & MP_IOCTL_POLL_WR) && common_hal_usb_cdc_serial_get_out_waiting(self) == 0) { + ret |= MP_IOCTL_POLL_WR; + } + break; } - } else { - *errcode = MP_EINVAL; - ret = MP_STREAM_ERROR; + + case MP_STREAM_FLUSH: + common_hal_usb_cdc_serial_flush(self); + break; + + default: + *errcode = MP_EINVAL; + ret = MP_STREAM_ERROR; } return ret; } +//| connected: bool +//| """True if this Serial is connected to a host. (read-only)""" +//| +STATIC mp_obj_t usb_cdc_serial_get_connected(mp_obj_t self_in) { + usb_cdc_serial_obj_t *self = MP_OBJ_TO_PTR(self_in); + return mp_obj_new_bool(common_hal_usb_cdc_serial_get_connected(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(usb_cdc_serial_get_connected_obj, usb_cdc_serial_get_connected); + +const mp_obj_property_t usb_cdc_serial__connected_obj = { + .base.type = &mp_type_property, + .proxy = {(mp_obj_t)&usb_cdc_serial_get_connected_obj, + (mp_obj_t)&mp_const_none_obj, + (mp_obj_t)&mp_const_none_obj}, +}; + +//| in_waiting: int +//| """Returns the number of bytes waiting to be read on the USB serial input. (read-only)""" +//| +STATIC mp_obj_t usb_cdc_serial_get_in_waiting(mp_obj_t self_in) { + usb_cdc_serial_obj_t *self = MP_OBJ_TO_PTR(self_in); + return mp_obj_new_int(common_hal_usb_cdc_serial_get_in_waiting(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(usb_cdc_serial_get_in_waiting_obj, usb_cdc_serial_get_in_waiting); + +const mp_obj_property_t usb_cdc_serial_in_waiting_obj = { + .base.type = &mp_type_property, + .proxy = {(mp_obj_t)&usb_cdc_serial_get_in_waiting_obj, + (mp_obj_t)&mp_const_none_obj, + (mp_obj_t)&mp_const_none_obj}, +}; + +//| out_waiting: int +//| """Returns the number of bytes waiting to be written on the USB serial output. (read-only)""" +//| +STATIC mp_obj_t usb_cdc_serial_get_out_waiting(mp_obj_t self_in) { + usb_cdc_serial_obj_t *self = MP_OBJ_TO_PTR(self_in); + return mp_obj_new_int(common_hal_usb_cdc_serial_get_out_waiting(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(usb_cdc_serial_get_out_waiting_obj, usb_cdc_serial_get_out_waiting); + +const mp_obj_property_t usb_cdc_serial_out_waiting_obj = { + .base.type = &mp_type_property, + .proxy = {(mp_obj_t)&usb_cdc_serial_get_out_waiting_obj, + (mp_obj_t)&mp_const_none_obj, + (mp_obj_t)&mp_const_none_obj}, +}; + +//| def reset_input_buffer(self) -> None: +//| """Clears any unread bytes.""" +//| ... +//| +STATIC mp_obj_t usb_cdc_serial_reset_input_buffer(mp_obj_t self_in) { + usb_cdc_serial_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_usb_cdc_serial_reset_input_buffer(self); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(usb_cdc_serial_reset_input_buffer_obj, usb_cdc_serial_reset_input_buffer); + +//| def reset_output_buffer(self) -> None: +//| """Clears any unwritten bytes.""" +//| ... +//| +STATIC mp_obj_t usb_cdc_serial_reset_output_buffer(mp_obj_t self_in) { + usb_cdc_serial_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_usb_cdc_serial_reset_output_buffer(self); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(usb_cdc_serial_reset_output_buffer_obj, usb_cdc_serial_reset_output_buffer); + + STATIC const mp_rom_map_elem_t usb_cdc_serial_locals_dict_table[] = { // Standard stream methods. + { MP_ROM_QSTR(MP_QSTR_flush), MP_ROM_PTR(&mp_stream_flush_obj) }, { MP_OBJ_NEW_QSTR(MP_QSTR_read), MP_ROM_PTR(&mp_stream_read_obj) }, { MP_OBJ_NEW_QSTR(MP_QSTR_readinto), MP_ROM_PTR(&mp_stream_readinto_obj) }, + { MP_ROM_QSTR(MP_QSTR_readline), MP_ROM_PTR(&mp_stream_unbuffered_readline_obj)}, + { MP_ROM_QSTR(MP_QSTR_readlines), MP_ROM_PTR(&mp_stream_unbuffered_readlines_obj)}, { MP_OBJ_NEW_QSTR(MP_QSTR_write), MP_ROM_PTR(&mp_stream_write_obj) }, + + // Other pyserial-inspired attributes. + { MP_OBJ_NEW_QSTR(MP_QSTR_in_waiting), MP_ROM_PTR(&usb_cdc_serial_in_waiting_obj) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_out_waiting), MP_ROM_PTR(&usb_cdc_serial_out_waiting_obj) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_reset_input_buffer), MP_ROM_PTR(&usb_cdc_serial_reset_input_buffer_obj) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_reset_output_buffer), MP_ROM_PTR(&usb_cdc_serial_reset_output_buffer_obj) }, + + // Not in pyserial protocol. + { MP_OBJ_NEW_QSTR(MP_QSTR_connected), MP_ROM_PTR(&usb_cdc_serial_get_connected_obj) }, + + + }; STATIC MP_DEFINE_CONST_DICT(usb_cdc_serial_locals_dict, usb_cdc_serial_locals_dict_table); diff --git a/shared-bindings/usb_cdc/Serial.h b/shared-bindings/usb_cdc/Serial.h index 1ef4167d5e32d..6e54b9ad35f8d 100644 --- a/shared-bindings/usb_cdc/Serial.h +++ b/shared-bindings/usb_cdc/Serial.h @@ -32,9 +32,16 @@ extern const mp_obj_type_t usb_cdc_serial_type; extern size_t common_hal_usb_cdc_serial_read(usb_cdc_serial_obj_t *self, uint8_t *data, size_t len, int *errcode); -extern uint32_t common_hal_usb_cdc_serial_bytes_available(usb_cdc_serial_obj_t *self); -extern void common_hal_usb_cdc_serial_clear_buffer(usb_cdc_serial_obj_t *self); extern size_t common_hal_usb_cdc_serial_write(usb_cdc_serial_obj_t *self, const uint8_t *data, size_t len, int *errcode); -extern bool common_hal_usb_cdc_serial_ready_to_tx(usb_cdc_serial_obj_t *self); + +extern uint32_t common_hal_usb_cdc_serial_get_in_waiting(usb_cdc_serial_obj_t *self); +extern uint32_t common_hal_usb_cdc_serial_get_out_waiting(usb_cdc_serial_obj_t *self); + +extern void common_hal_usb_cdc_serial_reset_input_buffer(usb_cdc_serial_obj_t *self); +extern uint32_t common_hal_usb_cdc_serial_reset_output_buffer(usb_cdc_serial_obj_t *self); + +extern uint32_t common_hal_usb_cdc_serial_flush(usb_cdc_serial_obj_t *self); + +extern bool common_hal_usb_cdc_serial_get_connected(usb_cdc_serial_obj_t *self); #endif // MICROPY_INCLUDED_SHARED_BINDINGS_USB_CDC_SERIAL_H diff --git a/shared-bindings/usb_cdc/__init__.c b/shared-bindings/usb_cdc/__init__.c index 6ded44c4f74ed..eb9c25e0014af 100644 --- a/shared-bindings/usb_cdc/__init__.c +++ b/shared-bindings/usb_cdc/__init__.c @@ -36,10 +36,13 @@ //| """USB CDC Serial streams //| -//| The `usb_cdc` module allows access to USB CDC (serial) communications. +//| The `usb_cdc` module allows access to USB CDC (serial) communications.""" //| //| serials: Tuple[Serial, ...] -//| """Tuple of all CDC streams. Each item is a `Serial`.""" +//| """Tuple of all CDC streams. Each item is a `Serial`. +//| ``serials[0]`` is the USB REPL connection. +//| ``serials[1]`` is a second USB serial connection, unconnected to the REPL. +//| """ //| static const mp_map_elem_t usb_cdc_module_globals_table[] = { diff --git a/shared-module/usb_cdc/Serial.c b/shared-module/usb_cdc/Serial.c index 663b23c259cad..bd3972c8d5feb 100644 --- a/shared-module/usb_cdc/Serial.c +++ b/shared-module/usb_cdc/Serial.c @@ -31,14 +31,33 @@ size_t common_hal_usb_cdc_serial_read(usb_cdc_serial_obj_t *self, uint8_t *data, return tud_cdc_n_read(self->idx, data, len); } -uint32_t common_hal_usb_cdc_serial_bytes_available(usb_cdc_serial_obj_t *self) { +size_t common_hal_usb_cdc_serial_write(usb_cdc_serial_obj_t *self, const uint8_t *data, size_t len, int *errcode) { + uint32_t num_written = tud_cdc_n_write(self->idx, data, len); + tud_cdc_n_write_flush(self->idx); + return num_written; +} + +uint32_t common_hal_usb_cdc_serial_get_in_waiting(usb_cdc_serial_obj_t *self) { return tud_cdc_n_available(self->idx); } -size_t common_hal_usb_cdc_serial_write(usb_cdc_serial_obj_t *self, const uint8_t *data, size_t len, int *errcode) { - return tud_cdc_n_write(self->idx, data, len); +uint32_t common_hal_usb_cdc_serial_get_out_waiting(usb_cdc_serial_obj_t *self) { + // Return number of FIFO bytes currently occupied. + return CFG_TUD_CDC_TX_BUFSIZE - tud_cdc_n_write_available(self->idx); +} + +void common_hal_usb_cdc_serial_reset_input_buffer(usb_cdc_serial_obj_t *self) { + tud_cdc_n_read_flush(self->idx); +} + +uint32_t common_hal_usb_cdc_serial_reset_output_buffer(usb_cdc_serial_obj_t *self) { + return tud_cdc_n_write_clear(self->idx); +} + +uint32_t common_hal_usb_cdc_serial_flush(usb_cdc_serial_obj_t *self) { + return tud_cdc_n_write_flush(self->idx); } -bool common_hal_usb_cdc_serial_ready_to_tx(usb_cdc_serial_obj_t *self) { +bool common_hal_usb_cdc_serial_get_connected(usb_cdc_serial_obj_t *self) { return tud_cdc_n_connected(self->idx); } diff --git a/tools/gen_usb_descriptor.py b/tools/gen_usb_descriptor.py index 3f4249f0a6165..089d00ff23186 100644 --- a/tools/gen_usb_descriptor.py +++ b/tools/gen_usb_descriptor.py @@ -76,12 +76,27 @@ 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" ) @@ -146,36 +161,41 @@ 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: + if args.cdc_ep_num_data_out == 0: raise ValueError("CDC data OUT endpoint number must not be 0") - elif args.cdc_ep_num_data_in == 0: + if args.cdc_ep_num_data_in == 0: raise ValueError("CDC data IN endpoint number must not be 0") if include_cdc2: - raise ValueError("Second CDC not supported without renumbering endpoints") + 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") - elif args.msc_ep_num_in == 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") - elif args.hid_ep_num_in == 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") - elif args.midi_ep_num_in == 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") - elif args.vendor_ep_num_in == 0: + if args.vendor_ep_num_in == 0: raise ValueError("VENDOR endpoint IN number must not be 0") @@ -228,18 +248,22 @@ def strings_in_order(cls): def make_cdc_union(name): return cdc.Union( description="{} comm".format(name), - bMasterInterface=0x00, # Adjust this after interfaces are renumbered. + # Set bMasterInterface and bSlaveInterface_list to proper values after interfaces are renumbered. + bMasterInterface=0x00, bSlaveInterface_list=[0x01], - ) # Adjust this after interfaces are renumbered. + ) 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 - ) # Adjust this after interfaces are renumbered. + ) -def make_cdc_comm_interface(name, cdc_union): +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 @@ -255,7 +279,7 @@ def make_cdc_comm_interface(name, cdc_union): cdc_union, standard.EndpointDescriptor( description="{} comm in".format(name), - bEndpointAddress=args.cdc_ep_num_notification + bEndpointAddress=cdc_ep_num_notification | standard.EndpointDescriptor.DIRECTION_IN, bmAttributes=standard.EndpointDescriptor.TYPE_INTERRUPT, wMaxPacketSize=0x0040, @@ -265,7 +289,7 @@ def make_cdc_comm_interface(name, cdc_union): ) -def make_cdc_data_interface(name): +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, @@ -273,7 +297,7 @@ def make_cdc_data_interface(name): subdescriptors=[ standard.EndpointDescriptor( description="{} data out".format(name), - bEndpointAddress=args.cdc_ep_num_data_out + bEndpointAddress=cdc_ep_num_data_out | standard.EndpointDescriptor.DIRECTION_OUT, bmAttributes=standard.EndpointDescriptor.TYPE_BULK, bInterval=0, @@ -281,7 +305,7 @@ def make_cdc_data_interface(name): ), standard.EndpointDescriptor( description="{} data in".format(name), - bEndpointAddress=args.cdc_ep_num_data_in + bEndpointAddress=cdc_ep_num_data_in | standard.EndpointDescriptor.DIRECTION_IN, bmAttributes=standard.EndpointDescriptor.TYPE_BULK, bInterval=0, @@ -294,16 +318,24 @@ def make_cdc_data_interface(name): 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_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] + 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_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] @@ -842,10 +874,10 @@ def make_cdc_iad(cdc_comm_interface, name): #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), + hid_report_descriptor_length=len(bytes(combined_hid_report_descriptor)), + hid_num_devices=len(args.hid_devices), + ) ) -) if include_vendor: h_file.write( @@ -876,7 +908,10 @@ def make_cdc_iad(cdc_comm_interface, name): 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)) @@ -895,7 +930,9 @@ def make_cdc_iad(cdc_comm_interface, name): static uint8_t {name}_report_buffer[{report_length}]; """.format( name=name.lower(), - report_length=hid_report_descriptors.HID_DEVICE_DATA[name].report_length, + report_length=hid_report_descriptors.HID_DEVICE_DATA[ + name + ].report_length, ) ) From ed49c02feb7e206e962bf8a40cd69771b83e25cf Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Wed, 17 Feb 2021 23:24:11 -0500 Subject: [PATCH 06/11] add timeout; finish up for PR --- py/stream.c | 32 +++++--- py/stream.h | 4 +- shared-bindings/_bleio/CharacteristicBuffer.c | 4 +- shared-bindings/busio/UART.c | 4 +- shared-bindings/usb_cdc/Serial.c | 74 +++++++++++++------ shared-bindings/usb_cdc/Serial.h | 3 + shared-module/usb_cdc/Serial.c | 30 ++++++++ shared-module/usb_cdc/Serial.h | 4 +- shared-module/usb_cdc/__init__.c | 4 +- 9 files changed, 119 insertions(+), 40 deletions(-) diff --git a/py/stream.c b/py/stream.c index ae702bb1b69dc..0813c1d7f218c 100644 --- a/py/stream.c +++ b/py/stream.c @@ -99,14 +99,22 @@ const mp_stream_p_t *mp_get_stream_raise(mp_obj_t self_in, int flags) { STATIC mp_obj_t stream_read_generic(size_t n_args, const mp_obj_t *args, byte flags) { // What to do if sz < -1? Python docs don't specify this case. - // CPython does a readall, but here we silently let negatives through, - // and they will cause a MemoryError. + // CPython does a readall, let's do the same. mp_int_t sz; - if (n_args == 1 || args[1] == mp_const_none || ((sz = mp_obj_get_int(args[1])) == -1)) { - return stream_readall(args[0]); - } - const mp_stream_p_t *stream_p = mp_get_stream(args[0]); + if (stream_p->pyserial_read_compatibility) { + // Pyserial defaults to sz=1 if not specified. + if (n_args == 1) { + sz = 1; + } else { + // Pyserial treats negative size as 0. + sz = MAX(0, mp_obj_get_int(args[1])); + } + } else { + if (n_args == 1 || args[1] == mp_const_none || (sz = mp_obj_get_int(args[1])) <= -1) { + return stream_readall(args[0]); + } + } #if MICROPY_PY_BUILTINS_STR_UNICODE if (stream_p->is_text) { @@ -284,7 +292,7 @@ STATIC mp_obj_t stream_readinto(size_t n_args, const mp_obj_t *args) { // https://docs.python.org/3/library/socket.html#socket.socket.recv_into mp_uint_t len = bufinfo.len; if (n_args > 2) { - if (mp_get_stream(args[0])->pyserial_compatibility) { + if (mp_get_stream(args[0])->pyserial_readinto_compatibility) { mp_raise_ValueError(translate("length argument not allowed for this type")); } len = mp_obj_get_int(args[2]); @@ -297,7 +305,10 @@ STATIC mp_obj_t stream_readinto(size_t n_args, const mp_obj_t *args) { mp_uint_t out_sz = mp_stream_read_exactly(args[0], bufinfo.buf, len, &error); if (error != 0) { if (mp_is_nonblocking_error(error)) { - return mp_const_none; + // pyserial readinto never returns None, just 0. + return mp_get_stream(args[0])->pyserial_dont_return_none_compatibility + ? MP_OBJ_NEW_SMALL_INT(0) + : mp_const_none; } mp_raise_OSError(error); } else { @@ -323,7 +334,10 @@ STATIC mp_obj_t stream_readall(mp_obj_t self_in) { // If we read nothing, return None, just like read(). // Otherwise, return data read so far. if (total_size == 0) { - return mp_const_none; + // pyserial read() never returns None, just b''. + return stream_p->pyserial_dont_return_none_compatibility + ? mp_const_empty_bytes + : mp_const_none; } break; } diff --git a/py/stream.h b/py/stream.h index be6b23d40d147..c05dcfc501860 100644 --- a/py/stream.h +++ b/py/stream.h @@ -72,7 +72,9 @@ typedef struct _mp_stream_p_t { mp_uint_t (*write)(mp_obj_t obj, const void *buf, mp_uint_t size, int *errcode); mp_uint_t (*ioctl)(mp_obj_t obj, mp_uint_t request, uintptr_t arg, int *errcode); mp_uint_t is_text : 1; // default is bytes, set this for text stream - bool pyserial_compatibility: 1; // adjust API to match pyserial more closely + bool pyserial_readinto_compatibility: 1; // Disallow size parameter in readinto() + bool pyserial_read_compatibility: 1; // Disallow omitting read(size) size parameter + bool pyserial_dont_return_none_compatibility: 1; // Don't return None for read() or readinto() } mp_stream_p_t; MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(mp_stream_read_obj); diff --git a/shared-bindings/_bleio/CharacteristicBuffer.c b/shared-bindings/_bleio/CharacteristicBuffer.c index 4d6c836c1379d..333b275ffbdc0 100644 --- a/shared-bindings/_bleio/CharacteristicBuffer.c +++ b/shared-bindings/_bleio/CharacteristicBuffer.c @@ -231,8 +231,8 @@ STATIC const mp_stream_p_t characteristic_buffer_stream_p = { .write = bleio_characteristic_buffer_write, .ioctl = bleio_characteristic_buffer_ioctl, .is_text = false, - // Match PySerial when possible, such as disallowing optional length argument for .readinto() - .pyserial_compatibility = true, + // Disallow readinto() size parameter. + .pyserial_readinto_compatibility = true, }; diff --git a/shared-bindings/busio/UART.c b/shared-bindings/busio/UART.c index bf0b7e721d050..f48109fdeff44 100644 --- a/shared-bindings/busio/UART.c +++ b/shared-bindings/busio/UART.c @@ -415,8 +415,8 @@ STATIC const mp_stream_p_t uart_stream_p = { .write = busio_uart_write, .ioctl = busio_uart_ioctl, .is_text = false, - // Match PySerial when possible, such as disallowing optional length argument for .readinto() - .pyserial_compatibility = true, + // Disallow optional length argument for .readinto() + .pyserial_readinto_compatibility = true, }; const mp_obj_type_t busio_uart_type = { diff --git a/shared-bindings/usb_cdc/Serial.c b/shared-bindings/usb_cdc/Serial.c index 03fb94f313f71..64f851a48e0da 100644 --- a/shared-bindings/usb_cdc/Serial.c +++ b/shared-bindings/usb_cdc/Serial.c @@ -41,36 +41,34 @@ //| def __init__(self) -> None: //| """You cannot create an instance of `usb_cdc.Serial`. //| -//| Serial objects are constructed for every corresponding entry in the USB +//| Serial objects are pre-constructed for each CDC device in the USB //| descriptor and added to the ``usb_cdc.ports`` tuple.""" //| ... //| -// These are standard stream methods. Code is in py/stream.c. -// -//| def read(self, nbytes: Optional[int] = None) -> Optional[bytes]: -//| """Read characters. If ``nbytes`` is specified then read at most that many -//| bytes. Otherwise, read everything that arrives until the connection -//| times out. Providing the number of bytes expected is highly recommended -//| because it will be faster. +//| def read(self, size: int = 1) -> bytes: +//| """Read at most ``size`` bytes. If ``size`` exceeds the internal buffer size +//| only the bytes in the buffer will be read. If `timeout` is > 0 or ``None``, +//| and fewer than ``size`` bytes are available, keep waiting until the timeout +//| expires or ``size`` bytes are available. //| //| :return: Data read -//| :rtype: bytes or None""" +//| :rtype: bytes""" //| ... //| -//| def readinto(self, buf: WriteableBuffer, nbytes: Optional[int] = None) -> Optional[bytes]: +//| def readinto(self, buf: WriteableBuffer) -> bytes: //| """Read bytes into the ``buf``. If ``nbytes`` is specified then read at most -//| that many bytes. Otherwise, read at most ``len(buf)`` bytes. +//| that many bytes, subject to `timeout`. Otherwise, read at most ``len(buf)`` bytes. //| //| :return: number of bytes read and stored into ``buf`` -//| :rtype: bytes or None""" +//| :rtype: bytes""" //| ... //| -//| def write(self, buf: ReadableBuffer) -> Optional[int]: +//| def write(self, buf: ReadableBuffer) -> int: //| """Write as many bytes as possible from the buffer of bytes. //| //| :return: the number of bytes written -//| :rtype: int or None""" +//| :rtype: int""" //| ... //| //| def flush(self) -> None: @@ -79,7 +77,7 @@ //| // These three methods are used by the shared stream methods. -STATIC mp_uint_t usb_cdc_serial_read(mp_obj_t self_in, void *buf_in, mp_uint_t size, int *errcode) { +STATIC mp_uint_t usb_cdc_serial_read_stream(mp_obj_t self_in, void *buf_in, mp_uint_t size, int *errcode) { usb_cdc_serial_obj_t *self = MP_OBJ_TO_PTR(self_in); byte *buf = buf_in; @@ -91,16 +89,16 @@ STATIC mp_uint_t usb_cdc_serial_read(mp_obj_t self_in, void *buf_in, mp_uint_t s return common_hal_usb_cdc_serial_read(self, buf, size, errcode); } -STATIC mp_uint_t usb_cdc_serial_write(mp_obj_t self_in, const void *buf_in, mp_uint_t size, int *errcode) { +STATIC mp_uint_t usb_cdc_serial_write_stream(mp_obj_t self_in, const void *buf_in, mp_uint_t size, int *errcode) { usb_cdc_serial_obj_t *self = MP_OBJ_TO_PTR(self_in); const byte *buf = buf_in; return common_hal_usb_cdc_serial_write(self, buf, size, errcode); } -STATIC mp_uint_t usb_cdc_serial_ioctl(mp_obj_t self_in, mp_uint_t request, mp_uint_t arg, int *errcode) { +STATIC mp_uint_t usb_cdc_serial_ioctl_stream(mp_obj_t self_in, mp_uint_t request, mp_uint_t arg, int *errcode) { usb_cdc_serial_obj_t *self = MP_OBJ_TO_PTR(self_in); - mp_uint_t ret; + mp_uint_t ret = 0; switch (request) { case MP_IOCTL_POLL: { mp_uint_t flags = arg; @@ -134,7 +132,7 @@ STATIC mp_obj_t usb_cdc_serial_get_connected(mp_obj_t self_in) { } MP_DEFINE_CONST_FUN_OBJ_1(usb_cdc_serial_get_connected_obj, usb_cdc_serial_get_connected); -const mp_obj_property_t usb_cdc_serial__connected_obj = { +const mp_obj_property_t usb_cdc_serial_connected_obj = { .base.type = &mp_type_property, .proxy = {(mp_obj_t)&usb_cdc_serial_get_connected_obj, (mp_obj_t)&mp_const_none_obj, @@ -195,6 +193,32 @@ STATIC mp_obj_t usb_cdc_serial_reset_output_buffer(mp_obj_t self_in) { } MP_DEFINE_CONST_FUN_OBJ_1(usb_cdc_serial_reset_output_buffer_obj, usb_cdc_serial_reset_output_buffer); +//| timeout: Optional[float] +//| """The initial value of `timeout` is ``None``. If ``None``, wait indefinitely to satisfy +//| the conditions of a read operation. If 0, do not wait. If > 0, wait only ``timeout`` seconds.""" +//| +STATIC mp_obj_t usb_cdc_serial_get_timeout(mp_obj_t self_in) { + usb_cdc_serial_obj_t *self = MP_OBJ_TO_PTR(self_in); + mp_float_t timeout = common_hal_usb_cdc_serial_get_timeout(self); + return (timeout < 0.0f) ? mp_const_none : mp_obj_new_float(self->timeout); +} +MP_DEFINE_CONST_FUN_OBJ_1(usb_cdc_serial_get_timeout_obj, usb_cdc_serial_get_timeout); + +STATIC mp_obj_t usb_cdc_serial_set_timeout(mp_obj_t self_in, mp_obj_t timeout_in) { + usb_cdc_serial_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_usb_cdc_serial_set_timeout(self, + timeout_in == mp_const_none ? -1.0f : mp_obj_get_float(timeout_in)); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(usb_cdc_serial_set_timeout_obj, usb_cdc_serial_set_timeout); + +const mp_obj_property_t usb_cdc_serial_timeout_obj = { + .base.type = &mp_type_property, + .proxy = {(mp_obj_t)&usb_cdc_serial_get_timeout_obj, + (mp_obj_t)&usb_cdc_serial_set_timeout_obj, + (mp_obj_t)&mp_const_none_obj}, +}; + STATIC const mp_rom_map_elem_t usb_cdc_serial_locals_dict_table[] = { // Standard stream methods. @@ -210,9 +234,10 @@ STATIC const mp_rom_map_elem_t usb_cdc_serial_locals_dict_table[] = { { MP_OBJ_NEW_QSTR(MP_QSTR_out_waiting), MP_ROM_PTR(&usb_cdc_serial_out_waiting_obj) }, { MP_OBJ_NEW_QSTR(MP_QSTR_reset_input_buffer), MP_ROM_PTR(&usb_cdc_serial_reset_input_buffer_obj) }, { MP_OBJ_NEW_QSTR(MP_QSTR_reset_output_buffer), MP_ROM_PTR(&usb_cdc_serial_reset_output_buffer_obj) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_timeout), MP_ROM_PTR(&usb_cdc_serial_timeout_obj) }, // Not in pyserial protocol. - { MP_OBJ_NEW_QSTR(MP_QSTR_connected), MP_ROM_PTR(&usb_cdc_serial_get_connected_obj) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_connected), MP_ROM_PTR(&usb_cdc_serial_connected_obj) }, @@ -221,10 +246,13 @@ STATIC MP_DEFINE_CONST_DICT(usb_cdc_serial_locals_dict, usb_cdc_serial_locals_di STATIC const mp_stream_p_t usb_cdc_serial_stream_p = { MP_PROTO_IMPLEMENT(MP_QSTR_protocol_stream) - .read = usb_cdc_serial_read, - .write = usb_cdc_serial_write, - .ioctl = usb_cdc_serial_ioctl, + .read = usb_cdc_serial_read_stream, + .write = usb_cdc_serial_write_stream, + .ioctl = usb_cdc_serial_ioctl_stream, .is_text = false, + .pyserial_read_compatibility = true, + .pyserial_readinto_compatibility = true, + .pyserial_dont_return_none_compatibility = true, }; const mp_obj_type_t usb_cdc_serial_type = { diff --git a/shared-bindings/usb_cdc/Serial.h b/shared-bindings/usb_cdc/Serial.h index 6e54b9ad35f8d..149d2c2d842d9 100644 --- a/shared-bindings/usb_cdc/Serial.h +++ b/shared-bindings/usb_cdc/Serial.h @@ -44,4 +44,7 @@ extern uint32_t common_hal_usb_cdc_serial_flush(usb_cdc_serial_obj_t *self); extern bool common_hal_usb_cdc_serial_get_connected(usb_cdc_serial_obj_t *self); +extern mp_float_t common_hal_usb_cdc_serial_get_timeout(usb_cdc_serial_obj_t *self); +extern void common_hal_usb_cdc_serial_set_timeout(usb_cdc_serial_obj_t *self, mp_float_t timeout); + #endif // MICROPY_INCLUDED_SHARED_BINDINGS_USB_CDC_SERIAL_H diff --git a/shared-module/usb_cdc/Serial.c b/shared-module/usb_cdc/Serial.c index bd3972c8d5feb..f569a19133240 100644 --- a/shared-module/usb_cdc/Serial.c +++ b/shared-module/usb_cdc/Serial.c @@ -24,10 +24,32 @@ * THE SOFTWARE. */ +#include "lib/utils/interrupt_char.h" #include "shared-module/usb_cdc/Serial.h" +#include "supervisor/shared/tick.h" + #include "tusb.h" size_t common_hal_usb_cdc_serial_read(usb_cdc_serial_obj_t *self, uint8_t *data, size_t len, int *errcode) { + if (self->timeout < 0.0f) { + while (tud_cdc_n_available(self->idx) < len) { + RUN_BACKGROUND_TASKS; + if (mp_hal_is_interrupted()) { + return 0; + } + } + } else if (self->timeout > 0.0f) { + uint64_t timeout_ms = self->timeout * 1000; + uint64_t start_ticks = supervisor_ticks_ms64(); + while (tud_cdc_n_available(self->idx) < len && + supervisor_ticks_ms64() - start_ticks <= timeout_ms) { + RUN_BACKGROUND_TASKS; + if (mp_hal_is_interrupted()) { + return 0; + } + } + } + // Timeout of 0.0f falls through to here with no waiting or unnecessary calculation. return tud_cdc_n_read(self->idx, data, len); } @@ -61,3 +83,11 @@ uint32_t common_hal_usb_cdc_serial_flush(usb_cdc_serial_obj_t *self) { bool common_hal_usb_cdc_serial_get_connected(usb_cdc_serial_obj_t *self) { return tud_cdc_n_connected(self->idx); } + +mp_float_t common_hal_usb_cdc_serial_get_timeout(usb_cdc_serial_obj_t *self) { + return self->timeout; +} + +void common_hal_usb_cdc_serial_set_timeout(usb_cdc_serial_obj_t *self, mp_float_t timeout) { + self->timeout = timeout; +} diff --git a/shared-module/usb_cdc/Serial.h b/shared-module/usb_cdc/Serial.h index 60a1d1922ccbe..ad4bed1a32ce3 100644 --- a/shared-module/usb_cdc/Serial.h +++ b/shared-module/usb_cdc/Serial.h @@ -31,8 +31,8 @@ typedef struct { mp_obj_base_t base; - // Which CDC device? - uint8_t idx; + mp_float_t timeout; // if negative, wait forever. + uint8_t idx; // which CDC device? } usb_cdc_serial_obj_t; #endif // SHARED_MODULE_USB_CDC_SERIAL_H diff --git a/shared-module/usb_cdc/__init__.c b/shared-module/usb_cdc/__init__.c index ecb5777b26f13..9eae188119404 100644 --- a/shared-module/usb_cdc/__init__.c +++ b/shared-module/usb_cdc/__init__.c @@ -38,11 +38,13 @@ #error CFG_TUD_CDC must be exactly 2 #endif -static const usb_cdc_serial_obj_t serial_objs[CFG_TUD_CDC] = { +static usb_cdc_serial_obj_t serial_objs[CFG_TUD_CDC] = { { .base.type = &usb_cdc_serial_type, + .timeout = -1.0f, .idx = 0, }, { .base.type = &usb_cdc_serial_type, + .timeout = -1.0f, .idx = 1, } }; From 5c37b73c6df1f7954a727401b17e7a1d31064d77 Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Thu, 18 Feb 2021 11:09:32 -0500 Subject: [PATCH 07/11] fix typo to fix nrf builds --- ports/nrf/common-hal/supervisor/Runtime.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/nrf/common-hal/supervisor/Runtime.c b/ports/nrf/common-hal/supervisor/Runtime.c index def609cda0c6a..a24e86cdf01fe 100755 --- a/ports/nrf/common-hal/supervisor/Runtime.c +++ b/ports/nrf/common-hal/supervisor/Runtime.c @@ -32,6 +32,6 @@ bool common_hal_supervisor_runtime_get_serial_connected(void) { return (bool) serial_connected(); } -bool common_hal_get_supervisor_runtime_serial_bytes_available(void) { +bool common_hal_supervisor_runtime_get_serial_bytes_available(void) { return (bool) serial_bytes_available(); } From e344c6d684dedab09b8b373af02af58382b69428 Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Thu, 18 Feb 2021 14:24:58 -0500 Subject: [PATCH 08/11] fix some builds --- ports/atmel-samd/boards/sparkfun_samd21_mini/mpconfigboard.mk | 1 + ports/cxd56/mpconfigport.mk | 2 +- ports/nrf/boards/pca10100/mpconfigboard.mk | 1 + ports/stm/boards/stm32f412zg_discovery/mpconfigboard.mk | 2 -- supervisor/shared/usb/usb.c | 2 +- 5 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ports/atmel-samd/boards/sparkfun_samd21_mini/mpconfigboard.mk b/ports/atmel-samd/boards/sparkfun_samd21_mini/mpconfigboard.mk index 9460d0009b2e6..aef38661be4a3 100644 --- a/ports/atmel-samd/boards/sparkfun_samd21_mini/mpconfigboard.mk +++ b/ports/atmel-samd/boards/sparkfun_samd21_mini/mpconfigboard.mk @@ -11,3 +11,4 @@ LONGINT_IMPL = NONE CIRCUITPY_FULL_BUILD = 0 SUPEROPT_GC = 0 +SUPEROPT_VM = 0 diff --git a/ports/cxd56/mpconfigport.mk b/ports/cxd56/mpconfigport.mk index ab39e3bb7258d..33a993ad9585e 100644 --- a/ports/cxd56/mpconfigport.mk +++ b/ports/cxd56/mpconfigport.mk @@ -8,7 +8,7 @@ USB_MSC_EP_NUM_OUT = 5 USB_MSC_EP_NUM_IN = 4 # Number of USB endpoint pairs. -USB_NUM_EP = 5 +USB_NUM_EP = 6 MPY_TOOL_LONGINT_IMPL = -mlongint-impl=mpz diff --git a/ports/nrf/boards/pca10100/mpconfigboard.mk b/ports/nrf/boards/pca10100/mpconfigboard.mk index c8cba2877cf5a..fbb9987c246ce 100644 --- a/ports/nrf/boards/pca10100/mpconfigboard.mk +++ b/ports/nrf/boards/pca10100/mpconfigboard.mk @@ -29,6 +29,7 @@ CIRCUITPY_BUSDEVICE = 0 MICROPY_PY_ASYNC_AWAIT = 0 SUPEROPT_GC = 0 +SUPEROPT_VM = 0 # Override optimization to keep binary small OPTIMIZATION_FLAGS = -Os diff --git a/ports/stm/boards/stm32f412zg_discovery/mpconfigboard.mk b/ports/stm/boards/stm32f412zg_discovery/mpconfigboard.mk index c99eb1a0082ef..28874f9c6c95f 100644 --- a/ports/stm/boards/stm32f412zg_discovery/mpconfigboard.mk +++ b/ports/stm/boards/stm32f412zg_discovery/mpconfigboard.mk @@ -16,5 +16,3 @@ MCU_PACKAGE = LQFP144 LD_COMMON = boards/common_default.ld LD_FILE = boards/STM32F412_fs.ld - -CIRCUITPY_USB_HID = 1 diff --git a/supervisor/shared/usb/usb.c b/supervisor/shared/usb/usb.c index af48f46737959..5b020b8a00dc6 100644 --- a/supervisor/shared/usb/usb.c +++ b/supervisor/shared/usb/usb.c @@ -219,7 +219,7 @@ bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_requ return true; } -#endif CIRCUITPY_USB_VENDOR +#endif // CIRCUITPY_USB_VENDOR #if MICROPY_KBD_EXCEPTION From 9d4442e298f4e86fb0686d7986ba627231dd591b Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Fri, 19 Feb 2021 14:15:31 -0500 Subject: [PATCH 09/11] handle reads/writes larger than buffers; add .write_timeout --- shared-bindings/usb_cdc/Serial.c | 30 +++++++++++- shared-bindings/usb_cdc/Serial.h | 3 ++ shared-module/usb_cdc/Serial.c | 80 ++++++++++++++++++++++++++------ shared-module/usb_cdc/Serial.h | 5 +- shared-module/usb_cdc/__init__.c | 2 + 5 files changed, 104 insertions(+), 16 deletions(-) diff --git a/shared-bindings/usb_cdc/Serial.c b/shared-bindings/usb_cdc/Serial.c index 64f851a48e0da..c813dce5b3ef8 100644 --- a/shared-bindings/usb_cdc/Serial.c +++ b/shared-bindings/usb_cdc/Serial.c @@ -56,7 +56,7 @@ //| :rtype: bytes""" //| ... //| -//| def readinto(self, buf: WriteableBuffer) -> bytes: +//| def readinto(self, buf: WriteableBuffer) -> int: //| """Read bytes into the ``buf``. If ``nbytes`` is specified then read at most //| that many bytes, subject to `timeout`. Otherwise, read at most ``len(buf)`` bytes. //| @@ -219,6 +219,33 @@ const mp_obj_property_t usb_cdc_serial_timeout_obj = { (mp_obj_t)&mp_const_none_obj}, }; +//| write_timeout: Optional[float] +//| """The initial value of `write_timeout` is ``None``. If ``None``, wait indefinitely to finish +//| writing all the bytes passed to ``write()``.If 0, do not wait. +//| If > 0, wait only ``write_timeout`` seconds.""" +//| +STATIC mp_obj_t usb_cdc_serial_get_write_timeout(mp_obj_t self_in) { + usb_cdc_serial_obj_t *self = MP_OBJ_TO_PTR(self_in); + mp_float_t write_timeout = common_hal_usb_cdc_serial_get_write_timeout(self); + return (write_timeout < 0.0f) ? mp_const_none : mp_obj_new_float(self->write_timeout); +} +MP_DEFINE_CONST_FUN_OBJ_1(usb_cdc_serial_get_write_timeout_obj, usb_cdc_serial_get_write_timeout); + +STATIC mp_obj_t usb_cdc_serial_set_write_timeout(mp_obj_t self_in, mp_obj_t write_timeout_in) { + usb_cdc_serial_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_usb_cdc_serial_set_write_timeout(self, + write_timeout_in == mp_const_none ? -1.0f : mp_obj_get_float(write_timeout_in)); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(usb_cdc_serial_set_write_timeout_obj, usb_cdc_serial_set_write_timeout); + +const mp_obj_property_t usb_cdc_serial_write_timeout_obj = { + .base.type = &mp_type_property, + .proxy = {(mp_obj_t)&usb_cdc_serial_get_write_timeout_obj, + (mp_obj_t)&usb_cdc_serial_set_write_timeout_obj, + (mp_obj_t)&mp_const_none_obj}, +}; + STATIC const mp_rom_map_elem_t usb_cdc_serial_locals_dict_table[] = { // Standard stream methods. @@ -235,6 +262,7 @@ STATIC const mp_rom_map_elem_t usb_cdc_serial_locals_dict_table[] = { { MP_OBJ_NEW_QSTR(MP_QSTR_reset_input_buffer), MP_ROM_PTR(&usb_cdc_serial_reset_input_buffer_obj) }, { MP_OBJ_NEW_QSTR(MP_QSTR_reset_output_buffer), MP_ROM_PTR(&usb_cdc_serial_reset_output_buffer_obj) }, { MP_OBJ_NEW_QSTR(MP_QSTR_timeout), MP_ROM_PTR(&usb_cdc_serial_timeout_obj) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_write_timeout), MP_ROM_PTR(&usb_cdc_serial_write_timeout_obj) }, // Not in pyserial protocol. { MP_OBJ_NEW_QSTR(MP_QSTR_connected), MP_ROM_PTR(&usb_cdc_serial_connected_obj) }, diff --git a/shared-bindings/usb_cdc/Serial.h b/shared-bindings/usb_cdc/Serial.h index 149d2c2d842d9..cdf5c3a914acd 100644 --- a/shared-bindings/usb_cdc/Serial.h +++ b/shared-bindings/usb_cdc/Serial.h @@ -47,4 +47,7 @@ extern bool common_hal_usb_cdc_serial_get_connected(usb_cdc_serial_obj_t *self); extern mp_float_t common_hal_usb_cdc_serial_get_timeout(usb_cdc_serial_obj_t *self); extern void common_hal_usb_cdc_serial_set_timeout(usb_cdc_serial_obj_t *self, mp_float_t timeout); +extern mp_float_t common_hal_usb_cdc_serial_get_write_timeout(usb_cdc_serial_obj_t *self); +extern void common_hal_usb_cdc_serial_set_write_timeout(usb_cdc_serial_obj_t *self, mp_float_t write_timeout); + #endif // MICROPY_INCLUDED_SHARED_BINDINGS_USB_CDC_SERIAL_H diff --git a/shared-module/usb_cdc/Serial.c b/shared-module/usb_cdc/Serial.c index f569a19133240..6037fddcc510b 100644 --- a/shared-module/usb_cdc/Serial.c +++ b/shared-module/usb_cdc/Serial.c @@ -31,32 +31,78 @@ #include "tusb.h" size_t common_hal_usb_cdc_serial_read(usb_cdc_serial_obj_t *self, uint8_t *data, size_t len, int *errcode) { - if (self->timeout < 0.0f) { - while (tud_cdc_n_available(self->idx) < len) { + + const bool wait_forever = self->timeout < 0.0f; + const bool wait_for_timeout = self->timeout > 0.0f; + + // Read up to len bytes immediately. + // The number of bytes read will not be larger than what is already in the TinyUSB FIFO. + uint32_t total_num_read = tud_cdc_n_read(self->idx, data, len); + + if (wait_forever || wait_for_timeout) { + // Read more if we have time. + uint64_t timeout_ms = self->timeout * 1000; // Junk value if timeout < 0. + uint64_t start_ticks = supervisor_ticks_ms64(); + + uint32_t num_read = 0; + while (total_num_read < len && + (wait_forever || supervisor_ticks_ms64() - start_ticks <= timeout_ms)) { + + // Wait for a bit, and check for ctrl-C. RUN_BACKGROUND_TASKS; if (mp_hal_is_interrupted()) { return 0; } + + // Advance buffer pointer and reduce number of bytes that need to be read. + len -= num_read; + data += num_read; + + // Try to read another batch of bytes. + num_read = tud_cdc_n_read(self->idx, data, len); + total_num_read += num_read; } - } else if (self->timeout > 0.0f) { - uint64_t timeout_ms = self->timeout * 1000; + } + + return total_num_read; +} + +size_t common_hal_usb_cdc_serial_write(usb_cdc_serial_obj_t *self, const uint8_t *data, size_t len, int *errcode) { + const bool wait_forever = self->write_timeout < 0.0f; + const bool wait_for_timeout = self->write_timeout > 0.0f; + + // Write as many bytes as possible immediately. + // The number of bytes written at once will not be larger than what can fit in the TinyUSB FIFO. + uint32_t total_num_written = tud_cdc_n_write(self->idx, data, len); + tud_cdc_n_write_flush(self->idx); + + if (wait_forever || wait_for_timeout) { + // Write more if we have time. + uint64_t timeout_ms = self->write_timeout * 1000; // Junk value if write_timeout < 0. uint64_t start_ticks = supervisor_ticks_ms64(); - while (tud_cdc_n_available(self->idx) < len && - supervisor_ticks_ms64() - start_ticks <= timeout_ms) { + + uint32_t num_written = 0; + while (total_num_written < len && + (wait_forever || supervisor_ticks_ms64() - start_ticks <= timeout_ms)) { + + // Wait for a bit, and check for ctrl-C. RUN_BACKGROUND_TASKS; if (mp_hal_is_interrupted()) { return 0; } + + // Advance buffer pointer and reduce number of bytes that need to be written. + len -= num_written; + data += num_written; + + // Try to write another batch of bytes. + num_written = tud_cdc_n_write(self->idx, data, len); + tud_cdc_n_write_flush(self->idx); + total_num_written += num_written; } } - // Timeout of 0.0f falls through to here with no waiting or unnecessary calculation. - return tud_cdc_n_read(self->idx, data, len); -} -size_t common_hal_usb_cdc_serial_write(usb_cdc_serial_obj_t *self, const uint8_t *data, size_t len, int *errcode) { - uint32_t num_written = tud_cdc_n_write(self->idx, data, len); - tud_cdc_n_write_flush(self->idx); - return num_written; + return total_num_written; } uint32_t common_hal_usb_cdc_serial_get_in_waiting(usb_cdc_serial_obj_t *self) { @@ -91,3 +137,11 @@ mp_float_t common_hal_usb_cdc_serial_get_timeout(usb_cdc_serial_obj_t *self) { void common_hal_usb_cdc_serial_set_timeout(usb_cdc_serial_obj_t *self, mp_float_t timeout) { self->timeout = timeout; } + +mp_float_t common_hal_usb_cdc_serial_get_write_timeout(usb_cdc_serial_obj_t *self) { + return self->write_timeout; +} + +void common_hal_usb_cdc_serial_set_write_timeout(usb_cdc_serial_obj_t *self, mp_float_t write_timeout) { + self->write_timeout = write_timeout; +} diff --git a/shared-module/usb_cdc/Serial.h b/shared-module/usb_cdc/Serial.h index ad4bed1a32ce3..ddf78eefa63eb 100644 --- a/shared-module/usb_cdc/Serial.h +++ b/shared-module/usb_cdc/Serial.h @@ -31,8 +31,9 @@ typedef struct { mp_obj_base_t base; - mp_float_t timeout; // if negative, wait forever. - uint8_t idx; // which CDC device? + mp_float_t timeout; // if negative, wait forever. + mp_float_t write_timeout; // if negative, wait forever. + uint8_t idx; // which CDC device? } usb_cdc_serial_obj_t; #endif // SHARED_MODULE_USB_CDC_SERIAL_H diff --git a/shared-module/usb_cdc/__init__.c b/shared-module/usb_cdc/__init__.c index 9eae188119404..fe05cb10752ec 100644 --- a/shared-module/usb_cdc/__init__.c +++ b/shared-module/usb_cdc/__init__.c @@ -41,10 +41,12 @@ static usb_cdc_serial_obj_t serial_objs[CFG_TUD_CDC] = { { .base.type = &usb_cdc_serial_type, .timeout = -1.0f, + .write_timeout = -1.0f, .idx = 0, }, { .base.type = &usb_cdc_serial_type, .timeout = -1.0f, + .write_timeout = -1.0f, .idx = 1, } }; From 076a7b71617b0ac4ef35fb6f8d15ddf06f838b4b Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Fri, 19 Feb 2021 15:30:27 -0500 Subject: [PATCH 10/11] CIRCUITPY_BITOPS merge error --- py/circuitpy_mpconfig.mk | 3 +++ 1 file changed, 3 insertions(+) diff --git a/py/circuitpy_mpconfig.mk b/py/circuitpy_mpconfig.mk index f379bce309110..9d8e53ea7f301 100644 --- a/py/circuitpy_mpconfig.mk +++ b/py/circuitpy_mpconfig.mk @@ -95,6 +95,9 @@ CFLAGS += -DCIRCUITPY_BITBANG_APA102=$(CIRCUITPY_BITBANG_APA102) CIRCUITPY_BITBANGIO ?= $(CIRCUITPY_FULL_BUILD) CFLAGS += -DCIRCUITPY_BITBANGIO=$(CIRCUITPY_BITBANGIO) +CIRCUITPY_BITOPS ?= 0 +CFLAGS += -DCIRCUITPY_BITOPS=$(CIRCUITPY_BITOPS) + # _bleio can be supported on most any board via HCI CIRCUITPY_BLEIO_HCI ?= $(CIRCUITPY_FULL_BUILD) CFLAGS += -DCIRCUITPY_BLEIO_HCI=$(CIRCUITPY_BLEIO_HCI) From 985e02093690d835ef5adf8d253e1f0405d86538 Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Fri, 19 Feb 2021 17:39:32 -0500 Subject: [PATCH 11/11] shrink simmel build --- ports/nrf/boards/simmel/mpconfigboard.mk | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ports/nrf/boards/simmel/mpconfigboard.mk b/ports/nrf/boards/simmel/mpconfigboard.mk index 6bc05a7f0849e..52743d340fd5b 100644 --- a/ports/nrf/boards/simmel/mpconfigboard.mk +++ b/ports/nrf/boards/simmel/mpconfigboard.mk @@ -3,8 +3,6 @@ USB_PID = 0xc051 USB_PRODUCT = "Simmel" USB_MANUFACTURER = "Betrusted" -CIRCUITPY_DEVICES="CDC,MSC,HID" - MCU_CHIP = nrf52833 # SPI_FLASH_FILESYSTEM = 1 @@ -31,6 +29,7 @@ CIRCUITPY_RTC = 1 CIRCUITPY_SDCARDIO = 0 CIRCUITPY_TOUCHIO = 0 CIRCUITPY_ULAB = 0 +CIRCUITPY_USB_CDC = 0 CIRCUITPY_USB_MIDI = 0 CIRCUITPY_WATCHDOG = 1