aboutsummaryrefslogtreecommitdiff
path: root/circuitpython/supervisor
diff options
context:
space:
mode:
Diffstat (limited to 'circuitpython/supervisor')
-rw-r--r--circuitpython/supervisor/background_callback.h93
-rw-r--r--circuitpython/supervisor/board.h50
-rwxr-xr-xcircuitpython/supervisor/cpu.h34
-rw-r--r--circuitpython/supervisor/fatfs_port.h34
-rw-r--r--circuitpython/supervisor/filesystem.h48
-rw-r--r--circuitpython/supervisor/flash.h53
-rw-r--r--circuitpython/supervisor/flash_root_pointers.h35
-rw-r--r--circuitpython/supervisor/linker.h42
-rw-r--r--circuitpython/supervisor/memory.h77
-rw-r--r--circuitpython/supervisor/messages/default.h99
-rw-r--r--circuitpython/supervisor/port.h106
-rw-r--r--circuitpython/supervisor/serial.h62
-rw-r--r--circuitpython/supervisor/shared/background_callback.c148
-rw-r--r--circuitpython/supervisor/shared/bluetooth/bluetooth.c301
-rw-r--r--circuitpython/supervisor/shared/bluetooth/bluetooth.h41
-rw-r--r--circuitpython/supervisor/shared/bluetooth/file_transfer.c756
-rw-r--r--circuitpython/supervisor/shared/bluetooth/file_transfer.h36
-rw-r--r--circuitpython/supervisor/shared/bluetooth/file_transfer_protocol.h180
-rw-r--r--circuitpython/supervisor/shared/bluetooth/serial.c211
-rw-r--r--circuitpython/supervisor/shared/bluetooth/serial.h42
-rw-r--r--circuitpython/supervisor/shared/board.c48
-rw-r--r--circuitpython/supervisor/shared/board.h36
-rw-r--r--circuitpython/supervisor/shared/cpu.c40
-rw-r--r--circuitpython/supervisor/shared/cpu.h36
-rw-r--r--circuitpython/supervisor/shared/display.c347
-rw-r--r--circuitpython/supervisor/shared/display.h56
-rw-r--r--circuitpython/supervisor/shared/external_flash/common_commands.h48
-rw-r--r--circuitpython/supervisor/shared/external_flash/device.h78
-rw-r--r--circuitpython/supervisor/shared/external_flash/devices.h.jinja50
-rw-r--r--circuitpython/supervisor/shared/external_flash/external_flash.c593
-rw-r--r--circuitpython/supervisor/shared/external_flash/external_flash.h55
-rw-r--r--circuitpython/supervisor/shared/external_flash/external_flash_root_pointers.h35
-rw-r--r--circuitpython/supervisor/shared/external_flash/qspi_flash.c57
-rw-r--r--circuitpython/supervisor/shared/external_flash/qspi_flash.h31
-rw-r--r--circuitpython/supervisor/shared/external_flash/spi_flash.c166
-rw-r--r--circuitpython/supervisor/shared/filesystem.c203
-rw-r--r--circuitpython/supervisor/shared/flash.c227
-rw-r--r--circuitpython/supervisor/shared/internal_flash.h33
-rw-r--r--circuitpython/supervisor/shared/lock.c51
-rw-r--r--circuitpython/supervisor/shared/lock.h36
-rw-r--r--circuitpython/supervisor/shared/memory.c341
-rw-r--r--circuitpython/supervisor/shared/micropython.c83
-rw-r--r--circuitpython/supervisor/shared/reload.c113
-rw-r--r--circuitpython/supervisor/shared/reload.h84
-rw-r--r--circuitpython/supervisor/shared/rgb_led_colors.h30
-rw-r--r--circuitpython/supervisor/shared/safe_mode.c238
-rw-r--r--circuitpython/supervisor/shared/safe_mode.h61
-rw-r--r--circuitpython/supervisor/shared/serial.c297
-rw-r--r--circuitpython/supervisor/shared/stack.c112
-rw-r--r--circuitpython/supervisor/shared/stack.h52
-rw-r--r--circuitpython/supervisor/shared/status_leds.c366
-rw-r--r--circuitpython/supervisor/shared/status_leds.h62
-rw-r--r--circuitpython/supervisor/shared/tick.c178
-rw-r--r--circuitpython/supervisor/shared/tick.h75
-rw-r--r--circuitpython/supervisor/shared/traceback.c29
-rw-r--r--circuitpython/supervisor/shared/traceback.h34
-rw-r--r--circuitpython/supervisor/shared/translate.c152
-rw-r--r--circuitpython/supervisor/shared/translate.h93
-rw-r--r--circuitpython/supervisor/shared/usb/tusb_config.h167
-rw-r--r--circuitpython/supervisor/shared/usb/usb.c321
-rw-r--r--circuitpython/supervisor/shared/usb/usb_desc.c365
-rw-r--r--circuitpython/supervisor/shared/usb/usb_msc_flash.c287
-rw-r--r--circuitpython/supervisor/shared/usb/usb_vendor_descriptors.h39
-rw-r--r--circuitpython/supervisor/shared/workflow.c60
-rw-r--r--circuitpython/supervisor/shared/workflow.h29
-rw-r--r--circuitpython/supervisor/spi_flash_api.h49
-rw-r--r--circuitpython/supervisor/stub/filesystem.c80
-rw-r--r--circuitpython/supervisor/stub/internal_flash.c74
-rw-r--r--circuitpython/supervisor/stub/safe_mode.c42
-rw-r--r--circuitpython/supervisor/stub/stack.c48
-rw-r--r--circuitpython/supervisor/supervisor.mk203
-rw-r--r--circuitpython/supervisor/usb.h88
-rwxr-xr-xcircuitpython/supervisor/workflow.h32
73 files changed, 8958 insertions, 0 deletions
diff --git a/circuitpython/supervisor/background_callback.h b/circuitpython/supervisor/background_callback.h
new file mode 100644
index 0000000..651ac02
--- /dev/null
+++ b/circuitpython/supervisor/background_callback.h
@@ -0,0 +1,93 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 Jeff Epler 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 CIRCUITPY_INCLUDED_SUPERVISOR_BACKGROUND_CALLBACK_H
+#define CIRCUITPY_INCLUDED_SUPERVISOR_BACKGROUND_CALLBACK_H
+
+#include <stdbool.h>
+
+/** Background callbacks are a linked list of tasks to call in the background.
+ *
+ * Include a member of type `background_callback_t` inside an object
+ * which needs to queue up background work, and zero-initialize it.
+ *
+ * To schedule the work, use background_callback_add, with fun as the
+ * function to call and data pointing to the object itself.
+ *
+ * Next time run_background_tasks_if_tick is called, the callback will
+ * be run and removed from the linked list.
+ *
+ * Queueing a task that is already queued does nothing. Unconditionally
+ * re-queueing it from its own background task will cause it to run during the
+ * very next background-tasks invocation, leading to a CircuitPython freeze, so
+ * don't do that.
+ *
+ * background_callback_add can be called from interrupt context.
+ */
+typedef void (*background_callback_fun)(void *data);
+typedef struct background_callback {
+ background_callback_fun fun;
+ void *data;
+ struct background_callback *next;
+ struct background_callback *prev;
+} background_callback_t;
+
+/* Add a background callback for which 'fun' and 'data' were previously set */
+void background_callback_add_core(background_callback_t *cb);
+
+/* Add a background callback to the given function with the given data. When
+ * the callback involves an object on the GC heap, the 'data' must be a pointer
+ * to that object itself, not an internal pointer. Otherwise, it can be the
+ * case that no other references to the object itself survive, and the object
+ * becomes garbage collected while an outstanding background callback still
+ * exists.
+ */
+void background_callback_add(background_callback_t *cb, background_callback_fun fun, void *data);
+
+/* Run all background callbacks. Normally, this is done by the supervisor
+ * whenever the list is non-empty */
+void background_callback_run_all(void);
+
+/* True when a background callback is pending. Helpful for checking background state when
+ * interrupts are disabled. */
+bool background_callback_pending(void);
+
+/* During soft reset, remove all pending callbacks and clear the critical section flag */
+void background_callback_reset(void);
+
+/* Sometimes background callbacks must be blocked. Use these functions to
+ * bracket the section of code where this is the case. These calls nest, and
+ * begins must be balanced with ends.
+ */
+void background_callback_begin_critical_section(void);
+void background_callback_end_critical_section(void);
+
+/*
+ * Background callbacks may stop objects from being collected
+ */
+void background_callback_gc_collect(void);
+
+#endif
diff --git a/circuitpython/supervisor/board.h b/circuitpython/supervisor/board.h
new file mode 100644
index 0000000..e3f0af7
--- /dev/null
+++ b/circuitpython/supervisor/board.h
@@ -0,0 +1,50 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 Scott Shawcroft 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_SUPERVISOR_BOARD_H
+#define MICROPY_INCLUDED_SUPERVISOR_BOARD_H
+
+#include <stdbool.h>
+
+#include "supervisor/shared/safe_mode.h"
+
+// Returns true if the user initiates safe mode in a board specific way.
+// Also add BOARD_USER_SAFE_MODE in mpconfigboard.h to explain the board specific
+// way.
+bool board_requests_safe_mode(void);
+
+// Initializes board related state once on start up.
+void board_init(void);
+
+// Reset the state of off MCU components such as neopixels.
+void reset_board(void);
+
+// Deinit the board. This should put the board in deep sleep durable, low power
+// state. It should not prevent the user access method from working (such as
+// disabling USB, BLE or flash) because CircuitPython may continue to run.
+void board_deinit(void);
+
+#endif // MICROPY_INCLUDED_SUPERVISOR_BOARD_H
diff --git a/circuitpython/supervisor/cpu.h b/circuitpython/supervisor/cpu.h
new file mode 100755
index 0000000..c4f8131
--- /dev/null
+++ b/circuitpython/supervisor/cpu.h
@@ -0,0 +1,34 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2017 Scott Shawcroft 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_SUPERVISOR_CPU_H
+#define MICROPY_INCLUDED_SUPERVISOR_CPU_H
+
+// Adds up to 10 pointers from the CPUs registers to regs. This is used to make sure no actively
+// used heap memory is freed. Its usually implemented in assembly.
+mp_uint_t cpu_get_regs_and_sp(mp_uint_t *regs);
+
+#endif // MICROPY_INCLUDED_SUPERVISOR_CPU_H
diff --git a/circuitpython/supervisor/fatfs_port.h b/circuitpython/supervisor/fatfs_port.h
new file mode 100644
index 0000000..e76ced5
--- /dev/null
+++ b/circuitpython/supervisor/fatfs_port.h
@@ -0,0 +1,34 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2021 Scott Shawcroft 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_SUPERVISOR_FATFS_PORT_H
+#define MICROPY_INCLUDED_SUPERVISOR_FATFS_PORT_H
+
+#include "lib/oofatfs/ff.h"
+
+void override_fattime(DWORD time);
+
+#endif // MICROPY_INCLUDED_SUPERVISOR_FATFS_PORT_H
diff --git a/circuitpython/supervisor/filesystem.h b/circuitpython/supervisor/filesystem.h
new file mode 100644
index 0000000..6f4faf0
--- /dev/null
+++ b/circuitpython/supervisor/filesystem.h
@@ -0,0 +1,48 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2017 Scott Shawcroft 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_SUPERVISOR_FILESYSTEM_H
+#define MICROPY_INCLUDED_SUPERVISOR_FILESYSTEM_H
+
+#include <stdbool.h>
+
+#include "extmod/vfs_fat.h"
+
+extern volatile bool filesystem_flush_requested;
+
+void filesystem_background(void);
+void filesystem_tick(void);
+bool filesystem_init(bool create_allowed, bool force_create);
+void filesystem_flush(void);
+bool filesystem_present(void);
+void filesystem_set_internal_writable_by_usb(bool usb_writable);
+void filesystem_set_internal_concurrent_write_protection(bool concurrent_write_protection);
+void filesystem_set_writable_by_usb(fs_user_mount_t *vfs, bool usb_writable);
+void filesystem_set_concurrent_write_protection(fs_user_mount_t *vfs, bool concurrent_write_protection);
+bool filesystem_is_writable_by_python(fs_user_mount_t *vfs);
+bool filesystem_is_writable_by_usb(fs_user_mount_t *vfs);
+
+#endif // MICROPY_INCLUDED_SUPERVISOR_FILESYSTEM_H
diff --git a/circuitpython/supervisor/flash.h b/circuitpython/supervisor/flash.h
new file mode 100644
index 0000000..21d76c9
--- /dev/null
+++ b/circuitpython/supervisor/flash.h
@@ -0,0 +1,53 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * SPDX-FileCopyrightText: Copyright (c) 2013, 2014 Damien P. George
+ *
+ * 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_SUPERVISOR_FLASH_H
+#define MICROPY_INCLUDED_SUPERVISOR_FLASH_H
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#include "py/mpconfig.h"
+
+#if INTERNAL_FLASH_FILESYSTEM
+#include "supervisor/shared/internal_flash.h"
+#else
+#include "supervisor/shared/external_flash/external_flash.h"
+#endif
+
+void supervisor_flash_init(void);
+uint32_t supervisor_flash_get_block_size(void);
+uint32_t supervisor_flash_get_block_count(void);
+
+// these return 0 on success, non-zero on error
+mp_uint_t supervisor_flash_read_blocks(uint8_t *dest, uint32_t block_num, uint32_t num_blocks);
+mp_uint_t supervisor_flash_write_blocks(const uint8_t *src, uint32_t block_num, uint32_t num_blocks);
+
+struct _fs_user_mount_t;
+void supervisor_flash_init_vfs(struct _fs_user_mount_t *vfs);
+void supervisor_flash_flush(void);
+void supervisor_flash_release_cache(void);
+
+#endif // MICROPY_INCLUDED_SUPERVISOR_FLASH_H
diff --git a/circuitpython/supervisor/flash_root_pointers.h b/circuitpython/supervisor/flash_root_pointers.h
new file mode 100644
index 0000000..a426b9c
--- /dev/null
+++ b/circuitpython/supervisor/flash_root_pointers.h
@@ -0,0 +1,35 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2018 Scott Shawcroft for Adafruit Industries LLC
+ *
+ * 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_SUPERVISOR_FLASH_ROOT_POINTERS_H
+#define MICROPY_INCLUDED_SUPERVISOR_FLASH_ROOT_POINTERS_H
+
+#ifdef EXTERNAL_FLASH_DEVICES
+#include "supervisor/shared/external_flash/external_flash_root_pointers.h"
+#else
+#include "supervisor/internal_flash_root_pointers.h"
+#endif
+
+#endif // MICROPY_INCLUDED_SUPERVISOR_FLASH_ROOT_POINTERS_H
diff --git a/circuitpython/supervisor/linker.h b/circuitpython/supervisor/linker.h
new file mode 100644
index 0000000..58068c1
--- /dev/null
+++ b/circuitpython/supervisor/linker.h
@@ -0,0 +1,42 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 Scott Shawcroft 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.
+ */
+
+// These macros are used to place code and data into different linking sections.
+
+#ifndef MICROPY_INCLUDED_SUPERVISOR_LINKER_H
+#define MICROPY_INCLUDED_SUPERVISOR_LINKER_H
+
+#if defined(IMXRT10XX) || defined(FOMU) || defined(STM32H7) || defined(RASPBERRYPI)
+#define PLACE_IN_DTCM_DATA(name) name __attribute__((section(".dtcm_data." #name)))
+#define PLACE_IN_DTCM_BSS(name) name __attribute__((section(".dtcm_bss." #name)))
+#define PLACE_IN_ITCM(name) __attribute__((section(".itcm." #name))) name
+#else
+#define PLACE_IN_DTCM_DATA(name) name
+#define PLACE_IN_DTCM_BSS(name) name
+#define PLACE_IN_ITCM(name) name
+#endif
+
+#endif // MICROPY_INCLUDED_SUPERVISOR_LINKER_H
diff --git a/circuitpython/supervisor/memory.h b/circuitpython/supervisor/memory.h
new file mode 100644
index 0000000..0aa38ee
--- /dev/null
+++ b/circuitpython/supervisor/memory.h
@@ -0,0 +1,77 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2018 Scott Shawcroft 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.
+ */
+
+// Basic allocations outside them for areas such as the VM heap and stack.
+// supervisor/shared/memory.c has a basic implementation for a continuous chunk of memory. Add it
+// to a SRC_ in a Makefile to use it.
+
+#ifndef MICROPY_INCLUDED_SUPERVISOR_MEMORY_H
+#define MICROPY_INCLUDED_SUPERVISOR_MEMORY_H
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stddef.h>
+
+typedef struct {
+ uint32_t *ptr;
+} supervisor_allocation;
+
+
+
+void free_memory(supervisor_allocation *allocation);
+
+// Find the allocation with the given ptr, NULL if not found. When called from the context of a
+// supervisor_move_memory() callback, finds the allocation that had that ptr *before* the move, but
+// the returned allocation already contains the ptr after the move.
+// When called with NULL, may return either NULL or an unused allocation whose ptr is NULL (this is
+// a feature used internally in allocate_memory to save code size). Passing the return value to
+// free_memory() is a permissible no-op in either case.
+supervisor_allocation *allocation_from_ptr(void *ptr);
+
+supervisor_allocation *allocate_remaining_memory(void);
+
+// Allocate a piece of a given length in bytes. If high_address is true then it should be allocated
+// at a lower address from the top of the stack. Otherwise, addresses will increase starting after
+// statically allocated memory. If movable is false, memory will be taken from outside the GC heap
+// and will stay stationary until freed. While the VM is running, this will fail unless a previous
+// allocation of exactly matching length has recently been freed. If movable is true, memory will be
+// taken from either outside or inside the GC heap, and when the VM exits, will be moved outside.
+// The ptr of the returned supervisor_allocation will change at that point. If you need to be
+// notified of that, add your own callback function at the designated place near the end of
+// supervisor_move_memory().
+supervisor_allocation *allocate_memory(uint32_t length, bool high_address, bool movable);
+
+static inline size_t align32_size(size_t size) {
+ return (size + 3) & ~3;
+}
+
+size_t get_allocation_length(supervisor_allocation *allocation);
+
+// Called after the GC heap is freed, transfers movable allocations from the GC heap to the
+// supervisor heap and compacts the supervisor heap.
+void supervisor_move_memory(void);
+
+#endif // MICROPY_INCLUDED_SUPERVISOR_MEMORY_H
diff --git a/circuitpython/supervisor/messages/default.h b/circuitpython/supervisor/messages/default.h
new file mode 100644
index 0000000..8cdd671
--- /dev/null
+++ b/circuitpython/supervisor/messages/default.h
@@ -0,0 +1,99 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2017 Scott Shawcroft 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_SUPERVISOR_MESSAGES_DEFAULT_H
+#define MICROPY_SUPERVISOR_MESSAGES_DEFAULT_H
+
+#ifndef MSG_OUTPUT_SUFFIX
+#define MSG_OUTPUT_SUFFIX " output:\r\n"
+#endif
+
+#ifndef MSG_NEWLINE
+#define MSG_NEWLINE "\r\n"
+#endif
+
+#ifndef MSG_AUTORELOAD_ON
+#define MSG_AUTORELOAD_ON "Auto-reload is on. Simply save files over USB to run them or enter REPL to disable.\r\n"
+#endif
+
+#ifndef MSG_AUTORELOAD_OFF
+#define MSG_AUTORELOAD_OFF "Auto-reload is off.\r\n"
+#endif
+
+#ifndef MSG_SAFE_MODE_ON
+#define MSG_SAFE_MODE_ON "Running in safe mode! Auto-reload is off.\r\n"
+#endif
+
+#ifndef MSG_SAFE_MODE_NO_MAIN
+#define MSG_SAFE_MODE_NO_MAIN "Running in safe mode! Not running saved code.\r\n"
+#endif
+
+#ifndef MSG_SAFE_MODE_USER_REQUESTED
+#define MSG_SAFE_MODE_USER_REQUESTED "You requested starting safe mode by "
+#endif
+
+#ifndef MSG_SAFE_MODE_USER_EXIT
+#define MSG_SAFE_MODE_USER_EXIT "To exit, please reset the board without "
+#endif
+
+#ifndef MSG_BAD_SAFE_MODE
+#define MSG_BAD_SAFE_MODE "You are running in safe mode which means something really bad happened."
+#endif
+
+#ifndef MSG_SAFE_MODE_CRASH
+#define MSG_SAFE_MODE_CRASH "Looks like our core CircuitPython code crashed hard. Whoops!"
+#endif
+
+#ifndef MSG_SAFE_MODE_FILE_ISSUE
+#define MSG_SAFE_MODE_FILE_ISSUE "Please file an issue here with the contents of your CIRCUITPY drive:"
+#endif
+
+#ifndef MSG_SAFE_MODE_ISSUE_LINK
+#define MSG_SAFE_MODE_ISSUE_LINK "https://github.com/adafruit/circuitpython/issues"
+#endif
+
+#ifndef MSG_SAFE_MODE_BROWN_OUT_LINE_1
+#define MSG_SAFE_MODE_BROWN_OUT_LINE_1 "The microcontroller's power dipped. Please make sure your power supply provides"
+#endif
+
+#ifndef MSG_SAFE_MODE_BROWN_OUT_LINE_2
+#define MSG_SAFE_MODE_BROWN_OUT_LINE_2 "enough power for the whole circuit and press reset (after ejecting CIRCUITPY)."
+#endif
+
+#ifndef MSG_WAIT_BEFORE_REPL
+#define MSG_WAIT_BEFORE_REPL "Press any key to enter the REPL. Use CTRL-D to reload."
+#endif
+
+// Be careful, some tools depend on this.
+#ifndef MSG_SOFT_REBOOT
+#define MSG_SOFT_REBOOT "soft reboot"
+#endif
+
+#ifndef MSG_DOUBLE_FILE_EXTENSION
+#define MSG_DOUBLE_FILE_EXTENSION "WARNING: Your code filename has two extensions\r\n"
+#endif
+
+#endif // MICROPY_SUPERVISOR_MESSAGES_DEFAULT_H
diff --git a/circuitpython/supervisor/port.h b/circuitpython/supervisor/port.h
new file mode 100644
index 0000000..0a8cdfd
--- /dev/null
+++ b/circuitpython/supervisor/port.h
@@ -0,0 +1,106 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2016-2017 Scott Shawcroft 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_SUPERVISOR_PORT_H
+#define MICROPY_INCLUDED_SUPERVISOR_PORT_H
+
+#include "py/mpconfig.h"
+
+#include "supervisor/memory.h"
+#include "supervisor/shared/safe_mode.h"
+
+// Provided by the linker;
+extern uint32_t _ezero;
+
+// This file defines core methods that must be implemented by a port.
+extern uint32_t _estack;
+
+// Stored at the end of the bss section (which includes the heap).
+extern uint32_t _ebss;
+
+safe_mode_t port_init(void);
+
+// Reset the microcontroller completely.
+void reset_cpu(void) NORETURN;
+
+// Reset the microcontroller state.
+void reset_port(void);
+
+// Reset to the bootloader
+void reset_to_bootloader(void) NORETURN;
+
+// Get stack limit address
+uint32_t *port_stack_get_limit(void);
+
+// Get stack top address
+uint32_t *port_stack_get_top(void);
+
+// True if stack is not located inside heap (at the top)
+bool port_has_fixed_stack(void);
+
+// Get heap bottom address
+uint32_t *port_heap_get_bottom(void);
+
+// Get heap top address
+uint32_t *port_heap_get_top(void);
+
+// Save and retrieve a word from memory that is preserved over reset. Used for safe mode.
+void port_set_saved_word(uint32_t);
+uint32_t port_get_saved_word(void);
+
+// Get the raw tick count since start up. A tick is 1/1024 of a second, a common low frequency
+// clock rate. If subticks is not NULL then the port will fill in the number of subticks where each
+// tick is 32 subticks (for a resolution of 1/32768 or 30.5ish microseconds.)
+uint64_t port_get_raw_ticks(uint8_t *subticks);
+
+// Enable 1/1024 second tick.
+void port_enable_tick(void);
+
+// Disable 1/1024 second tick.
+void port_disable_tick(void);
+
+// Wake the CPU after the given number of ticks or sooner. Only the last call to this will apply.
+// Only the common sleep routine should use it.
+void port_interrupt_after_ticks(uint32_t ticks);
+
+// Sleep the CPU until an interrupt is received. We call this idle because it
+// may not be a system level sleep.
+void port_idle_until_interrupt(void);
+
+// Execute port specific actions during background tasks.
+void port_background_task(void);
+
+// Take port specific actions at the beginning and end of background tasks.
+// This is used e.g., to set a monitoring pin for debug purposes. "Actual
+// work" should be done in port_background_task() instead.
+void port_start_background_task(void);
+void port_finish_background_task(void);
+
+// Some ports need special handling to wake the main task from an interrupt
+// context or other task. The port must implement the necessary code in this
+// function. A default weak implementation is provided that does nothing.
+void port_wake_main_task(void);
+#endif // MICROPY_INCLUDED_SUPERVISOR_PORT_H
diff --git a/circuitpython/supervisor/serial.h b/circuitpython/supervisor/serial.h
new file mode 100644
index 0000000..a6646e9
--- /dev/null
+++ b/circuitpython/supervisor/serial.h
@@ -0,0 +1,62 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2017 Scott Shawcroft 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_SUPERVISOR_SERIAL_H
+#define MICROPY_INCLUDED_SUPERVISOR_SERIAL_H
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+
+#include "py/mpconfig.h"
+
+#ifdef CIRCUITPY_BOOT_OUTPUT_FILE
+#include "py/misc.h"
+
+extern vstr_t *boot_output;
+#endif
+
+
+void serial_early_init(void);
+void serial_init(void);
+void serial_write(const char *text);
+// Only writes up to given length. Does not check for null termination at all.
+void serial_write_substring(const char *text, uint32_t length);
+char serial_read(void);
+bool serial_bytes_available(void);
+bool serial_connected(void);
+
+// These have no-op versions that are weak and the port can override. They work
+// in tandem with the cross-port mechanics like USB and BLE.
+void port_serial_init(void);
+bool port_serial_connected(void);
+char port_serial_read(void);
+bool port_serial_bytes_available(void);
+void port_serial_write_substring(const char *text, uint32_t length);
+
+int debug_uart_printf(const char *fmt, ...) __attribute__((format(printf, 1, 2)));
+
+#endif // MICROPY_INCLUDED_SUPERVISOR_SERIAL_H
diff --git a/circuitpython/supervisor/shared/background_callback.c b/circuitpython/supervisor/shared/background_callback.c
new file mode 100644
index 0000000..e53edb5
--- /dev/null
+++ b/circuitpython/supervisor/shared/background_callback.c
@@ -0,0 +1,148 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 Jeff Epler 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 <string.h>
+
+#include "py/gc.h"
+#include "py/mpconfig.h"
+#include "supervisor/background_callback.h"
+#include "supervisor/linker.h"
+#include "supervisor/port.h"
+#include "supervisor/shared/tick.h"
+#include "shared-bindings/microcontroller/__init__.h"
+
+STATIC volatile background_callback_t *volatile callback_head, *volatile callback_tail;
+
+#define CALLBACK_CRITICAL_BEGIN (common_hal_mcu_disable_interrupts())
+#define CALLBACK_CRITICAL_END (common_hal_mcu_enable_interrupts())
+
+MP_WEAK void port_wake_main_task(void) {
+}
+
+void background_callback_add_core(background_callback_t *cb) {
+ CALLBACK_CRITICAL_BEGIN;
+ if (cb->prev || callback_head == cb) {
+ CALLBACK_CRITICAL_END;
+ return;
+ }
+ cb->next = 0;
+ cb->prev = (background_callback_t *)callback_tail;
+ if (callback_tail) {
+ callback_tail->next = cb;
+ }
+ if (!callback_head) {
+ callback_head = cb;
+ }
+ callback_tail = cb;
+ CALLBACK_CRITICAL_END;
+
+ port_wake_main_task();
+}
+
+void background_callback_add(background_callback_t *cb, background_callback_fun fun, void *data) {
+ cb->fun = fun;
+ cb->data = data;
+ background_callback_add_core(cb);
+}
+
+bool PLACE_IN_ITCM(background_callback_pending)(void) {
+ return callback_head != NULL;
+}
+
+static bool in_background_callback;
+void PLACE_IN_ITCM(background_callback_run_all)() {
+ if (!background_callback_pending()) {
+ return;
+ }
+ CALLBACK_CRITICAL_BEGIN;
+ if (in_background_callback) {
+ CALLBACK_CRITICAL_END;
+ return;
+ }
+ in_background_callback = true;
+ background_callback_t *cb = (background_callback_t *)callback_head;
+ callback_head = NULL;
+ callback_tail = NULL;
+ while (cb) {
+ background_callback_t *next = cb->next;
+ cb->next = cb->prev = NULL;
+ background_callback_fun fun = cb->fun;
+ void *data = cb->data;
+ CALLBACK_CRITICAL_END;
+ // Leave the critical section in order to run the callback function
+ if (fun) {
+ fun(data);
+ }
+ CALLBACK_CRITICAL_BEGIN;
+ cb = next;
+ }
+ in_background_callback = false;
+ CALLBACK_CRITICAL_END;
+}
+
+void background_callback_begin_critical_section() {
+ CALLBACK_CRITICAL_BEGIN;
+}
+
+void background_callback_end_critical_section() {
+ CALLBACK_CRITICAL_END;
+}
+
+void background_callback_reset() {
+ CALLBACK_CRITICAL_BEGIN;
+ background_callback_t *cb = (background_callback_t *)callback_head;
+ while (cb) {
+ background_callback_t *next = cb->next;
+ memset(cb, 0, sizeof(*cb));
+ cb = next;
+ }
+ callback_head = NULL;
+ callback_tail = NULL;
+ in_background_callback = false;
+ CALLBACK_CRITICAL_END;
+}
+
+void background_callback_gc_collect(void) {
+ // We don't enter the callback critical section here. We rely on
+ // gc_collect_ptr _NOT_ entering background callbacks, so it is not
+ // possible for the list to be cleared.
+ //
+ // However, it is possible for the list to be extended. We make the
+ // minor assumption that no newly added callback is for a
+ // collectable object. That is, we only plug the hole where an
+ // object becomes collectable AFTER it is added but before the
+ // callback is run, not the hole where an object was ALREADY
+ // collectable but adds a background task for itself.
+ //
+ // It's necessary to traverse the whole list here, as the callbacks
+ // themselves can be in non-gc memory, and some of the cb->data
+ // objects themselves might be in non-gc memory.
+ background_callback_t *cb = (background_callback_t *)callback_head;
+ while (cb) {
+ gc_collect_ptr(cb->data);
+ cb = cb->next;
+ }
+}
diff --git a/circuitpython/supervisor/shared/bluetooth/bluetooth.c b/circuitpython/supervisor/shared/bluetooth/bluetooth.c
new file mode 100644
index 0000000..426aad9
--- /dev/null
+++ b/circuitpython/supervisor/shared/bluetooth/bluetooth.c
@@ -0,0 +1,301 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2021 Scott Shawcroft 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 <string.h>
+
+#include "supervisor/shared/bluetooth/bluetooth.h"
+
+#include "shared-bindings/_bleio/__init__.h"
+#include "shared-bindings/_bleio/Adapter.h"
+#if defined(CIRCUITPY_BOOT_BUTTON)
+#include "shared-bindings/digitalio/DigitalInOut.h"
+#endif
+#include "shared-bindings/microcontroller/Processor.h"
+#include "shared-bindings/microcontroller/ResetReason.h"
+#include "shared-module/storage/__init__.h"
+
+#include "common-hal/_bleio/__init__.h"
+
+#include "supervisor/shared/status_leds.h"
+#include "supervisor/shared/tick.h"
+
+#include "py/mpstate.h"
+
+#if CIRCUITPY_BLE_FILE_SERVICE
+#include "supervisor/shared/bluetooth/file_transfer.h"
+#include "bluetooth/ble_drv.h"
+#endif
+
+#if CIRCUITPY_SERIAL_BLE
+#include "supervisor/shared/bluetooth/serial.h"
+#include "bluetooth/ble_drv.h"
+#endif
+
+// This standard advertisement advertises the CircuitPython editing service and a CIRCUITPY short name.
+const uint8_t public_advertising_data[] = { 0x02, 0x01, 0x06, // 0-2 Flags
+ 0x02, 0x0a, 0xec, // 3-5 TX power level -20
+ #if CIRCUITPY_BLE_FILE_SERVICE
+ 0x03, 0x02, 0xbb, 0xfe, // 6 - 9 Incomplete service list (File Transfer service)
+ #endif
+ 0x0e, 0xff, 0x22, 0x08, // 10 - 13 Adafruit Manufacturer Data
+ 0x0a, 0x04, 0x00, // 14 - 16 Creator ID / Creation ID
+ CIRCUITPY_CREATOR_ID & 0xff, // 17 - 20 Creator ID
+ (CIRCUITPY_CREATOR_ID >> 8) & 0xff,
+ (CIRCUITPY_CREATOR_ID >> 16) & 0xff,
+ (CIRCUITPY_CREATOR_ID >> 24) & 0xff,
+ CIRCUITPY_CREATION_ID & 0xff, // 21 - 24 Creation ID
+ (CIRCUITPY_CREATION_ID >> 8) & 0xff,
+ (CIRCUITPY_CREATION_ID >> 16) & 0xff,
+ (CIRCUITPY_CREATION_ID >> 24) & 0xff,
+ 0x05, 0x08, 0x43, 0x49, 0x52, 0x43 // 25 - 31 - Short name
+};
+const uint8_t private_advertising_data[] = { 0x02, 0x01, 0x06, // 0-2 Flags
+ 0x02, 0x0a, 0x00 // 3-5 TX power level 0
+};
+// This scan response advertises the full CIRCPYXXXX device name.
+uint8_t circuitpython_scan_response_data[] = {
+ 0x0a, 0x09, 0x43, 0x49, 0x52, 0x50, 0x59, 0x00, 0x00, 0x00, 0x00,
+ #if CIRCUITPY_SERIAL_BLE
+ 0x11, 0x06, 0x6e, 0x68, 0x74, 0x79, 0x50, 0x74, 0x69, 0x75, 0x63, 0x72, 0x69, 0x43, 0x01, 0x00, 0xaf, 0xad
+ #endif
+};
+
+
+#if CIRCUITPY_BLE_FILE_SERVICE || CIRCUITPY_SERIAL_BLE
+STATIC bool boot_in_discovery_mode = false;
+STATIC bool advertising = false;
+STATIC bool ble_started = false;
+
+#define WORKFLOW_UNSET 0
+#define WORKFLOW_ENABLED 1
+#define WORKFLOW_DISABLED 2
+
+STATIC uint8_t workflow_state = WORKFLOW_UNSET;
+STATIC bool was_connected = false;
+
+STATIC void supervisor_bluetooth_start_advertising(void) {
+ if (workflow_state != WORKFLOW_ENABLED) {
+ return;
+ }
+ bool is_connected = common_hal_bleio_adapter_get_connected(&common_hal_bleio_adapter_obj);
+ if (is_connected) {
+ return;
+ }
+ bool bonded = common_hal_bleio_adapter_is_bonded_to_central(&common_hal_bleio_adapter_obj);
+ #if CIRCUITPY_USB
+ // Don't advertise when we have USB instead of BLE.
+ if (!bonded && !boot_in_discovery_mode) {
+ return;
+ }
+ #endif
+ uint32_t timeout = 0;
+ float interval = 0.1f;
+ int tx_power = 0;
+ const uint8_t *adv = private_advertising_data;
+ size_t adv_len = sizeof(private_advertising_data);
+ const uint8_t *scan_response = NULL;
+ size_t scan_response_len = 0;
+ // Advertise with less power when doing so publicly to reduce who can hear us. This will make it
+ // harder for someone with bad intentions to pair from a distance.
+ if (!bonded) {
+ tx_power = -20;
+ adv = public_advertising_data;
+ adv_len = sizeof(public_advertising_data);
+ scan_response = circuitpython_scan_response_data;
+ scan_response_len = sizeof(circuitpython_scan_response_data);
+ }
+ uint32_t status = _common_hal_bleio_adapter_start_advertising(&common_hal_bleio_adapter_obj,
+ true,
+ bonded, // Advertise anonymously if we are bonded
+ timeout,
+ interval,
+ adv,
+ adv_len,
+ scan_response,
+ scan_response_len,
+ tx_power,
+ NULL);
+ // This may fail if we are already advertising.
+ advertising = status == NRF_SUCCESS;
+}
+
+#endif // CIRCUITPY_BLE_FILE_SERVICE || CIRCUITPY_SERIAL_BLE
+
+#define BLE_DISCOVERY_DATA_GUARD 0xbb0000bb
+#define BLE_DISCOVERY_DATA_GUARD_MASK 0xff0000ff
+
+void supervisor_bluetooth_init(void) {
+ #if CIRCUITPY_BLE_FILE_SERVICE || CIRCUITPY_SERIAL_BLE
+ uint32_t reset_state = port_get_saved_word();
+ uint32_t ble_mode = 0;
+ if ((reset_state & BLE_DISCOVERY_DATA_GUARD_MASK) == BLE_DISCOVERY_DATA_GUARD) {
+ ble_mode = (reset_state & ~BLE_DISCOVERY_DATA_GUARD_MASK) >> 8;
+ }
+ const mcu_reset_reason_t reset_reason = common_hal_mcu_processor_get_reset_reason();
+ boot_in_discovery_mode = false;
+ if (reset_reason != RESET_REASON_POWER_ON &&
+ reset_reason != RESET_REASON_RESET_PIN &&
+ reset_reason != RESET_REASON_UNKNOWN &&
+ reset_reason != RESET_REASON_SOFTWARE) {
+ return;
+ }
+
+ if (ble_mode == 0) {
+ port_set_saved_word(BLE_DISCOVERY_DATA_GUARD | (0x01 << 8));
+ }
+ // Wait for a while to allow for reset.
+
+ #ifdef CIRCUITPY_BOOT_BUTTON
+ digitalio_digitalinout_obj_t boot_button;
+ common_hal_digitalio_digitalinout_construct(&boot_button, CIRCUITPY_BOOT_BUTTON);
+ common_hal_digitalio_digitalinout_switch_to_input(&boot_button, PULL_UP);
+ #endif
+ #if CIRCUITPY_STATUS_LED
+ status_led_init();
+ #endif
+ uint64_t start_ticks = supervisor_ticks_ms64();
+ uint64_t diff = 0;
+ if (ble_mode != 0) {
+ #ifdef CIRCUITPY_STATUS_LED
+ new_status_color(0x0000ff);
+ #endif
+ common_hal_bleio_adapter_erase_bonding(&common_hal_bleio_adapter_obj);
+ boot_in_discovery_mode = true;
+ reset_state = 0x0;
+ }
+ #if !CIRCUITPY_USB
+ // Boot into discovery if USB isn't available and we aren't bonded already.
+ // Checking here allows us to have the status LED solidly on even if no button was
+ // pressed.
+ bool bonded = common_hal_bleio_adapter_is_bonded_to_central(&common_hal_bleio_adapter_obj);
+ if (!bonded) {
+ boot_in_discovery_mode = true;
+ }
+ #endif
+ while (diff < 1000) {
+ #ifdef CIRCUITPY_STATUS_LED
+ // Blink on for 50 and off for 100
+ bool led_on = boot_in_discovery_mode || (diff % 150) <= 50;
+ if (led_on) {
+ new_status_color(0x0000ff);
+ } else {
+ new_status_color(BLACK);
+ }
+ #endif
+ #ifdef CIRCUITPY_BOOT_BUTTON
+ if (!common_hal_digitalio_digitalinout_get_value(&boot_button)) {
+ boot_in_discovery_mode = true;
+ break;
+ }
+ #endif
+ diff = supervisor_ticks_ms64() - start_ticks;
+ }
+ #if CIRCUITPY_STATUS_LED
+ new_status_color(BLACK);
+ status_led_deinit();
+ #endif
+ port_set_saved_word(reset_state);
+ #endif
+}
+
+void supervisor_bluetooth_background(void) {
+ #if CIRCUITPY_BLE_FILE_SERVICE || CIRCUITPY_SERIAL_BLE
+ if (!ble_started) {
+ return;
+ }
+ bool is_connected = common_hal_bleio_adapter_get_connected(&common_hal_bleio_adapter_obj);
+ if (was_connected && !is_connected) {
+ #if CIRCUITPY_BLE_FILE_SERVICE
+ supervisor_bluetooth_file_transfer_disconnected();
+ #endif
+ }
+ was_connected = is_connected;
+ if (!is_connected) {
+ supervisor_bluetooth_start_advertising();
+ return;
+ }
+
+ #if CIRCUITPY_BLE_FILE_SERVICE
+ supervisor_bluetooth_file_transfer_background();
+ #endif
+ #endif
+}
+
+void supervisor_start_bluetooth(void) {
+ #if CIRCUITPY_BLE_FILE_SERVICE || CIRCUITPY_SERIAL_BLE
+
+ if (workflow_state != WORKFLOW_ENABLED) {
+ return;
+ }
+
+ common_hal_bleio_adapter_set_enabled(&common_hal_bleio_adapter_obj, true);
+
+ #if CIRCUITPY_BLE_FILE_SERVICE
+ supervisor_start_bluetooth_file_transfer();
+ #endif
+
+ #if CIRCUITPY_SERIAL_BLE
+ supervisor_start_bluetooth_serial();
+ #endif
+
+ // Mark as started so that the background call does something.
+ ble_started = true;
+
+ // Kick off advertisements
+ supervisor_bluetooth_background();
+
+ #endif
+}
+
+void supervisor_stop_bluetooth(void) {
+ #if CIRCUITPY_BLE_FILE_SERVICE || CIRCUITPY_SERIAL_BLE
+
+ if (!ble_started && workflow_state != WORKFLOW_ENABLED) {
+ return;
+ }
+
+ #if CIRCUITPY_SERIAL_BLE
+ supervisor_stop_bluetooth_serial();
+ #endif
+
+ #endif
+}
+
+void supervisor_bluetooth_enable_workflow(void) {
+ #if CIRCUITPY_BLE_FILE_SERVICE || CIRCUITPY_SERIAL_BLE
+ if (workflow_state == WORKFLOW_DISABLED) {
+ return;
+ }
+
+ workflow_state = WORKFLOW_ENABLED;
+ #endif
+}
+
+void supervisor_bluetooth_disable_workflow(void) {
+ #if CIRCUITPY_BLE_FILE_SERVICE || CIRCUITPY_SERIAL_BLE
+ workflow_state = WORKFLOW_DISABLED;
+ #endif
+}
diff --git a/circuitpython/supervisor/shared/bluetooth/bluetooth.h b/circuitpython/supervisor/shared/bluetooth/bluetooth.h
new file mode 100644
index 0000000..9de8271
--- /dev/null
+++ b/circuitpython/supervisor/shared/bluetooth/bluetooth.h
@@ -0,0 +1,41 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Scott Shawcroft 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_SUPERVISOR_SHARED_BLUETOOTH_H
+#define MICROPY_INCLUDED_SUPERVISOR_SHARED_BLUETOOTH_H
+
+#include <stdbool.h>
+
+void supervisor_bluetooth_background(void);
+void supervisor_bluetooth_init(void);
+void supervisor_start_bluetooth(void);
+void supervisor_stop_bluetooth(void);
+
+// Enable only works if it hasn't been set yet.
+void supervisor_bluetooth_enable_workflow(void);
+void supervisor_bluetooth_disable_workflow(void);
+
+#endif // MICROPY_INCLUDED_SUPERVISOR_SHARED_BLUETOOTH_H
diff --git a/circuitpython/supervisor/shared/bluetooth/file_transfer.c b/circuitpython/supervisor/shared/bluetooth/file_transfer.c
new file mode 100644
index 0000000..a6a2f80
--- /dev/null
+++ b/circuitpython/supervisor/shared/bluetooth/file_transfer.c
@@ -0,0 +1,756 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019-2021 Scott Shawcroft 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 <string.h>
+
+#include "extmod/vfs.h"
+#include "extmod/vfs_fat.h"
+#include "shared/timeutils/timeutils.h"
+
+#include "shared-bindings/_bleio/__init__.h"
+#include "shared-bindings/_bleio/Adapter.h"
+#include "shared-bindings/_bleio/Characteristic.h"
+#include "shared-bindings/_bleio/PacketBuffer.h"
+#include "shared-bindings/_bleio/Service.h"
+#include "shared-bindings/_bleio/UUID.h"
+#include "shared-module/storage/__init__.h"
+
+#include "bluetooth/ble_drv.h"
+
+#include "common-hal/_bleio/__init__.h"
+
+#include "supervisor/fatfs_port.h"
+#include "supervisor/shared/reload.h"
+#include "supervisor/shared/bluetooth/file_transfer.h"
+#include "supervisor/shared/bluetooth/file_transfer_protocol.h"
+#include "supervisor/shared/tick.h"
+#include "supervisor/usb.h"
+
+#include "py/mpstate.h"
+#include "py/stackctrl.h"
+
+STATIC bleio_service_obj_t supervisor_ble_service;
+STATIC bleio_uuid_obj_t supervisor_ble_service_uuid;
+STATIC bleio_characteristic_obj_t supervisor_ble_version_characteristic;
+STATIC bleio_uuid_obj_t supervisor_ble_version_uuid;
+STATIC bleio_characteristic_obj_t supervisor_ble_transfer_characteristic;
+STATIC bleio_uuid_obj_t supervisor_ble_transfer_uuid;
+
+// This is the base UUID for the file transfer service.
+const uint8_t file_transfer_base_uuid[16] = {0x72, 0x65, 0x66, 0x73, 0x6e, 0x61, 0x72, 0x54, 0x65, 0x6c, 0x69, 0x46, 0x00, 0x00, 0xaf, 0xad };
+
+STATIC mp_obj_list_t characteristic_list;
+STATIC mp_obj_t characteristic_list_items[2];
+// 2 * 10 ringbuf packets, 512 for a disk sector and 12 for the file transfer write header.
+#define PACKET_BUFFER_SIZE (2 * 10 + 512 + 12)
+// uint32_t so its aligned
+STATIC uint32_t _buffer[PACKET_BUFFER_SIZE / 4 + 1];
+STATIC uint32_t _outgoing1[BLE_GATTS_VAR_ATTR_LEN_MAX / 4];
+STATIC uint32_t _outgoing2[BLE_GATTS_VAR_ATTR_LEN_MAX / 4];
+STATIC ble_drv_evt_handler_entry_t static_handler_entry;
+STATIC bleio_packet_buffer_obj_t _transfer_packet_buffer;
+
+void supervisor_start_bluetooth_file_transfer(void) {
+ supervisor_ble_service_uuid.base.type = &bleio_uuid_type;
+ common_hal_bleio_uuid_construct(&supervisor_ble_service_uuid, 0xfebb, NULL);
+
+ // We know we'll only be 1 characteristic so we can statically allocate it.
+ characteristic_list.base.type = &mp_type_list;
+ characteristic_list.alloc = sizeof(characteristic_list_items) / sizeof(characteristic_list_items[0]);
+ characteristic_list.len = 0;
+ characteristic_list.items = characteristic_list_items;
+ mp_seq_clear(characteristic_list.items, 0, characteristic_list.alloc, sizeof(*characteristic_list.items));
+
+ _common_hal_bleio_service_construct(&supervisor_ble_service, &supervisor_ble_service_uuid, false /* is secondary */, &characteristic_list);
+
+ // Version number
+ supervisor_ble_version_uuid.base.type = &bleio_uuid_type;
+ common_hal_bleio_uuid_construct(&supervisor_ble_version_uuid, 0x0100, file_transfer_base_uuid);
+ common_hal_bleio_characteristic_construct(&supervisor_ble_version_characteristic,
+ &supervisor_ble_service,
+ 0, // handle (for remote only)
+ &supervisor_ble_version_uuid,
+ CHAR_PROP_READ,
+ SECURITY_MODE_OPEN,
+ SECURITY_MODE_NO_ACCESS,
+ 4, // max length
+ true, // fixed length
+ NULL, // no initial value
+ NULL); // no description
+
+ uint32_t version = 4;
+ mp_buffer_info_t bufinfo;
+ bufinfo.buf = &version;
+ bufinfo.len = sizeof(version);
+ common_hal_bleio_characteristic_set_value(&supervisor_ble_version_characteristic, &bufinfo);
+
+ // Active filename.
+ supervisor_ble_transfer_uuid.base.type = &bleio_uuid_type;
+ common_hal_bleio_uuid_construct(&supervisor_ble_transfer_uuid, 0x0200, file_transfer_base_uuid);
+ common_hal_bleio_characteristic_construct(&supervisor_ble_transfer_characteristic,
+ &supervisor_ble_service,
+ 0, // handle (for remote only)
+ &supervisor_ble_transfer_uuid,
+ CHAR_PROP_READ | CHAR_PROP_WRITE_NO_RESPONSE | CHAR_PROP_NOTIFY,
+ SECURITY_MODE_ENC_NO_MITM,
+ SECURITY_MODE_ENC_NO_MITM,
+ BLE_GATTS_VAR_ATTR_LEN_MAX, // max length
+ false, // fixed length
+ NULL, // no initial valuen
+ NULL);
+
+ _common_hal_bleio_packet_buffer_construct(
+ &_transfer_packet_buffer, &supervisor_ble_transfer_characteristic,
+ _buffer, PACKET_BUFFER_SIZE,
+ _outgoing1, _outgoing2, BLE_GATTS_VAR_ATTR_LEN_MAX,
+ &static_handler_entry);
+}
+
+#define COMMAND_SIZE 1024
+
+#define ANY_COMMAND 0x00
+#define THIS_COMMAND 0x01
+
+// FATFS has a two second timestamp resolution but the BLE API allows for nanosecond resolution.
+// This function truncates the time the time to a resolution storable by FATFS and fills in the
+// FATFS encoded version into fattime.
+STATIC uint64_t truncate_time(uint64_t input_time, DWORD *fattime) {
+ timeutils_struct_time_t tm;
+ uint64_t seconds_since_epoch = timeutils_seconds_since_epoch_from_nanoseconds_since_1970(input_time);
+ timeutils_seconds_since_epoch_to_struct_time(seconds_since_epoch, &tm);
+ uint64_t truncated_time = timeutils_nanoseconds_since_epoch_to_nanoseconds_since_1970((seconds_since_epoch / 2) * 2 * 1000000000);
+
+ *fattime = ((tm.tm_year - 1980) << 25) | (tm.tm_mon << 21) | (tm.tm_mday << 16) |
+ (tm.tm_hour << 11) | (tm.tm_min << 5) | (tm.tm_sec >> 1);
+ return truncated_time;
+}
+
+// Used by read and write.
+STATIC FIL active_file;
+STATIC uint8_t _process_read(const uint8_t *raw_buf, size_t command_len) {
+ struct read_command *command = (struct read_command *)raw_buf;
+ size_t header_size = sizeof(struct read_command);
+ size_t response_size = sizeof(struct read_data);
+ uint8_t data_buffer[response_size];
+ struct read_data response;
+ response.command = READ_DATA;
+ response.status = STATUS_OK;
+ if (command->path_length > (COMMAND_SIZE - response_size - 1)) { // -1 for the null we'll write
+ // TODO: throw away any more packets of path.
+ response.status = STATUS_ERROR;
+ common_hal_bleio_packet_buffer_write(&_transfer_packet_buffer, (const uint8_t *)&response, response_size, NULL, 0);
+ return ANY_COMMAND;
+ }
+ // We need to receive another packet to have the full path.
+ if (command_len < header_size + command->path_length) {
+ return THIS_COMMAND;
+ }
+
+ char *path = (char *)((uint8_t *)command) + header_size;
+ path[command->path_length] = '\0';
+
+ FATFS *fs = &((fs_user_mount_t *)MP_STATE_VM(vfs_mount_table)->obj)->fatfs;
+ FRESULT result = f_open(fs, &active_file, path, FA_READ);
+ if (result != FR_OK) {
+ response.status = STATUS_ERROR;
+ common_hal_bleio_packet_buffer_write(&_transfer_packet_buffer, (const uint8_t *)&response, response_size, NULL, 0);
+ return ANY_COMMAND;
+ }
+ uint32_t total_length = f_size(&active_file);
+ // Write out the response header.
+ uint32_t offset = command->chunk_offset;
+ uint32_t chunk_size = command->chunk_size;
+ chunk_size = MIN(chunk_size, total_length - offset);
+ response.chunk_offset = offset;
+ response.total_length = total_length;
+ response.data_size = chunk_size;
+ common_hal_bleio_packet_buffer_write(&_transfer_packet_buffer, (const uint8_t *)&response, response_size, NULL, 0);
+ f_lseek(&active_file, offset);
+ // Write out the chunk contents. We can do this in small pieces because PacketBuffer
+ // will assemble them into larger packets of its own.
+ size_t chunk_end = offset + chunk_size;
+ while (offset < chunk_end) {
+ size_t quantity_read;
+ size_t read_amount = MIN(response_size, chunk_end - offset);
+ f_read(&active_file, data_buffer, read_amount, &quantity_read);
+ offset += quantity_read;
+ // TODO: Do something if the read fails
+ common_hal_bleio_packet_buffer_write(&_transfer_packet_buffer, data_buffer, quantity_read, NULL, 0);
+ }
+ if (offset >= total_length) {
+ f_close(&active_file);
+ return ANY_COMMAND;
+ }
+ return READ_PACING;
+}
+
+STATIC uint8_t _process_read_pacing(const uint8_t *raw_buf, size_t command_len) {
+ struct read_pacing *command = (struct read_pacing *)raw_buf;
+ struct read_data response;
+ response.command = READ_DATA;
+ response.status = STATUS_OK;
+ size_t response_size = sizeof(struct read_data);
+
+ uint32_t total_length = f_size(&active_file);
+ // Write out the response header.
+ uint32_t chunk_size = MIN(command->chunk_size, total_length - command->chunk_offset);
+ response.chunk_offset = command->chunk_offset;
+ response.total_length = total_length;
+ response.data_size = chunk_size;
+ common_hal_bleio_packet_buffer_write(&_transfer_packet_buffer, (const uint8_t *)&response, response_size, NULL, 0);
+ f_lseek(&active_file, command->chunk_offset);
+ // Write out the chunk contents. We can do this in small pieces because PacketBuffer
+ // will assemble them into larger packets of its own.
+ size_t chunk_offset = 0;
+ uint8_t data[20];
+ while (chunk_offset < chunk_size) {
+ size_t quantity_read;
+ size_t read_size = MIN(chunk_size - chunk_offset, sizeof(data));
+ FRESULT result = f_read(&active_file, &data, read_size, &quantity_read);
+ if (quantity_read == 0 || result != FR_OK) {
+ // TODO: If we can't read everything, then the file must have been shortened. Maybe we
+ // should return 0s to pad it out.
+ break;
+ }
+ common_hal_bleio_packet_buffer_write(&_transfer_packet_buffer, (const uint8_t *)&data, quantity_read, NULL, 0);
+ chunk_offset += quantity_read;
+ }
+ if ((chunk_offset + chunk_size) >= total_length) {
+ f_close(&active_file);
+ return ANY_COMMAND;
+ }
+ return READ_PACING;
+}
+
+// Used by write and write data to know when the write is complete.
+STATIC size_t total_write_length;
+STATIC uint64_t _truncated_time;
+
+// Returns true if usb is active and replies with an error if so. If not, it grabs
+// the USB mass storage lock and returns false. Make sure to release the lock with
+// usb_msc_unlock() when the transaction is complete.
+STATIC bool _usb_active(void *response, size_t response_size) {
+ // Check to see if USB has already been mounted. If not, then we "eject" from USB until we're done.
+ #if CIRCUITPY_USB && CIRCUITPY_USB_MSC
+ if (storage_usb_enabled() && !usb_msc_lock()) {
+ // Status is always the second byte of the response.
+ ((uint8_t *)response)[1] = STATUS_ERROR_READONLY;
+ common_hal_bleio_packet_buffer_write(&_transfer_packet_buffer, (const uint8_t *)response, response_size, NULL, 0);
+ return true;
+ }
+ #endif
+ return false;
+}
+
+STATIC uint8_t _process_write(const uint8_t *raw_buf, size_t command_len) {
+ struct write_command *command = (struct write_command *)raw_buf;
+ size_t header_size = sizeof(struct write_command);
+ struct write_pacing response;
+ response.command = WRITE_PACING;
+ response.status = STATUS_OK;
+ if (command->path_length > (COMMAND_SIZE - header_size - 1)) { // -1 for the null we'll write
+ // TODO: throw away any more packets of path.
+ response.status = STATUS_ERROR;
+ common_hal_bleio_packet_buffer_write(&_transfer_packet_buffer, (const uint8_t *)&response, sizeof(struct write_pacing), NULL, 0);
+ return ANY_COMMAND;
+ }
+ // We need to receive another packet to have the full path.
+ if (command_len < header_size + command->path_length) {
+ return THIS_COMMAND;
+ }
+ total_write_length = command->total_length;
+
+ char *path = (char *)command->path;
+ path[command->path_length] = '\0';
+ if (_usb_active(&response, sizeof(struct write_pacing))) {
+ return ANY_COMMAND;
+ }
+
+ FATFS *fs = &((fs_user_mount_t *)MP_STATE_VM(vfs_mount_table)->obj)->fatfs;
+ DWORD fattime;
+ _truncated_time = truncate_time(command->modification_time, &fattime);
+ override_fattime(fattime);
+ FRESULT result = f_open(fs, &active_file, path, FA_WRITE | FA_OPEN_ALWAYS);
+ if (result != FR_OK) {
+ response.status = STATUS_ERROR;
+ common_hal_bleio_packet_buffer_write(&_transfer_packet_buffer, (const uint8_t *)&response, sizeof(struct write_pacing), NULL, 0);
+ #if CIRCUITPY_USB_MSC
+ usb_msc_unlock();
+ #endif
+ override_fattime(0);
+ return ANY_COMMAND;
+ }
+ // Write out the pacing response.
+
+ // Align the next chunk to a sector boundary.
+ uint32_t offset = command->offset;
+ size_t chunk_size = MIN(total_write_length - offset, 512 - (offset % 512));
+ // Special case when truncating the file. (Deleting stuff off the end.)
+ if (chunk_size == 0) {
+ f_lseek(&active_file, offset);
+ f_truncate(&active_file);
+ f_close(&active_file);
+ override_fattime(0);
+ #if CIRCUITPY_USB_MSC
+ usb_msc_unlock();
+ #endif
+ }
+ response.offset = offset;
+ response.free_space = chunk_size;
+ response.truncated_time = _truncated_time;
+ common_hal_bleio_packet_buffer_write(&_transfer_packet_buffer, (const uint8_t *)&response, sizeof(struct write_pacing), NULL, 0);
+ if (chunk_size == 0) {
+ // Don't reload until everything is written out of the packet buffer.
+ common_hal_bleio_packet_buffer_flush(&_transfer_packet_buffer);
+ return ANY_COMMAND;
+ }
+
+ return WRITE_DATA;
+}
+
+STATIC uint8_t _process_write_data(const uint8_t *raw_buf, size_t command_len) {
+ struct write_data *command = (struct write_data *)raw_buf;
+ size_t header_size = sizeof(struct write_data);
+ struct write_pacing response;
+ response.command = WRITE_PACING;
+ response.status = STATUS_OK;
+ if (command->data_size > (COMMAND_SIZE - header_size - 1)) { // -1 for the null we'll write
+ // TODO: throw away any more packets of path.
+ response.status = STATUS_ERROR;
+ common_hal_bleio_packet_buffer_write(&_transfer_packet_buffer, (const uint8_t *)&response, sizeof(struct write_pacing), NULL, 0);
+ #if CIRCUITPY_USB_MSC
+ usb_msc_unlock();
+ #endif
+ override_fattime(0);
+ return ANY_COMMAND;
+ }
+ // We need to receive another packet to have the full path.
+ if (command_len < header_size + command->data_size) {
+ return THIS_COMMAND;
+ }
+ uint32_t offset = command->offset;
+ f_lseek(&active_file, offset);
+ UINT actual;
+ f_write(&active_file, command->data, command->data_size, &actual);
+ if (actual < command->data_size) { // -1 for the null we'll write
+ // TODO: throw away any more packets of path.
+ response.status = STATUS_ERROR;
+ common_hal_bleio_packet_buffer_write(&_transfer_packet_buffer, (const uint8_t *)&response, sizeof(struct write_pacing), NULL, 0);
+ #if CIRCUITPY_USB_MSC
+ usb_msc_unlock();
+ #endif
+ override_fattime(0);
+ return ANY_COMMAND;
+ }
+ offset += command->data_size;
+ // Align the next chunk to a sector boundary.
+ size_t chunk_size = MIN(total_write_length - offset, 512);
+ response.offset = offset;
+ response.free_space = chunk_size;
+ response.truncated_time = _truncated_time;
+ common_hal_bleio_packet_buffer_write(&_transfer_packet_buffer, (const uint8_t *)&response, sizeof(struct write_pacing), NULL, 0);
+ if (total_write_length == offset) {
+ f_truncate(&active_file);
+ f_close(&active_file);
+ override_fattime(0);
+ #if CIRCUITPY_USB_MSC
+ usb_msc_unlock();
+ #endif
+ // Don't reload until everything is written out of the packet buffer.
+ common_hal_bleio_packet_buffer_flush(&_transfer_packet_buffer);
+ return ANY_COMMAND;
+ }
+ return WRITE_DATA;
+}
+
+STATIC FRESULT _delete_directory_contents(FATFS *fs, const TCHAR *path) {
+ FF_DIR dir;
+ FRESULT res = f_opendir(fs, &dir, path);
+ FILINFO file_info;
+ // Check the stack since we're putting paths on it.
+ if (mp_stack_usage() >= MP_STATE_THREAD(stack_limit)) {
+ return FR_INT_ERR;
+ }
+ while (res == FR_OK) {
+ res = f_readdir(&dir, &file_info);
+ if (res != FR_OK || file_info.fname[0] == '\0') {
+ break;
+ }
+ size_t pathlen = strlen(path);
+ size_t fnlen = strlen(file_info.fname);
+ TCHAR full_path[pathlen + 1 + fnlen];
+ memcpy(full_path, path, pathlen);
+ full_path[pathlen] = '/';
+ size_t full_pathlen = pathlen + 1 + fnlen;
+ memcpy(full_path + pathlen + 1, file_info.fname, fnlen);
+ full_path[full_pathlen] = '\0';
+ if ((file_info.fattrib & AM_DIR) != 0) {
+ res = _delete_directory_contents(fs, full_path);
+ }
+ if (res != FR_OK) {
+ break;
+ }
+ res = f_unlink(fs, full_path);
+ }
+ f_closedir(&dir);
+ return res;
+}
+
+STATIC uint8_t _process_delete(const uint8_t *raw_buf, size_t command_len) {
+ const struct delete_command *command = (struct delete_command *)raw_buf;
+ size_t header_size = sizeof(struct delete_command);
+ struct delete_status response;
+ response.command = DELETE_STATUS;
+ response.status = STATUS_OK;
+ if (command->path_length > (COMMAND_SIZE - header_size - 1)) { // -1 for the null we'll write
+ // TODO: throw away any more packets of path.
+ response.status = STATUS_ERROR;
+ common_hal_bleio_packet_buffer_write(&_transfer_packet_buffer, (const uint8_t *)&response, sizeof(struct delete_status), NULL, 0);
+ return ANY_COMMAND;
+ }
+ if (_usb_active(&response, sizeof(struct delete_status))) {
+ return ANY_COMMAND;
+ }
+ // We need to receive another packet to have the full path.
+ if (command_len < header_size + command->path_length) {
+ return THIS_COMMAND;
+ }
+ FATFS *fs = &((fs_user_mount_t *)MP_STATE_VM(vfs_mount_table)->obj)->fatfs;
+ char *path = (char *)((uint8_t *)command) + header_size;
+ path[command->path_length] = '\0';
+ FILINFO file;
+ FRESULT result = f_stat(fs, path, &file);
+ if (result == FR_OK) {
+ if ((file.fattrib & AM_DIR) != 0) {
+ result = _delete_directory_contents(fs, path);
+ }
+ if (result == FR_OK) {
+ result = f_unlink(fs, path);
+ }
+ }
+ #if CIRCUITPY_USB_MSC
+ usb_msc_unlock();
+ #endif
+ if (result != FR_OK) {
+ response.status = STATUS_ERROR;
+ }
+ common_hal_bleio_packet_buffer_write(&_transfer_packet_buffer, (const uint8_t *)&response, sizeof(struct delete_status), NULL, 0);
+ if (result == FR_OK) {
+ // Don't reload until everything is written out of the packet buffer.
+ common_hal_bleio_packet_buffer_flush(&_transfer_packet_buffer);
+ }
+ return ANY_COMMAND;
+}
+
+// NULL-terminate the path and remove any trailing /. Older versions of the
+// protocol require it but newer ones do not.
+STATIC void _terminate_path(char *path, size_t path_length) {
+ // -1 because fatfs doesn't want a trailing /
+ if (path[path_length - 1] == '/') {
+ path[path_length - 1] = '\0';
+ } else {
+ path[path_length] = '\0';
+ }
+}
+
+STATIC uint8_t _process_mkdir(const uint8_t *raw_buf, size_t command_len) {
+ const struct mkdir_command *command = (struct mkdir_command *)raw_buf;
+ size_t header_size = sizeof(struct mkdir_command);
+ struct mkdir_status response;
+ response.command = MKDIR_STATUS;
+ response.status = STATUS_OK;
+ if (command->path_length > (COMMAND_SIZE - header_size - 1)) { // -1 for the null we'll write
+ // TODO: throw away any more packets of path.
+ response.status = STATUS_ERROR;
+ common_hal_bleio_packet_buffer_write(&_transfer_packet_buffer, (const uint8_t *)&response, sizeof(struct mkdir_status), NULL, 0);
+ return ANY_COMMAND;
+ }
+ if (_usb_active(&response, sizeof(struct mkdir_status))) {
+ return ANY_COMMAND;
+ }
+ // We need to receive another packet to have the full path.
+ if (command_len < header_size + command->path_length) {
+ return THIS_COMMAND;
+ }
+ FATFS *fs = &((fs_user_mount_t *)MP_STATE_VM(vfs_mount_table)->obj)->fatfs;
+ char *path = (char *)command->path;
+ _terminate_path(path, command->path_length);
+
+ DWORD fattime;
+ response.truncated_time = truncate_time(command->modification_time, &fattime);
+ override_fattime(fattime);
+ FRESULT result = f_mkdir(fs, path);
+ override_fattime(0);
+ #if CIRCUITPY_USB_MSC
+ usb_msc_unlock();
+ #endif
+ if (result != FR_OK) {
+ response.status = STATUS_ERROR;
+ }
+ common_hal_bleio_packet_buffer_write(&_transfer_packet_buffer, (const uint8_t *)&response, sizeof(struct mkdir_status), NULL, 0);
+ if (result == FR_OK) {
+ // Don't reload until everything is written out of the packet buffer.
+ common_hal_bleio_packet_buffer_flush(&_transfer_packet_buffer);
+ }
+ return ANY_COMMAND;
+}
+
+STATIC void send_listdir_entry_header(const struct listdir_entry *entry, mp_int_t max_packet_size) {
+ mp_int_t response_size = sizeof(struct listdir_entry);
+ if (max_packet_size >= response_size) {
+ common_hal_bleio_packet_buffer_write(&_transfer_packet_buffer, (const uint8_t *)entry, response_size, NULL, 0);
+ return;
+ }
+ // Split into 16 + 12 size packets to fit into 20 byte minimum packet size.
+ common_hal_bleio_packet_buffer_write(&_transfer_packet_buffer, (const uint8_t *)entry, 16, NULL, 0);
+ common_hal_bleio_packet_buffer_write(&_transfer_packet_buffer, ((const uint8_t *)entry) + 16, response_size - 16, NULL, 0);
+}
+
+STATIC uint8_t _process_listdir(uint8_t *raw_buf, size_t command_len) {
+ const struct listdir_command *command = (struct listdir_command *)raw_buf;
+ struct listdir_entry *entry = (struct listdir_entry *)raw_buf;
+ size_t header_size = sizeof(struct listdir_command);
+ mp_int_t max_packet_size = common_hal_bleio_packet_buffer_get_outgoing_packet_length(&_transfer_packet_buffer);
+ if (max_packet_size < 0) {
+ // -1 means we're disconnected
+ return ANY_COMMAND;
+ }
+ // We reuse the command buffer so that we can produce long packets without
+ // making the stack large.
+ if (command->path_length > (COMMAND_SIZE - header_size - 1)) { // -1 for the null we'll write
+ // TODO: throw away any more packets of path.
+ entry->command = LISTDIR_ENTRY;
+ entry->status = STATUS_ERROR;
+ send_listdir_entry_header(entry, max_packet_size);
+ return ANY_COMMAND;
+ }
+ // We need to receive another packet to have the full path.
+ if (command_len < header_size + command->path_length) {
+ return THIS_COMMAND;
+ }
+
+ FATFS *fs = &((fs_user_mount_t *)MP_STATE_VM(vfs_mount_table)->obj)->fatfs;
+ char *path = (char *)&command->path;
+ _terminate_path(path, command->path_length);
+ // mp_printf(&mp_plat_print, "list %s\n", path);
+ FF_DIR dir;
+ FRESULT res = f_opendir(fs, &dir, path);
+
+ entry->command = LISTDIR_ENTRY;
+ entry->status = STATUS_OK;
+ entry->path_length = 0;
+ entry->entry_number = 0;
+ entry->entry_count = 0;
+ entry->flags = 0;
+
+ if (res != FR_OK) {
+ entry->status = STATUS_ERROR_NO_FILE;
+ send_listdir_entry_header(entry, max_packet_size);
+ return ANY_COMMAND;
+ }
+ FILINFO file_info;
+ res = f_readdir(&dir, &file_info);
+ char *fn = file_info.fname;
+ size_t total_entries = 0;
+ while (res == FR_OK && fn[0] != 0) {
+ res = f_readdir(&dir, &file_info);
+ total_entries += 1;
+ }
+ // Rewind the directory.
+ f_readdir(&dir, NULL);
+ entry->entry_count = total_entries;
+ for (size_t i = 0; i < total_entries; i++) {
+ res = f_readdir(&dir, &file_info);
+ entry->entry_number = i;
+ uint64_t truncated_time = timeutils_mktime(1980 + (file_info.fdate >> 9),
+ (file_info.fdate >> 5) & 0xf,
+ file_info.fdate & 0x1f,
+ file_info.ftime >> 11,
+ (file_info.ftime >> 5) & 0x1f,
+ (file_info.ftime & 0x1f) * 2) * 1000000000ULL;
+ entry->truncated_time = truncated_time;
+ if ((file_info.fattrib & AM_DIR) != 0) {
+ entry->flags = 1; // Directory
+ entry->file_size = 0;
+ } else {
+ entry->flags = 0;
+ entry->file_size = file_info.fsize;
+ }
+
+ size_t name_length = strlen(file_info.fname);
+ entry->path_length = name_length;
+ send_listdir_entry_header(entry, max_packet_size);
+ size_t fn_offset = 0;
+ while (fn_offset < name_length) {
+ size_t fn_size = MIN(name_length - fn_offset, 4);
+ common_hal_bleio_packet_buffer_write(&_transfer_packet_buffer, ((uint8_t *)file_info.fname) + fn_offset, fn_size, NULL, 0);
+ fn_offset += fn_size;
+ }
+ }
+ f_closedir(&dir);
+ entry->path_length = 0;
+ entry->entry_number = entry->entry_count;
+ entry->flags = 0;
+ entry->file_size = 0;
+ send_listdir_entry_header(entry, max_packet_size);
+ return ANY_COMMAND;
+}
+
+STATIC uint8_t _process_move(const uint8_t *raw_buf, size_t command_len) {
+ const struct move_command *command = (struct move_command *)raw_buf;
+ size_t header_size = sizeof(struct move_command);
+ struct move_status response;
+ response.command = MOVE_STATUS;
+ response.status = STATUS_OK;
+ // +2 for null terminators.
+ uint32_t total_path_length = command->old_path_length + command->new_path_length + 1;
+ if (total_path_length > (COMMAND_SIZE - header_size - 1)) {
+ // TODO: throw away any more packets of path.
+ response.status = STATUS_ERROR;
+ common_hal_bleio_packet_buffer_write(&_transfer_packet_buffer, (const uint8_t *)&response, sizeof(struct move_status), NULL, 0);
+ return ANY_COMMAND;
+ }
+ if (_usb_active(&response, sizeof(struct move_status))) {
+ return ANY_COMMAND;
+ }
+ // We need to receive another packet to have the full path.
+ if (command_len < header_size + total_path_length) {
+ return THIS_COMMAND;
+ }
+ FATFS *fs = &((fs_user_mount_t *)MP_STATE_VM(vfs_mount_table)->obj)->fatfs;
+ char *old_path = (char *)command->paths;
+ old_path[command->old_path_length] = '\0';
+
+ char *new_path = old_path + command->old_path_length + 1;
+ new_path[command->new_path_length] = '\0';
+
+ // mp_printf(&mp_plat_print, "move %s to %s\n", old_path, new_path);
+
+ FRESULT result = f_rename(fs, old_path, new_path);
+ #if CIRCUITPY_USB_MSC
+ usb_msc_unlock();
+ #endif
+ if (result != FR_OK) {
+ response.status = STATUS_ERROR;
+ }
+ common_hal_bleio_packet_buffer_write(&_transfer_packet_buffer, (const uint8_t *)&response, sizeof(struct move_status), NULL, 0);
+ if (result == FR_OK) {
+ // Don't reload until everything is written out of the packet buffer.
+ common_hal_bleio_packet_buffer_flush(&_transfer_packet_buffer);
+ }
+ return ANY_COMMAND;
+}
+
+// Background state that must live across background calls. After the _process
+// helpers to force them to not use them.
+STATIC uint8_t current_command[COMMAND_SIZE] __attribute__ ((aligned(4)));
+STATIC volatile size_t current_offset;
+STATIC uint8_t next_command;
+STATIC bool running = false;
+void supervisor_bluetooth_file_transfer_background(void) {
+ if (running) {
+ return;
+ }
+ running = true;
+ mp_int_t size = 1;
+ while (size > 0) {
+ size = common_hal_bleio_packet_buffer_readinto(&_transfer_packet_buffer, current_command + current_offset, COMMAND_SIZE - current_offset);
+
+ if (size == 0) {
+ break;
+ }
+ autoreload_suspend(AUTORELOAD_SUSPEND_BLE);
+ // TODO: If size < 0 return an error.
+ current_offset += size;
+ #if CIRCUITPY_VERBOSE_BLE
+ mp_printf(&mp_plat_print, "buffer[:%d]:", current_offset);
+ for (size_t i = 0; i < current_offset; i++) {
+ mp_printf(&mp_plat_print, " (%x %c)", current_command[i], current_command[i]);
+ }
+ mp_printf(&mp_plat_print, "\n");
+ #endif
+ uint8_t current_state = current_command[0];
+ // mp_printf(&mp_plat_print, "current command 0x%02x\n", current_state);
+ // Check for protocol error.
+ if (next_command != ANY_COMMAND && next_command != THIS_COMMAND && ((current_state & 0xf) != 0) && current_state != next_command) {
+ uint8_t response[2];
+ response[0] = next_command;
+ response[1] = STATUS_ERROR_PROTOCOL;
+ common_hal_bleio_packet_buffer_write(&_transfer_packet_buffer, response, 2, NULL, 0);
+ autoreload_resume(AUTORELOAD_SUSPEND_BLE);
+ break;
+ }
+ switch (current_state) {
+ case READ:
+ next_command = _process_read(current_command, current_offset);
+ break;
+ case READ_PACING:
+ next_command = _process_read_pacing(current_command, current_offset);
+ break;
+ case WRITE:
+ next_command = _process_write(current_command, current_offset);
+ break;
+ case WRITE_DATA:
+ next_command = _process_write_data(current_command, current_offset);
+ break;
+ case DELETE:
+ next_command = _process_delete(current_command, current_offset);
+ break;
+ case MKDIR:
+ next_command = _process_mkdir(current_command, current_offset);
+ break;
+ case LISTDIR:
+ next_command = _process_listdir(current_command, current_offset);
+ break;
+ case MOVE:
+ next_command = _process_move(current_command, current_offset);
+ break;
+ }
+ // Preserve the offset if we are waiting for more from this command.
+ if (next_command != THIS_COMMAND) {
+ current_offset = 0;
+ }
+ if (next_command == ANY_COMMAND) {
+ autoreload_resume(AUTORELOAD_SUSPEND_BLE);
+ // Trigger a reload if the command may have mutated the file system.
+ if (current_state == WRITE ||
+ current_state == WRITE_DATA ||
+ current_state == DELETE ||
+ current_state == MKDIR ||
+ current_state == MOVE) {
+ autoreload_trigger();
+ }
+ }
+ }
+ running = false;
+}
+
+void supervisor_bluetooth_file_transfer_disconnected(void) {
+ next_command = ANY_COMMAND;
+ current_offset = 0;
+ f_close(&active_file);
+ autoreload_resume(AUTORELOAD_SUSPEND_BLE);
+}
diff --git a/circuitpython/supervisor/shared/bluetooth/file_transfer.h b/circuitpython/supervisor/shared/bluetooth/file_transfer.h
new file mode 100644
index 0000000..e27924e
--- /dev/null
+++ b/circuitpython/supervisor/shared/bluetooth/file_transfer.h
@@ -0,0 +1,36 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2021 Scott Shawcroft 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_SUPERVISOR_SHARED_BLUETOOTH_FILE_TRANSFER_H
+#define MICROPY_INCLUDED_SUPERVISOR_SHARED_BLUETOOTH_FILE_TRANSFER_H
+
+#include <stdbool.h>
+
+void supervisor_bluetooth_file_transfer_background(void);
+void supervisor_start_bluetooth_file_transfer(void);
+void supervisor_bluetooth_file_transfer_disconnected(void);
+
+#endif // MICROPY_INCLUDED_SUPERVISOR_SHARED_BLUETOOTH_FILE_TRANSFER_H
diff --git a/circuitpython/supervisor/shared/bluetooth/file_transfer_protocol.h b/circuitpython/supervisor/shared/bluetooth/file_transfer_protocol.h
new file mode 100644
index 0000000..3bc1f61
--- /dev/null
+++ b/circuitpython/supervisor/shared/bluetooth/file_transfer_protocol.h
@@ -0,0 +1,180 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2021 Scott Shawcroft 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_SUPERVISOR_SHARED_BLUETOOTH_FILE_TRANSFER_PROTOCOL_H
+#define MICROPY_INCLUDED_SUPERVISOR_SHARED_BLUETOOTH_FILE_TRANSFER_PROTOCOL_H
+
+#include <stdint.h>
+
+// See https://github.com/adafruit/Adafruit_CircuitPython_BLE_File_Transfer
+// for full protocol documentation and a Python client API.
+
+// Each struct is packed so that no padding is added by the compiler. (structs
+// may having padding at the end in order to align a particular element when in
+// an array of the struct.) So, be careful that types added are aligned. Otherwise,
+// the compiler may generate more code than necessary.
+
+// 0x00 - 0x0f are never used by the protocol as a command
+#define READ 0x10
+struct read_command {
+ uint8_t command;
+ uint8_t reserved;
+ uint16_t path_length;
+ uint32_t chunk_offset;
+ uint32_t chunk_size;
+ uint8_t path[];
+} __attribute__((packed));
+
+#define READ_DATA 0x11
+struct read_data {
+ uint8_t command;
+ uint8_t status;
+ uint16_t reserved;
+ uint32_t chunk_offset;
+ uint32_t total_length;
+ uint32_t data_size;
+ uint8_t data[];
+} __attribute__((packed));
+
+#define READ_PACING 0x12
+struct read_pacing {
+ uint8_t command;
+ uint8_t status;
+ uint16_t reserved;
+ uint32_t chunk_offset;
+ uint32_t chunk_size;
+} __attribute__((packed));
+
+#define WRITE 0x20
+struct write_command {
+ uint8_t command;
+ uint8_t reserved;
+ uint16_t path_length;
+ uint32_t offset;
+ uint64_t modification_time;
+ uint32_t total_length;
+ uint8_t path[];
+} __attribute__((packed));
+
+#define WRITE_PACING 0x21
+struct write_pacing {
+ uint8_t command;
+ uint8_t status;
+ uint16_t reserved;
+ uint32_t offset;
+ uint64_t truncated_time;
+ uint32_t free_space;
+} __attribute__((packed));
+
+#define WRITE_DATA 0x22
+struct write_data {
+ uint8_t command;
+ uint8_t status;
+ uint16_t reserved;
+ uint32_t offset;
+ uint32_t data_size;
+ uint8_t data[];
+} __attribute__((packed));
+
+#define DELETE 0x30
+struct delete_command {
+ uint8_t command;
+ uint8_t reserved;
+ uint16_t path_length;
+ uint8_t path[];
+} __attribute__((packed));
+
+#define DELETE_STATUS 0x31
+struct delete_status {
+ uint8_t command;
+ uint8_t status;
+} __attribute__((packed));
+
+#define MKDIR 0x40
+struct mkdir_command {
+ uint8_t command;
+ uint8_t reserved;
+ uint16_t path_length;
+ uint32_t reserved2;
+ uint64_t modification_time;
+ uint8_t path[];
+} __attribute__((packed));
+
+#define MKDIR_STATUS 0x41
+struct mkdir_status {
+ uint8_t command;
+ uint8_t status;
+ uint16_t reserved;
+ uint32_t reserved2;
+ uint64_t truncated_time;
+} __attribute__((packed));
+
+#define LISTDIR 0x50
+struct listdir_command {
+ uint8_t command;
+ uint8_t reserved;
+ uint16_t path_length;
+ uint8_t path[];
+} __attribute__((packed));
+
+#define LISTDIR_ENTRY 0x51
+struct listdir_entry {
+ uint8_t command;
+ uint8_t status;
+ uint16_t path_length;
+ uint32_t entry_number;
+ uint32_t entry_count;
+ uint32_t flags;
+ uint64_t truncated_time;
+ uint32_t file_size;
+ uint8_t path[];
+} __attribute__((packed));
+
+#define MOVE 0x60
+struct move_command {
+ uint8_t command;
+ uint8_t reserved;
+ uint16_t old_path_length;
+ uint16_t new_path_length;
+ // paths is two strings. The first is old_path and then a reserved byte.
+ // The last path is new_path.
+ uint8_t paths[];
+} __attribute__((packed));
+
+#define MOVE_STATUS 0x61
+struct move_status {
+ uint8_t command;
+ uint8_t status;
+} __attribute__((packed));
+
+#define STATUS_OK 0x01
+#define STATUS_ERROR 0x02
+#define STATUS_ERROR_NO_FILE 0x03
+#define STATUS_ERROR_PROTOCOL 0x04
+#define STATUS_ERROR_READONLY 0x05
+
+
+#endif // MICROPY_INCLUDED_SUPERVISOR_SHARED_BLUETOOTH_FILE_TRANSFER_PROTOCOL_H
diff --git a/circuitpython/supervisor/shared/bluetooth/serial.c b/circuitpython/supervisor/shared/bluetooth/serial.c
new file mode 100644
index 0000000..2edf3c7
--- /dev/null
+++ b/circuitpython/supervisor/shared/bluetooth/serial.c
@@ -0,0 +1,211 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2021 Scott Shawcroft 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 <string.h>
+
+#include "genhdr/mpversion.h"
+#include "shared-bindings/_bleio/__init__.h"
+#include "shared-bindings/_bleio/Adapter.h"
+#include "shared-bindings/_bleio/Characteristic.h"
+#include "shared-bindings/_bleio/CharacteristicBuffer.h"
+#include "shared-bindings/_bleio/PacketBuffer.h"
+#include "shared-bindings/_bleio/Service.h"
+#include "shared-bindings/_bleio/UUID.h"
+#include "shared-module/storage/__init__.h"
+#include "supervisor/shared/bluetooth/serial.h"
+
+#include "common-hal/_bleio/__init__.h"
+
+#include "py/mpstate.h"
+
+STATIC bleio_service_obj_t supervisor_ble_circuitpython_service;
+STATIC bleio_uuid_obj_t supervisor_ble_circuitpython_service_uuid;
+STATIC bleio_characteristic_obj_t supervisor_ble_circuitpython_rx_characteristic;
+STATIC bleio_uuid_obj_t supervisor_ble_circuitpython_rx_uuid;
+STATIC bleio_characteristic_obj_t supervisor_ble_circuitpython_tx_characteristic;
+STATIC bleio_uuid_obj_t supervisor_ble_circuitpython_tx_uuid;
+STATIC bleio_characteristic_obj_t supervisor_ble_circuitpython_version_characteristic;
+STATIC bleio_uuid_obj_t supervisor_ble_circuitpython_version_uuid;
+
+// This is the base UUID for the CircuitPython service.
+const uint8_t circuitpython_base_uuid[16] = {0x6e, 0x68, 0x74, 0x79, 0x50, 0x74, 0x69, 0x75, 0x63, 0x72, 0x69, 0x43, 0x00, 0x00, 0xaf, 0xad };
+
+STATIC mp_obj_list_t characteristic_list;
+STATIC mp_obj_t characteristic_list_items[3];
+
+STATIC uint32_t _outgoing1[BLE_GATTS_VAR_ATTR_LEN_MAX / 4];
+STATIC uint32_t _outgoing2[BLE_GATTS_VAR_ATTR_LEN_MAX / 4];
+STATIC ble_drv_evt_handler_entry_t rx_static_handler_entry;
+STATIC ble_drv_evt_handler_entry_t tx_static_handler_entry;
+STATIC bleio_packet_buffer_obj_t _tx_packet_buffer;
+STATIC uint32_t _incoming[64];
+STATIC bleio_characteristic_buffer_obj_t _rx_buffer;
+
+// Internal enabling so we can disable while printing BLE debugging.
+STATIC bool _enabled;
+
+void supervisor_start_bluetooth_serial(void) {
+ supervisor_ble_circuitpython_service_uuid.base.type = &bleio_uuid_type;
+ common_hal_bleio_uuid_construct(&supervisor_ble_circuitpython_service_uuid, 0x0001, circuitpython_base_uuid);
+
+ // We know we'll only be N characteristics so we can statically allocate it.
+ characteristic_list.base.type = &mp_type_list;
+ characteristic_list.alloc = sizeof(characteristic_list_items) / sizeof(characteristic_list_items[0]);
+ characteristic_list.len = 0;
+ characteristic_list.items = characteristic_list_items;
+ mp_seq_clear(characteristic_list.items, 0, characteristic_list.alloc, sizeof(*characteristic_list.items));
+
+ supervisor_ble_circuitpython_service.base.type = &bleio_service_type;
+ _common_hal_bleio_service_construct(&supervisor_ble_circuitpython_service, &supervisor_ble_circuitpython_service_uuid, false /* is secondary */, &characteristic_list);
+
+ // RX
+ supervisor_ble_circuitpython_rx_uuid.base.type = &bleio_uuid_type;
+ common_hal_bleio_uuid_construct(&supervisor_ble_circuitpython_rx_uuid, 0x0002, circuitpython_base_uuid);
+ common_hal_bleio_characteristic_construct(&supervisor_ble_circuitpython_rx_characteristic,
+ &supervisor_ble_circuitpython_service,
+ 0, // handle (for remote only)
+ &supervisor_ble_circuitpython_rx_uuid,
+ CHAR_PROP_WRITE | CHAR_PROP_WRITE_NO_RESPONSE,
+ SECURITY_MODE_NO_ACCESS,
+ SECURITY_MODE_ENC_NO_MITM,
+ BLE_GATTS_VAR_ATTR_LEN_MAX, // max length
+ false, // fixed length
+ NULL, // no initial value
+ NULL);
+
+ // TX
+ supervisor_ble_circuitpython_tx_uuid.base.type = &bleio_uuid_type;
+ common_hal_bleio_uuid_construct(&supervisor_ble_circuitpython_tx_uuid, 0x0003, circuitpython_base_uuid);
+ common_hal_bleio_characteristic_construct(&supervisor_ble_circuitpython_tx_characteristic,
+ &supervisor_ble_circuitpython_service,
+ 0, // handle (for remote only)
+ &supervisor_ble_circuitpython_tx_uuid,
+ CHAR_PROP_NOTIFY,
+ SECURITY_MODE_ENC_NO_MITM,
+ SECURITY_MODE_NO_ACCESS,
+ BLE_GATTS_VAR_ATTR_LEN_MAX, // max length
+ false, // fixed length
+ NULL, // no initial value
+ NULL);
+
+ // Version number
+ const char *version = MICROPY_GIT_TAG;
+ mp_buffer_info_t bufinfo;
+ bufinfo.buf = (uint8_t *)version;
+ bufinfo.len = strlen(version);
+
+ supervisor_ble_circuitpython_version_uuid.base.type = &bleio_uuid_type;
+ common_hal_bleio_uuid_construct(&supervisor_ble_circuitpython_version_uuid, 0x0100, circuitpython_base_uuid);
+ common_hal_bleio_characteristic_construct(&supervisor_ble_circuitpython_version_characteristic,
+ &supervisor_ble_circuitpython_service,
+ 0, // handle (for remote only)
+ &supervisor_ble_circuitpython_version_uuid,
+ CHAR_PROP_READ,
+ SECURITY_MODE_OPEN,
+ SECURITY_MODE_NO_ACCESS,
+ bufinfo.len, // max length
+ true, // fixed length
+ NULL, // no initial value
+ NULL); // no description
+
+ common_hal_bleio_characteristic_set_value(&supervisor_ble_circuitpython_version_characteristic, &bufinfo);
+
+ // Use a PacketBuffer to transmit so that we glom characters to transmit
+ // together and save BLE overhead.
+ _common_hal_bleio_packet_buffer_construct(
+ &_tx_packet_buffer, &supervisor_ble_circuitpython_tx_characteristic,
+ NULL, 0,
+ _outgoing1, _outgoing2, BLE_GATTS_VAR_ATTR_LEN_MAX,
+ &tx_static_handler_entry);
+
+ // Use a CharacteristicBuffer for rx so we can read a single character at a time.
+ _common_hal_bleio_characteristic_buffer_construct(&_rx_buffer,
+ &supervisor_ble_circuitpython_rx_characteristic,
+ 0.1f,
+ (uint8_t *)_incoming, sizeof(_incoming) * sizeof(uint32_t),
+ &rx_static_handler_entry);
+
+ _enabled = true;
+}
+
+void supervisor_stop_bluetooth_serial(void) {
+ if (common_hal_bleio_packet_buffer_deinited(&_tx_packet_buffer)) {
+ return;
+ }
+ if (!_enabled) {
+ return;
+ }
+ common_hal_bleio_packet_buffer_flush(&_tx_packet_buffer);
+}
+
+bool ble_serial_connected(void) {
+ return _tx_packet_buffer.conn_handle != BLE_CONN_HANDLE_INVALID;
+}
+
+bool ble_serial_available(void) {
+ return _enabled &&
+ !common_hal_bleio_characteristic_buffer_deinited(&_rx_buffer) &&
+ common_hal_bleio_characteristic_buffer_rx_characters_available(&_rx_buffer);
+}
+
+char ble_serial_read_char(void) {
+ if (common_hal_bleio_characteristic_buffer_deinited(&_rx_buffer)) {
+ return -1;
+ }
+ if (!_enabled) {
+ return -1;
+ }
+ uint8_t c;
+ common_hal_bleio_characteristic_buffer_read(&_rx_buffer, &c, 1, NULL);
+ return c;
+}
+
+void ble_serial_write(const char *text, size_t len) {
+ if (common_hal_bleio_packet_buffer_deinited(&_tx_packet_buffer)) {
+ return;
+ }
+ if (!_enabled) {
+ return;
+ }
+ size_t sent = 0;
+ while (sent < len) {
+ uint16_t packet_size = MIN(len - sent, (size_t)common_hal_bleio_packet_buffer_get_outgoing_packet_length(&_tx_packet_buffer));
+ mp_int_t written = common_hal_bleio_packet_buffer_write(&_tx_packet_buffer, (const uint8_t *)text + sent, packet_size, NULL, 0);
+ // Error, so we drop characters to transmit.
+ if (written < 0) {
+ break;
+ }
+ sent += written;
+ }
+}
+
+void ble_serial_enable(void) {
+ _enabled = true;
+}
+
+void ble_serial_disable(void) {
+ _enabled = false;
+}
diff --git a/circuitpython/supervisor/shared/bluetooth/serial.h b/circuitpython/supervisor/shared/bluetooth/serial.h
new file mode 100644
index 0000000..cc73a15
--- /dev/null
+++ b/circuitpython/supervisor/shared/bluetooth/serial.h
@@ -0,0 +1,42 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2021 Scott Shawcroft 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_SUPERVISOR_SHARED_BLUETOOTH_SERIAL_H
+#define MICROPY_INCLUDED_SUPERVISOR_SHARED_BLUETOOTH_SERIAL_H
+
+#include <stdbool.h>
+
+void supervisor_start_bluetooth_serial(void);
+void supervisor_stop_bluetooth_serial(void);
+
+bool ble_serial_connected(void);
+bool ble_serial_available(void);
+char ble_serial_read_char(void);
+void ble_serial_write(const char *text, size_t len);
+void ble_serial_enable(void);
+void ble_serial_disable(void);
+
+#endif // MICROPY_INCLUDED_SUPERVISOR_SHARED_BLUETOOTH_SERIAL_H
diff --git a/circuitpython/supervisor/shared/board.c b/circuitpython/supervisor/shared/board.c
new file mode 100644
index 0000000..427c179
--- /dev/null
+++ b/circuitpython/supervisor/shared/board.c
@@ -0,0 +1,48 @@
+/*
+ * 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.
+ */
+
+#include "supervisor/shared/board.h"
+
+#if CIRCUITPY_DIGITALIO && CIRCUITPY_NEOPIXEL_WRITE
+
+#include <string.h>
+
+#include "shared-bindings/digitalio/DigitalInOut.h"
+#include "shared-bindings/neopixel_write/__init__.h"
+
+void board_reset_user_neopixels(const mcu_pin_obj_t *pin, size_t count) {
+ // Turn off on-board NeoPixel string
+ uint8_t empty[count * 3];
+ memset(empty, 0, count * 3);
+ digitalio_digitalinout_obj_t neopixel_pin;
+ common_hal_digitalio_digitalinout_construct(&neopixel_pin, pin);
+ common_hal_digitalio_digitalinout_switch_to_output(&neopixel_pin, false,
+ DRIVE_MODE_PUSH_PULL);
+ common_hal_neopixel_write(&neopixel_pin, empty, count * 3);
+ common_hal_digitalio_digitalinout_deinit(&neopixel_pin);
+}
+
+#endif
diff --git a/circuitpython/supervisor/shared/board.h b/circuitpython/supervisor/shared/board.h
new file mode 100644
index 0000000..def5f48
--- /dev/null
+++ b/circuitpython/supervisor/shared/board.h
@@ -0,0 +1,36 @@
+/*
+ * 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 MICROPY_INCLUDED_SUPERVISOR_SHARED_BOARD_H
+#define MICROPY_INCLUDED_SUPERVISOR_SHARED_BOARD_H
+
+#include <stddef.h>
+
+#include "shared-bindings/microcontroller/Pin.h"
+
+void board_reset_user_neopixels(const mcu_pin_obj_t *pin, size_t count);
+
+#endif // MICROPY_INCLUDED_SUPERVISOR_SHARED_BOARD_H
diff --git a/circuitpython/supervisor/shared/cpu.c b/circuitpython/supervisor/shared/cpu.c
new file mode 100644
index 0000000..d1e4114
--- /dev/null
+++ b/circuitpython/supervisor/shared/cpu.c
@@ -0,0 +1,40 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2021 Scott Shawcroft 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 <stdint.h>
+
+#include "supervisor/shared/cpu.h"
+
+bool cpu_interrupt_active(void) {
+ #if defined(__ARM_ARCH) && (__ARM_ARCH >= 6) && defined(__ARM_ARCH_PROFILE) && (__ARM_ARCH_PROFILE == 'M')
+ // Check VECTACTIVE in ICSR. We don't need to disable interrupts because if
+ // one occurs after we read, we won't continue until it is resolved.
+ return (*((volatile uint32_t *)0xE000ED04) & 0x1ff) != 0;
+ #else
+ // We don't know.
+ return false;
+ #endif
+}
diff --git a/circuitpython/supervisor/shared/cpu.h b/circuitpython/supervisor/shared/cpu.h
new file mode 100644
index 0000000..9e2bed1
--- /dev/null
+++ b/circuitpython/supervisor/shared/cpu.h
@@ -0,0 +1,36 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2021 Scott Shawcroft 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_SUPERVISOR_SHARED_CPU_H
+#define MICROPY_INCLUDED_SUPERVISOR_SHARED_CPU_H
+
+#include <stdbool.h>
+#include <stddef.h>
+
+// True when we're in an interrupt handler.
+bool cpu_interrupt_active(void);
+
+#endif // MICROPY_INCLUDED_SUPERVISOR_SHARED_CPU_H
diff --git a/circuitpython/supervisor/shared/display.c b/circuitpython/supervisor/shared/display.c
new file mode 100644
index 0000000..a8a9b05
--- /dev/null
+++ b/circuitpython/supervisor/shared/display.c
@@ -0,0 +1,347 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Scott Shawcroft 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 "supervisor/shared/display.h"
+
+#include <string.h>
+
+#include "py/mpstate.h"
+#include "shared-bindings/displayio/Bitmap.h"
+#include "shared-bindings/displayio/Group.h"
+#include "shared-bindings/displayio/Palette.h"
+#include "shared-bindings/displayio/TileGrid.h"
+#include "supervisor/memory.h"
+
+#if CIRCUITPY_RGBMATRIX
+#include "shared-module/displayio/__init__.h"
+#endif
+
+#if CIRCUITPY_SHARPDISPLAY
+#include "shared-module/displayio/__init__.h"
+#include "shared-bindings/sharpdisplay/SharpMemoryFramebuffer.h"
+#include "shared-module/sharpdisplay/SharpMemoryFramebuffer.h"
+#endif
+
+#if CIRCUITPY_REPL_LOGO
+extern uint32_t blinka_bitmap_data[];
+extern displayio_bitmap_t blinka_bitmap;
+#endif
+extern displayio_group_t circuitpython_splash;
+
+#if CIRCUITPY_TERMINALIO
+static supervisor_allocation *tilegrid_tiles = NULL;
+#endif
+
+void supervisor_start_terminal(uint16_t width_px, uint16_t height_px) {
+ // Default the scale to 2 because we may show blinka without the terminal for
+ // languages that don't have font support.
+ uint8_t scale = 2;
+
+ #if CIRCUITPY_TERMINALIO
+ displayio_tilegrid_t *grid = &supervisor_terminal_text_grid;
+ bool tall = height_px > width_px;
+ bool reset_tiles = false;
+ #if CIRCUITPY_REPL_LOGO
+ uint16_t terminal_width_px = tall ? width_px : width_px - blinka_bitmap.width;
+ uint16_t terminal_height_px = tall ? height_px - blinka_bitmap.height : height_px;
+ #else
+ uint16_t terminal_width_px = width_px;
+ uint16_t terminal_height_px = height_px;
+ #endif
+ uint16_t width_in_tiles = terminal_width_px / grid->tile_width;
+ // determine scale based on h
+ if (width_in_tiles < 80) {
+ scale = 1;
+ }
+
+ width_in_tiles = terminal_width_px / (grid->tile_width * scale);
+ if (width_in_tiles < 1) {
+ width_in_tiles = 1;
+ }
+ uint16_t height_in_tiles = terminal_height_px / (grid->tile_height * scale);
+ uint16_t remaining_pixels = tall ? 0 : terminal_height_px % (grid->tile_height * scale);
+ if (height_in_tiles < 1 || remaining_pixels > 0) {
+ height_in_tiles += 1;
+ }
+
+ uint16_t total_tiles = width_in_tiles * height_in_tiles;
+
+ // check if the terminal tile dimensions are the same
+ if ((grid->width_in_tiles != width_in_tiles) ||
+ (grid->height_in_tiles != height_in_tiles)) {
+ reset_tiles = true;
+ }
+ // Reuse the previous allocation if possible
+ if (tilegrid_tiles) {
+ if (get_allocation_length(tilegrid_tiles) != align32_size(total_tiles)) {
+ free_memory(tilegrid_tiles);
+ tilegrid_tiles = NULL;
+ reset_tiles = true;
+ }
+ }
+ if (!tilegrid_tiles) {
+ tilegrid_tiles = allocate_memory(align32_size(total_tiles), false, true);
+ reset_tiles = true;
+ if (!tilegrid_tiles) {
+ return;
+ }
+ }
+
+ if (reset_tiles) {
+ uint8_t *tiles = (uint8_t *)tilegrid_tiles->ptr;
+
+ #if CIRCUITPY_REPL_LOGO
+ grid->y = tall ? blinka_bitmap.height : 0;
+ grid->x = tall ? 0 : blinka_bitmap.width;
+ #else
+ grid->y = 0;
+ grid->x = 0;
+ #endif
+ grid->top_left_y = 0;
+ if (remaining_pixels > 0) {
+ grid->y -= (grid->tile_height - remaining_pixels);
+ }
+ grid->width_in_tiles = width_in_tiles;
+ grid->height_in_tiles = height_in_tiles;
+ assert(width_in_tiles > 0);
+ assert(height_in_tiles > 0);
+ grid->pixel_width = width_in_tiles * grid->tile_width;
+ grid->pixel_height = height_in_tiles * grid->tile_height;
+ grid->tiles = tiles;
+
+ grid->full_change = true;
+
+ common_hal_terminalio_terminal_construct(&supervisor_terminal, grid, &supervisor_terminal_font);
+ }
+ #endif
+
+ circuitpython_splash.scale = scale;
+}
+
+void supervisor_stop_terminal(void) {
+ #if CIRCUITPY_TERMINALIO
+ if (tilegrid_tiles != NULL) {
+ free_memory(tilegrid_tiles);
+ tilegrid_tiles = NULL;
+ supervisor_terminal_text_grid.tiles = NULL;
+ supervisor_terminal.tilegrid = NULL;
+ }
+ #endif
+}
+
+void supervisor_display_move_memory(void) {
+ #if CIRCUITPY_TERMINALIO
+ if (tilegrid_tiles != NULL) {
+ supervisor_terminal_text_grid.tiles = (uint8_t *)tilegrid_tiles->ptr;
+ } else {
+ supervisor_terminal_text_grid.tiles = NULL;
+ }
+ #endif
+
+ #if CIRCUITPY_DISPLAYIO
+ for (uint8_t i = 0; i < CIRCUITPY_DISPLAY_LIMIT; i++) {
+ #if CIRCUITPY_RGBMATRIX
+ if (displays[i].rgbmatrix.base.type == &rgbmatrix_RGBMatrix_type) {
+ rgbmatrix_rgbmatrix_obj_t *pm = &displays[i].rgbmatrix;
+ common_hal_rgbmatrix_rgbmatrix_reconstruct(pm, NULL);
+ }
+ #endif
+ #if CIRCUITPY_SHARPDISPLAY
+ if (displays[i].bus_base.type == &sharpdisplay_framebuffer_type) {
+ sharpdisplay_framebuffer_obj_t *sharp = &displays[i].sharpdisplay;
+ common_hal_sharpdisplay_framebuffer_reconstruct(sharp);
+ }
+ #endif
+ }
+ #endif
+}
+
+#if CIRCUITPY_REPL_LOGO
+uint32_t blinka_bitmap_data[32] = {
+ 0x00000011, 0x11000000,
+ 0x00000111, 0x53100000,
+ 0x00000111, 0x56110000,
+ 0x00000111, 0x11140000,
+ 0x00000111, 0x20002000,
+ 0x00000011, 0x13000000,
+ 0x00000001, 0x11200000,
+ 0x00000000, 0x11330000,
+ 0x00000000, 0x01122000,
+ 0x00001111, 0x44133000,
+ 0x00032323, 0x24112200,
+ 0x00111114, 0x44113300,
+ 0x00323232, 0x34112200,
+ 0x11111144, 0x44443300,
+ 0x11111111, 0x11144401,
+ 0x23232323, 0x21111110
+};
+
+displayio_bitmap_t blinka_bitmap = {
+ .base = {.type = &displayio_bitmap_type },
+ .width = 16,
+ .height = 16,
+ .data = blinka_bitmap_data,
+ .stride = 2,
+ .bits_per_value = 4,
+ .x_shift = 3,
+ .x_mask = 0x7,
+ .bitmask = 0xf,
+ .read_only = true
+};
+
+_displayio_color_t blinka_colors[7] = {
+ {
+ .rgb888 = 0x000000,
+ .rgb565 = 0x0000,
+ .luma = 0x00,
+ .chroma = 0,
+ .transparent = true
+ },
+ {
+ .rgb888 = 0x8428bc,
+ .rgb565 = 0x8978,
+ .luma = 0xff, // We cheat the luma here. It is actually 0x60
+ .hue = 184,
+ .chroma = 148
+ },
+ {
+ .rgb888 = 0xff89bc,
+ .rgb565 = 0xFCB8,
+ .luma = 0xb5,
+ .hue = 222,
+ .chroma = 118
+ },
+ {
+ .rgb888 = 0x7beffe,
+ .rgb565 = 0x869F,
+ .luma = 0xe0,
+ .hue = 124,
+ .chroma = 131
+ },
+ {
+ .rgb888 = 0x51395f,
+ .rgb565 = 0x5A0D,
+ .luma = 0x47,
+ .hue = 185,
+ .chroma = 38
+ },
+ {
+ .rgb888 = 0xffffff,
+ .rgb565 = 0xffff,
+ .luma = 0xff,
+ .chroma = 0
+ },
+ {
+ .rgb888 = 0x0736a0,
+ .rgb565 = 0x01f5,
+ .luma = 0x44,
+ .hue = 147,
+ .chroma = 153
+ },
+};
+
+displayio_palette_t blinka_palette = {
+ .base = {.type = &displayio_palette_type },
+ .colors = blinka_colors,
+ .color_count = 7,
+ .needs_refresh = false
+};
+
+displayio_tilegrid_t blinka_sprite = {
+ .base = {.type = &displayio_tilegrid_type },
+ .bitmap = &blinka_bitmap,
+ .pixel_shader = &blinka_palette,
+ .x = 0,
+ .y = 0,
+ .pixel_width = 16,
+ .pixel_height = 16,
+ .bitmap_width_in_tiles = 1,
+ .width_in_tiles = 1,
+ .height_in_tiles = 1,
+ .tile_width = 16,
+ .tile_height = 16,
+ .top_left_x = 16,
+ .top_left_y = 16,
+ .tiles = 0,
+ .partial_change = false,
+ .full_change = false,
+ .hidden = false,
+ .hidden_by_parent = false,
+ .moved = false,
+ .inline_tiles = true,
+ .in_group = true
+};
+#endif
+
+#if CIRCUITPY_TERMINALIO
+#if CIRCUITPY_REPL_LOGO
+mp_obj_t members[] = { &blinka_sprite, &supervisor_terminal_text_grid, };
+mp_obj_list_t splash_children = {
+ .base = {.type = &mp_type_list },
+ .alloc = 2,
+ .len = 2,
+ .items = members,
+};
+#else
+mp_obj_t members[] = { &supervisor_terminal_text_grid, };
+mp_obj_list_t splash_children = {
+ .base = {.type = &mp_type_list },
+ .alloc = 1,
+ .len = 1,
+ .items = members,
+};
+#endif
+#else
+#if CIRCUITPY_REPL_LOGO
+mp_obj_t members[] = { &blinka_sprite };
+mp_obj_list_t splash_children = {
+ .base = {.type = &mp_type_list },
+ .alloc = 1,
+ .len = 1,
+ .items = members,
+};
+#else
+mp_obj_t members[] = {};
+mp_obj_list_t splash_children = {
+ .base = {.type = &mp_type_list },
+ .alloc = 0,
+ .len = 0,
+ .items = members,
+};
+#endif
+#endif
+
+displayio_group_t circuitpython_splash = {
+ .base = {.type = &displayio_group_type },
+ .x = 0,
+ .y = 0,
+ .scale = 2,
+ .members = &splash_children,
+ .item_removed = false,
+ .in_group = false,
+ .hidden = false,
+ .hidden_by_parent = false
+};
diff --git a/circuitpython/supervisor/shared/display.h b/circuitpython/supervisor/shared/display.h
new file mode 100644
index 0000000..4110cfe
--- /dev/null
+++ b/circuitpython/supervisor/shared/display.h
@@ -0,0 +1,56 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Scott Shawcroft 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_SUPERVISOR_SHARED_DISPLAY_H
+#define MICROPY_INCLUDED_SUPERVISOR_SHARED_DISPLAY_H
+
+#include <stdint.h>
+
+#if CIRCUITPY_TERMINALIO
+
+#include "shared-bindings/displayio/Bitmap.h"
+#include "shared-bindings/displayio/TileGrid.h"
+#include "shared-bindings/fontio/BuiltinFont.h"
+#include "shared-bindings/terminalio/Terminal.h"
+
+// These are autogenerated resources.
+
+// This is fixed so it doesn't need to be in RAM.
+extern const fontio_builtinfont_t supervisor_terminal_font;
+
+// These will change so they must live in RAM.
+extern displayio_bitmap_t supervisor_terminal_font_bitmap;
+extern displayio_tilegrid_t supervisor_terminal_text_grid;
+extern terminalio_terminal_obj_t supervisor_terminal;
+
+#endif
+
+void supervisor_start_terminal(uint16_t width_px, uint16_t height_px);
+void supervisor_stop_terminal(void);
+
+void supervisor_display_move_memory(void);
+
+#endif // MICROPY_INCLUDED_SUPERVISOR_SHARED_DISPLAY_H
diff --git a/circuitpython/supervisor/shared/external_flash/common_commands.h b/circuitpython/supervisor/shared/external_flash/common_commands.h
new file mode 100644
index 0000000..37efd8c
--- /dev/null
+++ b/circuitpython/supervisor/shared/external_flash/common_commands.h
@@ -0,0 +1,48 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * SPDX-FileCopyrightText: Copyright (c) 2013, 2014 Damien P. George
+ *
+ * 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_ATMEL_SAMD_EXTERNAL_FLASH_COMMON_COMMANDS_H
+#define MICROPY_INCLUDED_ATMEL_SAMD_EXTERNAL_FLASH_COMMON_COMMANDS_H
+
+#define CMD_READ_JEDEC_ID 0x9f
+#define CMD_READ_DATA 0x03
+#define CMD_FAST_READ_DATA 0x0B
+#define CMD_SECTOR_ERASE 0x20
+// #define CMD_SECTOR_ERASE CMD_READ_JEDEC_ID
+#define CMD_DISABLE_WRITE 0x04
+#define CMD_ENABLE_WRITE 0x06
+#define CMD_PAGE_PROGRAM 0x02
+// #define CMD_PAGE_PROGRAM CMD_READ_JEDEC_ID
+#define CMD_READ_STATUS 0x05
+#define CMD_READ_STATUS2 0x35
+#define CMD_WRITE_STATUS_BYTE1 0x01
+#define CMD_WRITE_STATUS_BYTE2 0x31
+#define CMD_DUAL_READ 0x3b
+#define CMD_QUAD_READ 0x6b
+#define CMD_ENABLE_RESET 0x66
+#define CMD_RESET 0x99
+#define CMD_WAKE 0xab
+
+#endif // MICROPY_INCLUDED_ATMEL_SAMD_EXTERNAL_FLASH_COMMON_COMMANDS_H
diff --git a/circuitpython/supervisor/shared/external_flash/device.h b/circuitpython/supervisor/shared/external_flash/device.h
new file mode 100644
index 0000000..bbf6bfd
--- /dev/null
+++ b/circuitpython/supervisor/shared/external_flash/device.h
@@ -0,0 +1,78 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2018 Scott Shawcroft for Adafruit Industries LLC
+ *
+ * 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_SUPERVISOR_SHARED_EXTERNAL_FLASH_DEVICE_H
+#define MICROPY_INCLUDED_SUPERVISOR_SHARED_EXTERNAL_FLASH_DEVICE_H
+
+#include <stdbool.h>
+#include <stdint.h>
+
+typedef struct {
+ uint32_t total_size;
+ uint16_t start_up_time_us;
+
+ // Three response bytes to 0x9f JEDEC ID command.
+ uint8_t manufacturer_id;
+ uint8_t memory_type;
+ uint8_t capacity;
+
+ // Max clock speed for all operations and the fastest read mode.
+ uint8_t max_clock_speed_mhz;
+
+ // Bitmask for Quad Enable bit if present. 0x00 otherwise. This is for the highest byte in the
+ // status register.
+ uint8_t quad_enable_bit_mask;
+
+ bool has_sector_protection : 1;
+
+ // Supports the 0x0b fast read command with 8 dummy cycles.
+ bool supports_fast_read : 1;
+
+ // Supports the fast read, quad output command 0x6b with 8 dummy cycles.
+ bool supports_qspi : 1;
+
+ // Supports the quad input page program command 0x32. This is known as 1-1-4 because it only
+ // uses all four lines for data.
+ bool supports_qspi_writes : 1;
+
+ // Requires a separate command 0x31 to write to the second byte of the status register.
+ // Otherwise two byte are written via 0x01.
+ bool write_status_register_split : 1;
+
+ // True when the status register is a single byte. This implies the Quad Enable bit is in the
+ // first byte and the Read Status Register 2 command (0x35) is unsupported.
+ bool single_status_byte : 1;
+
+ // Does not support using a ready bit within the status register
+ bool no_ready_bit : 1;
+
+ // Does not support the erase command (0x20)
+ bool no_erase_cmd : 1;
+
+ // Device does not have a reset command
+ bool no_reset_cmd : 1;
+} external_flash_device;
+
+#endif // MICROPY_INCLUDED_SUPERVISOR_SHARED_EXTERNAL_FLASH_DEVICE_H
diff --git a/circuitpython/supervisor/shared/external_flash/devices.h.jinja b/circuitpython/supervisor/shared/external_flash/devices.h.jinja
new file mode 100644
index 0000000..7cad90d
--- /dev/null
+++ b/circuitpython/supervisor/shared/external_flash/devices.h.jinja
@@ -0,0 +1,50 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2018 Scott Shawcroft for Adafruit Industries LLC
+ *
+ * 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_SUPERVISOR_SHARED_EXTERNAL_FLASH_DEVICES_H
+#define MICROPY_INCLUDED_SUPERVISOR_SHARED_EXTERNAL_FLASH_DEVICES_H
+
+{% for device in nvms %}
+#define {{ device.sku }} { \
+ .total_size = {{ device.total_size }}, \
+ .start_up_time_us = {{ device.start_up_time_us }}, \
+ .manufacturer_id = {{ device.manufacturer_id }}, \
+ .memory_type = {{ device.memory_type }}, \
+ .capacity = {{ device.capacity }}, \
+ .max_clock_speed_mhz = {{ device.max_clock_speed_mhz }}, \
+ .quad_enable_bit_mask = {{ device.quad_enable_bit_mask }}, \
+ .has_sector_protection = {{ device.has_sector_protection | lower() }}, \
+ .supports_fast_read = {{ device.supports_fast_read | lower() }}, \
+ .supports_qspi = {{ device["6b_quad_read"] | lower() }}, \
+ .supports_qspi_writes = {{ device["32_qspi_write"] | lower() }}, \
+ .write_status_register_split = {{ device.write_status_register_split | lower() }}, \
+ .single_status_byte = {{ (device.quad_enable_status_byte == 1) | lower() }}, \
+ .no_ready_bit = {{ (device.no_ready_bit == 1) | lower() }}, \
+ .no_erase_cmd = {{ (device.no_erase_cmd == 1) | lower() }}, \
+ .no_reset_cmd = {{ (device.no_reset_cmd == 1) | lower() }}, \
+}
+{% endfor %}
+
+#endif // MICROPY_INCLUDED_SUPERVISOR_SHARED_EXTERNAL_FLASH_DEVICES_H
diff --git a/circuitpython/supervisor/shared/external_flash/external_flash.c b/circuitpython/supervisor/shared/external_flash/external_flash.c
new file mode 100644
index 0000000..7da45fd
--- /dev/null
+++ b/circuitpython/supervisor/shared/external_flash/external_flash.c
@@ -0,0 +1,593 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2016, 2017 Scott Shawcroft 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 "supervisor/shared/external_flash/external_flash.h"
+
+#include <stdint.h>
+#include <string.h>
+#include "genhdr/devices.h"
+#include "supervisor/flash.h"
+#include "supervisor/spi_flash_api.h"
+#include "supervisor/shared/external_flash/common_commands.h"
+#include "extmod/vfs.h"
+#include "extmod/vfs_fat.h"
+#include "py/misc.h"
+#include "py/obj.h"
+#include "py/runtime.h"
+#include "lib/oofatfs/ff.h"
+#include "shared-bindings/microcontroller/__init__.h"
+#include "supervisor/memory.h"
+
+#define NO_SECTOR_LOADED 0xFFFFFFFF
+
+// The currently cached sector in the cache, ram or flash based.
+static uint32_t current_sector;
+
+STATIC const external_flash_device possible_devices[] = {EXTERNAL_FLASH_DEVICES};
+#define EXTERNAL_FLASH_DEVICE_COUNT MP_ARRAY_SIZE(possible_devices)
+
+static const external_flash_device *flash_device = NULL;
+
+// Track which blocks (up to 32) in the current sector currently live in the
+// cache.
+static uint32_t dirty_mask;
+
+static supervisor_allocation *supervisor_cache = NULL;
+
+// Wait until both the write enable and write in progress bits have cleared.
+static bool wait_for_flash_ready(void) {
+ bool ok = true;
+ // Both the write enable and write in progress bits should be low.
+ if (flash_device->no_ready_bit) {
+ // For NVM without a ready bit in status register
+ return ok;
+ }
+ uint8_t read_status_response[1] = {0x00};
+ do {
+ ok = spi_flash_read_command(CMD_READ_STATUS, read_status_response, 1);
+ } while (ok && (read_status_response[0] & 0x3) != 0);
+ return ok;
+}
+
+// Turn on the write enable bit so we can program and erase the flash.
+static bool write_enable(void) {
+ return spi_flash_command(CMD_ENABLE_WRITE);
+}
+
+// Read data_length's worth of bytes starting at address into data.
+static bool read_flash(uint32_t address, uint8_t *data, uint32_t data_length) {
+ if (flash_device == NULL) {
+ return false;
+ }
+ if (!wait_for_flash_ready()) {
+ return false;
+ }
+ return spi_flash_read_data(address, data, data_length);
+}
+
+// Writes data_length's worth of bytes starting at address from data. Assumes
+// that the sector that address resides in has already been erased. So make sure
+// to run erase_sector.
+static bool write_flash(uint32_t address, const uint8_t *data, uint32_t data_length) {
+ if (flash_device == NULL) {
+ return false;
+ }
+ // Don't bother writing if the data is all 1s. Thats equivalent to the flash
+ // state after an erase.
+ if (!flash_device->no_erase_cmd) {
+ // Only do this if the device has an erase command
+ bool all_ones = true;
+ for (uint16_t i = 0; i < data_length; i++) {
+ if (data[i] != 0xff) {
+ all_ones = false;
+ break;
+ }
+ }
+ if (all_ones) {
+ return true;
+ }
+ }
+
+ for (uint32_t bytes_written = 0;
+ bytes_written < data_length;
+ bytes_written += SPI_FLASH_PAGE_SIZE) {
+ if (!wait_for_flash_ready() || !write_enable()) {
+ return false;
+ }
+
+ if (!spi_flash_write_data(address + bytes_written, (uint8_t *)data + bytes_written,
+ SPI_FLASH_PAGE_SIZE)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+static bool page_erased(uint32_t sector_address) {
+ // Check the first few bytes to catch the common case where there is data
+ // without using a bunch of memory.
+ if (flash_device->no_erase_cmd) {
+ // skip this if device doesn't have an erase command.
+ return true;
+ }
+ uint8_t short_buffer[4];
+ if (read_flash(sector_address, short_buffer, 4)) {
+ for (uint16_t i = 0; i < 4; i++) {
+ if (short_buffer[i] != 0xff) {
+ return false;
+ }
+ }
+ } else {
+ return false;
+ }
+
+ // Now check the full length.
+ uint8_t full_buffer[FILESYSTEM_BLOCK_SIZE];
+ if (read_flash(sector_address, full_buffer, FILESYSTEM_BLOCK_SIZE)) {
+ for (uint16_t i = 0; i < FILESYSTEM_BLOCK_SIZE; i++) {
+ if (short_buffer[i] != 0xff) {
+ return false;
+ }
+ }
+ } else {
+ return false;
+ }
+ return true;
+}
+
+// Erases the given sector. Make sure you copied all of the data out of it you
+// need! Also note, sector_address is really 24 bits.
+static bool erase_sector(uint32_t sector_address) {
+ // Before we erase the sector we need to wait for any writes to finish and
+ // and then enable the write again.
+ if (flash_device->no_erase_cmd) {
+ // skip this if device doesn't have an erase command.
+ return true;
+ }
+ if (!wait_for_flash_ready() || !write_enable()) {
+ return false;
+ }
+ if (flash_device->no_erase_cmd) {
+ return true;
+ }
+ spi_flash_sector_command(CMD_SECTOR_ERASE, sector_address);
+ return true;
+}
+
+// Sector is really 24 bits.
+static bool copy_block(uint32_t src_address, uint32_t dest_address) {
+ // Copy page by page to minimize RAM buffer.
+ uint16_t page_size = SPI_FLASH_PAGE_SIZE;
+ uint8_t buffer[page_size];
+ for (uint32_t i = 0; i < FILESYSTEM_BLOCK_SIZE / page_size; i++) {
+ if (!read_flash(src_address + i * page_size, buffer, page_size)) {
+ return false;
+ }
+ if (!write_flash(dest_address + i * page_size, buffer, page_size)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+void supervisor_flash_init(void) {
+ if (flash_device != NULL) {
+ return;
+ }
+
+ // Delay to give the SPI Flash time to get going.
+ // TODO(tannewt): Only do this when we know power was applied vs a reset.
+ uint16_t max_start_up_delay_us = 0;
+ for (uint8_t i = 0; i < EXTERNAL_FLASH_DEVICE_COUNT; i++) {
+ if (possible_devices[i].start_up_time_us > max_start_up_delay_us) {
+ max_start_up_delay_us = possible_devices[i].start_up_time_us;
+ }
+ }
+ common_hal_mcu_delay_us(max_start_up_delay_us);
+
+ spi_flash_init();
+
+ #ifdef EXTERNAL_FLASH_NO_JEDEC
+ // For NVM that don't have JEDEC response
+ spi_flash_command(CMD_WAKE);
+ for (uint8_t i = 0; i < EXTERNAL_FLASH_DEVICE_COUNT; i++) {
+ const external_flash_device *possible_device = &possible_devices[i];
+ flash_device = possible_device;
+ break;
+ }
+ #else
+ // The response will be 0xff if the flash needs more time to start up.
+ uint8_t jedec_id_response[3] = {0xff, 0xff, 0xff};
+ while (jedec_id_response[0] == 0xff) {
+ spi_flash_read_command(CMD_READ_JEDEC_ID, jedec_id_response, 3);
+ }
+ for (uint8_t i = 0; i < EXTERNAL_FLASH_DEVICE_COUNT; i++) {
+ const external_flash_device *possible_device = &possible_devices[i];
+ if (jedec_id_response[0] == possible_device->manufacturer_id &&
+ jedec_id_response[1] == possible_device->memory_type &&
+ jedec_id_response[2] == possible_device->capacity) {
+ flash_device = possible_device;
+ break;
+ }
+ }
+ #endif
+ if (flash_device == NULL) {
+ return;
+ }
+
+ // We don't know what state the flash is in so wait for any remaining writes and then reset.
+ uint8_t read_status_response[1] = {0x00};
+ // The write in progress bit should be low.
+ do {
+ spi_flash_read_command(CMD_READ_STATUS, read_status_response, 1);
+ } while ((read_status_response[0] & 0x1) != 0);
+ if (!flash_device->single_status_byte) {
+ // The suspended write/erase bit should be low.
+ do {
+ spi_flash_read_command(CMD_READ_STATUS2, read_status_response, 1);
+ } while ((read_status_response[0] & 0x80) != 0);
+ }
+
+ if (!(flash_device->no_reset_cmd)) {
+ spi_flash_command(CMD_ENABLE_RESET);
+ spi_flash_command(CMD_RESET);
+ }
+
+ // Wait 30us for the reset
+ common_hal_mcu_delay_us(30);
+
+ spi_flash_init_device(flash_device);
+
+ // Activity LED for flash writes.
+ #ifdef MICROPY_HW_LED_MSC
+ gpio_set_pin_function(SPI_FLASH_CS_PIN, GPIO_PIN_FUNCTION_OFF);
+ gpio_set_pin_direction(MICROPY_HW_LED_MSC, GPIO_DIRECTION_OUT);
+ // There's already a pull-up on the board.
+ gpio_set_pin_level(MICROPY_HW_LED_MSC, false);
+ #endif
+
+ if (flash_device->has_sector_protection) {
+ write_enable();
+
+ // Turn off sector protection
+ uint8_t data[1] = {0x00};
+ spi_flash_write_command(CMD_WRITE_STATUS_BYTE1, data, 1);
+ }
+
+ // Turn off writes in case this is a microcontroller only reset.
+ spi_flash_command(CMD_DISABLE_WRITE);
+
+ wait_for_flash_ready();
+
+ current_sector = NO_SECTOR_LOADED;
+ dirty_mask = 0;
+ MP_STATE_VM(flash_ram_cache) = NULL;
+}
+
+// The size of each individual block.
+uint32_t supervisor_flash_get_block_size(void) {
+ return FILESYSTEM_BLOCK_SIZE;
+}
+
+// The total number of available blocks.
+uint32_t supervisor_flash_get_block_count(void) {
+ // We subtract one erase sector size because we may use it as a staging area
+ // for writes.
+ return (flash_device->total_size - SPI_FLASH_ERASE_SIZE) / FILESYSTEM_BLOCK_SIZE;
+}
+
+// Flush the cache that was written to the scratch portion of flash. Only used
+// when ram is tight.
+static bool flush_scratch_flash(void) {
+ if (current_sector == NO_SECTOR_LOADED) {
+ return true;
+ }
+ // First, copy out any blocks that we haven't touched from the sector we've
+ // cached.
+ bool copy_to_scratch_ok = true;
+ uint32_t scratch_sector = flash_device->total_size - SPI_FLASH_ERASE_SIZE;
+ for (uint8_t i = 0; i < SPI_FLASH_ERASE_SIZE / FILESYSTEM_BLOCK_SIZE; i++) {
+ if ((dirty_mask & (1 << i)) == 0) {
+ copy_to_scratch_ok = copy_to_scratch_ok &&
+ copy_block(current_sector + i * FILESYSTEM_BLOCK_SIZE,
+ scratch_sector + i * FILESYSTEM_BLOCK_SIZE);
+ }
+ }
+ if (!copy_to_scratch_ok) {
+ // TODO(tannewt): Do more here. We opted to not erase and copy bad data
+ // in. We still risk losing the data written to the scratch sector.
+ return false;
+ }
+ // Second, erase the current sector.
+ erase_sector(current_sector);
+ // Finally, copy the new version into it.
+ for (uint8_t i = 0; i < SPI_FLASH_ERASE_SIZE / FILESYSTEM_BLOCK_SIZE; i++) {
+ copy_block(scratch_sector + i * FILESYSTEM_BLOCK_SIZE,
+ current_sector + i * FILESYSTEM_BLOCK_SIZE);
+ }
+ return true;
+}
+
+// Attempts to allocate a new set of page buffers for caching a full sector in
+// ram. Each page is allocated separately so that the GC doesn't need to provide
+// one huge block. We can free it as we write if we want to also.
+static bool allocate_ram_cache(void) {
+ uint8_t blocks_per_sector = SPI_FLASH_ERASE_SIZE / FILESYSTEM_BLOCK_SIZE;
+ uint8_t pages_per_block = FILESYSTEM_BLOCK_SIZE / SPI_FLASH_PAGE_SIZE;
+
+ uint32_t table_size = blocks_per_sector * pages_per_block * sizeof(uint32_t);
+ // Attempt to allocate outside the heap first.
+ supervisor_cache = allocate_memory(table_size + SPI_FLASH_ERASE_SIZE, false, false);
+ if (supervisor_cache != NULL) {
+ MP_STATE_VM(flash_ram_cache) = (uint8_t **)supervisor_cache->ptr;
+ uint8_t *page_start = (uint8_t *)supervisor_cache->ptr + table_size;
+
+ for (uint8_t i = 0; i < blocks_per_sector; i++) {
+ for (uint8_t j = 0; j < pages_per_block; j++) {
+ uint32_t offset = i * pages_per_block + j;
+ MP_STATE_VM(flash_ram_cache)[offset] = page_start + offset * SPI_FLASH_PAGE_SIZE;
+ }
+ }
+ return true;
+ }
+
+ if (MP_STATE_MEM(gc_pool_start) == 0) {
+ return false;
+ }
+
+ MP_STATE_VM(flash_ram_cache) = m_malloc_maybe(blocks_per_sector * pages_per_block * sizeof(uint32_t), false);
+ if (MP_STATE_VM(flash_ram_cache) == NULL) {
+ return false;
+ }
+ // Declare i and j outside the loops in case we fail to allocate everything
+ // we need. In that case we'll give it back.
+ uint8_t i = 0;
+ uint8_t j = 0;
+ bool success = true;
+ for (i = 0; i < blocks_per_sector; i++) {
+ for (j = 0; j < pages_per_block; j++) {
+ uint8_t *page_cache = m_malloc_maybe(SPI_FLASH_PAGE_SIZE, false);
+ if (page_cache == NULL) {
+ success = false;
+ break;
+ }
+ MP_STATE_VM(flash_ram_cache)[i * pages_per_block + j] = page_cache;
+ }
+ if (!success) {
+ break;
+ }
+ }
+ // We couldn't allocate enough so give back what we got.
+ if (!success) {
+ // We add 1 so that we delete 0 when i is 1. Going to zero (i >= 0)
+ // would never stop because i is unsigned.
+ i++;
+ for (; i > 0; i--) {
+ for (; j > 0; j--) {
+ m_free(MP_STATE_VM(flash_ram_cache)[(i - 1) * pages_per_block + (j - 1)]);
+ }
+ j = pages_per_block;
+ }
+ m_free(MP_STATE_VM(flash_ram_cache));
+ MP_STATE_VM(flash_ram_cache) = NULL;
+ }
+ return success;
+}
+
+static void release_ram_cache(void) {
+ if (supervisor_cache != NULL) {
+ free_memory(supervisor_cache);
+ supervisor_cache = NULL;
+ } else if (MP_STATE_MEM(gc_pool_start)) {
+ m_free(MP_STATE_VM(flash_ram_cache));
+ }
+ MP_STATE_VM(flash_ram_cache) = NULL;
+}
+
+// Flush the cached sector from ram onto the flash. We'll free the cache unless
+// keep_cache is true.
+static bool flush_ram_cache(bool keep_cache) {
+ if (current_sector == NO_SECTOR_LOADED) {
+ if (!keep_cache) {
+ release_ram_cache();
+ }
+ return true;
+ }
+ // First, copy out any blocks that we haven't touched from the sector
+ // we've cached. If we don't do this we'll erase the data during the sector
+ // erase below.
+ bool copy_to_ram_ok = true;
+ uint8_t pages_per_block = FILESYSTEM_BLOCK_SIZE / SPI_FLASH_PAGE_SIZE;
+ for (uint8_t i = 0; i < SPI_FLASH_ERASE_SIZE / FILESYSTEM_BLOCK_SIZE; i++) {
+ if ((dirty_mask & (1 << i)) == 0) {
+ for (uint8_t j = 0; j < pages_per_block; j++) {
+ copy_to_ram_ok = read_flash(
+ current_sector + (i * pages_per_block + j) * SPI_FLASH_PAGE_SIZE,
+ MP_STATE_VM(flash_ram_cache)[i * pages_per_block + j],
+ SPI_FLASH_PAGE_SIZE);
+ if (!copy_to_ram_ok) {
+ break;
+ }
+ }
+ }
+ if (!copy_to_ram_ok) {
+ break;
+ }
+ }
+
+ if (!copy_to_ram_ok) {
+ return false;
+ }
+ // Second, erase the current sector.
+ erase_sector(current_sector);
+ // Lastly, write all the data in ram that we've cached.
+ for (uint8_t i = 0; i < SPI_FLASH_ERASE_SIZE / FILESYSTEM_BLOCK_SIZE; i++) {
+ for (uint8_t j = 0; j < pages_per_block; j++) {
+ write_flash(current_sector + (i * pages_per_block + j) * SPI_FLASH_PAGE_SIZE,
+ MP_STATE_VM(flash_ram_cache)[i * pages_per_block + j],
+ SPI_FLASH_PAGE_SIZE);
+ if (!keep_cache && supervisor_cache == NULL && MP_STATE_MEM(gc_pool_start)) {
+ m_free(MP_STATE_VM(flash_ram_cache)[i * pages_per_block + j]);
+ }
+ }
+ }
+ // We're done with the cache for now so give it back.
+ if (!keep_cache) {
+ release_ram_cache();
+ }
+ return true;
+}
+
+// Delegates to the correct flash flush method depending on the existing cache.
+// TODO Don't blink the status indicator if we don't actually do any writing (hard to tell right now).
+static void spi_flash_flush_keep_cache(bool keep_cache) {
+ #ifdef MICROPY_HW_LED_MSC
+ port_pin_set_output_level(MICROPY_HW_LED_MSC, true);
+ #endif
+ // If we've cached to the flash itself flush from there.
+ if (MP_STATE_VM(flash_ram_cache) == NULL) {
+ flush_scratch_flash();
+ } else {
+ flush_ram_cache(keep_cache);
+ }
+ current_sector = NO_SECTOR_LOADED;
+ #ifdef MICROPY_HW_LED_MSC
+ port_pin_set_output_level(MICROPY_HW_LED_MSC, false);
+ #endif
+}
+
+void supervisor_external_flash_flush(void) {
+ spi_flash_flush_keep_cache(true);
+}
+
+void supervisor_flash_release_cache(void) {
+ spi_flash_flush_keep_cache(false);
+}
+
+static int32_t convert_block_to_flash_addr(uint32_t block) {
+ if (0 <= block && block < supervisor_flash_get_block_count()) {
+ // a block in partition 1
+ return block * FILESYSTEM_BLOCK_SIZE;
+ }
+ // bad block
+ return -1;
+}
+
+static bool external_flash_read_block(uint8_t *dest, uint32_t block) {
+ int32_t address = convert_block_to_flash_addr(block);
+ if (address == -1) {
+ // bad block number
+ return false;
+ }
+
+ // Mask out the lower bits that designate the address within the sector.
+ uint32_t this_sector = address & (~(SPI_FLASH_ERASE_SIZE - 1));
+ uint8_t block_index = (address / FILESYSTEM_BLOCK_SIZE) % (SPI_FLASH_ERASE_SIZE / FILESYSTEM_BLOCK_SIZE);
+ uint8_t mask = 1 << (block_index);
+ // We're reading from the currently cached sector.
+ if (current_sector == this_sector && (mask & dirty_mask) > 0) {
+ if (MP_STATE_VM(flash_ram_cache) != NULL) {
+ uint8_t pages_per_block = FILESYSTEM_BLOCK_SIZE / SPI_FLASH_PAGE_SIZE;
+ for (int i = 0; i < pages_per_block; i++) {
+ memcpy(dest + i * SPI_FLASH_PAGE_SIZE,
+ MP_STATE_VM(flash_ram_cache)[block_index * pages_per_block + i],
+ SPI_FLASH_PAGE_SIZE);
+ }
+ return true;
+ } else {
+ uint32_t scratch_address = flash_device->total_size - SPI_FLASH_ERASE_SIZE + block_index * FILESYSTEM_BLOCK_SIZE;
+ return read_flash(scratch_address, dest, FILESYSTEM_BLOCK_SIZE);
+ }
+ }
+ return read_flash(address, dest, FILESYSTEM_BLOCK_SIZE);
+}
+
+static bool external_flash_write_block(const uint8_t *data, uint32_t block) {
+ // Non-MBR block, copy to cache
+ int32_t address = convert_block_to_flash_addr(block);
+ if (address == -1) {
+ // bad block number
+ return false;
+ }
+ // Wait for any previous writes to finish.
+ wait_for_flash_ready();
+ // Mask out the lower bits that designate the address within the sector.
+ uint32_t this_sector = address & (~(SPI_FLASH_ERASE_SIZE - 1));
+ uint8_t block_index = (address / FILESYSTEM_BLOCK_SIZE) % (SPI_FLASH_ERASE_SIZE / FILESYSTEM_BLOCK_SIZE);
+ uint8_t mask = 1 << (block_index);
+ // Flush the cache if we're moving onto a sector or we're writing the
+ // same block again.
+ if (current_sector != this_sector || (mask & dirty_mask) > 0) {
+ // Check to see if we'd write to an erased page. In that case we
+ // can write directly.
+ if (page_erased(address)) {
+ return write_flash(address, data, FILESYSTEM_BLOCK_SIZE);
+ }
+ if (current_sector != NO_SECTOR_LOADED) {
+ supervisor_flash_flush();
+ }
+ if (MP_STATE_VM(flash_ram_cache) == NULL && !allocate_ram_cache()) {
+ erase_sector(flash_device->total_size - SPI_FLASH_ERASE_SIZE);
+ wait_for_flash_ready();
+ }
+ current_sector = this_sector;
+ dirty_mask = 0;
+ }
+ dirty_mask |= mask;
+ // Copy the block to the appropriate cache.
+ if (MP_STATE_VM(flash_ram_cache) != NULL) {
+ uint8_t pages_per_block = FILESYSTEM_BLOCK_SIZE / SPI_FLASH_PAGE_SIZE;
+ for (int i = 0; i < pages_per_block; i++) {
+ memcpy(MP_STATE_VM(flash_ram_cache)[block_index * pages_per_block + i],
+ data + i * SPI_FLASH_PAGE_SIZE,
+ SPI_FLASH_PAGE_SIZE);
+ }
+ return true;
+ } else {
+ uint32_t scratch_address = flash_device->total_size - SPI_FLASH_ERASE_SIZE + block_index * FILESYSTEM_BLOCK_SIZE;
+ return write_flash(scratch_address, data, FILESYSTEM_BLOCK_SIZE);
+ }
+}
+
+mp_uint_t supervisor_flash_read_blocks(uint8_t *dest, uint32_t block_num, uint32_t num_blocks) {
+ for (size_t i = 0; i < num_blocks; i++) {
+ if (!external_flash_read_block(dest + i * FILESYSTEM_BLOCK_SIZE, block_num + i)) {
+ return 1; // error
+ }
+ }
+ return 0; // success
+}
+
+mp_uint_t supervisor_flash_write_blocks(const uint8_t *src, uint32_t block_num, uint32_t num_blocks) {
+ for (size_t i = 0; i < num_blocks; i++) {
+ if (!external_flash_write_block(src + i * FILESYSTEM_BLOCK_SIZE, block_num + i)) {
+ return 1; // error
+ }
+ }
+ return 0; // success
+}
+
+void MP_WEAK external_flash_setup(void) {
+}
diff --git a/circuitpython/supervisor/shared/external_flash/external_flash.h b/circuitpython/supervisor/shared/external_flash/external_flash.h
new file mode 100644
index 0000000..7966b64
--- /dev/null
+++ b/circuitpython/supervisor/shared/external_flash/external_flash.h
@@ -0,0 +1,55 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2018 Scott Shawcroft for Adafruit Industries LLC
+ *
+ * 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_SUPERVISOR_SHARED_EXTERNAL_FLASH_EXTERNAL_FLASH_H
+#define MICROPY_INCLUDED_SUPERVISOR_SHARED_EXTERNAL_FLASH_EXTERNAL_FLASH_H
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include "py/mpconfig.h"
+
+// Erase sector size.
+#define SPI_FLASH_SECTOR_SIZE (0x1000 - 100)
+
+// These are common across all NOR Flash.
+#define SPI_FLASH_ERASE_SIZE (1 << 12)
+#define SPI_FLASH_PAGE_SIZE (256)
+
+#define SPI_FLASH_SYSTICK_MASK (0x1ff) // 512ms
+#define SPI_FLASH_IDLE_TICK(tick) (((tick) & SPI_FLASH_SYSTICK_MASK) == 2)
+
+#ifndef SPI_FLASH_MAX_BAUDRATE
+#define SPI_FLASH_MAX_BAUDRATE 8000000
+#endif
+
+void supervisor_external_flash_flush(void);
+
+// Configure anything that needs to get set up before the external flash
+// is init'ed. For example, if GPIO needs to be configured to enable the
+// flash chip, as is the case on some boards.
+void external_flash_setup(void);
+
+#endif // MICROPY_INCLUDED_SUPERVISOR_SHARED_EXTERNAL_FLASH_EXTERNAL_FLASH_H
diff --git a/circuitpython/supervisor/shared/external_flash/external_flash_root_pointers.h b/circuitpython/supervisor/shared/external_flash/external_flash_root_pointers.h
new file mode 100644
index 0000000..0613c9a
--- /dev/null
+++ b/circuitpython/supervisor/shared/external_flash/external_flash_root_pointers.h
@@ -0,0 +1,35 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2018 Scott Shawcroft for Adafruit Industries LLC
+ *
+ * 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_SUPERVISOR_SHARED_EXTERNAL_FLASH_EXTERNAL_FLASH_ROOT_POINTERS_H
+#define MICROPY_INCLUDED_SUPERVISOR_SHARED_EXTERNAL_FLASH_EXTERNAL_FLASH_ROOT_POINTERS_H
+
+#include <stdint.h>
+
+// We use this when we can allocate the whole cache in RAM.
+#define FLASH_ROOT_POINTERS \
+ uint8_t **flash_ram_cache; \
+
+#endif // MICROPY_INCLUDED_SUPERVISOR_SHARED_EXTERNAL_FLASH_EXTERNAL_FLASH_ROOT_POINTERS_H
diff --git a/circuitpython/supervisor/shared/external_flash/qspi_flash.c b/circuitpython/supervisor/shared/external_flash/qspi_flash.c
new file mode 100644
index 0000000..293654b
--- /dev/null
+++ b/circuitpython/supervisor/shared/external_flash/qspi_flash.c
@@ -0,0 +1,57 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2016, 2017, 2018 Scott Shawcroft 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 "supervisor/spi_flash_api.h"
+
+#include "supervisor/shared/external_flash/common_commands.h"
+#include "supervisor/shared/external_flash/qspi_flash.h"
+
+void check_quad_enable(const external_flash_device *device) {
+ if (device->quad_enable_bit_mask == 0x00) {
+ return;
+ }
+
+ // Verify that QSPI mode is enabled.
+ uint8_t status;
+ if (device->single_status_byte) {
+ spi_flash_read_command(CMD_READ_STATUS, &status, 1);
+ } else {
+ spi_flash_read_command(CMD_READ_STATUS2, &status, 1);
+ }
+
+ // Check the quad enable bit.
+ if ((status & device->quad_enable_bit_mask) == 0) {
+ uint8_t full_status[2] = {0x00, device->quad_enable_bit_mask};
+ spi_flash_command(CMD_ENABLE_WRITE);
+ if (device->write_status_register_split) {
+ spi_flash_write_command(CMD_WRITE_STATUS_BYTE2, full_status + 1, 1);
+ } else if (device->single_status_byte) {
+ spi_flash_write_command(CMD_WRITE_STATUS_BYTE1, full_status + 1, 1);
+ } else {
+ spi_flash_write_command(CMD_WRITE_STATUS_BYTE1, full_status, 2);
+ }
+ }
+}
diff --git a/circuitpython/supervisor/shared/external_flash/qspi_flash.h b/circuitpython/supervisor/shared/external_flash/qspi_flash.h
new file mode 100644
index 0000000..c0c089e
--- /dev/null
+++ b/circuitpython/supervisor/shared/external_flash/qspi_flash.h
@@ -0,0 +1,31 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2018 Scott Shawcroft for Adafruit Industries LLC
+ *
+ * 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_SUPERVISOR_SHARED_EXTERNAL_FLASH_QSPI_FLASH_H
+#define MICROPY_INCLUDED_SUPERVISOR_SHARED_EXTERNAL_FLASH_QSPI_FLASH_H
+
+void check_quad_enable(const external_flash_device *device);
+
+#endif // MICROPY_INCLUDED_SUPERVISOR_SHARED_EXTERNAL_FLASH_QSPI_FLASH_H
diff --git a/circuitpython/supervisor/shared/external_flash/spi_flash.c b/circuitpython/supervisor/shared/external_flash/spi_flash.c
new file mode 100644
index 0000000..b47508e
--- /dev/null
+++ b/circuitpython/supervisor/shared/external_flash/spi_flash.c
@@ -0,0 +1,166 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2016, 2017 Scott Shawcroft 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 "supervisor/spi_flash_api.h"
+
+#include <stdint.h>
+#include <string.h>
+
+#include "shared-bindings/busio/SPI.h"
+#include "shared-bindings/digitalio/DigitalInOut.h"
+#include "shared-bindings/microcontroller/Pin.h"
+#include "supervisor/shared/external_flash/common_commands.h"
+#include "supervisor/shared/external_flash/external_flash.h"
+#include "py/mpconfig.h"
+
+digitalio_digitalinout_obj_t cs_pin;
+busio_spi_obj_t supervisor_flash_spi_bus;
+
+const external_flash_device *flash_device;
+uint32_t spi_flash_baudrate;
+
+// Enable the flash over SPI.
+static bool flash_enable(void) {
+ if (common_hal_busio_spi_try_lock(&supervisor_flash_spi_bus)) {
+ common_hal_digitalio_digitalinout_set_value(&cs_pin, false);
+ return true;
+ }
+ return false;
+}
+
+// Disable the flash over SPI.
+static void flash_disable(void) {
+ common_hal_digitalio_digitalinout_set_value(&cs_pin, true);
+ common_hal_busio_spi_unlock(&supervisor_flash_spi_bus);
+}
+
+static bool transfer(uint8_t *command, uint32_t command_length, uint8_t *data_in, uint8_t *data_out, uint32_t data_length) {
+ if (!flash_enable()) {
+ return false;
+ }
+ bool status = common_hal_busio_spi_write(&supervisor_flash_spi_bus, command, command_length);
+ if (status) {
+ if (data_in != NULL && data_out != NULL) {
+ status = common_hal_busio_spi_transfer(&supervisor_flash_spi_bus, data_out, data_in, data_length);
+ } else if (data_out != NULL) {
+ status = common_hal_busio_spi_read(&supervisor_flash_spi_bus, data_out, data_length, 0xff);
+ } else if (data_in != NULL) {
+ status = common_hal_busio_spi_write(&supervisor_flash_spi_bus, data_in, data_length);
+ }
+ }
+ flash_disable();
+ return status;
+}
+
+static bool transfer_command(uint8_t command, uint8_t *data_in, uint8_t *data_out, uint32_t data_length) {
+ return transfer(&command, 1, data_in, data_out, data_length);
+}
+
+bool spi_flash_command(uint8_t command) {
+ return transfer_command(command, NULL, NULL, 0);
+}
+
+bool spi_flash_read_command(uint8_t command, uint8_t *data, uint32_t data_length) {
+ return transfer_command(command, NULL, data, data_length);
+}
+
+bool spi_flash_write_command(uint8_t command, uint8_t *data, uint32_t data_length) {
+ return transfer_command(command, data, NULL, data_length);
+}
+
+// Pack the low 24 bits of the address into a uint8_t array.
+static void address_to_bytes(uint32_t address, uint8_t *bytes) {
+ bytes[0] = (address >> 16) & 0xff;
+ bytes[1] = (address >> 8) & 0xff;
+ bytes[2] = address & 0xff;
+}
+
+bool spi_flash_sector_command(uint8_t command, uint32_t address) {
+ uint8_t request[4] = {command, 0x00, 0x00, 0x00};
+ address_to_bytes(address, request + 1);
+ return transfer(request, 4, NULL, NULL, 0);
+}
+
+bool spi_flash_write_data(uint32_t address, uint8_t *data, uint32_t data_length) {
+ uint8_t request[4] = {CMD_PAGE_PROGRAM, 0x00, 0x00, 0x00};
+ common_hal_busio_spi_configure(&supervisor_flash_spi_bus, spi_flash_baudrate, 0, 0, 8);
+ // Write the SPI flash write address into the bytes following the command byte.
+ address_to_bytes(address, request + 1);
+ if (!flash_enable()) {
+ return false;
+ }
+ bool status = common_hal_busio_spi_write(&supervisor_flash_spi_bus, request, 4);
+ if (status) {
+ status = common_hal_busio_spi_write(&supervisor_flash_spi_bus, data, data_length);
+ }
+ flash_disable();
+ return status;
+}
+
+bool spi_flash_read_data(uint32_t address, uint8_t *data, uint32_t data_length) {
+ uint8_t request[5] = {CMD_READ_DATA, 0x00, 0x00, 0x00};
+ uint8_t command_length = 4;
+ if (flash_device->supports_fast_read) {
+ request[0] = CMD_FAST_READ_DATA;
+ command_length = 5;
+ }
+ common_hal_busio_spi_configure(&supervisor_flash_spi_bus, spi_flash_baudrate, 0, 0, 8);
+ // Write the SPI flash read address into the bytes following the command byte.
+ address_to_bytes(address, request + 1);
+ if (!flash_enable()) {
+ return false;
+ }
+ bool status = common_hal_busio_spi_write(&supervisor_flash_spi_bus, request, command_length);
+ if (status) {
+ status = common_hal_busio_spi_read(&supervisor_flash_spi_bus, data, data_length, 0xff);
+ }
+ flash_disable();
+ return status;
+}
+
+void spi_flash_init(void) {
+ cs_pin.base.type = &digitalio_digitalinout_type;
+ common_hal_digitalio_digitalinout_construct(&cs_pin, SPI_FLASH_CS_PIN);
+
+ // Set CS high (disabled).
+ common_hal_digitalio_digitalinout_switch_to_output(&cs_pin, true, DRIVE_MODE_PUSH_PULL);
+ common_hal_digitalio_digitalinout_never_reset(&cs_pin);
+
+ supervisor_flash_spi_bus.base.type = &busio_spi_type;
+ common_hal_busio_spi_construct(&supervisor_flash_spi_bus, SPI_FLASH_SCK_PIN, SPI_FLASH_MOSI_PIN, SPI_FLASH_MISO_PIN, false);
+ common_hal_busio_spi_never_reset(&supervisor_flash_spi_bus);
+
+ return;
+}
+
+void spi_flash_init_device(const external_flash_device *device) {
+ flash_device = device;
+ spi_flash_baudrate = device->max_clock_speed_mhz * 1000000;
+ if (spi_flash_baudrate > SPI_FLASH_MAX_BAUDRATE) {
+ spi_flash_baudrate = SPI_FLASH_MAX_BAUDRATE;
+ }
+ common_hal_busio_spi_configure(&supervisor_flash_spi_bus, spi_flash_baudrate, 0, 0, 8);
+ return;
+}
diff --git a/circuitpython/supervisor/shared/filesystem.c b/circuitpython/supervisor/shared/filesystem.c
new file mode 100644
index 0000000..820dbe9
--- /dev/null
+++ b/circuitpython/supervisor/shared/filesystem.c
@@ -0,0 +1,203 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2017 Scott Shawcroft 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 "supervisor/filesystem.h"
+
+#include "extmod/vfs_fat.h"
+#include "lib/oofatfs/ff.h"
+#include "lib/oofatfs/diskio.h"
+
+#include "py/mpstate.h"
+
+#include "supervisor/flash.h"
+
+static mp_vfs_mount_t _mp_vfs;
+static fs_user_mount_t _internal_vfs;
+
+static volatile uint32_t filesystem_flush_interval_ms = CIRCUITPY_FILESYSTEM_FLUSH_INTERVAL_MS;
+volatile bool filesystem_flush_requested = false;
+
+void filesystem_background(void) {
+ if (filesystem_flush_requested) {
+ filesystem_flush_interval_ms = CIRCUITPY_FILESYSTEM_FLUSH_INTERVAL_MS;
+ // Flush but keep caches
+ supervisor_flash_flush();
+ filesystem_flush_requested = false;
+ }
+}
+
+inline void filesystem_tick(void) {
+ if (filesystem_flush_interval_ms == 0) {
+ // 0 means not turned on.
+ return;
+ }
+ if (filesystem_flush_interval_ms == 1) {
+ filesystem_flush_requested = true;
+ filesystem_flush_interval_ms = CIRCUITPY_FILESYSTEM_FLUSH_INTERVAL_MS;
+ } else {
+ filesystem_flush_interval_ms--;
+ }
+}
+
+
+static void make_empty_file(FATFS *fatfs, const char *path) {
+ FIL fp;
+ f_open(fatfs, &fp, path, FA_WRITE | FA_CREATE_ALWAYS);
+ f_close(&fp);
+}
+
+
+static void make_sample_code_file(FATFS *fatfs) {
+ #if CIRCUITPY_FULL_BUILD
+ FIL fs;
+ UINT char_written = 0;
+ const byte buffer[] = "print(\"Hello World!\")\n";
+ // Create or modify existing code.py file
+ f_open(fatfs, &fs, "/code.py", FA_WRITE | FA_CREATE_ALWAYS);
+ f_write(&fs, buffer, sizeof(buffer) - 1, &char_written);
+ f_close(&fs);
+ #else
+ make_empty_file(fatfs, "/code.py");
+ #endif
+}
+
+// we don't make this function static because it needs a lot of stack and we
+// want it to be executed without using stack within main() function
+bool filesystem_init(bool create_allowed, bool force_create) {
+ // init the vfs object
+ fs_user_mount_t *vfs_fat = &_internal_vfs;
+ vfs_fat->blockdev.flags = 0;
+ supervisor_flash_init_vfs(vfs_fat);
+
+ // try to mount the flash
+ FRESULT res = f_mount(&vfs_fat->fatfs);
+ if ((res == FR_NO_FILESYSTEM && create_allowed) || force_create) {
+ // No filesystem so create a fresh one, or reformat has been requested.
+ uint8_t working_buf[FF_MAX_SS];
+ BYTE formats = FM_FAT;
+ #if FF_FS_EXFAT
+ formats |= FM_EXFAT | FM_FAT32;
+ #endif
+ res = f_mkfs(&vfs_fat->fatfs, formats, 0, working_buf, sizeof(working_buf));
+ if (res != FR_OK) {
+ return false;
+ }
+ // Flush the new file system to make sure it's repaired immediately.
+ supervisor_flash_flush();
+
+ // set label
+ #ifdef CIRCUITPY_DRIVE_LABEL
+ res = f_setlabel(&vfs_fat->fatfs, CIRCUITPY_DRIVE_LABEL);
+ #else
+ res = f_setlabel(&vfs_fat->fatfs, "CIRCUITPY");
+ #endif
+ if (res != FR_OK) {
+ return false;
+ }
+
+ // inhibit file indexing on MacOS
+ res = f_mkdir(&vfs_fat->fatfs, "/.fseventsd");
+ if (res != FR_OK) {
+ return false;
+ }
+ make_empty_file(&vfs_fat->fatfs, "/.metadata_never_index");
+ make_empty_file(&vfs_fat->fatfs, "/.Trashes");
+ make_empty_file(&vfs_fat->fatfs, "/.fseventsd/no_log");
+ // make a sample code.py file
+ make_sample_code_file(&vfs_fat->fatfs);
+
+ // create empty lib directory
+ res = f_mkdir(&vfs_fat->fatfs, "/lib");
+ if (res != FR_OK) {
+ return false;
+ }
+
+ // and ensure everything is flushed
+ supervisor_flash_flush();
+ } else if (res != FR_OK) {
+ return false;
+ }
+ mp_vfs_mount_t *vfs = &_mp_vfs;
+ vfs->str = "/";
+ vfs->len = 1;
+ vfs->obj = MP_OBJ_FROM_PTR(vfs_fat);
+ vfs->next = NULL;
+ MP_STATE_VM(vfs_mount_table) = vfs;
+
+ // The current directory is used as the boot up directory.
+ // It is set to the internal flash filesystem by default.
+ MP_STATE_PORT(vfs_cur) = vfs;
+
+ return true;
+}
+
+void filesystem_flush(void) {
+ // Reset interval before next flush.
+ filesystem_flush_interval_ms = CIRCUITPY_FILESYSTEM_FLUSH_INTERVAL_MS;
+ supervisor_flash_flush();
+ // Don't keep caches because this is called when starting or stopping the VM.
+ supervisor_flash_release_cache();
+}
+
+void filesystem_set_internal_writable_by_usb(bool writable) {
+ fs_user_mount_t *vfs = &_internal_vfs;
+
+ filesystem_set_writable_by_usb(vfs, writable);
+}
+
+void filesystem_set_writable_by_usb(fs_user_mount_t *vfs, bool usb_writable) {
+ if (usb_writable) {
+ vfs->blockdev.flags |= MP_BLOCKDEV_FLAG_USB_WRITABLE;
+ } else {
+ vfs->blockdev.flags &= ~MP_BLOCKDEV_FLAG_USB_WRITABLE;
+ }
+}
+
+bool filesystem_is_writable_by_python(fs_user_mount_t *vfs) {
+ return (vfs->blockdev.flags & MP_BLOCKDEV_FLAG_CONCURRENT_WRITE_PROTECTED) == 0 ||
+ (vfs->blockdev.flags & MP_BLOCKDEV_FLAG_USB_WRITABLE) == 0;
+}
+
+bool filesystem_is_writable_by_usb(fs_user_mount_t *vfs) {
+ return (vfs->blockdev.flags & MP_BLOCKDEV_FLAG_CONCURRENT_WRITE_PROTECTED) == 0 ||
+ (vfs->blockdev.flags & MP_BLOCKDEV_FLAG_USB_WRITABLE) != 0;
+}
+
+void filesystem_set_internal_concurrent_write_protection(bool concurrent_write_protection) {
+ filesystem_set_concurrent_write_protection(&_internal_vfs, concurrent_write_protection);
+}
+
+void filesystem_set_concurrent_write_protection(fs_user_mount_t *vfs, bool concurrent_write_protection) {
+ if (concurrent_write_protection) {
+ vfs->blockdev.flags |= MP_BLOCKDEV_FLAG_CONCURRENT_WRITE_PROTECTED;
+ } else {
+ vfs->blockdev.flags &= ~MP_BLOCKDEV_FLAG_CONCURRENT_WRITE_PROTECTED;
+ }
+}
+
+bool filesystem_present(void) {
+ return true;
+}
diff --git a/circuitpython/supervisor/shared/flash.c b/circuitpython/supervisor/shared/flash.c
new file mode 100644
index 0000000..dfd7cf2
--- /dev/null
+++ b/circuitpython/supervisor/shared/flash.c
@@ -0,0 +1,227 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2018 Scott Shawcroft
+ *
+ * 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 "supervisor/flash.h"
+
+#include "extmod/vfs_fat.h"
+#include "py/runtime.h"
+#include "lib/oofatfs/ff.h"
+#include "supervisor/flash.h"
+#include "supervisor/shared/tick.h"
+
+#define VFS_INDEX 0
+
+#define PART1_START_BLOCK (0x1)
+
+// there is a singleton Flash object
+const mp_obj_type_t supervisor_flash_type;
+STATIC const mp_obj_base_t supervisor_flash_obj = {&supervisor_flash_type};
+
+STATIC mp_obj_t supervisor_flash_obj_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
+ // check arguments
+ mp_arg_check_num(n_args, n_kw, 0, 0, false);
+
+ // return singleton object
+ return (mp_obj_t)&supervisor_flash_obj;
+}
+
+static uint32_t flash_get_block_count(void) {
+ return PART1_START_BLOCK + supervisor_flash_get_block_count();
+}
+
+static void build_partition(uint8_t *buf, int boot, int type, uint32_t start_block, uint32_t num_blocks) {
+ buf[0] = boot;
+
+ if (num_blocks == 0) {
+ buf[1] = 0;
+ buf[2] = 0;
+ buf[3] = 0;
+ } else {
+ buf[1] = 0xff;
+ buf[2] = 0xff;
+ buf[3] = 0xff;
+ }
+
+ buf[4] = type;
+
+ if (num_blocks == 0) {
+ buf[5] = 0;
+ buf[6] = 0;
+ buf[7] = 0;
+ } else {
+ buf[5] = 0xff;
+ buf[6] = 0xff;
+ buf[7] = 0xff;
+ }
+
+ buf[8] = start_block;
+ buf[9] = start_block >> 8;
+ buf[10] = start_block >> 16;
+ buf[11] = start_block >> 24;
+
+ buf[12] = num_blocks;
+ buf[13] = num_blocks >> 8;
+ buf[14] = num_blocks >> 16;
+ buf[15] = num_blocks >> 24;
+}
+
+static mp_uint_t flash_read_blocks(uint8_t *dest, uint32_t block_num, uint32_t num_blocks) {
+ if (block_num == 0) {
+ // fake the MBR so we can decide on our own partition table
+
+ for (int i = 0; i < 446; i++) {
+ dest[i] = 0;
+ }
+
+ build_partition(dest + 446, 0, 0x01 /* FAT12 */, PART1_START_BLOCK, supervisor_flash_get_block_count());
+ build_partition(dest + 462, 0, 0, 0, 0);
+ build_partition(dest + 478, 0, 0, 0, 0);
+ build_partition(dest + 494, 0, 0, 0, 0);
+
+ dest[510] = 0x55;
+ dest[511] = 0xaa;
+ if (num_blocks > 1) {
+ dest += 512;
+ num_blocks -= 1;
+ // Fall through and do a read from flash.
+ } else {
+ return 0; // Done and ok.
+ }
+ }
+ return supervisor_flash_read_blocks(dest, block_num - PART1_START_BLOCK, num_blocks);
+}
+
+static volatile bool filesystem_dirty = false;
+
+static mp_uint_t flash_write_blocks(const uint8_t *src, uint32_t block_num, uint32_t num_blocks) {
+ if (block_num == 0) {
+ if (num_blocks > 1) {
+ return 1; // error
+ }
+ // can't write MBR, but pretend we did
+ return 0;
+ } else {
+ if (!filesystem_dirty) {
+ // Turn on ticks so that we can flush after a period of time elapses.
+ supervisor_enable_tick();
+ filesystem_dirty = true;
+ }
+ return supervisor_flash_write_blocks(src, block_num - PART1_START_BLOCK, num_blocks);
+ }
+}
+
+void supervisor_flash_flush(void) {
+ #if INTERNAL_FLASH_FILESYSTEM
+ port_internal_flash_flush();
+ #else
+ supervisor_external_flash_flush();
+ #endif
+ // Turn off ticks now that our filesystem has been flushed.
+ if (filesystem_dirty) {
+ supervisor_disable_tick();
+ }
+ filesystem_dirty = false;
+}
+
+STATIC mp_obj_t supervisor_flash_obj_readblocks(mp_obj_t self, mp_obj_t block_num, mp_obj_t buf) {
+ mp_buffer_info_t bufinfo;
+ mp_get_buffer_raise(buf, &bufinfo, MP_BUFFER_WRITE);
+ mp_uint_t ret = flash_read_blocks(bufinfo.buf, mp_obj_get_int(block_num), bufinfo.len / FILESYSTEM_BLOCK_SIZE);
+ return MP_OBJ_NEW_SMALL_INT(ret);
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_3(supervisor_flash_obj_readblocks_obj, supervisor_flash_obj_readblocks);
+
+STATIC mp_obj_t supervisor_flash_obj_writeblocks(mp_obj_t self, mp_obj_t block_num, mp_obj_t buf) {
+ mp_buffer_info_t bufinfo;
+ mp_get_buffer_raise(buf, &bufinfo, MP_BUFFER_READ);
+ mp_uint_t ret = flash_write_blocks(bufinfo.buf, mp_obj_get_int(block_num), bufinfo.len / FILESYSTEM_BLOCK_SIZE);
+ return MP_OBJ_NEW_SMALL_INT(ret);
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_3(supervisor_flash_obj_writeblocks_obj, supervisor_flash_obj_writeblocks);
+
+static bool flash_ioctl(size_t cmd, mp_int_t *out_value) {
+ *out_value = 0;
+ switch (cmd) {
+ case MP_BLOCKDEV_IOCTL_INIT:
+ supervisor_flash_init();
+ break;
+ case MP_BLOCKDEV_IOCTL_DEINIT:
+ supervisor_flash_flush();
+ break; // TODO properly
+ case MP_BLOCKDEV_IOCTL_SYNC:
+ supervisor_flash_flush();
+ break;
+ case MP_BLOCKDEV_IOCTL_BLOCK_COUNT:
+ *out_value = flash_get_block_count();
+ break;
+ case MP_BLOCKDEV_IOCTL_BLOCK_SIZE:
+ *out_value = supervisor_flash_get_block_size();
+ break;
+ default:
+ return false;
+ }
+ return true;
+}
+
+STATIC mp_obj_t supervisor_flash_obj_ioctl(mp_obj_t self, mp_obj_t cmd_in, mp_obj_t arg_in) {
+ mp_int_t cmd = mp_obj_get_int(cmd_in);
+ mp_int_t out_value;
+ if (flash_ioctl(cmd, &out_value)) {
+ return MP_OBJ_NEW_SMALL_INT(out_value);
+ }
+ return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_3(supervisor_flash_obj_ioctl_obj, supervisor_flash_obj_ioctl);
+
+STATIC const mp_rom_map_elem_t supervisor_flash_obj_locals_dict_table[] = {
+ { MP_ROM_QSTR(MP_QSTR_readblocks), MP_ROM_PTR(&supervisor_flash_obj_readblocks_obj) },
+ { MP_ROM_QSTR(MP_QSTR_writeblocks), MP_ROM_PTR(&supervisor_flash_obj_writeblocks_obj) },
+ { MP_ROM_QSTR(MP_QSTR_ioctl), MP_ROM_PTR(&supervisor_flash_obj_ioctl_obj) },
+};
+
+STATIC MP_DEFINE_CONST_DICT(supervisor_flash_obj_locals_dict, supervisor_flash_obj_locals_dict_table);
+
+const mp_obj_type_t supervisor_flash_type = {
+ { &mp_type_type },
+ .name = MP_QSTR_Flash,
+ .make_new = supervisor_flash_obj_make_new,
+ .locals_dict = (struct _mp_obj_dict_t *)&supervisor_flash_obj_locals_dict,
+};
+
+void supervisor_flash_init_vfs(fs_user_mount_t *vfs) {
+ vfs->base.type = &mp_fat_vfs_type;
+ vfs->blockdev.flags |= MP_BLOCKDEV_FLAG_NATIVE | MP_BLOCKDEV_FLAG_HAVE_IOCTL;
+ vfs->fatfs.drv = vfs;
+ vfs->fatfs.part = 1; // flash filesystem lives on first fake partition
+ vfs->blockdev.readblocks[0] = (mp_obj_t)&supervisor_flash_obj_readblocks_obj;
+ vfs->blockdev.readblocks[1] = (mp_obj_t)&supervisor_flash_obj;
+ vfs->blockdev.readblocks[2] = (mp_obj_t)flash_read_blocks; // native version
+ vfs->blockdev.writeblocks[0] = (mp_obj_t)&supervisor_flash_obj_writeblocks_obj;
+ vfs->blockdev.writeblocks[1] = (mp_obj_t)&supervisor_flash_obj;
+ vfs->blockdev.writeblocks[2] = (mp_obj_t)flash_write_blocks; // native version
+ vfs->blockdev.u.ioctl[0] = (mp_obj_t)&supervisor_flash_obj_ioctl_obj;
+ vfs->blockdev.u.ioctl[1] = (mp_obj_t)&supervisor_flash_obj;
+ vfs->blockdev.u.ioctl[2] = (mp_obj_t)flash_ioctl; // native version
+}
diff --git a/circuitpython/supervisor/shared/internal_flash.h b/circuitpython/supervisor/shared/internal_flash.h
new file mode 100644
index 0000000..80257a2
--- /dev/null
+++ b/circuitpython/supervisor/shared/internal_flash.h
@@ -0,0 +1,33 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 Scott Shawcroft, for Adafruit Industries LLC
+ *
+ * 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_SUPERVISOR_SHARED_INTERNAL_FLASH_H
+#define MICROPY_INCLUDED_SUPERVISOR_SHARED_INTERNAL_FLASH_H
+
+#include "supervisor/internal_flash.h" // This is per-port.
+
+void port_internal_flash_flush(void);
+
+#endif // MICROPY_INCLUDED_SUPERVISOR_SHARED_INTERNAL_FLASH_H
diff --git a/circuitpython/supervisor/shared/lock.c b/circuitpython/supervisor/shared/lock.c
new file mode 100644
index 0000000..36971ba
--- /dev/null
+++ b/circuitpython/supervisor/shared/lock.c
@@ -0,0 +1,51 @@
+/*
+ * 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-bindings/microcontroller/__init__.h"
+#include "supervisor/shared/lock.h"
+
+void supervisor_acquire_lock(supervisor_lock_t *lock) {
+ while (!supervisor_try_lock(lock)) {
+ RUN_BACKGROUND_TASKS;
+ }
+}
+
+bool supervisor_try_lock(supervisor_lock_t *lock) {
+ bool grabbed_lock = false;
+ common_hal_mcu_disable_interrupts();
+ if (!*lock) {
+ *lock = true;
+ grabbed_lock = true;
+ }
+ common_hal_mcu_enable_interrupts();
+ return grabbed_lock;
+}
+
+void supervisor_release_lock(supervisor_lock_t *lock) {
+ common_hal_mcu_disable_interrupts();
+ *lock = false;
+ common_hal_mcu_enable_interrupts();
+}
diff --git a/circuitpython/supervisor/shared/lock.h b/circuitpython/supervisor/shared/lock.h
new file mode 100644
index 0000000..89f81cf
--- /dev/null
+++ b/circuitpython/supervisor/shared/lock.h
@@ -0,0 +1,36 @@
+/*
+ * 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_SUPERVISOR_LOCK_H
+#define MICROPY_INCLUDED_SUPERVISOR_LOCK_H
+
+typedef volatile bool supervisor_lock_t;
+
+void supervisor_acquire_lock(supervisor_lock_t *lock);
+bool supervisor_try_lock(supervisor_lock_t *lock);
+void supervisor_release_lock(supervisor_lock_t *lock);
+
+#endif // MICROPY_INCLUDED_SUPERVISOR_LOCK_H
diff --git a/circuitpython/supervisor/shared/memory.c b/circuitpython/supervisor/shared/memory.c
new file mode 100644
index 0000000..ff0382f
--- /dev/null
+++ b/circuitpython/supervisor/shared/memory.c
@@ -0,0 +1,341 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2018 Scott Shawcroft 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 "supervisor/memory.h"
+#include "supervisor/port.h"
+
+#include <string.h>
+
+#include "py/gc.h"
+#include "supervisor/shared/display.h"
+
+enum {
+ CIRCUITPY_SUPERVISOR_IMMOVABLE_ALLOC_COUNT =
+ 0
+ // stack + heap
+ + 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
+
+ #if CIRCUITPY_USB_VENDOR
+ + 1 // usb_vendor_add_descriptor
+ #endif
+ ,
+
+ CIRCUITPY_SUPERVISOR_MOVABLE_ALLOC_COUNT =
+ 0
+ // next_code_allocation
+ + 1
+ // prev_traceback_allocation
+ + 1
+ #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
+ ,
+
+ CIRCUITPY_SUPERVISOR_ALLOC_COUNT = CIRCUITPY_SUPERVISOR_IMMOVABLE_ALLOC_COUNT + CIRCUITPY_SUPERVISOR_MOVABLE_ALLOC_COUNT
+};
+
+// The lowest two bits of a valid length are always zero, so we can use them to mark an allocation
+// as a hole (freed by the client but not yet reclaimed into the free middle) and as movable.
+#define FLAGS 3
+#define HOLE 1
+#define MOVABLE 2
+
+static supervisor_allocation allocations[CIRCUITPY_SUPERVISOR_ALLOC_COUNT];
+supervisor_allocation *old_allocations;
+
+typedef struct _supervisor_allocation_node {
+ struct _supervisor_allocation_node *next;
+ size_t length;
+ // We use uint32_t to ensure word (4 byte) alignment.
+ uint32_t data[];
+} supervisor_allocation_node;
+
+supervisor_allocation_node *low_head;
+supervisor_allocation_node *high_head;
+
+// Intermediate (void*) is to suppress -Wcast-align warning. Alignment will always be correct
+// because this only reverses how (alloc)->ptr was obtained as &(node->data[0]).
+#define ALLOCATION_NODE(alloc) ((supervisor_allocation_node *)(void *)((char *)((alloc)->ptr) - sizeof(supervisor_allocation_node)))
+
+void free_memory(supervisor_allocation *allocation) {
+ if (allocation == NULL || allocation->ptr == NULL) {
+ return;
+ }
+ supervisor_allocation_node *node = ALLOCATION_NODE(allocation);
+ if (node == low_head) {
+ do {
+ low_head = low_head->next;
+ } while (low_head != NULL && (low_head->length & HOLE));
+ } else if (node == high_head) {
+ do {
+ high_head = high_head->next;
+ } while (high_head != NULL && (high_head->length & HOLE));
+ } else {
+ // Check if it's in the list of embedded allocations.
+ supervisor_allocation_node **emb = &MP_STATE_VM(first_embedded_allocation);
+ while (*emb != NULL && *emb != node) {
+ emb = &((*emb)->next);
+ }
+ if (*emb != NULL) {
+ // Found, remove it from the list.
+ *emb = node->next;
+ m_free(node
+ #if MICROPY_MALLOC_USES_ALLOCATED_SIZE
+ , sizeof(supervisor_allocation_node) + (node->length & ~FLAGS)
+ #endif
+ );
+ } else {
+ // Else it must be within the low or high ranges and becomes a hole.
+ node->length = ((node->length & ~FLAGS) | HOLE);
+ }
+ }
+ allocation->ptr = NULL;
+}
+
+supervisor_allocation *allocation_from_ptr(void *ptr) {
+ // When called from the context of supervisor_move_memory() (old_allocations != NULL), search
+ // by old pointer to give clients a way of mapping from old to new pointer. But not if
+ // ptr == NULL, then the caller wants an allocation whose current ptr is NULL.
+ supervisor_allocation *list = (old_allocations && ptr) ? old_allocations : &allocations[0];
+ for (size_t index = 0; index < CIRCUITPY_SUPERVISOR_ALLOC_COUNT; index++) {
+ if (list[index].ptr == ptr) {
+ return &allocations[index];
+ }
+ }
+ return NULL;
+}
+
+supervisor_allocation *allocate_remaining_memory(void) {
+ return allocate_memory((uint32_t)-1, false, false);
+}
+
+static supervisor_allocation_node *find_hole(supervisor_allocation_node *node, size_t length) {
+ for (; node != NULL; node = node->next) {
+ if (node->length == (length | HOLE)) {
+ break;
+ }
+ }
+ return node;
+}
+
+static supervisor_allocation_node *allocate_memory_node(uint32_t length, bool high, bool movable) {
+ if (CIRCUITPY_SUPERVISOR_MOVABLE_ALLOC_COUNT == 0) {
+ assert(!movable);
+ }
+ // supervisor_move_memory() currently does not support movable allocations on the high side, it
+ // must be extended first if this is ever needed.
+ assert(!(high && movable));
+ uint32_t *low_address = low_head ? low_head->data + low_head->length / 4 : port_heap_get_bottom();
+ uint32_t *high_address = high_head ? (uint32_t *)high_head : port_heap_get_top();
+ // Special case for allocate_remaining_memory(), avoids computing low/high_address twice.
+ if (length == (uint32_t)-1) {
+ length = (high_address - low_address) * 4 - sizeof(supervisor_allocation_node);
+ }
+ if (length == 0 || length % 4 != 0) {
+ return NULL;
+ }
+ // 1. Matching hole on the requested side?
+ supervisor_allocation_node *node = find_hole(high ? high_head : low_head, length);
+ if (!node) {
+ // 2. Enough free space in the middle?
+ if ((high_address - low_address) * 4 >= (int32_t)(sizeof(supervisor_allocation_node) + length)) {
+ if (high) {
+ high_address -= (sizeof(supervisor_allocation_node) + length) / 4;
+ node = (supervisor_allocation_node *)high_address;
+ node->next = high_head;
+ high_head = node;
+ } else {
+ node = (supervisor_allocation_node *)low_address;
+ node->next = low_head;
+ low_head = node;
+ }
+ } else {
+ // 3. Matching hole on the other side?
+ node = find_hole(high ? low_head : high_head, length);
+ if (!node) {
+ // 4. GC allocation?
+ if (movable && gc_alloc_possible()) {
+ node = m_malloc_maybe(sizeof(supervisor_allocation_node) + length, true);
+ if (node) {
+ node->next = MP_STATE_VM(first_embedded_allocation);
+ MP_STATE_VM(first_embedded_allocation) = node;
+ }
+ }
+ if (!node) {
+ // 5. Give up.
+ return NULL;
+ }
+ }
+ }
+ }
+ node->length = length;
+ if (movable) {
+ node->length |= MOVABLE;
+ }
+ return node;
+}
+
+supervisor_allocation *allocate_memory(uint32_t length, bool high, bool movable) {
+ supervisor_allocation_node *node = allocate_memory_node(length, high, movable);
+ if (!node) {
+ return NULL;
+ }
+ // Find the first free allocation.
+ supervisor_allocation *alloc = allocation_from_ptr(NULL);
+ if (!alloc) {
+ // We should free node again to avoid leaking, but something is wrong anyway if clients try
+ // to make more allocations than available, so don't bother.
+ return NULL;
+ }
+ alloc->ptr = &(node->data[0]);
+ return alloc;
+}
+
+size_t get_allocation_length(supervisor_allocation *allocation) {
+ return ALLOCATION_NODE(allocation)->length & ~FLAGS;
+}
+
+
+void supervisor_move_memory(void) {
+ // This whole function is not needed when there are no movable allocations, let it be optimized
+ // out.
+ if (CIRCUITPY_SUPERVISOR_MOVABLE_ALLOC_COUNT == 0) {
+ return;
+ }
+ // This must be called exactly after freeing the heap, so that the embedded allocations, if any,
+ // are now in the free region.
+ assert(MP_STATE_VM(first_embedded_allocation) == NULL || (
+ (low_head == NULL || low_head < MP_STATE_VM(first_embedded_allocation)) &&
+ (high_head == NULL || MP_STATE_VM(first_embedded_allocation) < high_head)));
+
+ // Save the old pointers for allocation_from_ptr().
+ supervisor_allocation old_allocations_array[CIRCUITPY_SUPERVISOR_ALLOC_COUNT];
+ memcpy(old_allocations_array, allocations, sizeof(allocations));
+
+ // Compact the low side. Traverse the list repeatedly, finding movable allocations preceded by a
+ // hole and swapping them, until no more are found. This is not the most runtime-efficient way,
+ // but probably the shortest and simplest code.
+ bool acted;
+ do {
+ acted = false;
+ supervisor_allocation_node **nodep = &low_head;
+ while (*nodep != NULL && (*nodep)->next != NULL) {
+ if (((*nodep)->length & MOVABLE) && ((*nodep)->next->length & HOLE)) {
+ supervisor_allocation_node *oldnode = *nodep;
+ supervisor_allocation_node *start = oldnode->next;
+ supervisor_allocation *alloc = allocation_from_ptr(&(oldnode->data[0]));
+ assert(alloc != NULL);
+ alloc->ptr = &(start->data[0]);
+ oldnode->next = start->next;
+ size_t holelength = start->length;
+ size_t size = sizeof(supervisor_allocation_node) + (oldnode->length & ~FLAGS);
+ memmove(start, oldnode, size);
+ supervisor_allocation_node *newhole = (supervisor_allocation_node *)(void *)((char *)start + size);
+ newhole->next = start;
+ newhole->length = holelength;
+ *nodep = newhole;
+ acted = true;
+ }
+ nodep = &((*nodep)->next);
+ }
+ } while (acted);
+ // Any holes bubbled to the top can be absorbed into the free middle.
+ while (low_head != NULL && (low_head->length & HOLE)) {
+ low_head = low_head->next;
+ }
+ ;
+
+ // Don't bother compacting the high side, there are no movable allocations and no holes there in
+ // current usage.
+
+ // Promote the embedded allocations to top-level ones, compacting them at the beginning of the
+ // now free region (or possibly in matching holes).
+ // The linked list is unordered, but allocations must be processed in order to avoid risking
+ // overwriting each other. To that end, repeatedly find the lowest element of the list, remove
+ // it from the list, and process it. This ad-hoc selection sort results in substantially shorter
+ // code than using the qsort() function from the C library.
+ while (MP_STATE_VM(first_embedded_allocation)) {
+ // First element is first candidate.
+ supervisor_allocation_node **pminnode = &MP_STATE_VM(first_embedded_allocation);
+ // Iterate from second element (if any) on.
+ for (supervisor_allocation_node **pnode = &(MP_STATE_VM(first_embedded_allocation)->next); *pnode != NULL; pnode = &(*pnode)->next) {
+ if (*pnode < *pminnode) {
+ pminnode = pnode;
+ }
+ }
+ // Remove from list.
+ supervisor_allocation_node *node = *pminnode;
+ *pminnode = node->next;
+ // Process.
+ size_t length = (node->length & ~FLAGS);
+ supervisor_allocation *alloc = allocation_from_ptr(&(node->data[0]));
+ assert(alloc != NULL);
+ // This may overwrite the header of node if it happened to be there already, but not the
+ // data.
+ supervisor_allocation_node *new_node = allocate_memory_node(length, false, true);
+ // There must be enough free space.
+ assert(new_node != NULL);
+ memmove(&(new_node->data[0]), &(node->data[0]), length);
+ alloc->ptr = &(new_node->data[0]);
+ }
+
+ // Notify clients that their movable allocations may have moved.
+ old_allocations = &old_allocations_array[0];
+
+ #if CIRCUITPY_DISPLAYIO
+ supervisor_display_move_memory();
+ #endif
+
+ // Add calls to further clients here.
+ old_allocations = NULL;
+}
diff --git a/circuitpython/supervisor/shared/micropython.c b/circuitpython/supervisor/shared/micropython.c
new file mode 100644
index 0000000..ebc0aef
--- /dev/null
+++ b/circuitpython/supervisor/shared/micropython.c
@@ -0,0 +1,83 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2018 Scott Shawcroft 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 <string.h>
+
+#include "supervisor/serial.h"
+#include "lib/oofatfs/ff.h"
+#include "py/mpconfig.h"
+#include "py/mphal.h"
+#include "py/mpstate.h"
+#include "py/runtime.h"
+#include "py/stream.h"
+
+#include "supervisor/shared/status_leds.h"
+
+#if CIRCUITPY_WATCHDOG
+#include "shared-bindings/watchdog/__init__.h"
+#define WATCHDOG_EXCEPTION_CHECK() (MP_STATE_VM(mp_pending_exception) == &mp_watchdog_timeout_exception)
+#else
+#define WATCHDOG_EXCEPTION_CHECK() 0
+#endif
+
+int mp_hal_stdin_rx_chr(void) {
+ for (;;) {
+ #ifdef MICROPY_VM_HOOK_LOOP
+ MICROPY_VM_HOOK_LOOP
+ #endif
+ mp_handle_pending(true);
+ if (serial_bytes_available()) {
+ toggle_rx_led();
+ return serial_read();
+ }
+ }
+}
+
+void mp_hal_stdout_tx_strn(const char *str, size_t len) {
+ toggle_tx_led();
+
+ #ifdef CIRCUITPY_BOOT_OUTPUT_FILE
+ if (boot_output != NULL) {
+ // Ensure boot_out.txt is capped at 1 filesystem block and ends with a newline
+ if (len + boot_output->len > 508) {
+ vstr_add_str(boot_output, "...\n");
+ boot_output = NULL;
+ } else {
+ vstr_add_strn(boot_output, str, len);
+ }
+ }
+ #endif
+
+ serial_write_substring(str, len);
+}
+
+uintptr_t mp_hal_stdio_poll(uintptr_t poll_flags) {
+ uintptr_t ret = 0;
+ if ((poll_flags & MP_STREAM_POLL_RD) && serial_bytes_available()) {
+ ret |= MP_STREAM_POLL_RD;
+ }
+ return ret;
+}
diff --git a/circuitpython/supervisor/shared/reload.c b/circuitpython/supervisor/shared/reload.c
new file mode 100644
index 0000000..e1ae2e6
--- /dev/null
+++ b/circuitpython/supervisor/shared/reload.c
@@ -0,0 +1,113 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2016 Scott Shawcroft 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 "reload.h"
+
+#include "py/mphal.h"
+#include "py/mpstate.h"
+#include "supervisor/shared/reload.h"
+#include "supervisor/shared/tick.h"
+
+supervisor_allocation *next_code_allocation;
+#include "shared-bindings/supervisor/Runtime.h"
+
+// True if user has disabled autoreload.
+static bool autoreload_enabled = false;
+
+// Non-zero if autoreload is temporarily off, due to an AUTORELOAD_SUSPEND_... reason.
+static uint32_t autoreload_suspended = 0;
+
+volatile uint32_t last_autoreload_trigger = 0;
+
+void reload_initiate(supervisor_run_reason_t run_reason) {
+ supervisor_set_run_reason(run_reason);
+
+ // Raise reload exception, in case code is running.
+ MP_STATE_THREAD(mp_pending_exception) = MP_OBJ_FROM_PTR(&MP_STATE_VM(mp_reload_exception));
+ #if MICROPY_ENABLE_SCHEDULER
+ if (MP_STATE_VM(sched_state) == MP_SCHED_IDLE) {
+ MP_STATE_VM(sched_state) = MP_SCHED_PENDING;
+ }
+ #endif
+}
+
+void autoreload_reset() {
+ last_autoreload_trigger = 0;
+}
+
+void autoreload_enable() {
+ autoreload_enabled = true;
+ last_autoreload_trigger = 0;
+}
+
+void autoreload_disable() {
+ autoreload_enabled = false;
+}
+
+void autoreload_suspend(uint32_t suspend_reason_mask) {
+ autoreload_suspended |= suspend_reason_mask;
+}
+
+void autoreload_resume(uint32_t suspend_reason_mask) {
+ autoreload_suspended &= ~suspend_reason_mask;
+}
+
+inline bool autoreload_is_enabled() {
+ return autoreload_enabled;
+}
+
+void autoreload_trigger() {
+ if (autoreload_enabled & !autoreload_suspended) {
+ last_autoreload_trigger = supervisor_ticks_ms32();
+ // Guard against the rare time that ticks is 0;
+ if (last_autoreload_trigger == 0) {
+ last_autoreload_trigger += 1;
+ }
+ // Initiate a reload of the VM immediately. Later code will pause to
+ // wait for the autoreload to become ready. Doing the VM exit
+ // immediately is clearer for the user.
+ reload_initiate(RUN_REASON_AUTO_RELOAD);
+ }
+}
+
+bool autoreload_ready() {
+ if (last_autoreload_trigger == 0 || autoreload_suspended != 0) {
+ return false;
+ }
+ // Wait for autoreload interval before reloading
+ uint32_t now = supervisor_ticks_ms32();
+ uint32_t diff;
+ if (now >= last_autoreload_trigger) {
+ diff = now - last_autoreload_trigger;
+ } else {
+ diff = now + (0xffffffff - last_autoreload_trigger);
+ }
+ return diff > CIRCUITPY_AUTORELOAD_DELAY_MS;
+}
+
+bool autoreload_pending(void) {
+ return last_autoreload_trigger != 0;
+}
diff --git a/circuitpython/supervisor/shared/reload.h b/circuitpython/supervisor/shared/reload.h
new file mode 100644
index 0000000..cb3385e
--- /dev/null
+++ b/circuitpython/supervisor/shared/reload.h
@@ -0,0 +1,84 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2016 Scott Shawcroft 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_SUPERVISOR_AUTORELOAD_H
+#define MICROPY_INCLUDED_SUPERVISOR_AUTORELOAD_H
+
+#include "supervisor/memory.h"
+#include "py/obj.h"
+#include "shared-bindings/supervisor/RunReason.h"
+
+enum {
+ SUPERVISOR_NEXT_CODE_OPT_RELOAD_ON_SUCCESS = 0x1,
+ SUPERVISOR_NEXT_CODE_OPT_RELOAD_ON_ERROR = 0x2,
+ SUPERVISOR_NEXT_CODE_OPT_STICKY_ON_SUCCESS = 0x4,
+ SUPERVISOR_NEXT_CODE_OPT_STICKY_ON_ERROR = 0x8,
+ SUPERVISOR_NEXT_CODE_OPT_STICKY_ON_RELOAD = 0x10,
+ SUPERVISOR_NEXT_CODE_OPT_NEWLY_SET = 0x20,
+};
+
+enum {
+ AUTORELOAD_SUSPEND_REPL = 0x1,
+ AUTORELOAD_SUSPEND_BLE = 0x2,
+ AUTORELOAD_SUSPEND_USB = 0x4
+};
+
+typedef struct {
+ uint8_t options;
+ char filename[];
+} next_code_info_t;
+
+extern supervisor_allocation *next_code_allocation;
+
+// Helper for exiting the VM and reloading immediately.
+void reload_initiate(supervisor_run_reason_t run_reason);
+
+// Enabled state is user controllable and very sticky. We don't reset it.
+void autoreload_enable(void);
+void autoreload_disable(void);
+bool autoreload_is_enabled(void);
+
+// Start the autoreload process.
+void autoreload_trigger(void);
+// True when the autoreload should occur. (A trigger happened and the delay has
+// passed.)
+bool autoreload_ready(void);
+// Reset the autoreload timer in preparation for another trigger. Call when the
+// last trigger starts being executed.
+void autoreload_reset(void);
+// True when a trigger has occurred but we're still delaying in case another
+// trigger occurs.
+bool autoreload_pending(void);
+
+// Temporarily turn autoreload off, for the given reason(s). Autoreload triggers
+// will still be tracked so resuming with autoreload ready with cause an
+// immediate reload.
+// Used during the REPL or during parts of BLE workflow.
+void autoreload_suspend(uint32_t suspend_reason_mask);
+// Allow autoreloads again, for the given reason(s).
+void autoreload_resume(uint32_t suspend_reason_mask);
+
+#endif // MICROPY_INCLUDED_SUPERVISOR_AUTORELOAD_H
diff --git a/circuitpython/supervisor/shared/rgb_led_colors.h b/circuitpython/supervisor/shared/rgb_led_colors.h
new file mode 100644
index 0000000..56dc0f4
--- /dev/null
+++ b/circuitpython/supervisor/shared/rgb_led_colors.h
@@ -0,0 +1,30 @@
+
+#define COLOR(r, g, b) (((r) << 16) | ((g) << 8) | (b))
+// For brightness == 255 (full). This will be adjusted downward for various different RGB indicators,
+// which vary in brightness.
+#define INTENSITY (0x30)
+
+#define BLACK COLOR(0, 0, 0)
+#define GREEN COLOR(0, INTENSITY, 0)
+#define BLUE COLOR(0, 0, INTENSITY)
+#define CYAN COLOR(0, INTENSITY, INTENSITY)
+#define RED COLOR(INTENSITY, 0, 0)
+#define ORANGE COLOR(INTENSITY, INTENSITY * 2 / 3, 0)
+#define YELLOW COLOR(INTENSITY, INTENSITY, 0)
+#define PURPLE COLOR(INTENSITY, 0, INTENSITY)
+#define WHITE COLOR(INTENSITY, INTENSITY, INTENSITY)
+
+#define ALL_DONE GREEN
+#define EXCEPTION RED
+#define SAFE_MODE YELLOW
+#define REPL_RUNNING WHITE
+
+#define ALL_DONE_BLINKS 1
+#define EXCEPTION_BLINKS 2
+#define SAFE_MODE_BLINKS 3
+
+#define OFF_ON_RATIO 3
+
+#define LED_SLEEP_TIME_MS 5000u
+
+#define BLINK_TIME_MS 100u
diff --git a/circuitpython/supervisor/shared/safe_mode.c b/circuitpython/supervisor/shared/safe_mode.c
new file mode 100644
index 0000000..7922cff
--- /dev/null
+++ b/circuitpython/supervisor/shared/safe_mode.c
@@ -0,0 +1,238 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2018 Scott Shawcroft 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 "supervisor/shared/safe_mode.h"
+
+#include "mphalport.h"
+
+#if defined(CIRCUITPY_BOOT_BUTTON)
+#include "shared-bindings/digitalio/DigitalInOut.h"
+#endif
+#include "shared-bindings/microcontroller/Processor.h"
+#include "shared-bindings/microcontroller/ResetReason.h"
+
+#include "supervisor/serial.h"
+#include "supervisor/shared/rgb_led_colors.h"
+#include "supervisor/shared/status_leds.h"
+#include "supervisor/shared/translate.h"
+#include "supervisor/shared/tick.h"
+
+#define SAFE_MODE_DATA_GUARD 0xad0000af
+#define SAFE_MODE_DATA_GUARD_MASK 0xff0000ff
+
+static safe_mode_t current_safe_mode;
+
+safe_mode_t wait_for_safe_mode_reset(void) {
+ uint32_t reset_state = port_get_saved_word();
+ safe_mode_t safe_mode = NO_SAFE_MODE;
+ if ((reset_state & SAFE_MODE_DATA_GUARD_MASK) == SAFE_MODE_DATA_GUARD) {
+ safe_mode = (reset_state & ~SAFE_MODE_DATA_GUARD_MASK) >> 8;
+ }
+ if (safe_mode != NO_SAFE_MODE) {
+ port_set_saved_word(SAFE_MODE_DATA_GUARD);
+ current_safe_mode = safe_mode;
+ return safe_mode;
+ } else {
+ current_safe_mode = 0;
+ }
+
+ const mcu_reset_reason_t reset_reason = common_hal_mcu_processor_get_reset_reason();
+ if (reset_reason != RESET_REASON_POWER_ON &&
+ reset_reason != RESET_REASON_RESET_PIN &&
+ reset_reason != RESET_REASON_UNKNOWN &&
+ reset_reason != RESET_REASON_SOFTWARE) {
+ return NO_SAFE_MODE;
+ }
+ #ifdef CIRCUITPY_SKIP_SAFE_MODE_WAIT
+ return NO_SAFE_MODE;
+ #endif
+ port_set_saved_word(SAFE_MODE_DATA_GUARD | (MANUAL_SAFE_MODE << 8));
+ // Wait for a while to allow for reset.
+
+ #if CIRCUITPY_STATUS_LED
+ status_led_init();
+ #endif
+ #ifdef CIRCUITPY_BOOT_BUTTON
+ digitalio_digitalinout_obj_t boot_button;
+ common_hal_digitalio_digitalinout_construct(&boot_button, CIRCUITPY_BOOT_BUTTON);
+ common_hal_digitalio_digitalinout_switch_to_input(&boot_button, PULL_UP);
+ #endif
+ uint64_t start_ticks = supervisor_ticks_ms64();
+ uint64_t diff = 0;
+ bool boot_in_safe_mode = false;
+ while (diff < 1000) {
+ #ifdef CIRCUITPY_STATUS_LED
+ // Blink on for 100, off for 100
+ bool led_on = (diff % 250) < 125;
+ if (led_on) {
+ new_status_color(SAFE_MODE);
+ } else {
+ new_status_color(BLACK);
+ }
+ #endif
+ #ifdef CIRCUITPY_BOOT_BUTTON
+ if (!common_hal_digitalio_digitalinout_get_value(&boot_button)) {
+ boot_in_safe_mode = true;
+ break;
+ }
+ #endif
+ diff = supervisor_ticks_ms64() - start_ticks;
+ }
+ #if CIRCUITPY_STATUS_LED
+ new_status_color(BLACK);
+ status_led_deinit();
+ #endif
+ if (boot_in_safe_mode) {
+ return USER_SAFE_MODE;
+ }
+ // Restore the original state of the saved word if no reset occured during our wait period.
+ port_set_saved_word(reset_state);
+ return NO_SAFE_MODE;
+}
+
+void safe_mode_on_next_reset(safe_mode_t reason) {
+ port_set_saved_word(SAFE_MODE_DATA_GUARD | (reason << 8));
+}
+
+// Don't inline this so it's easy to break on it from GDB.
+void __attribute__((noinline,)) reset_into_safe_mode(safe_mode_t reason) {
+ if (current_safe_mode > BROWNOUT && reason > BROWNOUT) {
+ while (true) {
+ // This very bad because it means running in safe mode didn't save us. Only ignore brownout
+ // because it may be due to a switch bouncing.
+ }
+ }
+
+ safe_mode_on_next_reset(reason);
+ reset_cpu();
+}
+
+
+
+void print_safe_mode_message(safe_mode_t reason) {
+ if (reason == NO_SAFE_MODE) {
+ return;
+ }
+
+ serial_write("\r\n");
+ serial_write_compressed(translate("You are in safe mode because:\n"));
+
+ const compressed_string_t *message = NULL;
+
+ // First check for safe mode reasons that do not necessarily reflect bugs.
+
+ switch (reason) {
+ case USER_SAFE_MODE:
+ #ifdef BOARD_USER_SAFE_MODE_ACTION
+ message = BOARD_USER_SAFE_MODE_ACTION;
+ #elif defined(CIRCUITPY_BOOT_BUTTON)
+ message = translate("pressing boot button at start up.\n");
+ #endif
+ if (message != NULL) {
+ // Output a user safe mode string if it's set.
+ serial_write_compressed(translate("You requested starting safe mode by "));
+ serial_write_compressed(message);
+ serial_write_compressed(translate("To exit, please reset the board without "));
+ // The final piece is printed below.
+ }
+ break;
+ case MANUAL_SAFE_MODE:
+ message = translate("You pressed the reset button during boot. Press again to exit safe mode.");
+ break;
+ case PROGRAMMATIC_SAFE_MODE:
+ message = translate("The `microcontroller` module was used to boot into safe mode. Press reset to exit safe mode.");
+ break;
+ case BROWNOUT:
+ message = translate("The microcontroller's power dipped. Make sure your power supply provides\nenough power for the whole circuit and press reset (after ejecting CIRCUITPY).");
+ break;
+ case USB_TOO_MANY_ENDPOINTS:
+ message = translate("USB devices need more endpoints than are available.");
+ break;
+ case USB_TOO_MANY_INTERFACE_NAMES:
+ message = translate("USB devices specify too many interface names.");
+ break;
+ case USB_BOOT_DEVICE_NOT_INTERFACE_ZERO:
+ message = translate("Boot device must be first device (interface #0).");
+ break;
+ case WATCHDOG_RESET:
+ message = translate("Watchdog timer expired.");
+ break;
+ case NO_CIRCUITPY:
+ message = translate("CIRCUITPY drive could not be found or created.");
+ break;
+ default:
+ break;
+ }
+
+ if (message) {
+ serial_write_compressed(message);
+ serial_write("\r\n");
+ return;
+ }
+
+ // Something worse happened.
+
+ serial_write_compressed(translate("CircuitPython core code crashed hard. Whoops!\n"));
+
+ switch (reason) {
+ case HARD_CRASH:
+ message = translate("Crash into the HardFault_Handler.");
+ break;
+ case MICROPY_NLR_JUMP_FAIL:
+ message = translate("NLR jump failed. Likely memory corruption.");
+ break;
+ case MICROPY_FATAL_ERROR:
+ message = translate("Fatal error.");
+ break;
+ case NO_HEAP:
+ message = translate("CircuitPython was unable to allocate the heap.");
+ break;
+ case HEAP_OVERWRITTEN:
+ message = translate("The CircuitPython heap was corrupted because the stack was too small.\nIncrease the stack size if you know how. If not:");
+ break;
+ case GC_ALLOC_OUTSIDE_VM:
+ message = translate("Attempted heap allocation when VM not running.");
+ break;
+ #ifdef SOFTDEVICE_PRESENT
+ // defined in ports/nrf/bluetooth/bluetooth_common.mk
+ // will print "Unknown reason" if somehow encountered on other ports
+ case NORDIC_SOFT_DEVICE_ASSERT:
+ message = translate("Nordic system firmware failure assertion.");
+ break;
+ #endif
+ case FLASH_WRITE_FAIL:
+ message = translate("Failed to write internal flash.");
+ break;
+ case MEM_MANAGE:
+ message = translate("Invalid memory access.");
+ break;
+ default:
+ message = translate("Unknown reason.");
+ break;
+ }
+ serial_write_compressed(message);
+ serial_write_compressed(translate("\nPlease file an issue with the contents of your CIRCUITPY drive at \nhttps://github.com/adafruit/circuitpython/issues\n"));
+}
diff --git a/circuitpython/supervisor/shared/safe_mode.h b/circuitpython/supervisor/shared/safe_mode.h
new file mode 100644
index 0000000..0c8d018
--- /dev/null
+++ b/circuitpython/supervisor/shared/safe_mode.h
@@ -0,0 +1,61 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2018 Scott Shawcroft 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_SUPERVISOR_SAFE_MODE_H
+#define MICROPY_INCLUDED_SUPERVISOR_SAFE_MODE_H
+
+#include "py/mpconfig.h"
+
+typedef enum {
+ NO_SAFE_MODE = 0,
+ BROWNOUT,
+ HARD_CRASH,
+ USER_SAFE_MODE,
+ HEAP_OVERWRITTEN,
+ MANUAL_SAFE_MODE,
+ MICROPY_NLR_JUMP_FAIL,
+ MICROPY_FATAL_ERROR,
+ GC_ALLOC_OUTSIDE_VM,
+ PROGRAMMATIC_SAFE_MODE,
+ NORDIC_SOFT_DEVICE_ASSERT,
+ FLASH_WRITE_FAIL,
+ MEM_MANAGE,
+ WATCHDOG_RESET,
+ USB_TOO_MANY_ENDPOINTS,
+ USB_TOO_MANY_INTERFACE_NAMES,
+ USB_BOOT_DEVICE_NOT_INTERFACE_ZERO,
+ NO_HEAP,
+ NO_CIRCUITPY,
+} safe_mode_t;
+
+safe_mode_t wait_for_safe_mode_reset(void);
+
+void safe_mode_on_next_reset(safe_mode_t reason);
+void reset_into_safe_mode(safe_mode_t reason) NORETURN;
+
+void print_safe_mode_message(safe_mode_t reason);
+
+#endif // MICROPY_INCLUDED_SUPERVISOR_SAFE_MODE_H
diff --git a/circuitpython/supervisor/shared/serial.c b/circuitpython/supervisor/shared/serial.c
new file mode 100644
index 0000000..af90fce
--- /dev/null
+++ b/circuitpython/supervisor/shared/serial.c
@@ -0,0 +1,297 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2017 Scott Shawcroft 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 <stdarg.h>
+#include <string.h>
+
+#include "py/mpconfig.h"
+
+#include "supervisor/shared/cpu.h"
+#include "supervisor/shared/display.h"
+#include "shared-bindings/terminalio/Terminal.h"
+#include "supervisor/serial.h"
+#include "supervisor/usb.h"
+#include "shared-bindings/microcontroller/Pin.h"
+#include "shared-module/usb_cdc/__init__.h"
+
+#if CIRCUITPY_SERIAL_BLE
+#include "supervisor/shared/bluetooth/serial.h"
+#endif
+
+#if CIRCUITPY_USB
+#include "tusb.h"
+#endif
+
+/*
+ * Note: DEBUG_UART currently only works on STM32 and nRF.
+ * Enabling on another platform will cause a crash.
+ */
+
+#if defined(CIRCUITPY_DEBUG_UART_TX) || defined(CIRCUITPY_DEBUG_UART_RX)
+#include "py/mpprint.h"
+#include "shared-bindings/busio/UART.h"
+busio_uart_obj_t debug_uart;
+byte buf_array[64];
+#endif
+
+#if CIRCUITPY_USB_VENDOR
+bool tud_vendor_connected(void);
+#endif
+
+#if defined(CIRCUITPY_DEBUG_UART_TX)
+STATIC void debug_uart_print_strn(void *env, const char *str, size_t len) {
+ (void)env;
+ int uart_errcode;
+ common_hal_busio_uart_write(&debug_uart, (const uint8_t *)str, len, &uart_errcode);
+}
+
+const mp_print_t debug_uart_print = {NULL, debug_uart_print_strn};
+#endif
+
+int debug_uart_printf(const char *fmt, ...) {
+ #if defined(CIRCUITPY_DEBUG_UART_TX)
+ // Skip prints that occur before debug serial is started. It's better than
+ // crashing.
+ if (common_hal_busio_uart_deinited(&debug_uart)) {
+ return 0;
+ }
+ va_list ap;
+ va_start(ap, fmt);
+ int ret = mp_vprintf(&debug_uart_print, fmt, ap);
+ va_end(ap);
+ return ret;
+ #else
+ return 0;
+ #endif
+}
+
+MP_WEAK void port_serial_init(void) {
+}
+
+MP_WEAK bool port_serial_connected(void) {
+ return false;
+}
+
+MP_WEAK char port_serial_read(void) {
+ return -1;
+}
+
+MP_WEAK bool port_serial_bytes_available(void) {
+ return false;
+}
+
+MP_WEAK void port_serial_write_substring(const char *text, uint32_t length) {
+ (void)text;
+ (void)length;
+}
+
+void serial_early_init(void) {
+ #if defined(CIRCUITPY_DEBUG_UART_TX) || defined(CIRCUITPY_DEBUG_UART_RX)
+ debug_uart.base.type = &busio_uart_type;
+
+ #if defined(CIRCUITPY_DEBUG_UART_RX)
+ const mcu_pin_obj_t *rx = MP_OBJ_TO_PTR(CIRCUITPY_DEBUG_UART_RX);
+ #else
+ const mcu_pin_obj_t *rx = NULL;
+ #endif
+
+ #if defined(CIRCUITPY_DEBUG_UART_TX)
+ const mcu_pin_obj_t *tx = MP_OBJ_TO_PTR(CIRCUITPY_DEBUG_UART_TX);
+ #else
+ const mcu_pin_obj_t *tx = NULL;
+ #endif
+
+ common_hal_busio_uart_construct(&debug_uart, tx, rx, NULL, NULL, NULL,
+ false, 115200, 8, BUSIO_UART_PARITY_NONE, 1, 1.0f, 64,
+ buf_array, true);
+ common_hal_busio_uart_never_reset(&debug_uart);
+
+ // Do an initial print so that we can confirm the serial output is working.
+ debug_uart_printf("Serial debug setup\r\n");
+ #endif
+}
+
+void serial_init(void) {
+ port_serial_init();
+}
+
+bool serial_connected(void) {
+ #if CIRCUITPY_USB_VENDOR
+ if (tud_vendor_connected()) {
+ return true;
+ }
+ #endif
+
+ #if defined(CIRCUITPY_DEBUG_UART_TX) && defined(CIRCUITPY_DEBUG_UART_RX)
+ return true;
+ #endif
+
+ #if CIRCUITPY_SERIAL_BLE
+ if (ble_serial_connected()) {
+ return true;
+ }
+ #endif
+
+ #if CIRCUITPY_USB_CDC
+ if (usb_cdc_console_enabled() && tud_cdc_connected()) {
+ return true;
+ }
+ #elif CIRCUITPY_USB
+ if (tud_cdc_connected()) {
+ return true;
+ }
+ #endif
+
+ if (port_serial_connected()) {
+ return true;
+ }
+ return false;
+}
+
+char serial_read(void) {
+ #if CIRCUITPY_USB_VENDOR
+ if (tud_vendor_connected() && tud_vendor_available() > 0) {
+ char tiny_buffer;
+ tud_vendor_read(&tiny_buffer, 1);
+ return tiny_buffer;
+ }
+ #endif
+
+ #if defined(CIRCUITPY_DEBUG_UART_RX)
+ if (common_hal_busio_uart_rx_characters_available(&debug_uart)) {
+ int uart_errcode;
+ char text;
+ common_hal_busio_uart_read(&debug_uart, (uint8_t *)&text, 1, &uart_errcode);
+ return text;
+ }
+ #endif
+
+ #if CIRCUITPY_SERIAL_BLE
+ if (ble_serial_available() > 0) {
+ return ble_serial_read_char();
+ }
+ #endif
+
+ #if CIRCUITPY_USB_CDC
+ if (!usb_cdc_console_enabled()) {
+ return -1;
+ }
+ #endif
+ #if CIRCUITPY_USB
+ return (char)tud_cdc_read_char();
+ #endif
+
+ if (port_serial_bytes_available() > 0) {
+ return port_serial_read();
+ }
+ return -1;
+}
+
+bool serial_bytes_available(void) {
+ #if CIRCUITPY_USB_VENDOR
+ if (tud_vendor_connected() && tud_vendor_available() > 0) {
+ return true;
+ }
+ #endif
+
+ #if defined(CIRCUITPY_DEBUG_UART_RX)
+ if (common_hal_busio_uart_rx_characters_available(&debug_uart)) {
+ return true;
+ }
+ #endif
+
+ #if CIRCUITPY_SERIAL_BLE
+ if (ble_serial_available()) {
+ return true;
+ }
+ #endif
+
+ #if CIRCUITPY_USB_CDC
+ if (usb_cdc_console_enabled() && tud_cdc_available() > 0) {
+ return true;
+ }
+ #endif
+ #if CIRCUITPY_USB
+ if (tud_cdc_available() > 0) {
+ return true;
+ }
+ #endif
+
+ if (port_serial_bytes_available() > 0) {
+ return true;
+ }
+ return false;
+}
+
+void serial_write_substring(const char *text, uint32_t length) {
+ if (length == 0) {
+ return;
+ }
+ #if CIRCUITPY_TERMINALIO
+ int errcode;
+ common_hal_terminalio_terminal_write(&supervisor_terminal, (const uint8_t *)text, length, &errcode);
+ #endif
+
+ #if CIRCUITPY_USB_VENDOR
+ if (tud_vendor_connected()) {
+ tud_vendor_write(text, length);
+ }
+ #endif
+
+ #if defined(CIRCUITPY_DEBUG_UART_TX)
+ int uart_errcode;
+
+ common_hal_busio_uart_write(&debug_uart, (const uint8_t *)text, length, &uart_errcode);
+ #endif
+
+ #if CIRCUITPY_SERIAL_BLE
+ ble_serial_write(text, length);
+ #endif
+
+ #if CIRCUITPY_USB_CDC
+ if (!usb_cdc_console_enabled()) {
+ return;
+ }
+ #endif
+
+ #if CIRCUITPY_USB
+ uint32_t count = 0;
+ while (count < length && tud_cdc_connected()) {
+ count += tud_cdc_write(text + count, length - count);
+ // If we're in an interrupt, then don't wait for more room. Queue up what we can.
+ if (cpu_interrupt_active()) {
+ break;
+ }
+ usb_background();
+ }
+ #endif
+
+ port_serial_write_substring(text, length);
+}
+
+void serial_write(const char *text) {
+ serial_write_substring(text, strlen(text));
+}
diff --git a/circuitpython/supervisor/shared/stack.c b/circuitpython/supervisor/shared/stack.c
new file mode 100644
index 0000000..d2188e0
--- /dev/null
+++ b/circuitpython/supervisor/shared/stack.c
@@ -0,0 +1,112 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2018 Scott Shawcroft 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 "stack.h"
+
+#include "py/mpconfig.h"
+#include "py/runtime.h"
+#include "supervisor/cpu.h"
+#include "supervisor/port.h"
+#include "supervisor/shared/safe_mode.h"
+
+extern uint32_t _estack;
+
+// Requested size.
+static uint32_t next_stack_size = CIRCUITPY_DEFAULT_STACK_SIZE;
+static uint32_t current_stack_size = 0;
+// Actual location and size, may be larger than requested.
+static uint32_t *stack_limit = NULL;
+static size_t stack_length = 0;
+
+#define EXCEPTION_STACK_SIZE 1024
+
+static void allocate_stack(void) {
+
+ if (port_has_fixed_stack()) {
+ stack_limit = port_stack_get_limit();
+ stack_length = (port_stack_get_top() - stack_limit) * sizeof(uint32_t);
+ current_stack_size = stack_length;
+ } else {
+ mp_uint_t regs[10];
+ mp_uint_t sp = cpu_get_regs_and_sp(regs);
+
+ mp_uint_t c_size = (mp_uint_t)port_stack_get_top() - sp;
+ supervisor_allocation *stack_alloc = allocate_memory(c_size + next_stack_size + EXCEPTION_STACK_SIZE, true, false);
+ if (stack_alloc == NULL) {
+ stack_alloc = allocate_memory(c_size + CIRCUITPY_DEFAULT_STACK_SIZE + EXCEPTION_STACK_SIZE, true, false);
+ current_stack_size = CIRCUITPY_DEFAULT_STACK_SIZE;
+ } else {
+ current_stack_size = next_stack_size;
+ }
+ stack_limit = stack_alloc->ptr;
+ stack_length = get_allocation_length(stack_alloc);
+ }
+
+ *stack_limit = STACK_CANARY_VALUE;
+}
+
+inline bool stack_ok(void) {
+ return stack_limit == NULL || *stack_limit == STACK_CANARY_VALUE;
+}
+
+inline void assert_heap_ok(void) {
+ if (!stack_ok()) {
+ reset_into_safe_mode(HEAP_OVERWRITTEN);
+ }
+}
+
+void stack_init(void) {
+ allocate_stack();
+}
+
+void stack_resize(void) {
+ if (stack_limit == NULL) {
+ return;
+ }
+ if (next_stack_size == current_stack_size) {
+ *stack_limit = STACK_CANARY_VALUE;
+ return;
+ }
+ free_memory(allocation_from_ptr(stack_limit));
+ stack_limit = NULL;
+ allocate_stack();
+}
+
+uint32_t *stack_get_bottom(void) {
+ return stack_limit;
+}
+
+size_t stack_get_length(void) {
+ return stack_length;
+}
+
+void set_next_stack_size(uint32_t size) {
+ next_stack_size = size;
+}
+
+uint32_t get_current_stack_size(void) {
+ return current_stack_size;
+}
diff --git a/circuitpython/supervisor/shared/stack.h b/circuitpython/supervisor/shared/stack.h
new file mode 100644
index 0000000..98cc5a1
--- /dev/null
+++ b/circuitpython/supervisor/shared/stack.h
@@ -0,0 +1,52 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2018 Scott Shawcroft 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_SUPERVISOR_STACK_H
+#define MICROPY_INCLUDED_SUPERVISOR_STACK_H
+
+#include <stddef.h>
+
+#include "supervisor/memory.h"
+
+void stack_init(void);
+void stack_resize(void);
+// Actual stack location and size, may be larger than requested.
+uint32_t *stack_get_bottom(void);
+size_t stack_get_length(void);
+// Next/current requested stack size.
+void set_next_stack_size(uint32_t size);
+uint32_t get_current_stack_size(void);
+bool stack_ok(void);
+
+// Use this after any calls into a library which may use a lot of stack. This will raise a Python
+// exception when the stack has likely overwritten a portion of the heap.
+void assert_heap_ok(void);
+
+#ifndef STACK_CANARY_VALUE
+#define STACK_CANARY_VALUE 0x017829ef
+#endif
+
+#endif // MICROPY_INCLUDED_SUPERVISOR_STACK_H
diff --git a/circuitpython/supervisor/shared/status_leds.c b/circuitpython/supervisor/shared/status_leds.c
new file mode 100644
index 0000000..07ff963
--- /dev/null
+++ b/circuitpython/supervisor/shared/status_leds.c
@@ -0,0 +1,366 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2017-2021 Scott Shawcroft 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 "supervisor/shared/status_leds.h"
+
+#include <string.h>
+
+#include "mphalport.h"
+#include "shared-bindings/microcontroller/Pin.h"
+#include "supervisor/shared/tick.h"
+#include "py/obj.h"
+
+
+#ifdef CIRCUITPY_STATUS_LED_POWER
+#include "shared-bindings/digitalio/DigitalInOut.h"
+static digitalio_digitalinout_obj_t _status_power;
+#endif
+
+#ifdef MICROPY_HW_NEOPIXEL
+uint8_t rgb_status_brightness = 63;
+#include "shared-bindings/digitalio/DigitalInOut.h"
+#include "shared-bindings/neopixel_write/__init__.h"
+
+#ifndef MICROPY_HW_NEOPIXEL_COUNT
+#define MICROPY_HW_NEOPIXEL_COUNT (1)
+#endif
+
+static uint64_t next_start_raw_ticks;
+static uint8_t status_neopixel_color[3 * MICROPY_HW_NEOPIXEL_COUNT];
+static digitalio_digitalinout_obj_t status_neopixel;
+
+#elif defined(MICROPY_HW_APA102_MOSI) && defined(MICROPY_HW_APA102_SCK)
+uint8_t rgb_status_brightness = 50;
+
+#ifndef MICROPY_HW_APA102_COUNT
+#define MICROPY_HW_APA102_COUNT (1)
+#endif
+
+ #define APA102_BUFFER_LENGTH (4 + 4 * MICROPY_HW_APA102_COUNT + 4)
+static uint8_t status_apa102_color[APA102_BUFFER_LENGTH];
+
+ #if CIRCUITPY_BITBANG_APA102
+ #include "shared-bindings/bitbangio/SPI.h"
+static bitbangio_spi_obj_t status_apa102 = {
+ .base = {
+ .type = &bitbangio_spi_type,
+ },
+};
+ #else
+ #include "shared-bindings/busio/SPI.h"
+busio_spi_obj_t status_apa102 = {
+ .base = {
+ .type = &busio_spi_type,
+ },
+};
+ #endif
+
+#elif CIRCUITPY_PWM_RGB_LED
+ #include "shared-bindings/pwmio/PWMOut.h"
+ #include "shared-bindings/microcontroller/Pin.h"
+
+pwmio_pwmout_obj_t rgb_status_r = {
+ .base = {
+ .type = &pwmio_pwmout_type,
+ },
+};
+pwmio_pwmout_obj_t rgb_status_g = {
+ .base = {
+ .type = &pwmio_pwmout_type,
+ },
+};
+pwmio_pwmout_obj_t rgb_status_b = {
+ .base = {
+ .type = &pwmio_pwmout_type,
+ },
+};
+
+uint8_t rgb_status_brightness = 0xFF;
+
+uint16_t status_rgb_color[3] = {
+ 0 /* red */, 0 /* green */, 0 /* blue */
+};
+#elif CIRCUITPY_DIGITALIO && defined(MICROPY_HW_LED_STATUS)
+#include "shared-bindings/digitalio/DigitalInOut.h"
+digitalio_digitalinout_obj_t single_color_led;
+
+uint8_t rgb_status_brightness = 0xff;
+
+#ifndef MICROPY_HW_LED_STATUS_INVERTED
+#define MICROPY_HW_LED_STATUS_INVERTED (0)
+#endif
+
+#endif
+
+#if CIRCUITPY_DIGITALIO && (defined(MICROPY_HW_LED_RX) || defined(MICROPY_HW_LED_TX))
+#include "shared-bindings/digitalio/DigitalInOut.h"
+
+#ifdef MICROPY_HW_LED_RX
+digitalio_digitalinout_obj_t rx_led;
+#endif
+
+#ifdef MICROPY_HW_LED_TX
+digitalio_digitalinout_obj_t tx_led;
+#endif
+#endif
+
+#if CIRCUITPY_STATUS_LED
+static uint32_t current_status_color = 0;
+#endif
+
+static bool status_led_init_in_progress = false;
+void status_led_init() {
+ if (status_led_init_in_progress) {
+ // Avoid recursion.
+ return;
+ }
+ status_led_init_in_progress = true;
+
+ #ifdef CIRCUITPY_STATUS_LED_POWER
+ common_hal_digitalio_digitalinout_construct(&_status_power, CIRCUITPY_STATUS_LED_POWER);
+ common_hal_digitalio_digitalinout_switch_to_output(&_status_power,
+ CIRCUITPY_STATUS_LED_POWER_INVERTED == 0, DRIVE_MODE_PUSH_PULL);
+ #endif
+
+ #ifdef MICROPY_HW_NEOPIXEL
+ common_hal_digitalio_digitalinout_construct(&status_neopixel, MICROPY_HW_NEOPIXEL);
+ common_hal_digitalio_digitalinout_switch_to_output(&status_neopixel, false, DRIVE_MODE_PUSH_PULL);
+ #elif defined(MICROPY_HW_APA102_MOSI) && defined(MICROPY_HW_APA102_SCK)
+ // Set every byte to 0xff except the start 4 bytes that make up the header.
+ memset(status_apa102_color + 4, 0xff, APA102_BUFFER_LENGTH - 4);
+ #if CIRCUITPY_BITBANG_APA102
+ shared_module_bitbangio_spi_construct(&status_apa102,
+ MICROPY_HW_APA102_SCK,
+ MICROPY_HW_APA102_MOSI,
+ NULL);
+ #else
+ if (!common_hal_busio_spi_deinited(&status_apa102)) {
+ common_hal_busio_spi_deinit(&status_apa102);
+ }
+ common_hal_busio_spi_construct(&status_apa102,
+ MICROPY_HW_APA102_SCK,
+ MICROPY_HW_APA102_MOSI,
+ NULL,
+ false);
+ #endif
+ #if CIRCUITPY_BITBANG_APA102
+ shared_module_bitbangio_spi_try_lock(&status_apa102);
+ // Use 1MHz for clock rate. Some APA102's are spec'd 800kHz-1200kHz,
+ // though many can run much faster. bitbang will probably run slower.
+ shared_module_bitbangio_spi_configure(&status_apa102, 1000000, 0, 0, 8);
+ #else
+ common_hal_busio_spi_try_lock(&status_apa102);
+ common_hal_busio_spi_configure(&status_apa102, 1000000, 0, 0, 8);
+ #endif
+
+
+ #elif CIRCUITPY_PWM_RGB_LED
+ if (common_hal_mcu_pin_is_free(CIRCUITPY_RGB_STATUS_R)) {
+ pwmout_result_t red_result = common_hal_pwmio_pwmout_construct(&rgb_status_r, CIRCUITPY_RGB_STATUS_R, 0, 50000, false);
+
+ if (PWMOUT_OK == red_result) {
+ common_hal_pwmio_pwmout_never_reset(&rgb_status_r);
+ }
+ }
+
+ if (common_hal_mcu_pin_is_free(CIRCUITPY_RGB_STATUS_G)) {
+ pwmout_result_t green_result = common_hal_pwmio_pwmout_construct(&rgb_status_g, CIRCUITPY_RGB_STATUS_G, 0, 50000, false);
+
+ if (PWMOUT_OK == green_result) {
+ common_hal_pwmio_pwmout_never_reset(&rgb_status_g);
+ }
+ }
+
+ if (common_hal_mcu_pin_is_free(CIRCUITPY_RGB_STATUS_B)) {
+ pwmout_result_t blue_result = common_hal_pwmio_pwmout_construct(&rgb_status_b, CIRCUITPY_RGB_STATUS_B, 0, 50000, false);
+
+ if (PWMOUT_OK == blue_result) {
+ common_hal_pwmio_pwmout_never_reset(&rgb_status_b);
+ }
+ }
+
+ #elif defined(MICROPY_HW_LED_STATUS)
+ common_hal_digitalio_digitalinout_construct(&single_color_led, MICROPY_HW_LED_STATUS);
+ common_hal_digitalio_digitalinout_switch_to_output(
+ &single_color_led, MICROPY_HW_LED_STATUS_INVERTED == 0, DRIVE_MODE_PUSH_PULL);
+ #endif
+
+ #if CIRCUITPY_DIGITALIO && CIRCUITPY_STATUS_LED
+ // Force a write of the current status color.
+ uint32_t rgb = current_status_color;
+ current_status_color = 0x1000000; // Not a valid color
+ new_status_color(rgb);
+ #endif
+
+ status_led_init_in_progress = false;
+}
+
+void status_led_deinit() {
+ #ifdef MICROPY_HW_NEOPIXEL
+ // Make sure the pin stays low for the reset period. The pin reset may pull
+ // it up and stop the reset period.
+ while (port_get_raw_ticks(NULL) < next_start_raw_ticks) {
+ }
+ common_hal_reset_pin(MICROPY_HW_NEOPIXEL);
+
+ #elif defined(MICROPY_HW_APA102_MOSI) && defined(MICROPY_HW_APA102_SCK)
+ #if CIRCUITPY_BITBANG_APA102
+ shared_module_bitbangio_spi_deinit(&status_apa102);
+ #else
+ common_hal_busio_spi_deinit(&status_apa102);
+ #endif
+
+ #elif CIRCUITPY_PWM_RGB_LED
+ if (!common_hal_mcu_pin_is_free(CIRCUITPY_RGB_STATUS_R)) {
+ common_hal_pwmio_pwmout_deinit(&rgb_status_r);
+ }
+
+ if (!common_hal_mcu_pin_is_free(CIRCUITPY_RGB_STATUS_G)) {
+ common_hal_pwmio_pwmout_deinit(&rgb_status_g);
+ }
+
+ if (!common_hal_mcu_pin_is_free(CIRCUITPY_RGB_STATUS_B)) {
+ common_hal_pwmio_pwmout_deinit(&rgb_status_b);
+ }
+
+ #elif defined(MICROPY_HW_LED_STATUS)
+ common_hal_digitalio_digitalinout_deinit(&single_color_led);
+ #endif
+
+ #ifdef CIRCUITPY_STATUS_LED_POWER
+ common_hal_digitalio_digitalinout_deinit(&_status_power);
+ #endif
+}
+
+void new_status_color(uint32_t rgb) {
+ #if CIRCUITPY_STATUS_LED
+ if (current_status_color == rgb) {
+ return;
+ }
+ uint32_t rgb_adjusted = color_brightness(rgb, rgb_status_brightness);
+ current_status_color = rgb;
+ #endif
+
+ #ifdef MICROPY_HW_NEOPIXEL
+ for (size_t i = 0; i < MICROPY_HW_NEOPIXEL_COUNT; i++) {
+ status_neopixel_color[3 * i + 0] = (rgb_adjusted >> 8) & 0xff;
+ status_neopixel_color[3 * i + 1] = (rgb_adjusted >> 16) & 0xff;
+ status_neopixel_color[3 * i + 2] = rgb_adjusted & 0xff;
+ }
+ common_hal_neopixel_write(&status_neopixel, status_neopixel_color, 3 * MICROPY_HW_NEOPIXEL_COUNT);
+ next_start_raw_ticks = port_get_raw_ticks(NULL) + 2;
+ #elif defined(MICROPY_HW_APA102_MOSI) && defined(MICROPY_HW_APA102_SCK)
+ for (size_t i = 0; i < MICROPY_HW_APA102_COUNT; i++) {
+ // Skip 4 + offset to skip the header bytes too.
+ status_apa102_color[4 * i + 4 + 1] = rgb_adjusted & 0xff;
+ status_apa102_color[4 * i + 4 + 2] = (rgb_adjusted >> 8) & 0xff;
+ status_apa102_color[4 * i + 4 + 3] = (rgb_adjusted >> 16) & 0xff;
+ }
+
+ #if CIRCUITPY_BITBANG_APA102
+ shared_module_bitbangio_spi_write(&status_apa102, status_apa102_color, APA102_BUFFER_LENGTH);
+ #else
+ common_hal_busio_spi_write(&status_apa102, status_apa102_color, APA102_BUFFER_LENGTH);
+ #endif
+
+ #elif CIRCUITPY_PWM_RGB_LED
+ uint8_t red_u8 = (rgb_adjusted >> 16) & 0xFF;
+ uint8_t green_u8 = (rgb_adjusted >> 8) & 0xFF;
+ uint8_t blue_u8 = rgb_adjusted & 0xFF;
+
+ #ifdef CIRCUITPY_RGB_STATUS_INVERTED_PWM
+ status_rgb_color[0] = (1 << 16) - 1 - ((uint16_t)(red_u8 << 8) + red_u8);
+ status_rgb_color[1] = (1 << 16) - 1 - ((uint16_t)(green_u8 << 8) + green_u8);
+ status_rgb_color[2] = (1 << 16) - 1 - ((uint16_t)(blue_u8 << 8) + blue_u8);
+ #else
+ status_rgb_color[0] = (uint16_t)(red_u8 << 8) + red_u8;
+ status_rgb_color[1] = (uint16_t)(green_u8 << 8) + green_u8;
+ status_rgb_color[2] = (uint16_t)(blue_u8 << 8) + blue_u8;
+ #endif
+
+ common_hal_pwmio_pwmout_set_duty_cycle(&rgb_status_r, status_rgb_color[0]);
+ common_hal_pwmio_pwmout_set_duty_cycle(&rgb_status_g, status_rgb_color[1]);
+ common_hal_pwmio_pwmout_set_duty_cycle(&rgb_status_b, status_rgb_color[2]);
+ #elif CIRCUITPY_DIGITALIO && defined(MICROPY_HW_LED_STATUS)
+ common_hal_digitalio_digitalinout_set_value(
+ &single_color_led, (rgb_adjusted > 0) ^ MICROPY_HW_LED_STATUS_INVERTED);
+ #endif
+}
+
+uint32_t color_brightness(uint32_t color, uint8_t brightness) {
+ #if CIRCUITPY_STATUS_LED
+ uint32_t result = ((color & 0xff0000) * brightness / 255) & 0xff0000;
+ result += ((color & 0xff00) * brightness / 255) & 0xff00;
+ result += ((color & 0xff) * brightness / 255) & 0xff;
+ return result;
+ #else
+ return color;
+ #endif
+}
+
+void set_status_brightness(uint8_t level) {
+ #if CIRCUITPY_STATUS_LED
+ rgb_status_brightness = level;
+ // This is only called by user code and we're never controlling the status
+ // LED when user code is running. So, we don't need to update the current
+ // state (there is none.)
+ #endif
+}
+
+void init_rxtx_leds(void) {
+ #if CIRCUITPY_DIGITALIO && defined(MICROPY_HW_LED_RX)
+ common_hal_digitalio_digitalinout_construct(&rx_led, MICROPY_HW_LED_RX);
+ common_hal_digitalio_digitalinout_switch_to_output(&rx_led, true, DRIVE_MODE_PUSH_PULL);
+ common_hal_digitalio_digitalinout_never_reset(&rx_led);
+ #endif
+ #if CIRCUITPY_DIGITALIO && defined(MICROPY_HW_LED_TX)
+ common_hal_digitalio_digitalinout_construct(&tx_led, MICROPY_HW_LED_TX);
+ common_hal_digitalio_digitalinout_switch_to_output(&tx_led, true, DRIVE_MODE_PUSH_PULL);
+ common_hal_digitalio_digitalinout_never_reset(&tx_led);
+ #endif
+}
+
+void deinit_rxtx_leds(void) {
+ #if CIRCUITPY_DIGITALIO && defined(MICROPY_HW_LED_RX)
+ common_hal_digitalio_digitalinout_deinit(&rx_led);
+ #endif
+ #if CIRCUITPY_DIGITALIO && defined(MICROPY_HW_LED_TX)
+ common_hal_digitalio_digitalinout_deinit(&tx_led);
+ #endif
+}
+
+void toggle_rx_led(void) {
+ #if CIRCUITPY_DIGITALIO && defined(MICROPY_HW_LED_RX)
+ common_hal_digitalio_digitalinout_set_value(&rx_led, !common_hal_digitalio_digitalinout_get_value(&rx_led));
+ #endif
+}
+
+
+void toggle_tx_led(void) {
+ #if CIRCUITPY_DIGITALIO && defined(MICROPY_HW_LED_TX)
+ common_hal_digitalio_digitalinout_set_value(&tx_led, !common_hal_digitalio_digitalinout_get_value(&tx_led));
+ #endif
+}
diff --git a/circuitpython/supervisor/shared/status_leds.h b/circuitpython/supervisor/shared/status_leds.h
new file mode 100644
index 0000000..99aa027
--- /dev/null
+++ b/circuitpython/supervisor/shared/status_leds.h
@@ -0,0 +1,62 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2018 Scott Shawcroft 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_SUPERVISOR_STATUS_LEDS_H
+#define MICROPY_INCLUDED_SUPERVISOR_STATUS_LEDS_H
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#include "shared/runtime/pyexec.h"
+#include "supervisor/port.h"
+
+#include "py/mpconfig.h"
+#include "rgb_led_colors.h"
+
+#include "supervisor/shared/safe_mode.h"
+
+// Overall, the time module must be implemented.
+// To work with a DotStar, one must have MICROPY_HW_APA102_SCK and
+// MICROPY_HW_APA102_MOSI defined and bitbangio.SPI or busio.SPI implemented.
+// To work with a NeoPixel, one must have MICROPY_HW_NEOPIXEL defined and
+// neopixel_write implemented.
+
+#define CIRCUITPY_PWM_RGB_LED (defined(CIRCUITPY_RGB_STATUS_R) || defined(CIRCUITPY_RGB_STATUS_G) || defined(CIRCUITPY_RGB_STATUS_B))
+#define CIRCUITPY_STATUS_LED ((CIRCUITPY_DIGITALIO && defined(MICROPY_HW_LED_STATUS)) || defined(MICROPY_HW_NEOPIXEL) || (defined(MICROPY_HW_APA102_MOSI) && defined(MICROPY_HW_APA102_SCK)) || CIRCUITPY_PWM_RGB_LED)
+
+void status_led_init(void);
+void status_led_deinit(void);
+void new_status_color(uint32_t rgb);
+
+uint32_t color_brightness(uint32_t color, uint8_t brightness);
+void set_status_brightness(uint8_t level);
+
+void init_rxtx_leds(void);
+void deinit_rxtx_leds(void);
+void toggle_rx_led(void);
+void toggle_tx_led(void);
+
+#endif // MICROPY_INCLUDED_SUPERVISOR_STATUS_LEDS_H
diff --git a/circuitpython/supervisor/shared/tick.c b/circuitpython/supervisor/shared/tick.c
new file mode 100644
index 0000000..104083f
--- /dev/null
+++ b/circuitpython/supervisor/shared/tick.c
@@ -0,0 +1,178 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Jeff Epler 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 "supervisor/shared/tick.h"
+
+#include "shared/runtime/interrupt_char.h"
+#include "py/mphal.h"
+#include "py/mpstate.h"
+#include "py/runtime.h"
+#include "supervisor/linker.h"
+#include "supervisor/filesystem.h"
+#include "supervisor/background_callback.h"
+#include "supervisor/port.h"
+#include "supervisor/shared/stack.h"
+
+#if CIRCUITPY_BLEIO_HCI
+#include "common-hal/_bleio/__init__.h"
+#endif
+
+#if CIRCUITPY_DISPLAYIO
+#include "shared-module/displayio/__init__.h"
+#endif
+
+#if CIRCUITPY_GAMEPADSHIFT
+#include "shared-module/gamepadshift/__init__.h"
+#endif
+
+#if CIRCUITPY_KEYPAD
+#include "shared-module/keypad/__init__.h"
+#endif
+
+#include "shared-bindings/microcontroller/__init__.h"
+
+#if CIRCUITPY_WATCHDOG
+#include "shared-bindings/watchdog/__init__.h"
+#define WATCHDOG_EXCEPTION_CHECK() (MP_STATE_VM(mp_pending_exception) == &mp_watchdog_timeout_exception)
+#else
+#define WATCHDOG_EXCEPTION_CHECK() 0
+#endif
+
+static volatile uint64_t PLACE_IN_DTCM_BSS(background_ticks);
+
+static background_callback_t tick_callback;
+
+static volatile uint64_t last_finished_tick = 0;
+
+static volatile size_t tick_enable_count = 0;
+
+static void supervisor_background_tasks(void *unused) {
+ port_start_background_task();
+
+ assert_heap_ok();
+
+ #if CIRCUITPY_BLEIO_HCI
+ bleio_hci_background();
+ #endif
+
+ #if CIRCUITPY_DISPLAYIO
+ displayio_background();
+ #endif
+
+ filesystem_background();
+
+ port_background_task();
+
+ assert_heap_ok();
+
+ last_finished_tick = port_get_raw_ticks(NULL);
+
+ port_finish_background_task();
+}
+
+bool supervisor_background_tasks_ok(void) {
+ return port_get_raw_ticks(NULL) - last_finished_tick < 1024;
+}
+
+void supervisor_tick(void) {
+ #if CIRCUITPY_FILESYSTEM_FLUSH_INTERVAL_MS > 0
+ filesystem_tick();
+ #endif
+
+ #ifdef CIRCUITPY_GAMEPAD_TICKS
+ if (!(port_get_raw_ticks(NULL) & CIRCUITPY_GAMEPAD_TICKS)) {
+ #if CIRCUITPY_GAMEPADSHIFT
+ gamepadshift_tick();
+ #endif
+ }
+ #endif
+
+ #if CIRCUITPY_KEYPAD
+ keypad_tick();
+ #endif
+
+ background_callback_add(&tick_callback, supervisor_background_tasks, NULL);
+}
+
+uint64_t supervisor_ticks_ms64() {
+ uint64_t result;
+ result = port_get_raw_ticks(NULL);
+ result = result * 1000 / 1024;
+ return result;
+}
+
+uint32_t supervisor_ticks_ms32() {
+ return supervisor_ticks_ms64();
+}
+
+
+void PLACE_IN_ITCM(supervisor_run_background_tasks_if_tick)() {
+ background_callback_run_all();
+}
+
+void mp_hal_delay_ms(mp_uint_t delay_ms) {
+ uint64_t start_tick = port_get_raw_ticks(NULL);
+ // Adjust the delay to ticks vs ms.
+ uint64_t delay_ticks = (delay_ms * (uint64_t)1024) / 1000;
+ uint64_t end_tick = start_tick + delay_ticks;
+ int64_t remaining = delay_ticks;
+
+ // Loop until we've waited long enough or we've been CTRL-Ced by autoreload
+ // or the user.
+ while (remaining > 0 && !mp_hal_is_interrupted()) {
+ RUN_BACKGROUND_TASKS;
+ remaining = end_tick - port_get_raw_ticks(NULL);
+ // We break a bit early so we don't risk setting the alarm before the time when we call
+ // sleep.
+ if (remaining < 1) {
+ break;
+ }
+ port_interrupt_after_ticks(remaining);
+ // Idle until an interrupt happens.
+ port_idle_until_interrupt();
+ remaining = end_tick - port_get_raw_ticks(NULL);
+ }
+}
+
+void supervisor_enable_tick(void) {
+ common_hal_mcu_disable_interrupts();
+ if (tick_enable_count == 0) {
+ port_enable_tick();
+ }
+ tick_enable_count++;
+ common_hal_mcu_enable_interrupts();
+}
+
+void supervisor_disable_tick(void) {
+ common_hal_mcu_disable_interrupts();
+ if (tick_enable_count > 0) {
+ tick_enable_count--;
+ }
+ if (tick_enable_count == 0) {
+ port_disable_tick();
+ }
+ common_hal_mcu_enable_interrupts();
+}
diff --git a/circuitpython/supervisor/shared/tick.h b/circuitpython/supervisor/shared/tick.h
new file mode 100644
index 0000000..d805aeb
--- /dev/null
+++ b/circuitpython/supervisor/shared/tick.h
@@ -0,0 +1,75 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Jeff Epler 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 __INCLUDED_SUPERVISOR_TICK_H
+#define __INCLUDED_SUPERVISOR_TICK_H
+
+#include <stdint.h>
+#include <stdbool.h>
+
+/** @brief To be called once every ms
+ *
+ * The port must call supervisor_tick once per millisecond to perform regular tasks.
+ * This is called from the SysTick interrupt or similar, and is safe to call in an
+ * interrupt context.
+ */
+extern void supervisor_tick(void);
+
+/** @brief Get the lower 32 bits of the time in milliseconds
+ *
+ * This can be more efficient than supervisor_ticks_ms64, for sites where a wraparound
+ * of ~49.5 days is not harmful.
+ */
+extern uint32_t supervisor_ticks_ms32(void);
+
+/** @brief Get the full time in milliseconds
+ *
+ * Because common ARM mcus cannot atomically work with 64-bit quantities, this
+ * function must briefly disable interrupts in order to return the value. If
+ * only relative durations of less than about ~49.5 days need to be considered,
+ * then it may be possible to use supervisor_ticks_ms32() instead.
+ */
+extern uint64_t supervisor_ticks_ms64(void);
+
+/** @brief Run background ticks, but only about every millisecond.
+ *
+ * Normally, this is not called directly. Instead use the RUN_BACKGROUND_TASKS
+ * macro.
+ */
+extern void supervisor_run_background_if_tick(void);
+
+extern void supervisor_enable_tick(void);
+extern void supervisor_disable_tick(void);
+
+/**
+ * @brief Return true if tick-based background tasks ran within the last 1s
+ *
+ * Note that when ticks are not enabled, this function can return false; this is
+ * intended.
+ */
+extern bool supervisor_background_tasks_ok(void);
+
+#endif
diff --git a/circuitpython/supervisor/shared/traceback.c b/circuitpython/supervisor/shared/traceback.c
new file mode 100644
index 0000000..4413794
--- /dev/null
+++ b/circuitpython/supervisor/shared/traceback.c
@@ -0,0 +1,29 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 Christian Walther
+ *
+ * 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 "traceback.h"
+
+supervisor_allocation *prev_traceback_allocation;
diff --git a/circuitpython/supervisor/shared/traceback.h b/circuitpython/supervisor/shared/traceback.h
new file mode 100644
index 0000000..dd3c72c
--- /dev/null
+++ b/circuitpython/supervisor/shared/traceback.h
@@ -0,0 +1,34 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 Christian Walther
+ *
+ * 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_SUPERVISOR_TRACEBACK_H
+#define MICROPY_INCLUDED_SUPERVISOR_TRACEBACK_H
+
+#include "supervisor/memory.h"
+
+extern supervisor_allocation *prev_traceback_allocation;
+
+#endif // MICROPY_INCLUDED_SUPERVISOR_TRACEBACK_H
diff --git a/circuitpython/supervisor/shared/translate.c b/circuitpython/supervisor/shared/translate.c
new file mode 100644
index 0000000..fefda46
--- /dev/null
+++ b/circuitpython/supervisor/shared/translate.c
@@ -0,0 +1,152 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2018 Scott Shawcroft 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 "supervisor/shared/translate.h"
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+
+#ifndef NO_QSTR
+#include "genhdr/compression.generated.h"
+#endif
+
+#include "py/misc.h"
+#include "py/mpprint.h"
+#include "supervisor/serial.h"
+
+void serial_write_compressed(const compressed_string_t *compressed) {
+ mp_printf(MP_PYTHON_PRINTER, "%S", compressed);
+}
+
+STATIC void get_word(int n, const mchar_t **pos, const mchar_t **end) {
+ int len = minlen;
+ int i = 0;
+ *pos = words;
+ while (wlencount[i] <= n) {
+ n -= wlencount[i];
+ *pos += len * wlencount[i];
+ i++;
+ len++;
+ }
+ *pos += len * n;
+ *end = *pos + len;
+}
+
+STATIC int put_utf8(char *buf, int u) {
+ if (u <= 0x7f) {
+ *buf = u;
+ return 1;
+ } else if (word_start <= u && u <= word_end) {
+ uint n = (u - word_start);
+ const mchar_t *pos, *end;
+ get_word(n, &pos, &end);
+ int ret = 0;
+ // note that at present, entries in the words table are
+ // guaranteed not to represent words themselves, so this adds
+ // at most 1 level of recursive call
+ for (; pos < end; pos++) {
+ int len = put_utf8(buf, *pos);
+ buf += len;
+ ret += len;
+ }
+ return ret;
+ } else if (u <= 0x07ff) {
+ *buf++ = 0b11000000 | (u >> 6);
+ *buf = 0b10000000 | (u & 0b00111111);
+ return 2;
+ } else { // u <= 0xffff
+ *buf++ = 0b11100000 | (u >> 12);
+ *buf++ = 0b10000000 | ((u >> 6) & 0b00111111);
+ *buf = 0b10000000 | (u & 0b00111111);
+ return 3;
+ }
+}
+
+uint16_t decompress_length(const compressed_string_t *compressed) {
+ #ifndef NO_QSTR
+ #if (compress_max_length_bits <= 8)
+ return 1 + (compressed->data >> (8 - compress_max_length_bits));
+ #else
+ return 1 + ((compressed->data * 256 + compressed->tail[0]) >> (16 - compress_max_length_bits));
+ #endif
+ #endif
+}
+
+char *decompress(const compressed_string_t *compressed, char *decompressed) {
+ uint8_t this_byte = compress_max_length_bits / 8;
+ uint8_t this_bit = 7 - compress_max_length_bits % 8;
+ uint8_t b = (&compressed->data)[this_byte] << (compress_max_length_bits % 8);
+ uint16_t length = decompress_length(compressed);
+
+ // Stop one early because the last byte is always NULL.
+ for (uint16_t i = 0; i < length - 1;) {
+ uint32_t bits = 0;
+ uint8_t bit_length = 0;
+ uint32_t max_code = lengths[0];
+ uint32_t searched_length = lengths[0];
+ while (true) {
+ bits <<= 1;
+ if ((0x80 & b) != 0) {
+ bits |= 1;
+ }
+ b <<= 1;
+ bit_length += 1;
+ if (this_bit == 0) {
+ this_bit = 7;
+ this_byte += 1;
+ b = (&compressed->data)[this_byte]; // This may read past the end but its never used.
+ } else {
+ this_bit -= 1;
+ }
+ if (max_code > 0 && bits < max_code) {
+ break;
+ }
+ max_code = (max_code << 1) + lengths[bit_length];
+ searched_length += lengths[bit_length];
+ }
+ i += put_utf8(decompressed + i, values[searched_length + bits - max_code]);
+ }
+
+ decompressed[length - 1] = '\0';
+ return decompressed;
+}
+
+inline
+// gcc10 -flto has issues with this being always_inline for debug builds.
+#if CIRCUITPY_DEBUG < 1
+__attribute__((always_inline))
+#endif
+const compressed_string_t *translate(const char *original) {
+ #ifndef NO_QSTR
+ #define QDEF(id, hash, len, str)
+ #define TRANSLATION(id, firstbyte, ...) if (strcmp(original, id) == 0) { static const compressed_string_t v = { .data = firstbyte, .tail = { __VA_ARGS__ } }; return &v; } else
+ #include "genhdr/qstrdefs.generated.h"
+#undef TRANSLATION
+#undef QDEF
+ #endif
+ return NULL;
+}
diff --git a/circuitpython/supervisor/shared/translate.h b/circuitpython/supervisor/shared/translate.h
new file mode 100644
index 0000000..da58e1e
--- /dev/null
+++ b/circuitpython/supervisor/shared/translate.h
@@ -0,0 +1,93 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2018 Scott Shawcroft 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_SUPERVISOR_TRANSLATE_H
+#define MICROPY_INCLUDED_SUPERVISOR_TRANSLATE_H
+
+#include <stdint.h>
+
+// The format of the compressed data is:
+// - the size of the uncompressed string in UTF-8 bytes, encoded as a
+// (compress_max_length_bits)-bit number. compress_max_length_bits is
+// computed during dictionary generation time, and happens to be 8
+// for all current platforms. However, it'll probably end up being
+// 9 in some translations sometime in the future. This length excludes
+// the trailing NUL, though notably decompress_length includes it.
+//
+// - followed by the huffman encoding of the individual UTF-16 code
+// points that make up the string. The trailing "\0" is not
+// represented by a huffman code, but is implied by the length.
+// (building the huffman encoding on UTF-16 code points gave better
+// compression than building it on UTF-8 bytes)
+//
+// - code points starting at 128 (word_start) and potentially extending
+// to 255 (word_end) (but never interfering with the target
+// language's used code points) stand for dictionary entries in a
+// dictionary with size up to 256 code points. The dictionary entries
+// are computed with a heuristic based on frequent substrings of 2 to
+// 9 code points. These are called "words" but are not, grammatically
+// speaking, words. They're just spans of code points that frequently
+// occur together. They are ordered shortest to longest.
+//
+// - dictionary entries are non-overlapping, and the _ending_ index of each
+// entry is stored in an array. A count of words of each length, from
+// minlen to maxlen, is given in the array called wlencount. From
+// this small array, the start and end of the N'th word can be
+// calculated by an efficient, small loop. (A bit of time is traded
+// to reduce the size of this table indicating lengths)
+//
+// The "data" / "tail" construct is so that the struct's last member is a
+// "flexible array". However, the _only_ member is not permitted to be
+// a flexible member, so we have to declare the first byte as a separte
+// member of the structure.
+//
+// For translations where length needs 8 bits, this saves about 1.5
+// bytes per string on average compared to a structure of {uint16_t,
+// flexible array}, but is also future-proofed against strings with
+// UTF-8 length above 256, with a savings of about 1.375 bytes per
+// string.
+typedef struct compressed_string {
+ uint8_t data;
+ const uint8_t tail[];
+} compressed_string_t;
+
+// Return the compressed, translated version of a source string
+// Usually, due to LTO, this is optimized into a load of a constant
+// pointer.
+const compressed_string_t *translate(const char *c);
+void serial_write_compressed(const compressed_string_t *compressed);
+char *decompress(const compressed_string_t *compressed, char *decompressed);
+uint16_t decompress_length(const compressed_string_t *compressed);
+
+
+// Map MicroPython's error messages to our translations.
+#if defined(MICROPY_ENABLE_DYNRUNTIME) && MICROPY_ENABLE_DYNRUNTIME
+#define MP_ERROR_TEXT(x) (x)
+#else
+#define MP_ERROR_TEXT(x) translate(x)
+#endif
+
+#endif // MICROPY_INCLUDED_SUPERVISOR_TRANSLATE_H
diff --git a/circuitpython/supervisor/shared/usb/tusb_config.h b/circuitpython/supervisor/shared/usb/tusb_config.h
new file mode 100644
index 0000000..13b4367
--- /dev/null
+++ b/circuitpython/supervisor/shared/usb/tusb_config.h
@@ -0,0 +1,167 @@
+/**************************************************************************/
+/*!
+ @file tusb_config.h
+ @author hathach (tinyusb.org)
+
+ @section LICENSE
+
+ Software License Agreement (BSD License)
+
+ Copyright (c) 2013, hathach (tinyusb.org)
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are met:
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. Neither the name of the copyright holders nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
+ EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
+ DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION HOWEVER CAUSED AND
+ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ INCLUDING NEGLIGENCE OR OTHERWISE ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+*/
+/**************************************************************************/
+
+#ifndef _TUSB_CONFIG_H_
+#define _TUSB_CONFIG_H_
+
+#include "py/mpconfig.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// --------------------------------------------------------------------+
+// COMMON CONFIGURATION
+// --------------------------------------------------------------------+
+
+// When debugging TinyUSB, only output to the UART debug link.
+#if CIRCUITPY_DEBUG_TINYUSB > 0 && defined(CIRCUITPY_DEBUG_UART_TX)
+#define CFG_TUSB_DEBUG CIRCUITPY_DEBUG_TINYUSB
+#define CFG_TUSB_DEBUG_PRINTF debug_uart_printf
+#endif
+
+/*------------- RTOS -------------*/
+#ifndef CFG_TUSB_OS
+#define CFG_TUSB_OS OPT_OS_NONE
+#endif
+// #define CFG_TUD_TASK_QUEUE_SZ 16
+
+// --------------------------------------------------------------------+
+// DEVICE CONFIGURATION
+// --------------------------------------------------------------------+
+
+#if CIRCUITPY_USB_DEVICE_INSTANCE == 0
+#if USB_HIGHSPEED
+#define CFG_TUSB_RHPORT0_MODE (OPT_MODE_DEVICE | OPT_MODE_HIGH_SPEED)
+#else
+#define CFG_TUSB_RHPORT0_MODE (OPT_MODE_DEVICE)
+#endif
+#elif CIRCUITPY_USB_DEVICE_INSTANCE == 1
+#if USB_HIGHSPEED
+#define CFG_TUSB_RHPORT1_MODE (OPT_MODE_DEVICE | OPT_MODE_HIGH_SPEED)
+#else
+#define CFG_TUSB_RHPORT1_MODE (OPT_MODE_DEVICE)
+#endif
+#endif
+
+// Vendor name included in Inquiry response, max 8 bytes
+#define CFG_TUD_MSC_VENDOR USB_MANUFACTURER_8
+
+// Product name included in Inquiry response, max 16 bytes
+#define CFG_TUD_MSC_PRODUCT USB_PRODUCT_16
+#define CFG_TUD_ENDPOINT0_SIZE 64
+
+// ------------- CLASS -------------//
+
+// Will be set to 2 in supervisor.mk if CIRCUITPY_USB_CDC is set.
+#ifndef CFG_TUD_CDC
+#define CFG_TUD_CDC 1
+#endif
+
+#define CFG_TUD_MSC CIRCUITPY_USB_MSC
+#define CFG_TUD_HID CIRCUITPY_USB_HID
+#define CFG_TUD_MIDI CIRCUITPY_USB_MIDI
+#define CFG_TUD_VENDOR CIRCUITPY_USB_VENDOR
+#define CFG_TUD_CUSTOM_CLASS 0
+
+/*------------------------------------------------------------------*/
+/* CLASS DRIVER
+ *------------------------------------------------------------------*/
+
+// Product revision string included in Inquiry response, max 4 bytes
+#define CFG_TUD_MSC_PRODUCT_REV "1.0"
+
+
+// --------------------------------------------------------------------+
+// USB RAM PLACEMENT
+// --------------------------------------------------------------------+
+#if !defined(CIRCUITPY_TUSB_ATTR_USBRAM)
+#define CIRCUITPY_TUSB_ATTR_USBRAM ".bss.usbram"
+#endif
+
+#define CFG_TUSB_ATTR_USBRAM __attribute__((section(CIRCUITPY_TUSB_ATTR_USBRAM)))
+
+
+#if !defined(CIRCUITPY_TUSB_MEM_ALIGN)
+#define CIRCUITPY_TUSB_MEM_ALIGN 4
+#endif
+
+#define CFG_TUSB_MEM_ALIGN __attribute__ ((aligned(CIRCUITPY_TUSB_MEM_ALIGN)))
+
+// --------------------------------------------------------------------
+// HOST CONFIGURATION
+// --------------------------------------------------------------------
+
+#if CIRCUITPY_USB_HOST
+
+#if CIRCUITPY_USB_HOST_INSTANCE == 0
+#if USB_HIGHSPEED
+#define CFG_TUSB_RHPORT0_MODE (OPT_MODE_HOST | OPT_MODE_HIGH_SPEED)
+#else
+#define CFG_TUSB_RHPORT0_MODE (OPT_MODE_HOST)
+#endif
+#elif CIRCUITPY_USB_HOST_INSTANCE == 1
+#if USB_HIGHSPEED
+#define CFG_TUSB_RHPORT1_MODE (OPT_MODE_HOST | OPT_MODE_HIGH_SPEED)
+#else
+#define CFG_TUSB_RHPORT1_MODE (OPT_MODE_HOST)
+#endif
+#endif
+
+// Size of buffer to hold descriptors and other data used for enumeration
+#ifndef CFG_TUH_ENUMERATION_BUFSIZE
+#define CFG_TUH_ENUMERATION_BUFSIZE 256
+#endif
+
+#define CFG_TUH_HUB 1
+#define CFG_TUH_CDC 0
+#define CFG_TUH_MSC 0
+#define CFG_TUH_VENDOR 0
+
+// max device support (excluding hub device)
+#define CFG_TUH_DEVICE_MAX (CFG_TUH_HUB ? 4 : 1) // hub typically has 4 ports
+
+// Number of endpoints per device
+#define CFG_TUH_ENDPOINT_MAX 8
+
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _TUSB_CONFIG_H_ */
diff --git a/circuitpython/supervisor/shared/usb/usb.c b/circuitpython/supervisor/shared/usb/usb.c
new file mode 100644
index 0000000..a188544
--- /dev/null
+++ b/circuitpython/supervisor/shared/usb/usb.c
@@ -0,0 +1,321 @@
+/*
+ * This file is part of the Micro Python 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 "py/objstr.h"
+#include "py/runtime.h"
+#include "shared-bindings/microcontroller/Processor.h"
+#include "supervisor/background_callback.h"
+#include "supervisor/port.h"
+#include "supervisor/serial.h"
+#include "supervisor/usb.h"
+#include "supervisor/shared/workflow.h"
+#include "shared/runtime/interrupt_char.h"
+#include "shared/readline/readline.h"
+
+#if CIRCUITPY_STORAGE
+#include "shared-module/storage/__init__.h"
+#endif
+
+#if CIRCUITPY_USB_CDC
+#include "shared-module/usb_cdc/__init__.h"
+#endif
+
+#if CIRCUITPY_USB_HID
+#include "shared-module/usb_hid/__init__.h"
+#endif
+
+#if CIRCUITPY_USB_MIDI
+#include "shared-module/usb_midi/__init__.h"
+#endif
+
+#include "tusb.h"
+
+#if CIRCUITPY_USB_VENDOR
+#include "usb_vendor_descriptors.h"
+
+// The WebUSB support being conditionally added to this file is based on the
+// tinyusb demo examples/device/webusb_serial.
+
+static bool web_serial_connected = false;
+
+#define URL "www.tinyusb.org/examples/webusb-serial"
+
+const tusb_desc_webusb_url_t desc_webusb_url =
+{
+ .bLength = 3 + sizeof(URL) - 1,
+ .bDescriptorType = 3, // WEBUSB URL type
+ .bScheme = 1, // 0: http, 1: https
+ .url = URL
+};
+
+#endif
+
+bool usb_enabled(void) {
+ return tusb_inited();
+}
+
+MP_WEAK void post_usb_init(void) {
+}
+
+void usb_init(void) {
+ init_usb_hardware();
+
+ tusb_init();
+
+ post_usb_init();
+
+ #if MICROPY_KBD_EXCEPTION && CIRCUITPY_USB_CDC
+ // Set Ctrl+C as wanted char, tud_cdc_rx_wanted_cb() usb_callback will be invoked when Ctrl+C is received
+ // This usb_callback always got invoked regardless of mp_interrupt_char value since we only set it once here
+
+ // Don't watch for ctrl-C if there is no REPL.
+ if (usb_cdc_console_enabled()) {
+ // Console will always be itf 0.
+ tud_cdc_set_wanted_char(CHAR_CTRL_C);
+ }
+ #endif
+}
+
+// Set up USB defaults before any USB changes are made in boot.py
+void usb_set_defaults(void) {
+ #if CIRCUITPY_STORAGE && CIRCUITPY_USB_MSC
+ storage_usb_set_defaults();
+ #endif
+
+ #if CIRCUITPY_USB_CDC
+ usb_cdc_set_defaults();
+ #endif
+
+ #if CIRCUITPY_USB_HID
+ usb_hid_set_defaults();
+ #endif
+
+ #if CIRCUITPY_USB_MIDI
+ usb_midi_set_defaults();
+ #endif
+};
+
+// 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;
+
+ #if CIRCUITPY_USB_HID
+ size += usb_hid_report_descriptor_length();
+ #endif
+
+ return size;
+}
+
+// Fill in the data to save.
+void usb_get_boot_py_data(uint8_t *temp_storage, size_t temp_storage_size) {
+ #if CIRCUITPY_USB_HID
+ usb_hid_build_report_descriptor(temp_storage, temp_storage_size);
+ #endif
+}
+
+// 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) {
+ #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();
+}
+
+// Call this when ready to run code.py or a REPL, and a VM has been started.
+void usb_setup_with_vm(void) {
+ #if CIRCUITPY_USB_HID
+ usb_hid_setup_devices();
+ #endif
+
+ #if CIRCUITPY_USB_MIDI
+ usb_midi_setup_ports();
+ #endif
+}
+
+void usb_disconnect(void) {
+ tud_disconnect();
+}
+
+void usb_background(void) {
+ if (usb_enabled()) {
+ #if CFG_TUSB_OS == OPT_OS_NONE
+ tud_task();
+ #if CIRCUITPY_USB_HOST
+ tuh_task();
+ #endif
+ #endif
+ // No need to flush if there's no REPL.
+ #if CIRCUITPY_USB_CDC
+ if (usb_cdc_console_enabled()) {
+ // Console will always be itf 0.
+ tud_cdc_write_flush();
+ }
+ #endif
+ }
+}
+
+static background_callback_t usb_callback;
+static void usb_background_do(void *unused) {
+ usb_background();
+}
+
+void usb_background_schedule(void) {
+ background_callback_add(&usb_callback, usb_background_do, NULL);
+}
+
+void usb_irq_handler(int instance) {
+ if (instance == CIRCUITPY_USB_DEVICE_INSTANCE) {
+ tud_int_handler(instance);
+ } else if (instance == CIRCUITPY_USB_HOST_INSTANCE) {
+ #if CIRCUITPY_USB_HOST
+ tuh_int_handler(instance);
+ #endif
+ }
+
+ usb_background_schedule();
+}
+
+// --------------------------------------------------------------------+
+// tinyusb callbacks
+// --------------------------------------------------------------------+
+
+// Invoked when device is mounted
+void tud_mount_cb(void) {
+ #if CIRCUITPY_USB_MSC
+ usb_msc_mount();
+ #endif
+}
+
+// Invoked when device is unmounted
+void tud_umount_cb(void) {
+ #if CIRCUITPY_USB_MSC
+ usb_msc_umount();
+ #endif
+}
+
+// Invoked when usb bus is suspended
+// remote_wakeup_en : if host allows us to perform remote wakeup
+// USB Specs: Within 7ms, device must draw an average current less than 2.5 mA from bus
+void tud_suspend_cb(bool remote_wakeup_en) {
+}
+
+// Invoked when usb bus is resumed
+void tud_resume_cb(void) {
+}
+
+// Invoked when cdc when line state changed e.g connected/disconnected
+// Use to reset to DFU when disconnect with 1200 bps
+void tud_cdc_line_state_cb(uint8_t itf, bool dtr, bool rts) {
+ (void)itf; // interface ID, not used
+
+ // DTR = false is counted as disconnected
+ if (!dtr) {
+ cdc_line_coding_t coding;
+ // Use whichever CDC is itf 0.
+ tud_cdc_get_line_coding(&coding);
+
+ if (coding.bit_rate == 1200) {
+ reset_to_bootloader();
+ }
+ }
+}
+
+#if CIRCUITPY_USB_VENDOR
+// --------------------------------------------------------------------+
+// WebUSB use vendor class
+// --------------------------------------------------------------------+
+
+bool tud_vendor_connected(void) {
+ return web_serial_connected;
+}
+
+// Invoked when a control transfer occurred on an interface of this class
+// Driver response accordingly to the request and the transfer stage (setup/data/ack)
+// return false to stall control endpoint (e.g unsupported request)
+bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request) {
+ // nothing to with DATA & ACK stage
+ if (stage != CONTROL_STAGE_SETUP) {
+ return true;
+ }
+
+ switch (request->bRequest)
+ {
+ case VENDOR_REQUEST_WEBUSB:
+ // match vendor request in BOS descriptor
+ // Get landing page url
+ return tud_control_xfer(rhport, request, (void *)&desc_webusb_url, desc_webusb_url.bLength);
+
+ case VENDOR_REQUEST_MICROSOFT:
+ if (request->wIndex == 7) {
+ // Get Microsoft OS 2.0 compatible descriptor
+ // let's just hope the target architecture always has the same endianness
+ uint16_t total_len;
+ memcpy(&total_len, vendor_ms_os_20_descriptor() + 8, 2);
+
+ return tud_control_xfer(rhport, request, (void *)vendor_ms_os_20_descriptor(), total_len);
+ } else {
+ return false;
+ }
+
+ case 0x22:
+ // Webserial simulate the CDC_REQUEST_SET_CONTROL_LINE_STATE (0x22) to
+ // connect and disconnect.
+ web_serial_connected = (request->wValue != 0);
+
+ // response with status OK
+ return tud_control_status(rhport, request);
+
+ default:
+ // stall unknown request
+ return false;
+ }
+
+ return true;
+}
+#endif // CIRCUITPY_USB_VENDOR
+
+
+#if MICROPY_KBD_EXCEPTION
+
+/**
+ * Callback invoked when received an "wanted" char.
+ * @param itf Interface index (for multiple cdc interfaces)
+ * @param wanted_char The wanted char (set previously)
+ */
+
+// Only called when console is enabled.
+void tud_cdc_rx_wanted_cb(uint8_t itf, char wanted_char) {
+ // Workaround for using shared/runtime/interrupt_char.c
+ // Compare mp_interrupt_char with wanted_char and ignore if not matched
+ if (mp_interrupt_char == wanted_char) {
+ tud_cdc_n_read_flush(itf); // flush read fifo
+ mp_sched_keyboard_interrupt();
+ }
+}
+
+#endif
diff --git a/circuitpython/supervisor/shared/usb/usb_desc.c b/circuitpython/supervisor/shared/usb/usb_desc.c
new file mode 100644
index 0000000..9fe1ead
--- /dev/null
+++ b/circuitpython/supervisor/shared/usb/usb_desc.c
@@ -0,0 +1,365 @@
+/*
+ * 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 "lib/tinyusb/src/tusb.h"
+
+#include "py/objstr.h"
+#include "py/runtime.h"
+#include "supervisor/memory.h"
+#include "supervisor/shared/safe_mode.h"
+#include "supervisor/usb.h"
+
+#if CIRCUITPY_USB_CDC
+#include "shared-bindings/usb_cdc/__init__.h"
+#endif
+
+#if CIRCUITPY_USB_HID
+#include "shared-bindings/usb_hid/__init__.h"
+#endif
+
+#if CIRCUITPY_USB_MIDI
+#include "shared-bindings/usb_midi/__init__.h"
+#endif
+
+#if CIRCUITPY_USB_MSC && CIRCUITPY_STORAGE
+#include "shared-bindings/storage/__init__.h"
+#endif
+
+#include "shared-bindings/microcontroller/Processor.h"
+
+
+// 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
+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;
+
+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];
+
+
+static const uint8_t device_descriptor_template[] = {
+ 0x12, // 0 bLength
+ 0x01, // 1 bDescriptorType (Device)
+ 0x00, 0x02, // 2,3 bcdUSB 2.00
+ 0x00, // 4 bDeviceClass (Use class information in the Interface Descriptors)
+ 0x00, // 5 bDeviceSubClass
+ 0x00, // 6 bDeviceProtocol
+ 0x40, // 7 bMaxPacketSize0 64
+ 0xFF, 0xFF, // 8,9 idVendor [SET AT RUNTIME: lo,hi]
+#define DEVICE_VID_LO_INDEX (8)
+#define DEVICE_VID_HI_INDEX (9)
+ 0xFF, 0xFF, // 10,11 idProduct [SET AT RUNTIME: lo,hi]
+#define DEVICE_PID_LO_INDEX (10)
+#define DEVICE_PID_HI_INDEX (11)
+ 0x00, 0x01, // 12,13 bcdDevice 2.00
+ 0xFF, // 14 iManufacturer (String Index) [SET AT RUNTIME]
+#define DEVICE_MANUFACTURER_STRING_INDEX (14)
+ 0xFF, // 15 iProduct (String Index) [SET AT RUNTIME]
+#define DEVICE_PRODUCT_STRING_INDEX (15)
+ 0xFF, // 16 iSerialNumber (String Index) [SET AT RUNTIME]
+#define DEVICE_SERIAL_NUMBER_STRING_INDEX (16)
+ 0x01, // 17 bNumConfigurations 1
+};
+
+static const uint8_t configuration_descriptor_template[] = {
+ 0x09, // 0 bLength
+ 0x02, // 1 bDescriptorType (Configuration)
+ 0xFF, 0xFF, // 2,3 wTotalLength [SET AT RUNTIME: lo, hi]
+#define CONFIG_TOTAL_LENGTH_LO_INDEX (2)
+#define CONFIG_TOTAL_LENGTH_HI_INDEX (3)
+ 0xFF, // 4 bNumInterfaces [SET AT RUNTIME]
+#define CONFIG_NUM_INTERFACES_INDEX (4)
+ 0x01, // 5 bConfigurationValue
+ 0x00, // 6 iConfiguration (String Index)
+ 0x80, // 7 bmAttributes
+ 0x32, // 8 bMaxPower 100mA
+};
+
+static void usb_build_device_descriptor(uint16_t vid, uint16_t pid) {
+ 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;
+
+ usb_add_interface_string(current_interface_string, manufacturer_name);
+ device_descriptor[DEVICE_MANUFACTURER_STRING_INDEX] = current_interface_string;
+ current_interface_string++;
+
+ usb_add_interface_string(current_interface_string, product_name);
+ device_descriptor[DEVICE_PRODUCT_STRING_INDEX] = current_interface_string;
+ current_interface_string++;
+
+ usb_add_interface_string(current_interface_string, serial_number_hex_string);
+ device_descriptor[DEVICE_SERIAL_NUMBER_STRING_INDEX] = current_interface_string;
+ current_interface_string++;
+}
+
+static void usb_build_configuration_descriptor(void) {
+ size_t total_descriptor_length = sizeof(configuration_descriptor_template);
+
+ // CDC should be first, for compatibility with Adafruit Windows 7 drivers.
+ // In the past, the order has been CDC, MSC, MIDI, HID, so preserve that order.
+ #if CIRCUITPY_USB_CDC
+ if (usb_cdc_console_enabled()) {
+ total_descriptor_length += usb_cdc_descriptor_length();
+ }
+ if (usb_cdc_data_enabled()) {
+ total_descriptor_length += usb_cdc_descriptor_length();
+ }
+ #endif
+
+ #if CIRCUITPY_USB_MSC
+ if (storage_usb_enabled()) {
+ total_descriptor_length += storage_usb_descriptor_length();
+ }
+ #endif
+
+ #if CIRCUITPY_USB_HID
+ if (usb_hid_enabled()) {
+ total_descriptor_length += usb_hid_descriptor_length();
+ }
+ #endif
+
+ #if CIRCUITPY_USB_MIDI
+ if (usb_midi_enabled()) {
+ total_descriptor_length += usb_midi_descriptor_length();
+ }
+ #endif
+
+ #if CIRCUITPY_USB_VENDOR
+ if (usb_vendor_enabled()) {
+ total_descriptor_length += usb_vendor_descriptor_length();
+ }
+ #endif
+
+
+ // Now we now how big the configuration descriptor will be, so we can allocate space for it.
+ configuration_descriptor_allocation =
+ allocate_memory(align32_size(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.
+
+ memcpy(configuration_descriptor, configuration_descriptor_template, sizeof(configuration_descriptor_template));
+
+ configuration_descriptor[CONFIG_TOTAL_LENGTH_LO_INDEX] = total_descriptor_length & 0xFF;
+ configuration_descriptor[CONFIG_TOTAL_LENGTH_HI_INDEX] = (total_descriptor_length >> 8) & 0xFF;
+
+ // Number interfaces and endpoints.
+ // Endpoint 0 is already used for USB control,
+ // so start with 1 for the current endpoint and for the number of in and out endpoints
+ // already in use.
+
+ descriptor_counts_t descriptor_counts = {
+ .current_interface = 0,
+ .current_endpoint = 1,
+ .num_in_endpoints = 1,
+ .num_out_endpoints = 1,
+ };
+
+ uint8_t *descriptor_buf_remaining = configuration_descriptor + sizeof(configuration_descriptor_template);
+
+ #if CIRCUITPY_USB_CDC
+ if (usb_cdc_console_enabled()) {
+ // Concatenate and fix up the CDC REPL descriptor.
+ descriptor_buf_remaining += usb_cdc_add_descriptor(
+ descriptor_buf_remaining, &descriptor_counts, &current_interface_string, true /*console*/);
+
+ }
+ if (usb_cdc_data_enabled()) {
+ // Concatenate and fix up the CDC data descriptor.
+ descriptor_buf_remaining += usb_cdc_add_descriptor(
+ descriptor_buf_remaining, &descriptor_counts, &current_interface_string, false /*console*/);
+ }
+ #endif
+
+ #if CIRCUITPY_USB_MSC
+ if (storage_usb_enabled()) {
+ // Concatenate and fix up the MSC descriptor.
+ descriptor_buf_remaining += storage_usb_add_descriptor(
+ descriptor_buf_remaining, &descriptor_counts, &current_interface_string);
+ }
+ #endif
+
+ #if CIRCUITPY_USB_HID
+ if (usb_hid_enabled()) {
+ if (usb_hid_boot_device() > 0 && descriptor_counts.current_interface > 0) {
+ // Hosts using boot devices generally to expect them to be at interface zero,
+ // and will not work properly otherwise.
+ reset_into_safe_mode(USB_BOOT_DEVICE_NOT_INTERFACE_ZERO);
+ }
+ descriptor_buf_remaining += usb_hid_add_descriptor(
+ descriptor_buf_remaining, &descriptor_counts, &current_interface_string,
+ usb_hid_report_descriptor_length(), usb_hid_boot_device());
+ }
+ #endif
+
+ #if CIRCUITPY_USB_MIDI
+ if (usb_midi_enabled()) {
+ // Concatenate and fix up the MIDI descriptor.
+ descriptor_buf_remaining += usb_midi_add_descriptor(
+ descriptor_buf_remaining, &descriptor_counts, &current_interface_string);
+ }
+ #endif
+
+ #if CIRCUITPY_USB_VENDOR
+ if (usb_vendor_enabled()) {
+ descriptor_buf_remaining += usb_vendor_add_descriptor(
+ descriptor_buf_remaining, &descriptor_counts, &current_interface_string);
+ }
+ #endif
+
+ // Now we know how many interfaces have been used.
+ configuration_descriptor[CONFIG_NUM_INTERFACES_INDEX] = descriptor_counts.current_interface;
+
+ // Did we run out of endpoints?
+ if (descriptor_counts.current_endpoint > USB_NUM_ENDPOINT_PAIRS ||
+ descriptor_counts.num_in_endpoints > USB_NUM_IN_ENDPOINTS ||
+ descriptor_counts.num_out_endpoints > USB_NUM_OUT_ENDPOINTS) {
+ reset_into_safe_mode(USB_TOO_MANY_ENDPOINTS);
+ }
+}
+
+// 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) {
+ reset_into_safe_mode(USB_TOO_MANY_INTERFACE_NAMES);
+ }
+
+ collected_interface_strings[interface_string_index].char_str = str;
+ collected_interface_strings_length += strlen(str);
+}
+
+static const uint16_t language_id[] = {
+ 0x0304,
+ 0x0409,
+};
+
+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(align32_size(current_interface_string * 2 + collected_interface_strings_length * 2),
+ /*high_address*/ false, /*movable*/ false);
+ uint16_t *string_descriptors = (uint16_t *)string_descriptors_allocation->ptr;
+
+
+ uint16_t *string_descriptor = string_descriptors;
+
+ // Language ID is always the 0th string descriptor.
+ collected_interface_strings[0].descriptor = language_id;
+
+ // 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);
+ // 1 word for descriptor type and length, 1 word for each character.
+ const uint8_t descriptor_size_words = 1 + str_len;
+ const uint8_t descriptor_size_bytes = descriptor_size_words * 2;
+ string_descriptor[0] = 0x0300 | descriptor_size_bytes;
+
+ // 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_words;
+ }
+}
+
+// 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) {
+ uint8_t raw_id[COMMON_HAL_MCU_PROCESSOR_UID_LENGTH];
+ common_hal_mcu_processor_get_uid(raw_id);
+
+ for (int i = 0; i < COMMON_HAL_MCU_PROCESSOR_UID_LENGTH; i++) {
+ for (int j = 0; j < 2; j++) {
+ uint8_t nibble = (raw_id[i] >> (j * 4)) & 0xf;
+ serial_number_hex_string[i * 2 + (1 - j)] = nibble_to_hex_upper[nibble];
+ }
+ }
+
+ // Null-terminate the string.
+ 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();
+}
+
+// Invoked when GET DEVICE DESCRIPTOR is received.
+// Application return pointer to descriptor
+uint8_t const *tud_descriptor_device_cb(void) {
+ return (uint8_t *)device_descriptor_allocation->ptr;
+}
+
+// Invoked when GET CONFIGURATION DESCRIPTOR is received.
+// Application return pointer to descriptor
+// 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 (uint8_t *)configuration_descriptor_allocation->ptr;
+}
+
+// Invoked when GET STRING DESCRIPTOR request is received.
+// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete
+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].descriptor;
+}
diff --git a/circuitpython/supervisor/shared/usb/usb_msc_flash.c b/circuitpython/supervisor/shared/usb/usb_msc_flash.c
new file mode 100644
index 0000000..f183033
--- /dev/null
+++ b/circuitpython/supervisor/shared/usb/usb_msc_flash.c
@@ -0,0 +1,287 @@
+/*
+ * 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 "tusb.h"
+// // #include "supervisor/flash.h"
+
+// For updating fatfs's cache
+#include "extmod/vfs.h"
+#include "extmod/vfs_fat.h"
+#include "lib/oofatfs/diskio.h"
+#include "lib/oofatfs/ff.h"
+#include "py/mpstate.h"
+
+#include "shared-module/storage/__init__.h"
+#include "supervisor/filesystem.h"
+#include "supervisor/shared/reload.h"
+
+#define MSC_FLASH_BLOCK_SIZE 512
+
+static bool ejected[1] = {true};
+
+// Lock to track if something else is using the filesystem when USB is plugged in. If so, the drive
+// will be made available once the lock is released.
+static bool _usb_msc_lock = false;
+static bool _usb_connected_while_locked = false;
+
+STATIC void _usb_msc_uneject(void) {
+ for (uint8_t i = 0; i < sizeof(ejected); i++) {
+ ejected[i] = false;
+ }
+}
+
+void usb_msc_mount(void) {
+ // Reset the ejection tracking every time we're plugged into USB. This allows for us to battery
+ // power the device, eject, unplug and plug it back in to get the drive.
+ if (_usb_msc_lock) {
+ _usb_connected_while_locked = true;
+ return;
+ }
+ _usb_msc_uneject();
+ _usb_connected_while_locked = false;
+}
+
+void usb_msc_umount(void) {
+}
+
+bool usb_msc_ejected(void) {
+ bool all_ejected = true;
+ for (uint8_t i = 0; i < sizeof(ejected); i++) {
+ all_ejected &= ejected[i];
+ }
+ return all_ejected;
+}
+
+bool usb_msc_lock(void) {
+ if ((storage_usb_enabled() && !usb_msc_ejected()) || _usb_msc_lock) {
+ return false;
+ }
+ _usb_msc_lock = true;
+ return true;
+}
+
+void usb_msc_unlock(void) {
+ if (!_usb_msc_lock) {
+ // Mismatched unlock.
+ return;
+ }
+ if (_usb_connected_while_locked) {
+ _usb_msc_uneject();
+ }
+ _usb_msc_lock = false;
+}
+
+// The root FS is always at the end of the list.
+static fs_user_mount_t *get_vfs(int lun) {
+ // TODO(tannewt): Return the mount which matches the lun where 0 is the end
+ // and is counted in reverse.
+ if (lun > 0) {
+ return NULL;
+ }
+ mp_vfs_mount_t *current_mount = MP_STATE_VM(vfs_mount_table);
+ if (current_mount == NULL) {
+ return NULL;
+ }
+ while (current_mount->next != NULL) {
+ current_mount = current_mount->next;
+ }
+ return current_mount->obj;
+}
+
+// Callback invoked when received an SCSI command not in built-in list below
+// - READ_CAPACITY10, READ_FORMAT_CAPACITY, INQUIRY, TEST_UNIT_READY, START_STOP_UNIT, MODE_SENSE6, REQUEST_SENSE
+// - READ10 and WRITE10 have their own callbacks
+int32_t tud_msc_scsi_cb(uint8_t lun, const uint8_t scsi_cmd[16], void *buffer, uint16_t bufsize) {
+ const void *response = NULL;
+ int32_t resplen = 0;
+
+ switch (scsi_cmd[0]) {
+ case SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL:
+ // Host is about to read/write etc ... better not to disconnect disk
+ resplen = 0;
+ break;
+
+ default:
+ // Set Sense = Invalid Command Operation
+ tud_msc_set_sense(lun, SCSI_SENSE_ILLEGAL_REQUEST, 0x20, 0x00);
+
+ // negative means error -> tinyusb could stall and/or response with failed status
+ resplen = -1;
+ break;
+ }
+
+ // return len must not larger than bufsize
+ if (resplen > bufsize) {
+ resplen = bufsize;
+ }
+
+ // copy response to stack's buffer if any
+ if (response && (resplen > 0)) {
+ memcpy(buffer, response, resplen);
+ }
+
+ return resplen;
+}
+
+void tud_msc_capacity_cb(uint8_t lun, uint32_t *block_count, uint16_t *block_size) {
+ fs_user_mount_t *vfs = get_vfs(lun);
+ disk_ioctl(vfs, GET_SECTOR_COUNT, block_count);
+ disk_ioctl(vfs, GET_SECTOR_SIZE, block_size);
+}
+
+bool tud_msc_is_writable_cb(uint8_t lun) {
+ if (lun > 1) {
+ return false;
+ }
+
+ fs_user_mount_t *vfs = get_vfs(lun);
+ if (vfs == NULL) {
+ return false;
+ }
+ if (vfs->blockdev.writeblocks[0] == MP_OBJ_NULL || !filesystem_is_writable_by_usb(vfs)) {
+ return false;
+ }
+ return true;
+}
+
+// Callback invoked when received READ10 command.
+// Copy disk's data to buffer (up to bufsize) and return number of copied bytes.
+int32_t tud_msc_read10_cb(uint8_t lun, uint32_t lba, uint32_t offset, void *buffer, uint32_t bufsize) {
+ (void)offset;
+
+ const uint32_t block_count = bufsize / MSC_FLASH_BLOCK_SIZE;
+
+ fs_user_mount_t *vfs = get_vfs(lun);
+ disk_read(vfs, buffer, lba, block_count);
+
+ return block_count * MSC_FLASH_BLOCK_SIZE;
+}
+
+// Callback invoked when received WRITE10 command.
+// Process data in buffer to disk's storage and return number of written bytes
+int32_t tud_msc_write10_cb(uint8_t lun, uint32_t lba, uint32_t offset, uint8_t *buffer, uint32_t bufsize) {
+ (void)lun;
+ (void)offset;
+ autoreload_suspend(AUTORELOAD_SUSPEND_USB);
+
+ const uint32_t block_count = bufsize / MSC_FLASH_BLOCK_SIZE;
+
+ fs_user_mount_t *vfs = get_vfs(lun);
+ disk_write(vfs, buffer, lba, block_count);
+ // Since by getting here we assume the mount is read-only to
+ // MicroPython let's update the cached FatFs sector if it's the one
+ // we just wrote.
+ #if FF_MAX_SS != FF_MIN_SS
+ if (vfs->ssize == MSC_FLASH_BLOCK_SIZE) {
+ #else
+ // The compiler can optimize this away.
+ if (FF_MAX_SS == FILESYSTEM_BLOCK_SIZE) {
+ #endif
+ if (lba == vfs->fatfs.winsect && lba > 0) {
+ memcpy(vfs->fatfs.win,
+ buffer + MSC_FLASH_BLOCK_SIZE * (vfs->fatfs.winsect - lba),
+ MSC_FLASH_BLOCK_SIZE);
+ }
+ }
+
+ return block_count * MSC_FLASH_BLOCK_SIZE;
+}
+
+// Callback invoked when WRITE10 command is completed (status received and accepted by host).
+// used to flush any pending cache.
+void tud_msc_write10_complete_cb(uint8_t lun) {
+ (void)lun;
+
+ // This write is complete; initiate an autoreload.
+ autoreload_resume(AUTORELOAD_SUSPEND_USB);
+ autoreload_trigger();
+}
+
+// Invoked when received SCSI_CMD_INQUIRY
+// Application fill vendor id, product id and revision with string up to 8, 16, 4 characters respectively
+void tud_msc_inquiry_cb(uint8_t lun, uint8_t vendor_id[8], uint8_t product_id[16], uint8_t product_rev[4]) {
+ (void)lun;
+
+ memcpy(vendor_id, CFG_TUD_MSC_VENDOR, strlen(CFG_TUD_MSC_VENDOR));
+ memcpy(product_id, CFG_TUD_MSC_PRODUCT, strlen(CFG_TUD_MSC_PRODUCT));
+ memcpy(product_rev, CFG_TUD_MSC_PRODUCT_REV, strlen(CFG_TUD_MSC_PRODUCT_REV));
+}
+
+// Invoked when received Test Unit Ready command.
+// return true allowing host to read/write this LUN e.g SD card inserted
+bool tud_msc_test_unit_ready_cb(uint8_t lun) {
+ if (lun > 1) {
+ return false;
+ }
+
+ fs_user_mount_t *current_mount = get_vfs(lun);
+ if (current_mount == NULL) {
+ return false;
+ }
+ if (ejected[lun]) {
+ // Set 0x3a for media not present.
+ tud_msc_set_sense(lun, SCSI_SENSE_NOT_READY, 0x3A, 0x00);
+ return false;
+ }
+
+ return true;
+}
+
+// Invoked when received Start Stop Unit command
+// - Start = 0 : stopped power mode, if load_eject = 1 : unload disk storage
+// - Start = 1 : active mode, if load_eject = 1 : load disk storage
+bool tud_msc_start_stop_cb(uint8_t lun, uint8_t power_condition, bool start, bool load_eject) {
+ if (lun > 1) {
+ return false;
+ }
+ fs_user_mount_t *current_mount = get_vfs(lun);
+ if (current_mount == NULL) {
+ return false;
+ }
+ if (load_eject) {
+ if (!start) {
+ // Eject but first flush.
+ if (disk_ioctl(current_mount, CTRL_SYNC, NULL) != RES_OK) {
+ return false;
+ } else {
+ ejected[lun] = true;
+ }
+ } else {
+ // We can only load if it hasn't been ejected.
+ return !ejected[lun];
+ }
+ } else {
+ if (!start) {
+ // Stop the unit but don't eject.
+ if (disk_ioctl(current_mount, CTRL_SYNC, NULL) != RES_OK) {
+ return false;
+ }
+ }
+ // Always start the unit, even if ejected. Whether media is present is a separate check.
+ }
+
+ return true;
+}
diff --git a/circuitpython/supervisor/shared/usb/usb_vendor_descriptors.h b/circuitpython/supervisor/shared/usb/usb_vendor_descriptors.h
new file mode 100644
index 0000000..0b41c09
--- /dev/null
+++ b/circuitpython/supervisor/shared/usb/usb_vendor_descriptors.h
@@ -0,0 +1,39 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Ha Thach (tinyusb.org)
+ *
+ * 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 USB_DESCRIPTORS_H_
+#define USB_DESCRIPTORS_H_
+
+#include <stdint.h>
+
+enum
+{
+ VENDOR_REQUEST_WEBUSB = 1,
+ VENDOR_REQUEST_MICROSOFT = 2
+};
+
+size_t vendor_ms_os_20_descriptor_length(void);
+uint8_t const *vendor_ms_os_20_descriptor(void);
+
+#endif /* USB_DESCRIPTORS_H_ */
diff --git a/circuitpython/supervisor/shared/workflow.c b/circuitpython/supervisor/shared/workflow.c
new file mode 100644
index 0000000..8d2c0f7
--- /dev/null
+++ b/circuitpython/supervisor/shared/workflow.c
@@ -0,0 +1,60 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 Scott Shawcroft 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 <stdbool.h>
+#include "py/mpconfig.h"
+#include "supervisor/workflow.h"
+#include "supervisor/shared/workflow.h"
+
+#if CIRCUITPY_USB
+#include "tusb.h"
+#endif
+
+void supervisor_workflow_reset(void) {
+}
+
+// Return true as soon as USB communication with host has started,
+// even before enumeration is done.
+// Not that some chips don't notice when USB is unplugged after first being plugged in,
+// so this is not perfect, but tud_suspended() check helps.
+bool supervisor_workflow_connecting(void) {
+ #if CIRCUITPY_USB
+ return tud_connected() && !tud_suspended();
+ #else
+ return false;
+ #endif
+}
+
+// Return true if host has completed connection to us (such as USB enumeration).
+bool supervisor_workflow_active(void) {
+ #if CIRCUITPY_USB
+ // Eventually there might be other non-USB workflows, such as BLE.
+ // tud_ready() checks for usb mounted and not suspended.
+ return tud_ready();
+ #else
+ return false;
+ #endif
+}
diff --git a/circuitpython/supervisor/shared/workflow.h b/circuitpython/supervisor/shared/workflow.h
new file mode 100644
index 0000000..b3c817f
--- /dev/null
+++ b/circuitpython/supervisor/shared/workflow.h
@@ -0,0 +1,29 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 Scott Shawcroft 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.
+ */
+
+#pragma once
+
+extern bool supervisor_workflow_connecting(void);
diff --git a/circuitpython/supervisor/spi_flash_api.h b/circuitpython/supervisor/spi_flash_api.h
new file mode 100644
index 0000000..1af8373
--- /dev/null
+++ b/circuitpython/supervisor/spi_flash_api.h
@@ -0,0 +1,49 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2018 Scott Shawcroft for Adafruit Industries LLC
+ *
+ * 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_SUPERVISOR_SPI_FLASH_H
+#define MICROPY_INCLUDED_SUPERVISOR_SPI_FLASH_H
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include "shared/external_flash/device.h"
+
+#include "shared-bindings/busio/SPI.h"
+
+extern busio_spi_obj_t supervisor_flash_spi_bus; // Used to share SPI bus on some boards
+
+// This API is implemented for both normal SPI peripherals and QSPI peripherals.
+
+bool spi_flash_command(uint8_t command);
+bool spi_flash_read_command(uint8_t command, uint8_t *response, uint32_t length);
+bool spi_flash_write_command(uint8_t command, uint8_t *data, uint32_t length);
+bool spi_flash_sector_command(uint8_t command, uint32_t address);
+bool spi_flash_write_data(uint32_t address, uint8_t *data, uint32_t data_length);
+bool spi_flash_read_data(uint32_t address, uint8_t *data, uint32_t data_length);
+void spi_flash_init(void);
+void spi_flash_init_device(const external_flash_device *device);
+
+#endif // MICROPY_INCLUDED_SUPERVISOR_SPI_FLASH_H
diff --git a/circuitpython/supervisor/stub/filesystem.c b/circuitpython/supervisor/stub/filesystem.c
new file mode 100644
index 0000000..920a757
--- /dev/null
+++ b/circuitpython/supervisor/stub/filesystem.c
@@ -0,0 +1,80 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2017 Scott Shawcroft 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 "supervisor/filesystem.h"
+
+
+void filesystem_background(void) {
+ return;
+}
+
+void filesystem_tick(void) {
+ return;
+}
+
+bool filesystem_init(bool create_allowed, bool force_create) {
+ (void)create_allowed;
+ (void)force_create;
+ return true;
+}
+
+void filesystem_flush(void) {
+}
+
+void filesystem_set_internal_writable_by_usb(bool writable) {
+ (void)writable;
+ return;
+}
+
+void filesystem_set_writable_by_usb(fs_user_mount_t *vfs, bool usb_writable) {
+ (void)vfs;
+ (void)usb_writable;
+ return;
+}
+
+bool filesystem_is_writable_by_python(fs_user_mount_t *vfs) {
+ (void)vfs;
+ return true;
+}
+
+bool filesystem_is_writable_by_usb(fs_user_mount_t *vfs) {
+ return true;
+}
+
+void filesystem_set_internal_concurrent_write_protection(bool concurrent_write_protection) {
+ (void)concurrent_write_protection;
+ return;
+}
+
+void filesystem_set_concurrent_write_protection(fs_user_mount_t *vfs, bool concurrent_write_protection) {
+ (void)vfs;
+ (void)concurrent_write_protection;
+ return;
+}
+
+bool filesystem_present(void) {
+ return false;
+}
diff --git a/circuitpython/supervisor/stub/internal_flash.c b/circuitpython/supervisor/stub/internal_flash.c
new file mode 100644
index 0000000..3bb7149
--- /dev/null
+++ b/circuitpython/supervisor/stub/internal_flash.c
@@ -0,0 +1,74 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * SPDX-FileCopyrightText: Copyright (c) 2013, 2014 Damien P. George
+ *
+ * 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 "supervisor/flash.h"
+
+#include <stdint.h>
+#include <string.h>
+
+#include "extmod/vfs.h"
+#include "extmod/vfs_fat.h"
+#include "py/mphal.h"
+#include "py/obj.h"
+#include "py/runtime.h"
+#include "lib/oofatfs/ff.h"
+
+void supervisor_flash_init(void) {
+}
+
+uint32_t supervisor_flash_get_block_size(void) {
+ return 0;
+}
+
+uint32_t supervisor_flash_get_block_count(void) {
+ return 0;
+}
+
+mp_uint_t supervisor_flash_read_blocks(uint8_t *dest, uint32_t block, uint32_t num_blocks) {
+ return 0; // success
+}
+
+mp_uint_t supervisor_flash_write_blocks(const uint8_t *src, uint32_t block_num, uint32_t num_blocks) {
+ return 0; // success
+}
+
+#if (0)
+// See definition in supervisor/flash.c
+void supervisor_flash_init_vfs(struct _fs_user_mount_t *vfs) {
+ return;
+}
+
+// See definition in supervisor/flash.c
+void supervisor_flash_flush(void) {
+ return;
+}
+#endif
+
+void supervisor_flash_release_cache(void) {
+}
+
+void port_internal_flash_flush(void) {
+ return;
+}
diff --git a/circuitpython/supervisor/stub/safe_mode.c b/circuitpython/supervisor/stub/safe_mode.c
new file mode 100644
index 0000000..b62cc05
--- /dev/null
+++ b/circuitpython/supervisor/stub/safe_mode.c
@@ -0,0 +1,42 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Scott Shawcroft 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 "supervisor/shared/safe_mode.h"
+
+#include <stdlib.h>
+
+safe_mode_t wait_for_safe_mode_reset(void) {
+ return NO_SAFE_MODE;
+}
+
+void reset_into_safe_mode(safe_mode_t reason) {
+ (void)reason;
+ abort();
+}
+
+void print_safe_mode_message(safe_mode_t reason) {
+ (void)reason;
+}
diff --git a/circuitpython/supervisor/stub/stack.c b/circuitpython/supervisor/stub/stack.c
new file mode 100644
index 0000000..2abc197
--- /dev/null
+++ b/circuitpython/supervisor/stub/stack.c
@@ -0,0 +1,48 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2018 Scott Shawcroft 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 "supervisor/shared/stack.h"
+
+bool stack_ok(void) {
+ return true;
+}
+
+void assert_heap_ok(void) {
+}
+
+void stack_init(void) {
+}
+
+void stack_resize(void) {
+}
+
+void set_next_stack_size(uint32_t size) {
+ (void)size;
+}
+
+uint32_t get_current_stack_size(void) {
+ return 0;
+}
diff --git a/circuitpython/supervisor/supervisor.mk b/circuitpython/supervisor/supervisor.mk
new file mode 100644
index 0000000..f4ca11d
--- /dev/null
+++ b/circuitpython/supervisor/supervisor.mk
@@ -0,0 +1,203 @@
+SRC_SUPERVISOR = \
+ main.c \
+ supervisor/port.c \
+ supervisor/shared/background_callback.c \
+ supervisor/shared/board.c \
+ supervisor/shared/cpu.c \
+ supervisor/shared/flash.c \
+ supervisor/shared/lock.c \
+ supervisor/shared/memory.c \
+ supervisor/shared/micropython.c \
+ supervisor/shared/reload.c \
+ supervisor/shared/safe_mode.c \
+ supervisor/shared/serial.c \
+ supervisor/shared/stack.c \
+ supervisor/shared/status_leds.c \
+ supervisor/shared/tick.c \
+ supervisor/shared/traceback.c \
+ supervisor/shared/translate.c \
+ supervisor/shared/workflow.c
+
+ifeq ($(DISABLE_FILESYSTEM),1)
+SRC_SUPERVISOR += supervisor/stub/filesystem.c
+else
+SRC_SUPERVISOR += supervisor/shared/filesystem.c
+endif
+
+NO_USB ?= $(wildcard supervisor/usb.c)
+
+INTERNAL_FLASH_FILESYSTEM ?= 0
+CFLAGS += -DINTERNAL_FLASH_FILESYSTEM=$(INTERNAL_FLASH_FILESYSTEM)
+
+QSPI_FLASH_FILESYSTEM ?= 0
+CFLAGS += -DQSPI_FLASH_FILESYSTEM=$(QSPI_FLASH_FILESYSTEM)
+
+SPI_FLASH_FILESYSTEM ?= 0
+CFLAGS += -DSPI_FLASH_FILESYSTEM=$(SPI_FLASH_FILESYSTEM)
+
+ifeq ($(CIRCUITPY_BLEIO),1)
+ SRC_SUPERVISOR += supervisor/shared/bluetooth/bluetooth.c
+ CIRCUITPY_CREATOR_ID ?= $(USB_VID)
+ CIRCUITPY_CREATION_ID ?= $(USB_PID)
+ CFLAGS += -DCIRCUITPY_CREATOR_ID=$(CIRCUITPY_CREATOR_ID)
+ CFLAGS += -DCIRCUITPY_CREATION_ID=$(CIRCUITPY_CREATION_ID)
+ ifeq ($(CIRCUITPY_BLE_FILE_SERVICE),1)
+ SRC_SUPERVISOR += supervisor/shared/bluetooth/file_transfer.c
+ endif
+ ifeq ($(CIRCUITPY_SERIAL_BLE),1)
+ SRC_SUPERVISOR += supervisor/shared/bluetooth/serial.c
+ endif
+endif
+
+# Choose which flash filesystem impl to use.
+# (Right now INTERNAL_FLASH_FILESYSTEM and (Q)SPI_FLASH_FILESYSTEM are mutually exclusive.
+# But that might not be true in the future.)
+ifeq ($(INTERNAL_FLASH_FILESYSTEM),1)
+ ifeq ($(DISABLE_FILESYSTEM),1)
+ SRC_SUPERVISOR += supervisor/stub/internal_flash.c
+ else
+ SRC_SUPERVISOR += supervisor/internal_flash.c
+ endif
+else
+ CFLAGS += -DEXTERNAL_FLASH_DEVICES=$(EXTERNAL_FLASH_DEVICES) \
+
+ 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
+
+$(HEADER_BUILD)/devices.h : ../../supervisor/shared/external_flash/devices.h.jinja ../../tools/gen_nvm_devices.py | $(HEADER_BUILD)
+ $(STEPECHO) "GEN $@"
+ $(Q)install -d $(BUILD)/genhdr
+ $(Q)$(PYTHON) ../../tools/gen_nvm_devices.py $< $@
+
+$(BUILD)/supervisor/shared/external_flash/external_flash.o: $(HEADER_BUILD)/devices.h
+
+endif
+
+ifneq ($(wildcard supervisor/serial.c),)
+ SRC_SUPERVISOR += supervisor/serial.c
+endif
+
+ifeq ($(CIRCUITPY_USB),1)
+ 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/usb.c \
+ supervisor/shared/usb/usb_desc.c \
+ supervisor/shared/usb/usb.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
+
+ ifeq ($(CIRCUITPY_USB_HOST), 1)
+ SRC_SUPERVISOR += \
+ lib/tinyusb/src/host/hub.c \
+ lib/tinyusb/src/host/usbh.c \
+ lib/tinyusb/src/host/usbh_control.c \
+
+ endif
+endif
+
+SRC_TINYUSB = $(filter lib/tinyusb/%.c, $(SRC_SUPERVISOR))
+$(patsubst %.c,$(BUILD)/%.o,$(SRC_TINYUSB)): CFLAGS += -Wno-missing-prototypes
+
+SUPERVISOR_O = $(addprefix $(BUILD)/, $(SRC_SUPERVISOR:.c=.o))
+
+ifeq ($(CIRCUITPY_DISPLAYIO), 1)
+ SRC_SUPERVISOR += \
+ supervisor/shared/display.c
+
+ ifeq ($(CIRCUITPY_TERMINALIO), 1)
+ SUPERVISOR_O += $(BUILD)/autogen_display_resources.o
+ endif
+endif
+
+# Preserve double quotes in these values by single-quoting them.
+
+USB_INTERFACE_NAME ?= "CircuitPython"
+CFLAGS += -DUSB_INTERFACE_NAME='$(USB_INTERFACE_NAME)'
+
+ifneq ($(USB_VID),)
+CFLAGS += -DUSB_VID=$(USB_VID)
+CFLAGS += -DUSB_PID=$(USB_PID)
+CFLAGS += -DUSB_MANUFACTURER='$(USB_MANUFACTURER)'
+USB_MANUFACTURER_8 := "$(shell echo $(USB_MANUFACTURER) | cut -c 1-8)"
+# Length-limited versions of strings for MSC names.
+CFLAGS += -DUSB_MANUFACTURER_8='$(USB_MANUFACTURER_8)'
+USB_PRODUCT_16 := "$(shell echo $(USB_PRODUCT) | cut -c 1-16)"
+CFLAGS += -DUSB_PRODUCT_16='$(USB_PRODUCT_16)'
+CFLAGS += -DUSB_PRODUCT='$(USB_PRODUCT)'
+
+endif
+
+# In the following URL, don't include the https:// prefix.
+# It gets added automatically.
+USB_WEBUSB_URL ?= "circuitpython.org"
+
+ifeq ($(CIRCUITPY_USB_CDC),1)
+# Inform TinyUSB there will be up to two CDC devices.
+CFLAGS += -DCFG_TUD_CDC=2
+endif
+
+USB_HIGHSPEED ?= 0
+CFLAGS += -DUSB_HIGHSPEED=$(USB_HIGHSPEED)
+
+$(BUILD)/supervisor/shared/translate.o: $(HEADER_BUILD)/qstrdefs.generated.h
+
+CIRCUITPY_DISPLAY_FONT ?= "../../tools/fonts/ter-u12n.bdf"
+
+$(BUILD)/autogen_display_resources.c: ../../tools/gen_display_resources.py $(HEADER_BUILD)/qstrdefs.generated.h Makefile | $(HEADER_BUILD)
+ $(STEPECHO) "GEN $@"
+ $(Q)install -d $(BUILD)/genhdr
+ $(Q)$(PYTHON) ../../tools/gen_display_resources.py \
+ --font $(CIRCUITPY_DISPLAY_FONT) \
+ --sample_file $(HEADER_BUILD)/qstrdefs.generated.h \
+ --output_c_file $(BUILD)/autogen_display_resources.c
diff --git a/circuitpython/supervisor/usb.h b/circuitpython/supervisor/usb.h
new file mode 100644
index 0000000..420f423
--- /dev/null
+++ b/circuitpython/supervisor/usb.h
@@ -0,0 +1,88 @@
+/*
+ * This file is part of the Micro Python 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.
+ */
+
+#ifndef MICROPY_INCLUDED_SUPERVISOR_USB_H
+#define MICROPY_INCLUDED_SUPERVISOR_USB_H
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.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
+// it may be necessary to call it directly.
+void usb_background(void);
+
+// Schedule usb background
+void usb_background_schedule(void);
+
+// Ports must call this from their particular USB IRQ handler
+void usb_irq_handler(int instance);
+
+// Only inits the USB peripheral clocks and pins. The peripheral will be initialized by
+// TinyUSB.
+void init_usb_hardware(void);
+
+// Temporary hook for code after init. Only used for RP2040.
+void post_usb_init(void);
+
+// Indexes and counts updated as descriptors are built.
+typedef struct {
+ size_t current_interface;
+ size_t current_endpoint;
+ size_t num_in_endpoints;
+ size_t num_out_endpoints;
+} descriptor_counts_t;
+
+// 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_disconnect(void);
+void usb_init(void);
+void usb_set_defaults(void);
+size_t usb_boot_py_data_size(void);
+void usb_get_boot_py_data(uint8_t *temp_storage, size_t temp_storage_size);
+void usb_return_boot_py_data(uint8_t *temp_storage, size_t temp_storage_size);
+
+// Further initialization that must be done with a VM present.
+void usb_setup_with_vm(void);
+
+
+// Propagate plug/unplug events to the MSC logic.
+#if CIRCUITPY_USB_MSC
+void usb_msc_mount(void);
+void usb_msc_umount(void);
+bool usb_msc_ejected(void);
+
+// Locking MSC prevents presenting the drive on plug-in when in use by something
+// else (likely BLE.)
+bool usb_msc_lock(void);
+void usb_msc_unlock(void);
+#endif
+
+#endif // MICROPY_INCLUDED_SUPERVISOR_USB_H
diff --git a/circuitpython/supervisor/workflow.h b/circuitpython/supervisor/workflow.h
new file mode 100755
index 0000000..3190039
--- /dev/null
+++ b/circuitpython/supervisor/workflow.h
@@ -0,0 +1,32 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 Scott Shawcroft 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.
+ */
+
+#pragma once
+
+void supervisor_workflow_reset(void);
+
+// True when the user could be actively iterating on their code.
+bool supervisor_workflow_active(void);