From 0b525a285af70d172eec9322df03a87aa41e2ed5 Mon Sep 17 00:00:00 2001 From: s-ol Date: Tue, 5 Apr 2022 19:53:54 +0200 Subject: [PATCH 1/4] supervisor: Add supervisor.set_usb_identification() function --- shared-bindings/supervisor/__init__.c | 64 +++++++++++++++++++++++++++ supervisor/shared/usb/usb.c | 28 +++++++++++- supervisor/shared/usb/usb_desc.c | 21 ++++----- supervisor/usb.h | 13 +++++- 4 files changed, 111 insertions(+), 15 deletions(-) diff --git a/shared-bindings/supervisor/__init__.c b/shared-bindings/supervisor/__init__.c index 0b86b7b2a6f0e..fb3b2ff3b62a6 100644 --- a/shared-bindings/supervisor/__init__.c +++ b/shared-bindings/supervisor/__init__.c @@ -38,6 +38,7 @@ #include "supervisor/shared/traceback.h" #include "supervisor/shared/translate.h" #include "supervisor/shared/workflow.h" +#include "supervisor/usb.h" #include "shared-bindings/microcontroller/__init__.h" #include "shared-bindings/supervisor/__init__.h" @@ -311,6 +312,66 @@ STATIC mp_obj_t supervisor_reset_terminal(mp_obj_t x_pixels, mp_obj_t y_pixels) } MP_DEFINE_CONST_FUN_OBJ_2(supervisor_reset_terminal_obj, supervisor_reset_terminal); +#if CIRCUITPY_USB +//| def set_usb_identification(manufacturer: Optional[str] = None, product: Optional[str] = None, vid: int = -1, pid: int = -1) -> None: +//| """Override identification constants in the USB Device Descriptor. +//| +//| If passed, `manufacturer` and `product` must be ASCII strings (or buffers) of at most 126 +//| characters. Any omitted arguments will be left at their default values. +//| +//| This method must be called in boot.py to have any effect.""" +//| ... +//| +STATIC mp_obj_t supervisor_set_usb_identification(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + static const mp_arg_t allowed_args[] = { + { MP_QSTR_manufacturer, MP_ARG_OBJ, {.u_rom_obj = mp_const_none} }, + { MP_QSTR_product, MP_ARG_OBJ, {.u_rom_obj = mp_const_none} }, + { MP_QSTR_vid, MP_ARG_INT, {.u_int = -1} }, + { MP_QSTR_pid, MP_ARG_INT, {.u_int = -1} }, + }; + struct { + mp_arg_val_t manufacturer; + mp_arg_val_t product; + mp_arg_val_t vid; + mp_arg_val_t pid; + } args; + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, (mp_arg_val_t *)&args); + + if (!usb_identification_allocation) { + usb_identification_allocation = allocate_memory(sizeof(usb_identification_t), false, true); + } + usb_identification_t *identification = (usb_identification_t *)usb_identification_allocation->ptr; + + mp_arg_validate_int_range(args.vid.u_int, -1, (1 << 16) - 1, MP_QSTR_vid); + mp_arg_validate_int_range(args.pid.u_int, -1, (1 << 16) - 1, MP_QSTR_pid); + + identification->vid = args.vid.u_int > -1 ? args.vid.u_int : USB_VID; + identification->pid = args.pid.u_int > -1 ? args.pid.u_int : USB_PID; + + mp_buffer_info_t info; + if (args.manufacturer.u_obj != mp_const_none) { + mp_get_buffer_raise(args.manufacturer.u_obj, &info, MP_BUFFER_READ); + mp_arg_validate_length_range(info.len, 0, 126, MP_QSTR_manufacturer); + memcpy(identification->manufacturer_name, info.buf, info.len); + identification->manufacturer_name[info.len] = 0; + } else { + memcpy(identification->manufacturer_name, USB_MANUFACTURER, sizeof(USB_MANUFACTURER)); + } + + if (args.product.u_obj != mp_const_none) { + mp_get_buffer_raise(args.product.u_obj, &info, MP_BUFFER_READ); + mp_arg_validate_length_range(info.len, 0, 126, MP_QSTR_product); + memcpy(identification->product_name, info.buf, info.len); + identification->product_name[info.len] = 0; + } else { + memcpy(identification->product_name, USB_MANUFACTURER, sizeof(USB_MANUFACTURER)); + } + + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_KW(supervisor_set_usb_identification_obj, 0, supervisor_set_usb_identification); +#endif + STATIC const mp_rom_map_elem_t supervisor_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_supervisor) }, { MP_ROM_QSTR(MP_QSTR_enable_autoreload), MP_ROM_PTR(&supervisor_enable_autoreload_obj) }, @@ -325,6 +386,9 @@ STATIC const mp_rom_map_elem_t supervisor_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_get_previous_traceback), MP_ROM_PTR(&supervisor_get_previous_traceback_obj) }, { MP_ROM_QSTR(MP_QSTR_disable_ble_workflow), MP_ROM_PTR(&supervisor_disable_ble_workflow_obj) }, { MP_ROM_QSTR(MP_QSTR_reset_terminal), MP_ROM_PTR(&supervisor_reset_terminal_obj) }, + #if CIRCUITPY_USB + { MP_ROM_QSTR(MP_QSTR_set_usb_identification), MP_ROM_PTR(&supervisor_set_usb_identification_obj) }, + #endif }; STATIC MP_DEFINE_CONST_DICT(supervisor_module_globals, supervisor_module_globals_table); diff --git a/supervisor/shared/usb/usb.c b/supervisor/shared/usb/usb.c index a1885448de0f2..aa239a5820e91 100644 --- a/supervisor/shared/usb/usb.c +++ b/supervisor/shared/usb/usb.c @@ -118,9 +118,11 @@ void usb_set_defaults(void) { #endif }; +supervisor_allocation *usb_identification_allocation; + // Some dynamic USB data must be saved after boot.py. How much is needed? size_t usb_boot_py_data_size(void) { - size_t size = 0; + size_t size = sizeof(usb_identification_t); #if CIRCUITPY_USB_HID size += usb_hid_report_descriptor_length(); @@ -131,6 +133,22 @@ size_t usb_boot_py_data_size(void) { // Fill in the data to save. void usb_get_boot_py_data(uint8_t *temp_storage, size_t temp_storage_size) { + if (usb_identification_allocation) { + memcpy(temp_storage, usb_identification_allocation->ptr, sizeof(usb_identification_t)); + free_memory(usb_identification_allocation); + } else { + usb_identification_t defaults = { + .vid = USB_VID, + .pid = USB_PID, + .manufacturer_name = USB_MANUFACTURER, + .product_name = USB_PRODUCT, + }; + memcpy(temp_storage, &defaults, sizeof(defaults)); + } + + temp_storage += sizeof(usb_identification_t); + temp_storage_size -= sizeof(usb_identification_t); + #if CIRCUITPY_USB_HID usb_hid_build_report_descriptor(temp_storage, temp_storage_size); #endif @@ -138,12 +156,18 @@ void usb_get_boot_py_data(uint8_t *temp_storage, size_t temp_storage_size) { // After VM is gone, save data into non-heap storage (storage_allocations). void usb_return_boot_py_data(uint8_t *temp_storage, size_t temp_storage_size) { + usb_identification_t identification; + memcpy(&identification, temp_storage, sizeof(usb_identification_t)); + + temp_storage += sizeof(usb_identification_t); + temp_storage_size -= sizeof(usb_identification_t); + #if CIRCUITPY_USB_HID usb_hid_save_report_descriptor(temp_storage, temp_storage_size); #endif // Now we can also build the rest of the descriptors and place them in storage_allocations. - usb_build_descriptors(); + usb_build_descriptors(&identification); } // Call this when ready to run code.py or a REPL, and a VM has been started. diff --git a/supervisor/shared/usb/usb_desc.c b/supervisor/shared/usb/usb_desc.c index 9fe1eadd4eb2e..fa4cf9607e72b 100644 --- a/supervisor/shared/usb/usb_desc.c +++ b/supervisor/shared/usb/usb_desc.c @@ -68,9 +68,6 @@ static supervisor_allocation *device_descriptor_allocation; static supervisor_allocation *configuration_descriptor_allocation; static supervisor_allocation *string_descriptors_allocation; -static const char manufacturer_name[] = USB_MANUFACTURER; -static const char product_name[] = USB_PRODUCT; - // Serial number string is UID length * 2 (2 nibbles per byte) + 1 byte for null termination. static char serial_number_hex_string[COMMON_HAL_MCU_PROCESSOR_UID_LENGTH * 2 + 1]; @@ -113,23 +110,23 @@ static const uint8_t configuration_descriptor_template[] = { 0x32, // 8 bMaxPower 100mA }; -static void usb_build_device_descriptor(uint16_t vid, uint16_t pid) { +static void usb_build_device_descriptor(const usb_identification_t *identification) { device_descriptor_allocation = allocate_memory(align32_size(sizeof(device_descriptor_template)), /*high_address*/ false, /*movable*/ false); uint8_t *device_descriptor = (uint8_t *)device_descriptor_allocation->ptr; memcpy(device_descriptor, device_descriptor_template, sizeof(device_descriptor_template)); - device_descriptor[DEVICE_VID_LO_INDEX] = vid & 0xFF; - device_descriptor[DEVICE_VID_HI_INDEX] = vid >> 8; - device_descriptor[DEVICE_PID_LO_INDEX] = pid & 0xFF; - device_descriptor[DEVICE_PID_HI_INDEX] = pid >> 8; + device_descriptor[DEVICE_VID_LO_INDEX] = identification->vid & 0xFF; + device_descriptor[DEVICE_VID_HI_INDEX] = identification->vid >> 8; + device_descriptor[DEVICE_PID_LO_INDEX] = identification->pid & 0xFF; + device_descriptor[DEVICE_PID_HI_INDEX] = identification->pid >> 8; - usb_add_interface_string(current_interface_string, manufacturer_name); + usb_add_interface_string(current_interface_string, identification->manufacturer_name); device_descriptor[DEVICE_MANUFACTURER_STRING_INDEX] = current_interface_string; current_interface_string++; - usb_add_interface_string(current_interface_string, product_name); + usb_add_interface_string(current_interface_string, identification->product_name); device_descriptor[DEVICE_PRODUCT_STRING_INDEX] = current_interface_string; current_interface_string++; @@ -319,7 +316,7 @@ static void usb_build_interface_string_table(void) { // After boot.py runs, the USB devices to be used have been chosen, and the descriptors can be set up. // This is called after the VM is finished, because it uses storage_allocations. -void usb_build_descriptors(void) { +void usb_build_descriptors(const usb_identification_t *identification) { uint8_t raw_id[COMMON_HAL_MCU_PROCESSOR_UID_LENGTH]; common_hal_mcu_processor_get_uid(raw_id); @@ -336,7 +333,7 @@ void usb_build_descriptors(void) { current_interface_string = 1; collected_interface_strings_length = 0; - usb_build_device_descriptor(USB_VID, USB_PID); + usb_build_device_descriptor(identification); usb_build_configuration_descriptor(); usb_build_interface_string_table(); } diff --git a/supervisor/usb.h b/supervisor/usb.h index 420f42391b0d7..3c8da6f3b7918 100644 --- a/supervisor/usb.h +++ b/supervisor/usb.h @@ -31,6 +31,8 @@ #include #include +#include "supervisor/memory.h" + // Ports must call this as frequently as they can in order to keep the USB // connection alive and responsive. Normally this is called from background // tasks after the USB IRQ handler is executed, but in specific circumstances @@ -58,10 +60,19 @@ typedef struct { size_t num_out_endpoints; } descriptor_counts_t; +typedef struct { + uint16_t vid; + uint16_t pid; + char manufacturer_name[128]; + char product_name[128]; +} usb_identification_t; + +extern supervisor_allocation *usb_identification_allocation; + // Shared implementation. bool usb_enabled(void); void usb_add_interface_string(uint8_t interface_string_index, const char str[]); -void usb_build_descriptors(void); +void usb_build_descriptors(const usb_identification_t *identification); void usb_disconnect(void); void usb_init(void); void usb_set_defaults(void); From c567b43441eb023c1111ccf5dcd82270ff0b09b9 Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Tue, 20 Sep 2022 14:32:38 -0400 Subject: [PATCH 2/4] add CIRCUITPY_USB_IDENTIFICATION to turn off on smallest builds --- ports/atmel-samd/mpconfigport.mk | 5 ++--- py/circuitpy_mpconfig.mk | 3 +++ shared-bindings/supervisor/__init__.c | 4 ++-- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/ports/atmel-samd/mpconfigport.mk b/ports/atmel-samd/mpconfigport.mk index a03fc1d320a3a..e6f5759f1c8d2 100644 --- a/ports/atmel-samd/mpconfigport.mk +++ b/ports/atmel-samd/mpconfigport.mk @@ -51,12 +51,11 @@ CIRCUITPY_ULAB = 0 CIRCUITPY_VECTORIO = 0 CIRCUITPY_ZLIB = 0 -# TODO: In CircuitPython 8.0, turn this back on, after `busio.OneWire` is removed. -# We'd like a smoother transition, but we can't afford the space to have both -# `busio.OneWire` and `onewireio.OneWire` present on these tiny builds. +# Turn off a few more things that don't fit in 192kB ifeq ($(INTERNAL_FLASH_FILESYSTEM),1) CIRCUITPY_ONEWIREIO ?= 0 +CIRCUITPY_USB_IDENTIFICATION ?= 0 endif MICROPY_PY_ASYNC_AWAIT = 0 diff --git a/py/circuitpy_mpconfig.mk b/py/circuitpy_mpconfig.mk index 0f4269c5229da..38bd031047dd2 100644 --- a/py/circuitpy_mpconfig.mk +++ b/py/circuitpy_mpconfig.mk @@ -447,6 +447,9 @@ CFLAGS += -DCIRCUITPY_USB_HID_ENABLED_DEFAULT=$(CIRCUITPY_USB_HID_ENABLED_DEFAUL CIRCUITPY_USB_HOST ?= 0 CFLAGS += -DCIRCUITPY_USB_HOST=$(CIRCUITPY_USB_HOST) +CIRCUITPY_USB_IDENTIFICATION ?= $(CIRCUITPY_USB) +CFLAGS += -DCIRCUITPY_USB_IDENTIFICATION=$(CIRCUITPY_USB_IDENTIFICATION) + # MIDI is available by default, but is not turned on if there are fewer than 8 endpoints. CIRCUITPY_USB_MIDI ?= $(CIRCUITPY_USB) CFLAGS += -DCIRCUITPY_USB_MIDI=$(CIRCUITPY_USB_MIDI) diff --git a/shared-bindings/supervisor/__init__.c b/shared-bindings/supervisor/__init__.c index 2a669eb3ad89d..0feb5055db72a 100644 --- a/shared-bindings/supervisor/__init__.c +++ b/shared-bindings/supervisor/__init__.c @@ -39,7 +39,7 @@ #include "supervisor/shared/translate/translate.h" #include "supervisor/shared/workflow.h" -#if CIRCUITPY_USB +#if CIRCUITPY_USB_IDENTIFICATION #include "supervisor/usb.h" #endif @@ -315,7 +315,7 @@ MP_DEFINE_CONST_FUN_OBJ_2(supervisor_reset_terminal_obj, supervisor_reset_termin //| ... //| STATIC mp_obj_t supervisor_set_usb_identification(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - #if CIRCUITPY_USB + #if CIRCUITPY_USB_IDENTIFICATION static const mp_arg_t allowed_args[] = { { MP_QSTR_manufacturer, MP_ARG_OBJ, {.u_rom_obj = mp_const_none} }, { MP_QSTR_product, MP_ARG_OBJ, {.u_rom_obj = mp_const_none} }, From daf415d9c7e3e249d926637e0110e2bd230a70d0 Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Tue, 20 Sep 2022 20:08:16 -0400 Subject: [PATCH 3/4] squeeze the usb identification code --- supervisor/shared/usb/usb.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/supervisor/shared/usb/usb.c b/supervisor/shared/usb/usb.c index 7837faaf3b6fc..3e64a79a7d64e 100644 --- a/supervisor/shared/usb/usb.c +++ b/supervisor/shared/usb/usb.c @@ -122,7 +122,9 @@ void usb_set_defaults(void) { #endif }; +#if CIRCUITPY_USB_IDENTIFICATION supervisor_allocation *usb_identification_allocation; +#endif // Some dynamic USB data must be saved after boot.py. How much is needed? size_t usb_boot_py_data_size(void) { @@ -137,16 +139,21 @@ size_t usb_boot_py_data_size(void) { // Fill in the data to save. void usb_get_boot_py_data(uint8_t *temp_storage, size_t temp_storage_size) { + #if CIRCUITPY_USB_IDENTIFICATION if (usb_identification_allocation) { memcpy(temp_storage, usb_identification_allocation->ptr, sizeof(usb_identification_t)); free_memory(usb_identification_allocation); + #else + if (false) { + // Nothing + #endif } else { - usb_identification_t defaults = { - .vid = USB_VID, - .pid = USB_PID, - .manufacturer_name = USB_MANUFACTURER, - .product_name = USB_PRODUCT, - }; + usb_identification_t defaults; + // This compiles to less code than using a struct initializer. + defaults.vid = USB_VID; + defaults.pid = USB_PID; + strcpy(defaults.manufacturer_name, USB_MANUFACTURER); + strcpy(defaults.product_name, USB_PRODUCT); memcpy(temp_storage, &defaults, sizeof(defaults)); } From 9aca092900cceacee8f4fd853bc9144e5f1de1b0 Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Tue, 20 Sep 2022 20:09:30 -0400 Subject: [PATCH 4/4] squeeze the usb identification code; manufacturer was being copied to product --- shared-bindings/supervisor/__init__.c | 4 ++-- supervisor/shared/usb/usb.c | 7 ++++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/shared-bindings/supervisor/__init__.c b/shared-bindings/supervisor/__init__.c index 0feb5055db72a..40f8461af4b52 100644 --- a/shared-bindings/supervisor/__init__.c +++ b/shared-bindings/supervisor/__init__.c @@ -348,7 +348,7 @@ STATIC mp_obj_t supervisor_set_usb_identification(size_t n_args, const mp_obj_t memcpy(identification->manufacturer_name, info.buf, info.len); identification->manufacturer_name[info.len] = 0; } else { - memcpy(identification->manufacturer_name, USB_MANUFACTURER, sizeof(USB_MANUFACTURER)); + strcpy(identification->manufacturer_name, USB_MANUFACTURER); } if (args.product.u_obj != mp_const_none) { @@ -357,7 +357,7 @@ STATIC mp_obj_t supervisor_set_usb_identification(size_t n_args, const mp_obj_t memcpy(identification->product_name, info.buf, info.len); identification->product_name[info.len] = 0; } else { - memcpy(identification->product_name, USB_MANUFACTURER, sizeof(USB_MANUFACTURER)); + strcpy(identification->product_name, USB_PRODUCT); } return mp_const_none; diff --git a/supervisor/shared/usb/usb.c b/supervisor/shared/usb/usb.c index 3e64a79a7d64e..a51c6c265921a 100644 --- a/supervisor/shared/usb/usb.c +++ b/supervisor/shared/usb/usb.c @@ -143,11 +143,12 @@ void usb_get_boot_py_data(uint8_t *temp_storage, size_t temp_storage_size) { if (usb_identification_allocation) { memcpy(temp_storage, usb_identification_allocation->ptr, sizeof(usb_identification_t)); free_memory(usb_identification_allocation); + } #else if (false) { - // Nothing - #endif - } else { + } + #endif + else { usb_identification_t defaults; // This compiles to less code than using a struct initializer. defaults.vid = USB_VID;