From 587aedd14f15972ac28cd30aca4078d2e336411b Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Wed, 28 Apr 2021 13:00:44 -0400 Subject: [PATCH] rework storage allocation --- main.c | 223 ++++++++++++++--------------- shared-bindings/usb_hid/__init__.h | 1 - shared-module/storage/__init__.c | 4 +- shared-module/storage/__init__.h | 2 +- shared-module/usb_cdc/__init__.c | 7 +- shared-module/usb_cdc/__init__.h | 2 +- shared-module/usb_hid/__init__.c | 44 +++--- shared-module/usb_hid/__init__.h | 2 +- shared-module/usb_midi/__init__.c | 53 +++---- shared-module/usb_midi/__init__.h | 4 +- supervisor/shared/memory.c | 54 ++++--- supervisor/shared/usb/usb.c | 39 +++-- supervisor/shared/usb/usb_desc.c | 141 +++++++++--------- supervisor/usb.h | 8 +- 14 files changed, 280 insertions(+), 304 deletions(-) diff --git a/main.c b/main.c index 7726ae6947..52191cee4f 100755 --- a/main.c +++ b/main.c @@ -266,97 +266,6 @@ STATIC void cleanup_after_vm(supervisor_allocation* heap) { reset_status_led(); } -FIL* boot_output_file; - -STATIC void __attribute__ ((noinline)) run_boot_py(safe_mode_t safe_mode) { - // If not in safe mode, run boot before initing USB and capture output in a - // file. - if (filesystem_present() && safe_mode == NO_SAFE_MODE && MP_STATE_VM(vfs_mount_table) != NULL) { - static const char * const boot_py_filenames[] = STRING_LIST("settings.txt", "settings.py", "boot.py", "boot.txt"); - - new_status_color(BOOT_RUNNING); - - #ifdef CIRCUITPY_BOOT_OUTPUT_FILE - FIL file_pointer; - boot_output_file = &file_pointer; - - // Get the base filesystem. - FATFS *fs = &((fs_user_mount_t *) MP_STATE_VM(vfs_mount_table)->obj)->fatfs; - - bool have_boot_py = first_existing_file_in_list(boot_py_filenames) != NULL; - - bool skip_boot_output = false; - - // If there's no boot.py file that might write some changing output, - // read the existing copy of CIRCUITPY_BOOT_OUTPUT_FILE and see if its contents - // match the version info we would print anyway. If so, skip writing CIRCUITPY_BOOT_OUTPUT_FILE. - // This saves wear and tear on the flash and also prevents filesystem damage if power is lost - // during the write, which may happen due to bobbling the power connector or weak power. - - static const size_t NUM_CHARS_TO_COMPARE = 160; - if (!have_boot_py && f_open(fs, boot_output_file, CIRCUITPY_BOOT_OUTPUT_FILE, FA_READ) == FR_OK) { - - char file_contents[NUM_CHARS_TO_COMPARE]; - UINT chars_read = 0; - f_read(boot_output_file, file_contents, NUM_CHARS_TO_COMPARE, &chars_read); - f_close(boot_output_file); - skip_boot_output = - // + 2 accounts for \r\n. - chars_read == strlen(MICROPY_FULL_VERSION_INFO) + 2 && - strncmp(file_contents, MICROPY_FULL_VERSION_INFO, strlen(MICROPY_FULL_VERSION_INFO)) == 0; - } - - if (!skip_boot_output) { - // Wait 1.5 seconds before opening CIRCUITPY_BOOT_OUTPUT_FILE for write, - // in case power is momentary or will fail shortly due to, say a low, battery. - if (common_hal_mcu_processor_get_reset_reason() == RESET_REASON_POWER_ON) { - mp_hal_delay_ms(1500); - } - // USB isn't up, so we can write the file. - filesystem_set_internal_writable_by_usb(false); - f_open(fs, boot_output_file, CIRCUITPY_BOOT_OUTPUT_FILE, FA_WRITE | FA_CREATE_ALWAYS); - - // Switch the filesystem back to non-writable by Python now instead of later, - // since boot.py might change it back to writable. - filesystem_set_internal_writable_by_usb(true); - - // Write version info to boot_out.txt. - mp_hal_stdout_tx_str(MICROPY_FULL_VERSION_INFO); - mp_hal_stdout_tx_str("\r\n"); - } - #endif - - filesystem_flush(); - supervisor_allocation* heap = allocate_remaining_memory(); - start_mp(heap); - - #if CIRCUITPY_USB - // Set up default USB values after boot.py VM starts but before running boot.py. - usb_pre_boot_py(); - #endif - - // TODO(tannewt): Re-add support for flashing boot error output. - bool found_boot = maybe_run_list(boot_py_filenames, NULL); - (void) found_boot; - - #ifdef CIRCUITPY_BOOT_OUTPUT_FILE - if (!skip_boot_output) { - f_close(boot_output_file); - filesystem_flush(); - } - boot_output_file = NULL; - #endif - - #if CIRCUITPY_USB - // Remember USB settings, which may have changed during boot.py. - // Call this before the boot.py heap is destroyed. - usb_post_boot_py(); - #endif - - cleanup_after_vm(heap); - } -} - STATIC void print_code_py_status_message(safe_mode_t safe_mode) { if (autoreload_is_enabled()) { serial_write_compressed(translate("Auto-reload is on. Simply save files over USB to run them or enter REPL to disable.\n")); @@ -369,7 +278,7 @@ STATIC void print_code_py_status_message(safe_mode_t safe_mode) { } } -STATIC bool run_code_py(safe_mode_t safe_mode, supervisor_allocation *heap) { +STATIC bool run_code_py(safe_mode_t safe_mode) { bool serial_connected_at_start = serial_connected(); #if CIRCUITPY_AUTORELOAD_DELAY_MS > 0 serial_write("\n"); @@ -397,6 +306,11 @@ STATIC bool run_code_py(safe_mode_t safe_mode, supervisor_allocation *heap) { "main.txt.py", "main.py.txt", "main.txt.txt","main.py.py"); #endif + stack_resize(); + filesystem_flush(); + supervisor_allocation* heap = allocate_remaining_memory(); + start_mp(heap); + found_main = maybe_run_list(supported_filenames, &result); #if CIRCUITPY_FULL_BUILD if (!found_main){ @@ -534,9 +448,103 @@ STATIC bool run_code_py(safe_mode_t safe_mode, supervisor_allocation *heap) { } } -STATIC int run_repl(supervisor_allocation *heap) { - int exit_code = PYEXEC_FORCED_EXIT; +FIL* boot_output_file; +STATIC void __attribute__ ((noinline)) run_boot_py(safe_mode_t safe_mode) { + // If not in safe mode, run boot before initing USB and capture output in a + // file. + if (filesystem_present() && safe_mode == NO_SAFE_MODE && MP_STATE_VM(vfs_mount_table) != NULL) { + static const char * const boot_py_filenames[] = STRING_LIST("settings.txt", "settings.py", "boot.py", "boot.txt"); + + new_status_color(BOOT_RUNNING); + + #ifdef CIRCUITPY_BOOT_OUTPUT_FILE + FIL file_pointer; + boot_output_file = &file_pointer; + + // Get the base filesystem. + FATFS *fs = &((fs_user_mount_t *) MP_STATE_VM(vfs_mount_table)->obj)->fatfs; + + bool have_boot_py = first_existing_file_in_list(boot_py_filenames) != NULL; + + bool skip_boot_output = false; + + // If there's no boot.py file that might write some changing output, + // read the existing copy of CIRCUITPY_BOOT_OUTPUT_FILE and see if its contents + // match the version info we would print anyway. If so, skip writing CIRCUITPY_BOOT_OUTPUT_FILE. + // This saves wear and tear on the flash and also prevents filesystem damage if power is lost + // during the write, which may happen due to bobbling the power connector or weak power. + + static const size_t NUM_CHARS_TO_COMPARE = 160; + if (!have_boot_py && f_open(fs, boot_output_file, CIRCUITPY_BOOT_OUTPUT_FILE, FA_READ) == FR_OK) { + + char file_contents[NUM_CHARS_TO_COMPARE]; + UINT chars_read = 0; + f_read(boot_output_file, file_contents, NUM_CHARS_TO_COMPARE, &chars_read); + f_close(boot_output_file); + skip_boot_output = + // + 2 accounts for \r\n. + chars_read == strlen(MICROPY_FULL_VERSION_INFO) + 2 && + strncmp(file_contents, MICROPY_FULL_VERSION_INFO, strlen(MICROPY_FULL_VERSION_INFO)) == 0; + } + + if (!skip_boot_output) { + // Wait 1.5 seconds before opening CIRCUITPY_BOOT_OUTPUT_FILE for write, + // in case power is momentary or will fail shortly due to, say a low, battery. + if (common_hal_mcu_processor_get_reset_reason() == RESET_REASON_POWER_ON) { + mp_hal_delay_ms(1500); + } + // USB isn't up, so we can write the file. + filesystem_set_internal_writable_by_usb(false); + f_open(fs, boot_output_file, CIRCUITPY_BOOT_OUTPUT_FILE, FA_WRITE | FA_CREATE_ALWAYS); + + // Switch the filesystem back to non-writable by Python now instead of later, + // since boot.py might change it back to writable. + filesystem_set_internal_writable_by_usb(true); + + // Write version info to boot_out.txt. + mp_hal_stdout_tx_str(MICROPY_FULL_VERSION_INFO); + mp_hal_stdout_tx_str("\r\n"); + } + #endif + + filesystem_flush(); + supervisor_allocation* heap = allocate_remaining_memory(); + start_mp(heap); + + #if CIRCUITPY_USB + // Set up default USB values after boot.py VM starts but before running boot.py. + usb_pre_boot_py(); + #endif + + // TODO(tannewt): Re-add support for flashing boot error output. + bool found_boot = maybe_run_list(boot_py_filenames, NULL); + (void) found_boot; + + #ifdef CIRCUITPY_BOOT_OUTPUT_FILE + if (!skip_boot_output) { + f_close(boot_output_file); + filesystem_flush(); + } + boot_output_file = NULL; + #endif + + #if CIRCUITPY_USB + // Remember USB settings, which may have changed during boot.py. + // Call this before the boot.py heap is destroyed. + usb_post_boot_py(); + #endif + + cleanup_after_vm(heap); + } +} + +STATIC int run_repl(void) { + int exit_code = PYEXEC_FORCED_EXIT; + stack_resize(); + filesystem_flush(); + supervisor_allocation* heap = allocate_remaining_memory(); + start_mp(heap); autoreload_suspend(); new_status_color(REPL_RUNNING); if (pyexec_mode_kind == PYEXEC_MODE_RAW_REPL) { @@ -580,7 +588,6 @@ int __attribute__((used)) main(void) { // Port-independent devices, like CIRCUITPY_BLEIO_HCI. reset_devices(); reset_board(); - reset_usb(); // This is first time we are running CircuitPython after a reset or power-up. supervisor_set_run_reason(RUN_REASON_STARTUP); @@ -597,18 +604,7 @@ int __attribute__((used)) main(void) { run_boot_py(safe_mode); - #if CIRCUITPY_BLEIO - supervisor_start_bluetooth(); - #endif - - // Boot script is finished, so now go into REPL/main mode. - - // Set up heap for REPL or code.py - stack_resize(); - filesystem_flush(); - supervisor_allocation* heap = allocate_remaining_memory(); - start_mp(heap); - + // Start USB after giving boot.py a chance to tweak behavior. #if CIRCUITPY_USB // Setup USB connection after heap is available. // It needs the heap to build descriptors. @@ -618,19 +614,24 @@ int __attribute__((used)) main(void) { // Set up any other serial connection. serial_init(); + #if CIRCUITPY_BLEIO + supervisor_start_bluetooth(); + #endif + + // Boot script is finished, so now go into REPL/main mode. int exit_code = PYEXEC_FORCED_EXIT; bool skip_repl = true; bool first_run = true; for (;;) { if (!skip_repl) { - exit_code = run_repl(heap); + exit_code = run_repl(); } if (exit_code == PYEXEC_FORCED_EXIT) { if (!first_run) { serial_write_compressed(translate("soft reboot\n")); } first_run = false; - skip_repl = run_code_py(safe_mode, heap); + skip_repl = run_code_py(safe_mode); } else if (exit_code != 0) { break; } @@ -651,10 +652,6 @@ void gc_collect(void) { background_callback_gc_collect(); - #if CIRCUITPY_USB - usb_gc_collect(); - #endif - #if CIRCUITPY_ALARM common_hal_alarm_gc_collect(); #endif diff --git a/shared-bindings/usb_hid/__init__.h b/shared-bindings/usb_hid/__init__.h index 3e51170cc4..49b83c9bf0 100644 --- a/shared-bindings/usb_hid/__init__.h +++ b/shared-bindings/usb_hid/__init__.h @@ -35,7 +35,6 @@ extern mp_obj_tuple_t common_hal_usb_hid_devices; void usb_hid_set_devices(mp_obj_t devices); -void common_hal_usb_hid_configure_usb_defaults(void); bool common_hal_usb_hid_configure_usb(mp_obj_t devices_seq); #endif // SHARED_BINDINGS_USB_HID_H diff --git a/shared-module/storage/__init__.c b/shared-module/storage/__init__.c index 9157118dd0..f53949d302 100644 --- a/shared-module/storage/__init__.c +++ b/shared-module/storage/__init__.c @@ -79,8 +79,8 @@ static const uint8_t usb_msc_descriptor_template[] = { // Is the MSC device enabled? bool storage_usb_is_enabled; -void storage_init_usb(void) { - storage_usb_is_enabled = true; +void storage_pre_boot_py(void) { + storage_usb_is_enabled = CIRCUITPY_USB_MSC_ENABLED_DEFAULT; } bool storage_usb_enabled(void) { diff --git a/shared-module/storage/__init__.h b/shared-module/storage/__init__.h index 2202fc0eb8..ed13696929 100644 --- a/shared-module/storage/__init__.h +++ b/shared-module/storage/__init__.h @@ -31,7 +31,7 @@ #if CIRCUITPY_USB bool storage_usb_enabled(void); -void storage_init_usb(void); +void storage_pre_boot_py(void); size_t storage_usb_descriptor_length(void); size_t storage_usb_add_descriptor(uint8_t *descriptor_buf, uint8_t *current_interface, uint8_t *current_endpoint, uint8_t* current_interface_string); #endif diff --git a/shared-module/usb_cdc/__init__.c b/shared-module/usb_cdc/__init__.c index 28e9c830ac..fb5da438a9 100644 --- a/shared-module/usb_cdc/__init__.c +++ b/shared-module/usb_cdc/__init__.c @@ -213,10 +213,9 @@ size_t usb_cdc_add_descriptor(uint8_t *descriptor_buf, uint8_t *current_interfac return sizeof(usb_cdc_descriptor_template); } -// Called only once, before boot.py -void usb_cdc_init_usb(void) { - usb_cdc_repl_is_enabled = true; - usb_cdc_data_is_enabled = false; +void usb_cdc_pre_boot_py(void) { + usb_cdc_repl_is_enabled = CIRCUITPY_USB_CDC_REPL_ENABLED_DEFAULT; + usb_cdc_data_is_enabled = CIRCUITPY_USB_CDC_DATA_ENABLED_DEFAULT; } bool common_hal_usb_cdc_configure_usb(bool repl_enabled, bool data_enabled) { diff --git a/shared-module/usb_cdc/__init__.h b/shared-module/usb_cdc/__init__.h index 6c947a84fd..546fe8f778 100644 --- a/shared-module/usb_cdc/__init__.h +++ b/shared-module/usb_cdc/__init__.h @@ -33,7 +33,7 @@ bool usb_cdc_repl_enabled(void); bool usb_cdc_data_enabled(void); -void usb_cdc_init_usb(void); +void usb_cdc_pre_boot_py(void); size_t usb_cdc_descriptor_length(void); size_t usb_cdc_add_descriptor(uint8_t *descriptor_buf, uint8_t *current_interface, uint8_t *current_endpoint, uint8_t* current_interface_string, bool repl); diff --git a/shared-module/usb_hid/__init__.c b/shared-module/usb_hid/__init__.c index 1bb88fed5c..0c614629ec 100644 --- a/shared-module/usb_hid/__init__.c +++ b/shared-module/usb_hid/__init__.c @@ -85,6 +85,23 @@ static size_t total_hid_report_descriptor_length; static supervisor_allocation *hid_devices_allocation; static mp_int_t hid_devices_num; +static mp_obj_tuple_t default_hid_devices_tuple = { + .base = { + .type = &mp_type_tuple, + }, + .len = 3, + .items = { + MP_OBJ_FROM_PTR(&usb_hid_device_keyboard_obj), + MP_OBJ_FROM_PTR(&usb_hid_device_mouse_obj), + MP_OBJ_FROM_PTR(&usb_hid_device_consumer_control_obj), + }, +}; + +void usb_hid_pre_boot_py(void) { + usb_hid_is_enabled = true; + common_hal_usb_hid_configure_usb(&default_hid_devices_tuple); +} + // This is the interface descriptor, not the report descriptor. size_t usb_hid_descriptor_length(void) { return sizeof(usb_hid_descriptor_template); @@ -122,23 +139,6 @@ size_t usb_hid_add_descriptor(uint8_t *descriptor_buf, uint8_t *current_interfac return sizeof(usb_hid_descriptor_template); } -static mp_rom_obj_tuple_t default_hid_devices_tuple = { - .base = { - .type = &mp_type_tuple, - }, - .len = 3, - .items = { - MP_OBJ_FROM_PTR(&usb_hid_device_keyboard_obj), - MP_OBJ_FROM_PTR(&usb_hid_device_mouse_obj), - MP_OBJ_FROM_PTR(&usb_hid_device_consumer_control_obj), - }, -}; - -// Set the default list of devices that will be included. Called before boot.py runs, in the boot.py VM. -void common_hal_usb_hid_configure_usb_defaults(void) { - common_hal_usb_hid_configure_usb(&default_hid_devices_tuple); -} - bool common_hal_usb_hid_configure_usb(mp_obj_t devices) { // We can't change the devices once we're connected. if (tud_connected()) { @@ -150,11 +150,6 @@ bool common_hal_usb_hid_configure_usb(mp_obj_t devices) { return true; } -// Called only once, before boot.py -void usb_hid_init_usb(void) { - usb_hid_is_enabled = true; -} - // Build the combined HID report descriptor and save the chosen devices. // Both are saved in supervisor allocations. void usb_hid_post_boot_py(void) { @@ -220,11 +215,6 @@ void usb_hid_post_boot_py(void) { } void usb_hid_gc_collect(void) { - if (tud_mounted()) { - // Once tud_mounted() is true, we're done with the constructed descriptors. - free_memory(hid_report_descriptor_allocation); - } - gc_collect_ptr(hid_devices_seq); gc_collect_ptr(hid_report_descriptor_allocation->ptr); gc_collect_ptr(hid_devices_allocation->ptr); diff --git a/shared-module/usb_hid/__init__.h b/shared-module/usb_hid/__init__.h index c55e1970ab..5262718126 100644 --- a/shared-module/usb_hid/__init__.h +++ b/shared-module/usb_hid/__init__.h @@ -33,7 +33,7 @@ extern usb_hid_device_obj_t usb_hid_devices[]; bool usb_hid_enabled(void); -void usb_hid_init_usb(void); +void usb_hid_pre_boot_py(void); void usb_hid_post_boot_py(void); size_t usb_hid_add_descriptor(uint8_t *descriptor_buf, uint8_t *current_interface, uint8_t *current_endpoint, uint8_t* current_interface_string, uint16_t report_descriptor_length); diff --git a/shared-module/usb_midi/__init__.c b/shared-module/usb_midi/__init__.c index 3a216da6e8..2f5cffc383 100644 --- a/shared-module/usb_midi/__init__.c +++ b/shared-module/usb_midi/__init__.c @@ -159,6 +159,10 @@ static const uint8_t usb_midi_descriptor_template[] = { // Is the USB MIDI device enabled? static bool usb_midi_is_enabled; +void usb_midi_pre_boot_py(void) { + usb_midi_is_enabled = CIRCUITPY_USB_MIDI_ENABLED_DEFAULT; +} + bool usb_midi_enabled(void) { return usb_midi_is_enabled; } @@ -208,35 +212,32 @@ size_t usb_midi_add_descriptor(uint8_t *descriptor_buf, uint8_t *current_interfa return sizeof(usb_midi_descriptor_template); } +static const usb_midi_portin_obj_t midi_portin_obj = { + .base = { + .type = &usb_midi_portin_type, + }, +}; -// Called once, before -void usb_midi_init_usb(void) { - usb_midi_is_enabled = true; -} - -// Called before REPL or code.py -void usb_midi_setup(void) { - mp_obj_tuple_t *ports; - - if (usb_midi_is_enabled) { - // Make these objects long-lived, because they will not be going away. - - usb_midi_portin_obj_t *in = gc_alloc(sizeof(usb_midi_portin_obj_t), false, true); - in->base.type = &usb_midi_portin_type; - - usb_midi_portout_obj_t *out = gc_alloc(sizeof(usb_midi_portout_obj_t), false, true); - out->base.type = &usb_midi_portout_type; - - mp_obj_t tuple_items[2] = { - MP_OBJ_FROM_PTR(in), - MP_OBJ_FROM_PTR(out), - }; - - ports = mp_obj_new_tuple(2, tuple_items); - } else { - ports = mp_const_empty_tuple; +static const usb_midi_portout_obj_t midi_portout_obj = { + .base = { + .type = &usb_midi_portout_type, } +}; +static const mp_rom_obj_tuple_t midi_ports_tuple = { + .base = { + .type = &mp_type_tuple, + }, + .len = 2, + .items = { + MP_ROM_PTR(&midi_portin_obj), + MP_ROM_PTR(&midi_portout_obj), + }, +}; + + +void usb_midi_post_boot_py(void) { + mp_obj_tuple_t *ports = usb_midi_is_enabled ? MP_OBJ_FROM_PTR(&midi_ports_tuple) : mp_const_empty_tuple; mp_map_lookup(&usb_midi_module_globals.map, MP_ROM_QSTR(MP_QSTR_ports), MP_MAP_LOOKUP)->value = MP_OBJ_FROM_PTR(ports); } diff --git a/shared-module/usb_midi/__init__.h b/shared-module/usb_midi/__init__.h index a29c56109c..fbcb89bd70 100644 --- a/shared-module/usb_midi/__init__.h +++ b/shared-module/usb_midi/__init__.h @@ -28,8 +28,8 @@ #define SHARED_MODULE_USB_MIDI___INIT___H -void usb_midi_init_usb(void); -void usb_midi_setup(void); +void usb_midi_pre_boot_py(void); +void usb_midi_post_boot_py(void); bool usb_midi_enabled(void); size_t usb_midi_descriptor_length(void); diff --git a/supervisor/shared/memory.c b/supervisor/shared/memory.c index 4bdbf67652..27022b2ff8 100644 --- a/supervisor/shared/memory.c +++ b/supervisor/shared/memory.c @@ -35,33 +35,43 @@ enum { CIRCUITPY_SUPERVISOR_IMMOVABLE_ALLOC_COUNT = // stack + heap - 2 - #if INTERNAL_FLASH_FILESYSTEM == 0 - + 1 - #endif + 2 + + #if INTERNAL_FLASH_FILESYSTEM == 0 + + 1 + #endif + + #if CIRCUITPY_USB + +1 // device_descriptor_allocation + +1 // configuration_descriptor_allocation + +1 // string_descriptors_allocation + #endif + + #if CIRCUITPY_USB_HID + + 1 // hid_report_descriptor_allocation + + 1 // hid_devices_allocation + #endif , + CIRCUITPY_SUPERVISOR_MOVABLE_ALLOC_COUNT = + 0 + #if CIRCUITPY_DISPLAYIO + #if CIRCUITPY_TERMINALIO + + 1 + #endif + + CIRCUITPY_DISPLAY_LIMIT * ( + // Maximum needs of one display: max(4 if RGBMATRIX, 1 if SHARPDISPLAY, 0) + #if CIRCUITPY_RGBMATRIX + 4 + #elif CIRCUITPY_SHARPDISPLAY + 1 + #else 0 - #if CIRCUITPY_USB_HID - + 1 // hid_report_descriptor_allocation - + 1 // hid_devices_allocation - #endif - #if CIRCUITPY_DISPLAYIO - #if CIRCUITPY_TERMINALIO - + 1 - #endif - + CIRCUITPY_DISPLAY_LIMIT * ( - // Maximum needs of one display: max(4 if RGBMATRIX, 1 if SHARPDISPLAY, 0) - #if CIRCUITPY_RGBMATRIX - 4 - #elif CIRCUITPY_SHARPDISPLAY - 1 - #else - 0 - #endif - ) #endif + ) + #endif , + CIRCUITPY_SUPERVISOR_ALLOC_COUNT = CIRCUITPY_SUPERVISOR_IMMOVABLE_ALLOC_COUNT + CIRCUITPY_SUPERVISOR_MOVABLE_ALLOC_COUNT }; diff --git a/supervisor/shared/usb/usb.c b/supervisor/shared/usb/usb.c index 47f431e08f..0c86e79026 100644 --- a/supervisor/shared/usb/usb.c +++ b/supervisor/shared/usb/usb.c @@ -66,18 +66,10 @@ bool usb_enabled(void) { return tusb_inited(); } -// Initialization done only once, before boot.py is run. -void reset_usb(void) { - reset_usb_desc(); -} - - MP_WEAK void post_usb_init(void) { } void usb_init(void) { - usb_desc_init(); - init_usb_hardware(); tusb_init(); @@ -89,34 +81,40 @@ void usb_init(void) { // This usb_callback always got invoked regardless of mp_interrupt_char value since we only set it once here tud_cdc_set_wanted_char(CHAR_CTRL_C); #endif - - #if CIRCUITPY_USB_MIDI - usb_midi_setup(); - #endif } +// Set up USB defaults before any USB changes are made in boot.py void usb_pre_boot_py(void) { #if CIRCUITPY_STORAGE - storage_init_usb(); + storage_pre_boot_py(); #endif #if CIRCUITPY_USB_CDC - usb_cdc_init_usb(); + usb_cdc_pre_boot_py(); #endif #if CIRCUITPY_USB_HID - usb_hid_init_usb(); + usb_hid_pre_boot_py(); #endif #if CIRCUITPY_USB_MIDI - usb_midi_init_usb(); + usb_midi_pre_boot_py(); #endif }; -// Remember USB settings done during boot.py. -// The boot.py heap is still valid at this point. +// Act on USB settings done during boot.py. void usb_post_boot_py(void) { + #if CIRCUITPY_USB usb_desc_post_boot_py(); + #endif + + #if CIRCUITPY_USB_MIDI + usb_midi_post_boot_py(); + #endif + + #if CIRCUITPY_USB_HID + usb_hid_post_boot_py(); + #endif } @@ -147,11 +145,6 @@ void usb_irq_handler(void) { usb_background_schedule(); } -void usb_gc_collect(void) { - usb_desc_gc_collect(); - usb_hid_gc_collect(); -} - // --------------------------------------------------------------------+ // tinyusb callbacks // --------------------------------------------------------------------+ diff --git a/supervisor/shared/usb/usb_desc.c b/supervisor/shared/usb/usb_desc.c index aa01ea8897..1da1d8d19e 100644 --- a/supervisor/shared/usb/usb_desc.c +++ b/supervisor/shared/usb/usb_desc.c @@ -26,9 +26,9 @@ #include "lib/tinyusb/src/tusb.h" -#include "py/gc.h" #include "py/objstr.h" #include "py/runtime.h" +#include "supervisor/memory.h" #include "supervisor/usb.h" #if CIRCUITPY_USB_CDC @@ -51,17 +51,29 @@ // Table for collecting interface strings (interface names) as descriptor is built. +// We reuse the same table after collection, replacing the char string pointers with le16 string pointers. #define MAX_INTERFACE_STRINGS 16 // slot 0 is always the Language ID -static uint16_t *collected_interface_strings[MAX_INTERFACE_STRINGS]; +typedef union { + const char *char_str; + const uint16_t *descriptor; +} interface_string_t; +static interface_string_t collected_interface_strings[MAX_INTERFACE_STRINGS]; + +static size_t collected_interface_strings_length; static uint8_t current_interface_string; +supervisor_allocation *device_descriptor_allocation; +supervisor_allocation *configuration_descriptor_allocation; +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]; + static const uint8_t device_descriptor_template[] = { 0x12, // 0 bLength 0x01, // 1 bDescriptorType (Device) @@ -86,8 +98,6 @@ static const uint8_t device_descriptor_template[] = { 0x01, // 17 bNumConfigurations 1 }; -static uint8_t device_descriptor[sizeof(device_descriptor_template)]; - static const uint8_t configuration_descriptor_template[] = { 0x09, // 0 bLength 0x02, // 1 bDescriptorType (Configuration) @@ -102,33 +112,10 @@ static const uint8_t configuration_descriptor_template[] = { 0x32, // 8 bMaxPower 100mA }; -static uint8_t *configuration_descriptor; - -// Initialization done before boot.py is run. -// Turn on or off various USB devices. On devices with limited endpoints, -// some may be off by default. -void reset_usb_desc(void) { - // Set defaults for enabling/disabling of various USB devices. -#if CIRCUITPY_USB_CDC - common_hal_usb_cdc_configure_usb( - (bool) CIRCUITPY_USB_CDC_REPL_ENABLED_DEFAULT, - (bool) CIRCUITPY_USB_CDC_DATA_ENABLED_DEFAULT); -#endif - -#if CIRCUITPY_USB_MSC - common_hal_storage_configure_usb((bool) CIRCUITPY_USB_MSC_ENABLED_DEFAULT); -#endif - -#if CIRCUITPY_USB_MIDI - common_hal_usb_midi_configure_usb((bool) CIRCUITPY_USB_MIDI_ENABLED_DEFAULT); -#endif - -#if CIRCUITPY_USB_HID - common_hal_usb_hid_configure_usb_defaults(); -#endif -} - static void usb_build_device_descriptor(uint16_t vid, uint16_t pid) { + device_descriptor_allocation = + allocate_memory(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; @@ -181,9 +168,13 @@ static void usb_build_configuration_descriptor(void) { } #endif - // Now we now how big the configuration descriptor will be. + // Now we now how big the configuration descriptor will be, so we can allocate space for it. + configuration_descriptor_allocation = + allocate_memory(total_descriptor_length, /*high_address*/ false, /*movable*/ false); + uint8_t *configuration_descriptor = (uint8_t *) configuration_descriptor_allocation->ptr; + // Copy the template, which is the first part of the descriptor, and fix up its length. - configuration_descriptor = gc_alloc(total_descriptor_length, false, false); + memcpy(configuration_descriptor, configuration_descriptor_template, sizeof(configuration_descriptor_template)); configuration_descriptor[CONFIG_TOTAL_LENGTH_LO_INDEX] = total_descriptor_length & 0xFF; @@ -234,10 +225,7 @@ static void usb_build_configuration_descriptor(void) { #endif // Now we know how many interfaces have been used. - // current_interface is the next free interface, counting from 0, - // so move back to the last interface number, and then get a count. - // (E.g., interfaces 0-5 are used, so the number of interfaces is 6.) - configuration_descriptor[CONFIG_NUM_INTERFACES_INDEX] = current_interface - 1 + 1; + configuration_descriptor[CONFIG_NUM_INTERFACES_INDEX] = current_interface; // Did we run out of endpoints? if (current_endpoint - 1 > USB_NUM_EP) { @@ -246,40 +234,59 @@ static void usb_build_configuration_descriptor(void) { } +// str must not be on the heap. void usb_add_interface_string(uint8_t interface_string_index, const char str[]) { if (interface_string_index > MAX_INTERFACE_STRINGS) { mp_raise_RuntimeError(translate("Too many USB interface names")); } - // 2 bytes for String Descriptor header, then 2 bytes for each character - const size_t str_len = strlen(str); - uint8_t descriptor_size = 2 + (str_len * 2); - uint16_t *string_descriptor = (uint16_t *) m_malloc(descriptor_size, false); - string_descriptor[0] = 0x0300 | descriptor_size; - // Convert to le16 - for (size_t i = 0; i <= str_len; i++) { - string_descriptor[i + 1] = str[i]; - } - collected_interface_strings[interface_string_index] = string_descriptor; + collected_interface_strings[interface_string_index].char_str = str; + collected_interface_strings_length += strlen(str); } +static void usb_build_interface_string_table(void) { + // Allocate space for the le16 String descriptors. + // Space needed is 2 bytes for String Descriptor header, then 2 bytes for each character + string_descriptors_allocation = + allocate_memory(current_interface_string * 2 + collected_interface_strings_length * 2, + /*high_address*/ false, /*movable*/ false); + uint16_t *string_descriptors = (uint16_t *) string_descriptors_allocation->ptr; -// Remember USB information that must persist from the boot.py VM to the next VM. -// Some of this is already remembered in globals, for example, usb_midi_enabled and similar bools. -void usb_desc_post_boot_py(void) { - usb_hid_post_boot_py(); -} -// Called in the new VM created after boot.py is run. The USB devices to be used are now chosen. -void usb_desc_init(void) { - memset(collected_interface_strings, 0, sizeof(collected_interface_strings)); + uint16_t *string_descriptor = string_descriptors; // Language ID is always the 0th string descriptor. - collected_interface_strings[0] = (uint16_t[]) { + collected_interface_strings[0].descriptor = (uint16_t[]) { 0x0304, 0x0409, }; + // Build the le16 versions of all the descriptor strings. + // Start at 1 to skip the Language ID. + for (uint8_t string_index = 1; string_index < current_interface_string; string_index++) { + const char *str = collected_interface_strings[string_index].char_str; + const size_t str_len = strlen(str); + uint8_t descriptor_size = 2 + (str_len * 2); + string_descriptor[0] = 0x0300 | descriptor_size; + + // Convert to le16. + for (size_t i = 0; i <= str_len; i++) { + string_descriptor[i + 1] = str[i]; + } + + // Save ptr to string descriptor with le16 str. + collected_interface_strings[string_index].descriptor = string_descriptor; + + // Move to next descriptor slot. + string_descriptor += descriptor_size; + } +} + +// After boot.py runs, the USB devices to be used have been chosen, and the descriptors can be set up. +// This should be called before the heap is destroyed, so that any objects in the heap, +// such as +// can be used. +void usb_desc_post_boot_py(void) { uint8_t raw_id[COMMON_HAL_MCU_PROCESSOR_UID_LENGTH]; common_hal_mcu_processor_get_uid(raw_id); @@ -294,31 +301,17 @@ void usb_desc_init(void) { serial_number_hex_string[sizeof(serial_number_hex_string) - 1] = '\0'; current_interface_string = 1; + collected_interface_strings_length = 0; usb_build_device_descriptor(USB_VID, USB_PID); usb_build_configuration_descriptor(); + usb_build_interface_string_table(); } -void usb_desc_gc_collect(void) { - // Once tud_mounted() is true, we're done with the constructed descriptors. - if (tud_mounted()) { - gc_free(device_descriptor); - gc_free(configuration_descriptor); - for (size_t i = 0; i < MAX_INTERFACE_STRINGS; i ++) { - gc_free(collected_interface_strings[i]); - } - } else { - gc_collect_ptr(device_descriptor); - gc_collect_ptr(configuration_descriptor); - gc_collect_root((void **) collected_interface_strings, MAX_INTERFACE_STRINGS); - } -} - - // Invoked when GET DEVICE DESCRIPTOR is received. // Application return pointer to descriptor uint8_t const *tud_descriptor_device_cb(void) { - return device_descriptor; + return (uint8_t *) device_descriptor_allocation; } // Invoked when GET CONFIGURATION DESCRIPTOR is received. @@ -326,7 +319,7 @@ uint8_t const *tud_descriptor_device_cb(void) { // Descriptor contents must exist long enough for transfer to complete uint8_t const *tud_descriptor_configuration_cb(uint8_t index) { (void)index; // for multiple configurations - return configuration_descriptor; + return (uint8_t *) configuration_descriptor_allocation->ptr; } // Invoked when GET STRING DESCRIPTOR request is received. @@ -335,5 +328,5 @@ uint16_t const *tud_descriptor_string_cb(uint8_t index, uint16_t langid) { if (index > MAX_INTERFACE_STRINGS) { return NULL; } - return collected_interface_strings[index]; + return collected_interface_strings[index].descriptor; } diff --git a/supervisor/usb.h b/supervisor/usb.h index a0e31f1913..d59217e491 100644 --- a/supervisor/usb.h +++ b/supervisor/usb.h @@ -49,20 +49,14 @@ void init_usb_hardware(void); void post_usb_init(void); // Shared implementation. -void reset_usb(void); bool usb_enabled(void); void usb_init(void); void usb_disconnect(void); -void usb_gc_collect(void); void usb_pre_boot_py(void); void usb_post_boot_py(void); -void usb_add_interface_string(uint8_t interface_string_index, const char str[]); - -void reset_usb_desc(void); -void usb_desc_gc_collect(void); -void usb_desc_init(void); void usb_desc_post_boot_py(void); +void usb_add_interface_string(uint8_t interface_string_index, const char str[]); // Propagate plug/unplug events to the MSC logic. #if CIRCUITPY_USB_MSC