|
30 | 30 | #include "extmod/vfs.h"
|
31 | 31 | #include "extmod/vfs_fat.h"
|
32 | 32 |
|
| 33 | +#include "genhdr/mpversion.h" |
33 | 34 | #include "py/nlr.h"
|
34 | 35 | #include "py/compile.h"
|
35 | 36 | #include "py/frozenmod.h"
|
@@ -99,19 +100,27 @@ void reset_mp(void) {
|
99 | 100 | }
|
100 | 101 | #define STRING_LIST(...) {__VA_ARGS__, ""}
|
101 | 102 |
|
102 |
| -bool maybe_run_list(const char ** filenames, pyexec_result_t* exec_result) { |
103 |
| - |
| 103 | +// Look for the first file that exists in the list of filenames, using mp_import_stat(). |
| 104 | +// Return its index. If no file found, return -1. |
| 105 | +const char* first_existing_file_in_list(const char ** filenames) { |
104 | 106 | for (int i = 0; filenames[i] != (char*)""; i++) {
|
105 | 107 | mp_import_stat_t stat = mp_import_stat(filenames[i]);
|
106 |
| - if (stat != MP_IMPORT_STAT_FILE) { |
107 |
| - continue; |
| 108 | + if (stat == MP_IMPORT_STAT_FILE) { |
| 109 | + return filenames[i]; |
108 | 110 | }
|
109 |
| - serial_write(filenames[i]); |
110 |
| - serial_write(MSG_OUTPUT_SUFFIX); |
111 |
| - pyexec_file(filenames[i], exec_result); |
112 |
| - return true; |
113 | 111 | }
|
114 |
| - return false; |
| 112 | + return NULL; |
| 113 | +} |
| 114 | + |
| 115 | +bool maybe_run_list(const char ** filenames, pyexec_result_t* exec_result) { |
| 116 | + const char* filename = first_existing_file_in_list(filenames); |
| 117 | + if (filename == NULL) { |
| 118 | + return false; |
| 119 | + } |
| 120 | + mp_hal_stdout_tx_str(filename); |
| 121 | + mp_hal_stdout_tx_str(MSG_OUTPUT_SUFFIX); |
| 122 | + pyexec_file(filename, exec_result); |
| 123 | + return true; |
115 | 124 | }
|
116 | 125 |
|
117 | 126 | bool start_mp(safe_mode_t safe_mode) {
|
@@ -261,27 +270,64 @@ int __attribute__((used)) main(void) {
|
261 | 270 | // If not in safe mode, run boot before initing USB and capture output in a
|
262 | 271 | // file.
|
263 | 272 | if (filesystem_present() && safe_mode == NO_SAFE_MODE && MP_STATE_VM(vfs_mount_table) != NULL) {
|
| 273 | + static const char *boot_py_filenames[] = STRING_LIST("settings.txt", "settings.py", "boot.py", "boot.txt"); |
| 274 | + |
264 | 275 | new_status_color(BOOT_RUNNING);
|
| 276 | + |
265 | 277 | #ifdef CIRCUITPY_BOOT_OUTPUT_FILE
|
266 |
| - // Since USB isn't up yet we can cheat and let ourselves write the boot |
267 |
| - // output file. |
268 |
| - filesystem_writable_by_python(true); |
269 | 278 | FIL file_pointer;
|
270 | 279 | boot_output_file = &file_pointer;
|
271 |
| - f_open(&((fs_user_mount_t *) MP_STATE_VM(vfs_mount_table)->obj)->fatfs, |
272 |
| - boot_output_file, CIRCUITPY_BOOT_OUTPUT_FILE, FA_WRITE | FA_CREATE_ALWAYS); |
273 |
| - filesystem_writable_by_python(false); |
| 280 | + |
| 281 | + // Get the base filesystem. |
| 282 | + FATFS *fs = &((fs_user_mount_t *) MP_STATE_VM(vfs_mount_table)->obj)->fatfs; |
| 283 | + |
| 284 | + bool have_boot_py = first_existing_file_in_list(boot_py_filenames) != NULL; |
| 285 | + |
| 286 | + bool skip_boot_output = false; |
| 287 | + |
| 288 | + // If there's no boot.py file that might write some changing output, |
| 289 | + // read the existing copy of CIRCUITPY_BOOT_OUTPUT_FILE and see if its contents |
| 290 | + // match the version info we would print anyway. If so, skip writing CIRCUITPY_BOOT_OUTPUT_FILE. |
| 291 | + // This saves wear and tear on the flash and also prevents filesystem damage if power is lost |
| 292 | + // during the write, which may happen due to bobbling the power connector or weak power. |
| 293 | + |
| 294 | + if (!have_boot_py && f_open(fs, boot_output_file, CIRCUITPY_BOOT_OUTPUT_FILE, FA_READ) == FR_OK) { |
| 295 | + char file_contents[512]; |
| 296 | + UINT chars_read = 0; |
| 297 | + f_read(boot_output_file, file_contents, 512, &chars_read); |
| 298 | + f_close(boot_output_file); |
| 299 | + skip_boot_output = |
| 300 | + // + 2 accounts for \r\n. |
| 301 | + chars_read == strlen(MICROPY_FULL_VERSION_INFO) + 2 && |
| 302 | + strncmp(file_contents, MICROPY_FULL_VERSION_INFO, strlen(MICROPY_FULL_VERSION_INFO)) == 0; |
| 303 | + } |
| 304 | + |
| 305 | + if (!skip_boot_output) { |
| 306 | + // Wait 1.5 seconds before opening CIRCUITPY_BOOT_OUTPUT_FILE for write, |
| 307 | + // in case power is momentary or will fail shortly due to, say a low, battery. |
| 308 | + mp_hal_delay_ms(1500); |
| 309 | + |
| 310 | + // USB isn't up, so we can write the file. |
| 311 | + filesystem_writable_by_python(true); |
| 312 | + f_open(fs, boot_output_file, CIRCUITPY_BOOT_OUTPUT_FILE, FA_WRITE | FA_CREATE_ALWAYS); |
| 313 | + |
| 314 | + // Write version info to boot_out.txt. |
| 315 | + mp_hal_stdout_tx_str(MICROPY_FULL_VERSION_INFO); |
| 316 | + mp_hal_stdout_tx_str("\r\n"); |
| 317 | + } |
274 | 318 | #endif
|
275 | 319 |
|
276 | 320 | // TODO(tannewt): Re-add support for flashing boot error output.
|
277 |
| - static const char *filenames[] = STRING_LIST("settings.txt", "settings.py", "boot.py", "boot.txt"); |
278 |
| - bool found_boot = maybe_run_list(filenames, NULL); |
| 321 | + bool found_boot = maybe_run_list(boot_py_filenames, NULL); |
279 | 322 | (void) found_boot;
|
280 | 323 |
|
281 | 324 | #ifdef CIRCUITPY_BOOT_OUTPUT_FILE
|
282 |
| - f_close(boot_output_file); |
283 |
| - filesystem_flush(); |
284 |
| - boot_output_file = NULL; |
| 325 | + if (!skip_boot_output) { |
| 326 | + f_close(boot_output_file); |
| 327 | + filesystem_flush(); |
| 328 | + boot_output_file = NULL; |
| 329 | + } |
| 330 | + filesystem_writable_by_python(false); |
285 | 331 | #endif
|
286 | 332 |
|
287 | 333 | // Reset to remove any state that boot.py setup. It should only be used to
|
|
0 commit comments