rework storage allocation

This commit is contained in:
Dan Halbert 2021-04-28 13:00:44 -04:00
parent 8500e846c6
commit 587aedd14f
14 changed files with 280 additions and 304 deletions

223
main.c
View File

@ -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

View File

@ -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

View File

@ -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) {

View File

@ -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

View File

@ -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) {

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);
}

View File

@ -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);

View File

@ -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
};

View File

@ -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
// --------------------------------------------------------------------+

View File

@ -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;
}

View File

@ -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