summaryrefslogtreecommitdiff
path: root/supervisor
diff options
context:
space:
mode:
authorBenjamin Shockley <benjaminshockley@hotmail.com>2020-08-20 13:48:15 -0500
committerGitHub <noreply@github.com>2020-08-20 13:48:15 -0500
commit0084f0af24e4e219bc040c52ee723959423de6e2 (patch)
tree1aebdb362f08bf6417caa87836e3e4e739afc964 /supervisor
parent9aebe2f1efc99871fcf283c304e3a8700050d736 (diff)
parent4d7b9cde33934a44c4c905a49d2f831339fc8bd2 (diff)
Merge pull request #2 from adafruit/master
Master
Diffstat (limited to 'supervisor')
-rw-r--r--supervisor/filesystem.h13
-rw-r--r--supervisor/flash.h5
-rwxr-xr-xsupervisor/linker.h42
-rwxr-xr-xsupervisor/memory.h13
-rw-r--r--supervisor/port.h55
-rw-r--r--supervisor/serial.h1
-rw-r--r--supervisor/shared/autoreload.c21
-rw-r--r--supervisor/shared/autoreload.h2
-rw-r--r--supervisor/shared/bluetooth.c358
-rw-r--r--supervisor/shared/bluetooth.h34
-rw-r--r--supervisor/shared/board.c49
-rw-r--r--supervisor/shared/board.h (renamed from supervisor/shared/usb/usb_desc.h)19
-rw-r--r--supervisor/shared/display.c262
-rw-r--r--supervisor/shared/display.h50
-rw-r--r--supervisor/shared/external_flash/common_commands.h1
-rw-r--r--supervisor/shared/external_flash/devices.h249
-rw-r--r--supervisor/shared/external_flash/external_flash.c139
-rw-r--r--supervisor/shared/external_flash/external_flash.h2
-rw-r--r--supervisor/shared/external_flash/spi_flash.c34
-rw-r--r--supervisor/shared/filesystem.c84
-rw-r--r--supervisor/shared/flash.c45
-rw-r--r--supervisor/shared/internal_flash.h33
-rwxr-xr-xsupervisor/shared/memory.c29
-rw-r--r--supervisor/shared/rgb_led_colors.h25
-rw-r--r--supervisor/shared/rgb_led_status.c239
-rw-r--r--supervisor/shared/rgb_led_status.h4
-rw-r--r--supervisor/shared/safe_mode.c171
-rw-r--r--supervisor/shared/safe_mode.h54
-rw-r--r--supervisor/shared/serial.c62
-rwxr-xr-xsupervisor/shared/stack.c42
-rwxr-xr-xsupervisor/shared/stack.h9
-rw-r--r--supervisor/shared/status_leds.c2
-rw-r--r--supervisor/shared/tick.c142
-rw-r--r--supervisor/shared/tick.h70
-rw-r--r--supervisor/shared/translate.c50
-rw-r--r--supervisor/shared/translate.h34
-rw-r--r--supervisor/shared/usb/tusb_config.h9
-rw-r--r--supervisor/shared/usb/usb.c34
-rw-r--r--supervisor/shared/usb/usb_desc.c42
-rw-r--r--supervisor/shared/usb/usb_msc_flash.c137
-rw-r--r--supervisor/spi_flash_api.h4
-rw-r--r--supervisor/stub/filesystem.c8
-rw-r--r--supervisor/stub/internal_flash.c65
-rw-r--r--supervisor/stub/safe_mode.c39
-rw-r--r--supervisor/stub/serial.c5
-rw-r--r--supervisor/stub/stack.c48
-rw-r--r--supervisor/supervisor.mk188
-rw-r--r--supervisor/usb.h5
48 files changed, 2722 insertions, 306 deletions
diff --git a/supervisor/filesystem.h b/supervisor/filesystem.h
index eb9e65c09..c7c951a5e 100644
--- a/supervisor/filesystem.h
+++ b/supervisor/filesystem.h
@@ -29,9 +29,20 @@
#include <stdbool.h>
+#include "extmod/vfs_fat.h"
+
+extern volatile bool filesystem_flush_requested;
+
+void filesystem_background(void);
+void filesystem_tick(void);
void filesystem_init(bool create_allowed, bool force_create);
void filesystem_flush(void);
-void filesystem_writable_by_python(bool writable);
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/supervisor/flash.h b/supervisor/flash.h
index ae6a44fd0..a8a77bf04 100644
--- a/supervisor/flash.h
+++ b/supervisor/flash.h
@@ -34,14 +34,12 @@
#ifdef EXTERNAL_FLASH_DEVICE_COUNT
#include "supervisor/shared/external_flash/external_flash.h"
#else
-#include "supervisor/internal_flash.h"
+#include "supervisor/shared/internal_flash.h"
#endif
-void supervisor_flash_set_usb_writable(bool usb_writable);
void supervisor_flash_init(void);
uint32_t supervisor_flash_get_block_size(void);
uint32_t supervisor_flash_get_block_count(void);
-void supervisor_flash_flush(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);
@@ -50,5 +48,6 @@ mp_uint_t supervisor_flash_write_blocks(const uint8_t *src, uint32_t block_num,
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/supervisor/linker.h b/supervisor/linker.h
new file mode 100755
index 000000000..74b2dd5a6
--- /dev/null
+++ b/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)
+#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/supervisor/memory.h b/supervisor/memory.h
index 4f8317a2d..f4359ca46 100755
--- a/supervisor/memory.h
+++ b/supervisor/memory.h
@@ -39,8 +39,11 @@ typedef struct {
uint32_t length; // in bytes
} supervisor_allocation;
+
+
void memory_init(void);
void free_memory(supervisor_allocation* allocation);
+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
@@ -48,4 +51,14 @@ supervisor_allocation* allocate_remaining_memory(void);
// statically allocated memory.
supervisor_allocation* allocate_memory(uint32_t length, bool high_address);
+static inline uint16_t align32_size(uint16_t size) {
+ if (size % 4 != 0) {
+ return (size & 0xfffc) + 0x4;
+ }
+ return size;
+}
+
+// Called after the heap is freed in case the supervisor wants to save some values.
+void supervisor_move_memory(void);
+
#endif // MICROPY_INCLUDED_SUPERVISOR_MEMORY_H
diff --git a/supervisor/port.h b/supervisor/port.h
index dd4a82209..8a12d34c8 100644
--- a/supervisor/port.h
+++ b/supervisor/port.h
@@ -29,16 +29,8 @@
#include "py/mpconfig.h"
-#ifndef PORT_HEAP_SIZE
-#error "Please define PORT_HEAP_SIZE to specify heap size in bytes."
-#endif
-
-typedef enum {
- NO_SAFE_MODE = 0,
- BROWNOUT,
- HARD_CRASH,
- USER_SAFE_MODE,
-} safe_mode_t;
+#include "supervisor/memory.h"
+#include "supervisor/shared/safe_mode.h"
// Provided by the linker;
extern uint32_t _ezero;
@@ -51,7 +43,10 @@ extern uint32_t _ebss;
safe_mode_t port_init(void);
-// Reset the microcontroller.
+// Reset the microcontroller completely.
+void reset_cpu(void);
+
+// Reset the microcontroller state.
void reset_port(void);
// Reset the rest of the board.
@@ -60,8 +55,40 @@ void reset_board(void);
// Reset to the bootloader
void reset_to_bootloader(void);
-#ifdef NRF52_SERIES
-void HardFault_Handler(void);
-#endif
+// Get stack limit address
+uint32_t *port_stack_get_limit(void);
+
+// Get stack top address
+uint32_t *port_stack_get_top(void);
+
+supervisor_allocation* port_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.
+void port_sleep_until_interrupt(void);
#endif // MICROPY_INCLUDED_SUPERVISOR_PORT_H
diff --git a/supervisor/serial.h b/supervisor/serial.h
index 84b3062a3..ef88ad346 100644
--- a/supervisor/serial.h
+++ b/supervisor/serial.h
@@ -38,6 +38,7 @@
FIL* boot_output_file;
#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.
diff --git a/supervisor/shared/autoreload.c b/supervisor/shared/autoreload.c
index 2a7fd1e9d..1976f6647 100644
--- a/supervisor/shared/autoreload.c
+++ b/supervisor/shared/autoreload.c
@@ -28,10 +28,12 @@
#include "py/mphal.h"
#include "py/reload.h"
+#include "supervisor/shared/tick.h"
-volatile uint32_t autoreload_delay_ms = 0;
-bool autoreload_enabled = false;
+static volatile uint32_t autoreload_delay_ms = 0;
+static bool autoreload_enabled = false;
static bool autoreload_suspended = false;
+
volatile bool reload_requested = false;
inline void autoreload_tick() {
@@ -42,6 +44,7 @@ inline void autoreload_tick() {
!autoreload_suspended && !reload_requested) {
mp_raise_reload_exception();
reload_requested = true;
+ supervisor_disable_tick();
}
autoreload_delay_ms--;
}
@@ -68,6 +71,12 @@ inline bool autoreload_is_enabled() {
}
void autoreload_start() {
+ // Enable ticks if we haven't been tracking an autoreload delay. We check
+ // our current state so that we only turn ticks on once. Multiple starts
+ // can occur before we reload and then turn ticks off.
+ if (autoreload_delay_ms == 0) {
+ supervisor_enable_tick();
+ }
autoreload_delay_ms = CIRCUITPY_AUTORELOAD_DELAY_MS;
}
@@ -75,3 +84,11 @@ void autoreload_stop() {
autoreload_delay_ms = 0;
reload_requested = false;
}
+
+void autoreload_now() {
+ if (!autoreload_enabled || autoreload_suspended || reload_requested) {
+ return;
+ }
+ mp_raise_reload_exception();
+ reload_requested = true;
+}
diff --git a/supervisor/shared/autoreload.h b/supervisor/shared/autoreload.h
index bcb100151..fbd482c19 100644
--- a/supervisor/shared/autoreload.h
+++ b/supervisor/shared/autoreload.h
@@ -43,4 +43,6 @@ bool autoreload_is_enabled(void);
void autoreload_suspend(void);
void autoreload_resume(void);
+void autoreload_now(void);
+
#endif // MICROPY_INCLUDED_SUPERVISOR_AUTORELOAD_H
diff --git a/supervisor/shared/bluetooth.c b/supervisor/shared/bluetooth.c
new file mode 100644
index 000000000..98d0fab38
--- /dev/null
+++ b/supervisor/shared/bluetooth.c
@@ -0,0 +1,358 @@
+/*
+ * 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 <string.h>
+
+#include "extmod/vfs.h"
+#include "extmod/vfs_fat.h"
+
+#include "shared-bindings/_bleio/__init__.h"
+#include "shared-bindings/_bleio/Adapter.h"
+#include "shared-bindings/_bleio/Characteristic.h"
+#include "shared-bindings/_bleio/Service.h"
+#include "shared-bindings/_bleio/UUID.h"
+
+#include "common-hal/_bleio/__init__.h"
+
+#include "supervisor/shared/autoreload.h"
+
+#include "py/mpstate.h"
+
+bleio_service_obj_t supervisor_ble_service;
+bleio_uuid_obj_t supervisor_ble_service_uuid;
+bleio_characteristic_obj_t supervisor_ble_version_characteristic;
+bleio_uuid_obj_t supervisor_ble_version_uuid;
+bleio_characteristic_obj_t supervisor_ble_filename_characteristic;
+bleio_uuid_obj_t supervisor_ble_filename_uuid;
+bleio_characteristic_obj_t supervisor_ble_length_characteristic;
+bleio_uuid_obj_t supervisor_ble_length_uuid;
+bleio_characteristic_obj_t supervisor_ble_contents_characteristic;
+bleio_uuid_obj_t supervisor_ble_contents_uuid;
+
+// This is the base UUID for CircuitPython services and characteristics.
+const uint8_t circuitpython_base_uuid[16] = {0x6e, 0x68, 0x74, 0x79, 0x50, 0x74, 0x69, 0x75, 0x63, 0x72, 0x69, 0x43, 0x00, 0x00, 0xaf, 0xad };
+// This standard advertisement advertises the CircuitPython editing service and a CIRCUITPY short name.
+uint8_t circuitpython_advertising_data[] = { 0x02, 0x01, 0x06, 0x02, 0x0a, 0x00, 0x11, 0x07, 0x6e, 0x68, 0x74, 0x79, 0x50, 0x74, 0x69, 0x75, 0x63, 0x72, 0x69, 0x43, 0x00, 0x01, 0xaf, 0xad, 0x06, 0x08, 0x43, 0x49, 0x52, 0x43, 0x55 };
+// This scan response advertises the full CIRCUITPYXXXX device name.
+uint8_t circuitpython_scan_response_data[15] = {0x0e, 0x09, 0x43, 0x49, 0x52, 0x43, 0x55, 0x49, 0x54, 0x50, 0x59, 0x00, 0x00, 0x00, 0x00};
+mp_obj_list_t service_list;
+mp_obj_t service_list_items[1];
+mp_obj_list_t characteristic_list;
+mp_obj_t characteristic_list_items[4];
+
+void supervisor_bluetooth_start_advertising(void) {
+ #if !CIRCUITPY_BLE_FILE_SERVICE
+ return;
+ #endif
+ bool is_connected = common_hal_bleio_adapter_get_connected(&common_hal_bleio_adapter_obj);
+ if (is_connected) {
+ return;
+ }
+ // TODO: switch to Adafruit short UUID for the advertisement and add manufacturing data to distinguish ourselves from arduino.
+ _common_hal_bleio_adapter_start_advertising(&common_hal_bleio_adapter_obj,
+ true,
+ false, 0,
+ 1.0,
+ circuitpython_advertising_data,
+ sizeof(circuitpython_advertising_data),
+ circuitpython_scan_response_data,
+ sizeof(circuitpython_scan_response_data));
+}
+
+void supervisor_start_bluetooth(void) {
+ #if !CIRCUITPY_BLE_FILE_SERVICE
+ return;
+ #endif
+
+ common_hal_bleio_adapter_set_enabled(&common_hal_bleio_adapter_obj, true);
+
+ supervisor_ble_service_uuid.base.type = &bleio_uuid_type;
+ common_hal_bleio_uuid_construct(&supervisor_ble_service_uuid, 0x0100, circuitpython_base_uuid);
+
+ // 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);
+
+ // File length
+ supervisor_ble_version_uuid.base.type = &bleio_uuid_type;
+ common_hal_bleio_uuid_construct(&supervisor_ble_version_uuid, 0x0203, circuitpython_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
+
+ uint32_t version = 1;
+ 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_filename_uuid.base.type = &bleio_uuid_type;
+ common_hal_bleio_uuid_construct(&supervisor_ble_filename_uuid, 0x0200, circuitpython_base_uuid);
+ common_hal_bleio_characteristic_construct(&supervisor_ble_filename_characteristic,
+ &supervisor_ble_service,
+ 0, // handle (for remote only)
+ &supervisor_ble_filename_uuid,
+ CHAR_PROP_READ | CHAR_PROP_WRITE,
+ SECURITY_MODE_OPEN,
+ SECURITY_MODE_OPEN,
+ 500, // max length
+ false, // fixed length
+ NULL); // no initial value
+
+ char code_py[] = "/code.py";
+ bufinfo.buf = code_py;
+ bufinfo.len = sizeof(code_py);
+ common_hal_bleio_characteristic_set_value(&supervisor_ble_filename_characteristic, &bufinfo);
+
+ // File length
+ supervisor_ble_length_uuid.base.type = &bleio_uuid_type;
+ common_hal_bleio_uuid_construct(&supervisor_ble_length_uuid, 0x0202, circuitpython_base_uuid);
+ common_hal_bleio_characteristic_construct(&supervisor_ble_length_characteristic,
+ &supervisor_ble_service,
+ 0, // handle (for remote only)
+ &supervisor_ble_length_uuid,
+ CHAR_PROP_NOTIFY | CHAR_PROP_READ,
+ SECURITY_MODE_OPEN,
+ SECURITY_MODE_NO_ACCESS,
+ 4, // max length
+ true, // fixed length
+ NULL); // no initial value
+
+ // File actions
+ supervisor_ble_contents_uuid.base.type = &bleio_uuid_type;
+ common_hal_bleio_uuid_construct(&supervisor_ble_contents_uuid, 0x0201, circuitpython_base_uuid);
+ common_hal_bleio_characteristic_construct(&supervisor_ble_contents_characteristic,
+ &supervisor_ble_service,
+ 0, // handle (for remote only)
+ &supervisor_ble_contents_uuid,
+ CHAR_PROP_NOTIFY | CHAR_PROP_WRITE_NO_RESPONSE | CHAR_PROP_WRITE,
+ SECURITY_MODE_OPEN,
+ SECURITY_MODE_OPEN,
+ 500, // max length
+ false, // fixed length
+ NULL); // no initial value
+
+ supervisor_bluetooth_start_advertising();
+ vm_used_ble = false;
+}
+
+FIL active_file;
+volatile bool new_filename;
+volatile bool run_ble_background;
+bool was_connected;
+
+void update_file_length(void) {
+ int32_t file_length = -1;
+ mp_buffer_info_t bufinfo;
+ bufinfo.buf = &file_length;
+ bufinfo.len = sizeof(file_length);
+ if (active_file.obj.fs != 0) {
+ file_length = (int32_t) f_size(&active_file);
+ }
+ common_hal_bleio_characteristic_set_value(&supervisor_ble_length_characteristic, &bufinfo);
+}
+
+void open_current_file(void) {
+ if (active_file.obj.fs != 0) {
+ return;
+ }
+ uint16_t max_len = supervisor_ble_filename_characteristic.max_length;
+ uint8_t path[max_len];
+ size_t length = common_hal_bleio_characteristic_get_value(&supervisor_ble_filename_characteristic, path, max_len - 1);
+ path[length] = '\0';
+
+ FATFS *fs = &((fs_user_mount_t *) MP_STATE_VM(vfs_mount_table)->obj)->fatfs;
+ f_open(fs, &active_file, (char*) path, FA_READ | FA_WRITE);
+
+ update_file_length();
+}
+
+void close_current_file(void) {
+ f_close(&active_file);
+}
+
+uint32_t current_command[1024 / sizeof(uint32_t)];
+volatile size_t current_offset;
+
+void supervisor_bluetooth_background(void) {
+ #if !CIRCUITPY_BLE_FILE_SERVICE
+ return;
+ #endif
+ if (!run_ble_background) {
+ return;
+ }
+ bool is_connected = common_hal_bleio_adapter_get_connected(&common_hal_bleio_adapter_obj);
+ if (!was_connected && is_connected) {
+ open_current_file();
+ } else if (was_connected && !is_connected) {
+ close_current_file();
+ new_filename = false;
+ }
+ was_connected = is_connected;
+ run_ble_background = false;
+ if (!is_connected) {
+ supervisor_bluetooth_start_advertising();
+ return;
+ }
+ if (new_filename) {
+ close_current_file();
+ open_current_file();
+
+ new_filename = false;
+ // get length and set the characteristic for it
+ }
+ uint16_t current_length = ((uint16_t*) current_command)[0];
+ if (current_length > 0 && current_length == current_offset) {
+ uint16_t command = ((uint16_t *) current_command)[1];
+
+ if (command == 1) {
+ uint16_t max_len = 20; //supervisor_ble_contents_characteristic.max_length;
+ uint8_t buf[max_len];
+ mp_buffer_info_t bufinfo;
+ bufinfo.buf = buf;
+ f_lseek(&active_file, 0);
+ while (f_read(&active_file, buf, max_len, &bufinfo.len) == FR_OK) {
+ if (bufinfo.len == 0) {
+ break;
+ }
+ common_hal_bleio_characteristic_set_value(&supervisor_ble_contents_characteristic, &bufinfo);
+ }
+ } else if (command == 2) { // patch
+ uint32_t offset = current_command[1];
+ uint32_t remove_length = current_command[2];
+ uint32_t insert_length = current_command[3];
+ uint32_t file_length = (int32_t) f_size(&active_file);
+ //uint32_t data_shift_length = fileLength - offset - remove_length;
+ int32_t data_shift = insert_length - remove_length;
+ uint32_t new_length = file_length + data_shift;
+
+ // TODO: Make these loops smarter to read and write on sector boundaries.
+ if (data_shift < 0) {
+ for (uint32_t shift_offset = offset + insert_length; shift_offset < new_length; shift_offset++) {
+ uint8_t data;
+ UINT actual;
+ f_lseek(&active_file, shift_offset - data_shift);
+ f_read(&active_file, &data, 1, &actual);
+ f_lseek(&active_file, shift_offset);
+ f_write(&active_file, &data, 1, &actual);
+ }
+ f_truncate(&active_file);
+ } else if (data_shift > 0) {
+ f_lseek(&active_file, file_length);
+ // Fill end with 0xff so we don't need to erase.
+ uint8_t data = 0xff;
+ for (size_t i = 0; i < (size_t) data_shift; i++) {
+ UINT actual;
+ f_write(&active_file, &data, 1, &actual);
+ }
+ for (uint32_t shift_offset = new_length - 1; shift_offset >= offset + insert_length ; shift_offset--) {
+ UINT actual;
+ f_lseek(&active_file, shift_offset - data_shift);
+ f_read(&active_file, &data, 1, &actual);
+ f_lseek(&active_file, shift_offset);
+ f_write(&active_file, &data, 1, &actual);
+ }
+ }
+
+ f_lseek(&active_file, offset);
+ uint8_t* data = (uint8_t *) (current_command + 4);
+ UINT written;
+ f_write(&active_file, data, insert_length, &written);
+ f_sync(&active_file);
+ // Notify the new file length.
+ update_file_length();
+
+ // Trigger an autoreload
+ autoreload_now();
+ }
+ current_offset = 0;
+ }
+}
+
+// This happens in an interrupt so we need to be quick.
+bool supervisor_bluetooth_hook(ble_evt_t *ble_evt) {
+ #if !CIRCUITPY_BLE_FILE_SERVICE
+ return false;
+ #endif
+ // Catch writes to filename or contents. Length is read-only.
+
+ bool done = false;
+ switch (ble_evt->header.evt_id) {
+ case BLE_GAP_EVT_CONNECTED:
+ // We run our background task even if it wasn't us connected to because we may want to
+ // advertise if the user code stopped advertising.
+ run_ble_background = true;
+ break;
+ case BLE_GAP_EVT_DISCONNECTED:
+ run_ble_background = true;
+ break;
+ case BLE_GATTS_EVT_WRITE: {
+ // A client wrote to a characteristic.
+
+ ble_gatts_evt_write_t *evt_write = &ble_evt->evt.gatts_evt.params.write;
+ // Event handle must match the handle for my characteristic.
+ if (evt_write->handle == supervisor_ble_contents_characteristic.handle) {
+ // Handle events
+ //write_to_ringbuf(self, evt_write->data, evt_write->len);
+ // First packet includes a uint16_t le for length at the start.
+ uint16_t current_length = ((uint16_t*) current_command)[0];
+ memcpy(((uint8_t*) current_command) + current_offset, evt_write->data, evt_write->len);
+ current_offset += evt_write->len;
+ current_length = ((uint16_t*) current_command)[0];
+ if (current_offset == current_length) {
+ run_ble_background = true;
+ done = true;
+ }
+ } else if (evt_write->handle == supervisor_ble_filename_characteristic.handle) {
+ new_filename = true;
+ run_ble_background = true;
+ done = true;
+ } else {
+ return done;
+ }
+ break;
+ }
+
+ default:
+ // For debugging.
+ // mp_printf(&mp_plat_print, "Unhandled peripheral event: 0x%04x\n", ble_evt->header.evt_id);
+ break;
+ }
+ return done;
+}
diff --git a/supervisor/shared/bluetooth.h b/supervisor/shared/bluetooth.h
new file mode 100644
index 000000000..1fb6a879a
--- /dev/null
+++ b/supervisor/shared/bluetooth.h
@@ -0,0 +1,34 @@
+/*
+ * 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
+
+void supervisor_start_bluetooth(void);
+bool supervisor_bluetooth_hook(ble_evt_t *ble_evt);
+void supervisor_bluetooth_background(void);
+
+#endif
diff --git a/supervisor/shared/board.c b/supervisor/shared/board.c
new file mode 100644
index 000000000..e3eb8fd0d
--- /dev/null
+++ b/supervisor/shared/board.c
@@ -0,0 +1,49 @@
+/*
+ * 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"
+
+#include "shared-bindings/digitalio/DigitalInOut.h"
+#include "shared-bindings/neopixel_write/__init__.h"
+
+#ifdef USER_NEOPIXELS_PIN
+
+// The maximum number of user neopixels right now is 10, on Circuit Playgrounds.
+// PyBadge and PyGamer have max 5
+#define USER_NEOPIXELS_MAX_COUNT 10
+
+void board_reset_user_neopixels(void) {
+ // Turn off on-board NeoPixel string
+ uint8_t empty[USER_NEOPIXELS_MAX_COUNT * 3] = { 0 };
+ digitalio_digitalinout_obj_t neopixel_pin;
+ common_hal_digitalio_digitalinout_construct(&neopixel_pin, USER_NEOPIXELS_PIN);
+ common_hal_digitalio_digitalinout_switch_to_output(&neopixel_pin, false,
+ DRIVE_MODE_PUSH_PULL);
+ common_hal_neopixel_write(&neopixel_pin, empty, USER_NEOPIXELS_MAX_COUNT * 3);
+ common_hal_digitalio_digitalinout_deinit(&neopixel_pin);
+}
+
+#endif
diff --git a/supervisor/shared/usb/usb_desc.h b/supervisor/shared/board.h
index 250147aa6..0e4d73455 100644
--- a/supervisor/shared/usb/usb_desc.h
+++ b/supervisor/shared/board.h
@@ -3,7 +3,7 @@
*
* The MIT License (MIT)
*
- * Copyright (c) 2018 hathach for Adafruit Industries
+ * 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
@@ -24,20 +24,15 @@
* THE SOFTWARE.
*/
-#ifndef USB_DESC_H_
-#define USB_DESC_H_
+#ifndef MICROPY_INCLUDED_SUPERVISOR_BOARD_H
+#define MICROPY_INCLUDED_SUPERVISOR_BOARD_H
-#include "lib/tinyusb/src/tusb.h"
+#include "py/mpconfig.h"
-#ifdef __cplusplus
- extern "C" {
-#endif
+#ifdef USER_NEOPIXELS_PIN
-// Descriptors set used by tinyusb stack
-extern tud_desc_set_t tud_desc_set;
+void board_reset_user_neopixels(void);
-#ifdef __cplusplus
- }
#endif
-#endif /* USB_DESC_H_ */
+#endif // MICROPY_INCLUDED_SUPERVISOR_BOARD_H
diff --git a/supervisor/shared/display.c b/supervisor/shared/display.c
new file mode 100644
index 000000000..9c074209b
--- /dev/null
+++ b/supervisor/shared/display.c
@@ -0,0 +1,262 @@
+/*
+ * 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/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
+
+extern size_t blinka_bitmap_data[];
+extern displayio_bitmap_t blinka_bitmap;
+extern displayio_group_t circuitpython_splash;
+
+static supervisor_allocation* tilegrid_tiles = NULL;
+
+void supervisor_start_terminal(uint16_t width_px, uint16_t height_px) {
+ displayio_tilegrid_t* grid = &supervisor_terminal_text_grid;
+ uint16_t width_in_tiles = (width_px - blinka_bitmap.width) / grid->tile_width;
+ // determine scale based on h
+ uint8_t scale = 1;
+ if (width_in_tiles > 80) {
+ scale = 2;
+ }
+ width_in_tiles = (width_px - blinka_bitmap.width * scale) / (grid->tile_width * scale);
+ uint16_t height_in_tiles = height_px / (grid->tile_height * scale);
+ uint16_t remaining_pixels = height_px % (grid->tile_height * scale);
+ if (remaining_pixels > 0) {
+ height_in_tiles += 1;
+ }
+ circuitpython_splash.scale = scale;
+
+ uint16_t total_tiles = width_in_tiles * height_in_tiles;
+
+ // First try to allocate outside the heap. This will fail when the VM is running.
+ tilegrid_tiles = allocate_memory(align32_size(total_tiles), false);
+ uint8_t* tiles;
+ if (tilegrid_tiles == NULL) {
+ tiles = m_malloc(total_tiles, true);
+ MP_STATE_VM(terminal_tilegrid_tiles) = tiles;
+ } else {
+ tiles = (uint8_t*) tilegrid_tiles->ptr;
+ }
+
+ if (tiles == NULL) {
+ return;
+ }
+ grid->y = 0;
+ 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;
+ 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);
+}
+
+void supervisor_stop_terminal(void) {
+ if (tilegrid_tiles != NULL) {
+ free_memory(tilegrid_tiles);
+ tilegrid_tiles = NULL;
+ supervisor_terminal_text_grid.inline_tiles = false;
+ supervisor_terminal_text_grid.tiles = NULL;
+ }
+}
+
+void supervisor_display_move_memory(void) {
+ #if CIRCUITPY_DISPLAYIO
+ displayio_tilegrid_t* grid = &supervisor_terminal_text_grid;
+ if (MP_STATE_VM(terminal_tilegrid_tiles) == NULL || grid->tiles != MP_STATE_VM(terminal_tilegrid_tiles)) {
+ return;
+ }
+ uint16_t total_tiles = grid->width_in_tiles * grid->height_in_tiles;
+
+ tilegrid_tiles = allocate_memory(align32_size(total_tiles), false);
+ if (tilegrid_tiles != NULL) {
+ memcpy(tilegrid_tiles->ptr, grid->tiles, total_tiles);
+ grid->tiles = (uint8_t*) tilegrid_tiles->ptr;
+ } else {
+ grid->tiles = NULL;
+ grid->inline_tiles = false;
+ }
+ MP_STATE_VM(terminal_tilegrid_tiles) = NULL;
+ #if CIRCUITPY_RGBMATRIX
+ for (uint8_t i = 0; i < CIRCUITPY_DISPLAY_LIMIT; i++) {
+ 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
+ #endif
+}
+
+size_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
+};
+
+displayio_group_child_t splash_children[2] = {
+ {&blinka_sprite, &blinka_sprite},
+ {&supervisor_terminal_text_grid, &supervisor_terminal_text_grid}
+};
+
+displayio_group_t circuitpython_splash = {
+ .base = {.type = &displayio_group_type },
+ .x = 0,
+ .y = 0,
+ .scale = 2,
+ .size = 2,
+ .max_size = 2,
+ .children = splash_children,
+ .item_removed = false,
+ .in_group = false,
+ .hidden = false,
+ .hidden_by_parent = false
+};
diff --git a/supervisor/shared/display.h b/supervisor/shared/display.h
new file mode 100644
index 000000000..2a2ccf46d
--- /dev/null
+++ b/supervisor/shared/display.h
@@ -0,0 +1,50 @@
+/*
+ * 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 "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;
+
+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/supervisor/shared/external_flash/common_commands.h b/supervisor/shared/external_flash/common_commands.h
index cc0da2175..2eaa84833 100644
--- a/supervisor/shared/external_flash/common_commands.h
+++ b/supervisor/shared/external_flash/common_commands.h
@@ -43,5 +43,6 @@
#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/supervisor/shared/external_flash/devices.h b/supervisor/shared/external_flash/devices.h
index 8d8574c47..466ab49eb 100644
--- a/supervisor/shared/external_flash/devices.h
+++ b/supervisor/shared/external_flash/devices.h
@@ -64,9 +64,18 @@ typedef struct {
// 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;
-// Settings for the Adesto Tech AT25DF081A 1MiB SPI flash. Its on the SAMD21
+// Settings for the Adesto Tech AT25DF081A 1MiB SPI flash. It's on the SAMD21
// Xplained board.
// Datasheet: https://www.adestotech.com/wp-content/uploads/doc8715.pdf
#define AT25DF081A {\
@@ -85,6 +94,45 @@ typedef struct {
.single_status_byte = false, \
}
+// Settings for the Adesto Tech AT25SF161-SSHD-T 2MiB SPI flash
+// for the StringCar M0 (SAMD21) Express board.
+// Source: https://www.digikey.com/product-detail/en/adesto-technologies/AT25SF161-SDHD-T/1265-1230-1-ND/
+// Datasheet: https://www.adestotech.com/wpo-content/uploads/jDS-AT25SF161_046.pdf
+#define AT25SF161 {\
+ .total_size = (1 << 21), /* 2 MiB */ \
+ .start_up_time_us = 10000, \
+ .manufacturer_id = 0x1f, \
+ .memory_type = 0x86, \
+ .capacity = 0x01, \
+ .max_clock_speed_mhz = 85, \
+ .quad_enable_bit_mask = 0x00, \
+ .has_sector_protection = true, \
+ .supports_fast_read = true, \
+ .supports_qspi = false, \
+ .supports_qspi_writes = false, \
+ .write_status_register_split = false, \
+ .single_status_byte = false, \
+}
+
+// Settings for the Adesto Tech AT25SF041 1MiB SPI flash. It's on the SparkFun
+// SAMD51 Thing Plus board
+// Datasheet: https://www.adestotech.com/wp-content/uploads/DS-AT25SF041_044.pdf
+#define AT25SF041A {\
+ .total_size = (1 << 19), /* 512 KiB */ \
+ .start_up_time_us = 10000, \
+ .manufacturer_id = 0x1f, \
+ .memory_type = 0x84, \
+ .capacity = 0x01, \
+ .max_clock_speed_mhz = 85, \
+ .quad_enable_bit_mask = 0x00, \
+ .has_sector_protection = false, \
+ .supports_fast_read = true, \
+ .supports_qspi = false, \
+ .supports_qspi_writes = false, \
+ .write_status_register_split = false, \
+ .single_status_byte = false, \
+}
+
// Settings for the Gigadevice GD25Q16C 2MiB SPI flash.
// Datasheet: http://www.gigadevice.com/datasheet/gd25q16c/
#define GD25Q16C {\
@@ -103,6 +151,24 @@ typedef struct {
.single_status_byte = false, \
}
+// Settings for the Gigadevice GD25Q32C 4MiB SPI flash.
+// Datasheet: http://www.elm-tech.com/en/products/spi-flash-memory/gd25q32/gd25q32.pdf
+#define GD25Q32C {\
+ .total_size = (1 << 22), /* 4 MiB */ \
+ .start_up_time_us = 5000, \
+ .manufacturer_id = 0xc8, \
+ .memory_type = 0x40, \
+ .capacity = 0x16, \
+ .max_clock_speed_mhz = 104, /* if we need 120 then we can turn on high performance mode */ \
+ .quad_enable_bit_mask = 0x02, \
+ .has_sector_protection = false, \
+ .supports_fast_read = true, \
+ .supports_qspi = true, \
+ .supports_qspi_writes = true, \
+ .write_status_register_split = true, \
+ .single_status_byte = false, \
+}
+
// Settings for the Gigadevice GD25Q64C 8MiB SPI flash.
// Datasheet: http://www.elm-tech.com/en/products/spi-flash-memory/gd25q64/gd25q64.pdf
#define GD25Q64C {\
@@ -121,6 +187,24 @@ typedef struct {
.single_status_byte = false, \
}
+// Settings for the Gigadevice GD25S512MD 64MiB SPI flash.
+// Datasheet: http://www.gigadevice.com/datasheet/gd25s512md/
+#define GD25S512MD {\
+ .total_size = (1 << 26), /* 64 MiB */ \
+ .start_up_time_us = 5000, \
+ .manufacturer_id = 0xc8, \
+ .memory_type = 0x40, \
+ .capacity = 0x19, \
+ .max_clock_speed_mhz = 104, /* if we need 120 then we can turn on high performance mode */ \
+ .quad_enable_bit_mask = 0x02, \
+ .has_sector_protection = false, \
+ .supports_fast_read = true, \
+ .supports_qspi = true, \
+ .supports_qspi_writes = true, \
+ .write_status_register_split = true, \
+ .single_status_byte = false, \
+}
+
// Settings for the Cypress (was Spansion) S25FL064L 8MiB SPI flash.
// Datasheet: http://www.cypress.com/file/316661/download
#define S25FL064L {\
@@ -262,6 +346,23 @@ typedef struct {
.write_status_register_split = false, \
}
+// Settings for the Winbond W25Q32JV-IQ 4MiB SPI flash.
+// Datasheet: https://www.mouser.com/datasheet/2/949/w25q32jv_revg_03272018_plus-1489806.pdf
+#define W25Q32JV_IQ {\
+ .total_size = (1 << 22), /* 4 MiB */ \
+ .start_up_time_us = 5000, \
+ .manufacturer_id = 0xef, \
+ .memory_type = 0x40, \
+ .capacity = 0x16, \
+ .max_clock_speed_mhz = 133, \
+ .quad_enable_bit_mask = 0x02, \
+ .has_sector_protection = false, \
+ .supports_fast_read = true, \
+ .supports_qspi = true, \
+ .supports_qspi_writes = true, \
+ .write_status_register_split = false, \
+}
+
// Settings for the Winbond W25Q64JV-IM 8MiB SPI flash. Note that JV-IQ has a different .memory_type (0x40)
// Datasheet: http://www.winbond.com/resource-files/w25q64jv%20revj%2003272018%20plus.pdf
#define W25Q64JV_IM {\
@@ -316,6 +417,23 @@ typedef struct {
.single_status_byte = false, \
}
+// Settings for the Winbond W25Q80DV 1MiB SPI flash.. Note that W25Q80DL has a different memory type (0x60)
+// Datasheet: https://www.winbond.com/resource-files/w25q80dv%20dl_revh_10022015.pdf
+#define W25Q80DV {\
+ .total_size = (1 << 20), /* 1 MiB */ \
+ .start_up_time_us = 5000, \
+ .manufacturer_id = 0xef, \
+ .memory_type = 0x40, \
+ .capacity = 0x14, \
+ .max_clock_speed_mhz = 104, \
+ .quad_enable_bit_mask = 0x02, \
+ .has_sector_protection = false, \
+ .supports_fast_read = true, \
+ .supports_qspi = true, \
+ .supports_qspi_writes = false, \
+ .write_status_register_split = false, \
+ .single_status_byte = false, \
+}
// Settings for the Winbond W25Q128JV-SQ 16MiB SPI flash. Note that JV-IM has a different .memory_type (0x70)
// Datasheet: https://www.winbond.com/resource-files/w25q128jv%20revf%2003272018%20plus.pdf
@@ -335,6 +453,63 @@ typedef struct {
.single_status_byte = false, \
}
+// Settings for the Everspin MR20H40 / MR25H40 magnetic non-volatile RAM
+// Datasheet: https://www.everspin.com/supportdocs/MR25H40CDFR
+#define MR2xH40 {\
+ .total_size = (1 << 22), /* 4 MiB */ \
+ .start_up_time_us = 10000, \
+ .manufacturer_id = 0xef, /*no JDEC*/ \
+ .memory_type = 0x40, /*no JDEC*/ \
+ .capacity = 0x14, /*no JDEC*/ \
+ .max_clock_speed_mhz = 10, \
+ .quad_enable_bit_mask = 0x00, \
+ .has_sector_protection = false, \
+ .supports_fast_read = false, \
+ .supports_qspi = false, \
+ .supports_qspi_writes = false, \
+ .write_status_register_split = false, \
+ .single_status_byte = true, \
+ .no_ready_bit = true, \
+ .no_erase_cmd = true, \
+ .no_reset_cmd = true, \
+}
+
+// Settings for the Macronix MX25L1606 2MiB SPI flash.
+// Datasheet:
+#define MX25L1606 {\
+ .total_size = (1 << 21), /* 2 MiB */ \
+ .start_up_time_us = 5000, \
+ .manufacturer_id = 0xc2, \
+ .memory_type = 0x20, \
+ .capacity = 0x15, \
+ .max_clock_speed_mhz = 8, \
+ .quad_enable_bit_mask = 0x40, \
+ .has_sector_protection = false, \
+ .supports_fast_read = true, \
+ .supports_qspi = true, \
+ .supports_qspi_writes = true, \
+ .write_status_register_split = false, \
+ .single_status_byte = true, \
+}
+
+// Settings for the Macronix MX25L3233F 4MiB SPI flash.
+// Datasheet: http://www.macronix.com/Lists/Datasheet/Attachments/7426/MX25L3233F,%203V,%2032Mb,%20v1.6.pdf
+#define MX25L3233F {\
+ .total_size = (1 << 22), /* 4 MiB */ \
+ .start_up_time_us = 5000, \
+ .manufacturer_id = 0xc2, \
+ .memory_type = 0x20, \
+ .capacity = 0x16, \
+ .max_clock_speed_mhz = 133, \
+ .quad_enable_bit_mask = 0x40, \
+ .has_sector_protection = false, \
+ .supports_fast_read = true, \
+ .supports_qspi = true, \
+ .supports_qspi_writes = true, \
+ .write_status_register_split = false, \
+ .single_status_byte = true, \
+}
+
// Settings for the Macronix MX25R6435F 8MiB SPI flash.
// Datasheet: http://www.macronix.com/Lists/Datasheet/Attachments/7428/MX25R6435F,%20Wide%20Range,%2064Mb,%20v1.4.pdf
// By default its in lower power mode which can only do 8mhz. In high power mode it can do 80mhz.
@@ -354,6 +529,43 @@ typedef struct {
.single_status_byte = true, \
}
+// Settings for the Macronix MX25R1635F 8MiB SPI flash.
+// Datasheet: https://www.macronix.com/Lists/Datasheet/Attachments/7595/MX25R1635F,%20Wide%20Range,%2016Mb,%20v1.6.pdf
+// In low power mode, quad operations can only run at 8 MHz.
+#define MX25R1635F {\
+ .total_size = (1 << 21), /* 2 MiB */ \
+ .start_up_time_us = 800, \
+ .manufacturer_id = 0xc2, \
+ .memory_type = 0x28, \
+ .capacity = 0x18, \
+ .max_clock_speed_mhz = 33, /* 8 mhz for dual/quad */ \
+ .quad_enable_bit_mask = 0x80, \
+ .has_sector_protection = false, \
+ .supports_fast_read = true, \
+ .supports_qspi = true, \
+ .supports_qspi_writes = true, \
+ .write_status_register_split = false, \
+ .single_status_byte = true, \
+}
+
+// Settings for the Macronix MX25L51245G 64MiB SPI flash.
+// Datasheet: https://www.macronix.com/Lists/Datasheet/Attachments/7437/MX25L51245G,%203V,%20512Mb,%20v1.6.pdf
+#define MX25L51245G {\
+ .total_size = (1 << 26), /* 64 MiB */ \
+ .start_up_time_us = 5000, \
+ .manufacturer_id = 0xc2, \
+ .memory_type = 0x20, \
+ .capacity = 0x1a, \
+ .max_clock_speed_mhz = 133, \
+ .quad_enable_bit_mask = 0x40, \
+ .has_sector_protection = false, \
+ .supports_fast_read = true, \
+ .supports_qspi = true, \
+ .supports_qspi_writes = true, \
+ .write_status_register_split = false, \
+ .single_status_byte = true, \
+}
+
// Settings for the Winbond W25Q128JV-PM 16MiB SPI flash. Note that JV-IM has a different .memory_type (0x70)
// Datasheet: https://www.winbond.com/resource-files/w25q128jv%20revf%2003272018%20plus.pdf
#define W25Q128JV_PM {\
@@ -371,4 +583,39 @@ typedef struct {
.write_status_register_split = false, \
}
+// Settings for the Winbond W25Q32FV 4MiB SPI flash.
+// Datasheet:http://www.winbond.com/resource-files/w25q32fv%20revj%2006032016.pdf?__locale=en
+#define W25Q32FV {\
+ .total_size = (1 << 22), /* 4 MiB */ \
+ .start_up_time_us = 5000, \
+ .manufacturer_id = 0xef, \
+ .memory_type = 0x40, \
+ .capacity = 0x16, \
+ .max_clock_speed_mhz = 104, \
+ .quad_enable_bit_mask = 0x00, \
+ .has_sector_protection = false, \
+ .supports_fast_read = true, \
+ .supports_qspi = false, \
+ .supports_qspi_writes = false, \
+ .write_status_register_split = false, \
+ .single_status_byte = false, \
+}
+
+// Settings for the ISSI IS25LP128F 16MiB SPI flash.
+// Datasheet: http://www.issi.com/WW/pdf/25LP-WP128F.pdf
+#define IS25LP128F {\
+ .total_size = (1 << 24), /* 16 MiB */ \
+ .start_up_time_us = 10000, \
+ .manufacturer_id = 0x9d, \
+ .memory_type = 0x60, \
+ .capacity = 0x18, \
+ .max_clock_speed_mhz = 133, \
+ .quad_enable_bit_mask = 0x02, \
+ .has_sector_protection = true, \
+ .supports_fast_read = true, \
+ .supports_qspi = true, \
+ .supports_qspi_writes = true, \
+ .write_status_register_split = false, \
+ .single_status_byte = true, \
+}
#endif // MICROPY_INCLUDED_ATMEL_SAMD_EXTERNAL_FLASH_DEVICES_H
diff --git a/supervisor/shared/external_flash/external_flash.c b/supervisor/shared/external_flash/external_flash.c
index 73b7f7f91..5bde7fd48 100644
--- a/supervisor/shared/external_flash/external_flash.c
+++ b/supervisor/shared/external_flash/external_flash.c
@@ -23,11 +23,11 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-#include "external_flash.h"
+#include "supervisor/shared/external_flash/external_flash.h"
#include <stdint.h>
#include <string.h>
-
+#include "supervisor/flash.h"
#include "supervisor/spi_flash_api.h"
#include "supervisor/shared/external_flash/common_commands.h"
#include "extmod/vfs.h"
@@ -57,9 +57,13 @@ 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) {
- uint8_t read_status_response[1] = {0x00};
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);
@@ -91,15 +95,18 @@ static bool write_flash(uint32_t address, const uint8_t* data, uint32_t data_len
}
// Don't bother writing if the data is all 1s. Thats equivalent to the flash
// state after an erase.
- bool all_ones = true;
- for (uint16_t i = 0; i < data_length; i++) {
- if (data[i] != 0xff) {
- all_ones = false;
- break;
+ 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;
}
- }
- if (all_ones) {
- return true;
}
for (uint32_t bytes_written = 0;
@@ -120,6 +127,10 @@ static bool write_flash(uint32_t address, const uint8_t* data, uint32_t data_len
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++) {
@@ -150,10 +161,16 @@ static bool page_erased(uint32_t sector_address) {
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;
}
@@ -191,25 +208,33 @@ void supervisor_flash_init(void) {
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;
+ 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;
}
- }
-
- 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};
@@ -217,14 +242,17 @@ void supervisor_flash_init(void) {
do {
spi_flash_read_command(CMD_READ_STATUS, read_status_response, 1);
} while ((read_status_response[0] & 0x1) != 0);
- // 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->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);
+ }
- spi_flash_command(CMD_ENABLE_RESET);
- spi_flash_command(CMD_RESET);
+ 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);
@@ -272,6 +300,9 @@ uint32_t supervisor_flash_get_block_count(void) {
// 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;
@@ -321,6 +352,10 @@ static bool allocate_ram_cache(void) {
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;
@@ -360,9 +395,25 @@ static bool allocate_ram_cache(void) {
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.
@@ -396,29 +447,21 @@ static bool flush_ram_cache(bool keep_cache) {
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) {
+ 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) {
- if (supervisor_cache != NULL) {
- free_memory(supervisor_cache);
- supervisor_cache = NULL;
- } else {
- m_free(MP_STATE_VM(flash_ram_cache));
- }
- MP_STATE_VM(flash_ram_cache) = NULL;
+ 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) {
- if (current_sector == NO_SECTOR_LOADED) {
- return;
- }
#ifdef MICROPY_HW_LED_MSC
port_pin_set_output_level(MICROPY_HW_LED_MSC, true);
#endif
@@ -436,9 +479,11 @@ static void spi_flash_flush_keep_cache(bool keep_cache) {
#endif
}
-// External flash function used. If called externally we assume we won't need
-// the cache after.
-void supervisor_flash_flush(void) {
+void supervisor_external_flash_flush(void) {
+ spi_flash_flush_keep_cache(true);
+}
+
+void supervisor_flash_release_cache(void) {
spi_flash_flush_keep_cache(false);
}
@@ -502,7 +547,7 @@ bool external_flash_write_block(const uint8_t *data, uint32_t block) {
return write_flash(address, data, FILESYSTEM_BLOCK_SIZE);
}
if (current_sector != NO_SECTOR_LOADED) {
- spi_flash_flush_keep_cache(true);
+ supervisor_flash_flush();
}
if (MP_STATE_VM(flash_ram_cache) == NULL && !allocate_ram_cache()) {
erase_sector(flash_device->total_size - SPI_FLASH_ERASE_SIZE);
diff --git a/supervisor/shared/external_flash/external_flash.h b/supervisor/shared/external_flash/external_flash.h
index 72b619a2a..db5c677eb 100644
--- a/supervisor/shared/external_flash/external_flash.h
+++ b/supervisor/shared/external_flash/external_flash.h
@@ -45,4 +45,6 @@
#define SPI_FLASH_MAX_BAUDRATE 8000000
#endif
+void supervisor_external_flash_flush(void);
+
#endif // MICROPY_INCLUDED_SUPERVISOR_SHARED_EXTERNAL_FLASH_EXTERNAL_FLASH_H
diff --git a/supervisor/shared/external_flash/spi_flash.c b/supervisor/shared/external_flash/spi_flash.c
index ec7101bc9..67e64c970 100644
--- a/supervisor/shared/external_flash/spi_flash.c
+++ b/supervisor/shared/external_flash/spi_flash.c
@@ -36,33 +36,33 @@
#include "py/mpconfig.h"
digitalio_digitalinout_obj_t cs_pin;
-busio_spi_obj_t spi;
+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 void flash_enable(void) {
- while (!common_hal_busio_spi_try_lock(&spi)) {}
+ while (!common_hal_busio_spi_try_lock(&supervisor_flash_spi_bus)) {}
common_hal_digitalio_digitalinout_set_value(&cs_pin, 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(&spi);
+ 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) {
flash_enable();
- bool status = common_hal_busio_spi_write(&spi, command, command_length);
+ 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(&spi, data_out, data_in, data_length);
+ 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(&spi, data_out, data_length, 0xff);
+ 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(&spi, data_in, data_length);
+ status = common_hal_busio_spi_write(&supervisor_flash_spi_bus, data_in, data_length);
}
}
flash_disable();
@@ -103,10 +103,10 @@ bool spi_flash_write_data(uint32_t address, uint8_t* data, uint32_t data_length)
// Write the SPI flash write address into the bytes following the command byte.
address_to_bytes(address, request + 1);
flash_enable();
- common_hal_busio_spi_configure(&spi, spi_flash_baudrate, 0, 0, 8);
- bool status = common_hal_busio_spi_write(&spi, request, 4);
+ common_hal_busio_spi_configure(&supervisor_flash_spi_bus, spi_flash_baudrate, 0, 0, 8);
+ bool status = common_hal_busio_spi_write(&supervisor_flash_spi_bus, request, 4);
if (status) {
- status = common_hal_busio_spi_write(&spi, data, data_length);
+ status = common_hal_busio_spi_write(&supervisor_flash_spi_bus, data, data_length);
}
flash_disable();
return status;
@@ -122,23 +122,27 @@ bool spi_flash_read_data(uint32_t address, uint8_t* data, uint32_t data_length)
// Write the SPI flash write address into the bytes following the command byte.
address_to_bytes(address, request + 1);
flash_enable();
- common_hal_busio_spi_configure(&spi, spi_flash_baudrate, 0, 0, 8);
- bool status = common_hal_busio_spi_write(&spi, request, command_length);
+ common_hal_busio_spi_configure(&supervisor_flash_spi_bus, spi_flash_baudrate, 0, 0, 8);
+ bool status = common_hal_busio_spi_write(&supervisor_flash_spi_bus, request, command_length);
if (status) {
- status = common_hal_busio_spi_read(&spi, data, data_length, 0xff);
+ 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);
- common_hal_busio_spi_construct(&spi, SPI_FLASH_SCK_PIN, SPI_FLASH_MOSI_PIN, SPI_FLASH_MISO_PIN);
- common_hal_busio_spi_never_reset(&spi);
+ 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);
+ common_hal_busio_spi_never_reset(&supervisor_flash_spi_bus);
}
void spi_flash_init_device(const external_flash_device* device) {
diff --git a/supervisor/shared/filesystem.c b/supervisor/shared/filesystem.c
index d968f4798..f6b94e38b 100644
--- a/supervisor/shared/filesystem.c
+++ b/supervisor/shared/filesystem.c
@@ -24,6 +24,8 @@
* THE SOFTWARE.
*/
+#include "supervisor/filesystem.h"
+
#include "extmod/vfs_fat.h"
#include "lib/oofatfs/ff.h"
#include "lib/oofatfs/diskio.h"
@@ -35,12 +37,51 @@
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);
+ #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
void filesystem_init(bool create_allowed, bool force_create) {
@@ -63,13 +104,22 @@ void filesystem_init(bool create_allowed, bool force_create) {
}
// set label
+#ifdef CIRCUITPY_DRIVE_LABEL
+ f_setlabel(&vfs_fat->fatfs, CIRCUITPY_DRIVE_LABEL);
+#else
f_setlabel(&vfs_fat->fatfs, "CIRCUITPY");
+#endif
// inhibit file indexing on MacOS
f_mkdir(&vfs_fat->fatfs, "/.fseventsd");
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
+ f_mkdir(&vfs_fat->fatfs, "/lib");
// and ensure everything is flushed
supervisor_flash_flush();
@@ -89,19 +139,49 @@ void filesystem_init(bool create_allowed, bool force_create) {
}
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_writable_by_python(bool writable) {
+void filesystem_set_internal_writable_by_usb(bool writable) {
fs_user_mount_t *vfs = &_internal_vfs;
- if (writable) {
+ 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->flags |= FSUSER_USB_WRITABLE;
} else {
vfs->flags &= ~FSUSER_USB_WRITABLE;
}
}
+bool filesystem_is_writable_by_python(fs_user_mount_t *vfs) {
+ return (vfs->flags & FSUSER_CONCURRENT_WRITE_PROTECTED) == 0 ||
+ (vfs->flags & FSUSER_USB_WRITABLE) == 0;
+}
+
+bool filesystem_is_writable_by_usb(fs_user_mount_t *vfs) {
+ return (vfs->flags & FSUSER_CONCURRENT_WRITE_PROTECTED) == 0 ||
+ (vfs->flags & FSUSER_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->flags |= FSUSER_CONCURRENT_WRITE_PROTECTED;
+ } else {
+ vfs->flags &= ~FSUSER_CONCURRENT_WRITE_PROTECTED;
+ }
+}
+
bool filesystem_present(void) {
return true;
}
diff --git a/supervisor/shared/flash.c b/supervisor/shared/flash.c
index c8921aa85..298e7d83e 100644
--- a/supervisor/shared/flash.c
+++ b/supervisor/shared/flash.c
@@ -28,38 +28,19 @@
#include "extmod/vfs_fat.h"
#include "py/runtime.h"
#include "lib/oofatfs/ff.h"
+#include "supervisor/shared/tick.h"
#define VFS_INDEX 0
#define PART1_START_BLOCK (0x1)
-void supervisor_flash_set_usb_writable(bool usb_writable) {
- mp_vfs_mount_t* current_mount = MP_STATE_VM(vfs_mount_table);
- for (uint8_t i = 0; current_mount != NULL; i++) {
- if (i == VFS_INDEX) {
- break;
- }
- current_mount = current_mount->next;
- }
- if (current_mount == NULL) {
- return;
- }
- fs_user_mount_t *vfs = (fs_user_mount_t *) current_mount->obj;
-
- if (usb_writable) {
- vfs->flags |= FSUSER_USB_WRITABLE;
- } else {
- vfs->flags &= ~FSUSER_USB_WRITABLE;
- }
-}
-
// 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) {
+STATIC mp_obj_t supervisor_flash_obj_make_new(const mp_obj_type_t *type, size_t n_args, const mp_obj_t *args, mp_map_t *kw_args) {
// check arguments
- mp_arg_check_num(n_args, n_kw, 0, 0, false);
+ mp_arg_check_num(n_args, kw_args, 0, 0, false);
// return singleton object
return (mp_obj_t)&supervisor_flash_obj;
@@ -130,6 +111,8 @@ mp_uint_t flash_read_blocks(uint8_t *dest, uint32_t block_num, uint32_t num_bloc
return supervisor_flash_read_blocks(dest, block_num - PART1_START_BLOCK, num_blocks);
}
+volatile bool filesystem_dirty = false;
+
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) {
@@ -138,10 +121,28 @@ mp_uint_t flash_write_blocks(const uint8_t *src, uint32_t block_num, uint32_t nu
// 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);
diff --git a/supervisor/shared/internal_flash.h b/supervisor/shared/internal_flash.h
new file mode 100644
index 000000000..80257a290
--- /dev/null
+++ b/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/supervisor/shared/memory.c b/supervisor/shared/memory.c
index 6c8be3be8..8ae8a1699 100755
--- a/supervisor/shared/memory.c
+++ b/supervisor/shared/memory.c
@@ -25,24 +25,28 @@
*/
#include "supervisor/memory.h"
+#include "supervisor/port.h"
#include <stddef.h>
-#define CIRCUITPY_SUPERVISOR_ALLOC_COUNT 8
+#include "supervisor/shared/display.h"
+
+#define CIRCUITPY_SUPERVISOR_ALLOC_COUNT (12)
static supervisor_allocation allocations[CIRCUITPY_SUPERVISOR_ALLOC_COUNT];
// We use uint32_t* to ensure word (4 byte) alignment.
uint32_t* low_address;
uint32_t* high_address;
-extern uint32_t _ebss;
-extern uint32_t _estack;
void memory_init(void) {
- low_address = &_ebss;
- high_address = &_estack;
+ low_address = port_heap_get_bottom();
+ high_address = port_heap_get_top();
}
void free_memory(supervisor_allocation* allocation) {
+ if (allocation == NULL) {
+ return;
+ }
int32_t index = 0;
bool found = false;
for (index = 0; index < CIRCUITPY_SUPERVISOR_ALLOC_COUNT; index++) {
@@ -78,6 +82,15 @@ void free_memory(supervisor_allocation* allocation) {
allocation->ptr = NULL;
}
+supervisor_allocation* allocation_from_ptr(void *ptr) {
+ for (size_t index = 0; index < CIRCUITPY_SUPERVISOR_ALLOC_COUNT; index++) {
+ if (allocations[index].ptr == ptr) {
+ return &allocations[index];
+ }
+ }
+ return NULL;
+}
+
supervisor_allocation* allocate_remaining_memory(void) {
if (low_address == high_address) {
return NULL;
@@ -114,3 +127,9 @@ supervisor_allocation* allocate_memory(uint32_t length, bool high) {
alloc->length = length;
return alloc;
}
+
+void supervisor_move_memory(void) {
+ #if CIRCUITPY_DISPLAYIO
+ supervisor_display_move_memory();
+ #endif
+}
diff --git a/supervisor/shared/rgb_led_colors.h b/supervisor/shared/rgb_led_colors.h
index cab702981..c723fbab3 100644
--- a/supervisor/shared/rgb_led_colors.h
+++ b/supervisor/shared/rgb_led_colors.h
@@ -1,12 +1,18 @@
-#define BLACK 0x000000
-#define GREEN 0x003000
-#define BLUE 0x000030
-#define CYAN 0x003030
-#define RED 0x300000
-#define ORANGE 0x302000
-#define YELLOW 0x303000
-#define PURPLE 0x300030
-#define WHITE 0x303030
+
+#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 BOOT_RUNNING BLUE
#define MAIN_RUNNING GREEN
@@ -31,4 +37,5 @@
#define NAME_ERROR WHITE
#define OS_ERROR ORANGE
#define VALUE_ERROR PURPLE
+#define MPY_ERROR BLUE
#define OTHER_ERROR YELLOW
diff --git a/supervisor/shared/rgb_led_status.c b/supervisor/shared/rgb_led_status.c
index 186e33ab9..f3c210647 100644
--- a/supervisor/shared/rgb_led_status.c
+++ b/supervisor/shared/rgb_led_status.c
@@ -25,8 +25,9 @@
*/
#include "mphalport.h"
-#include "common-hal/microcontroller/Pin.h"
+#include "shared-bindings/microcontroller/Pin.h"
#include "rgb_led_status.h"
+#include "supervisor/shared/tick.h"
#ifdef MICROPY_HW_NEOPIXEL
uint8_t rgb_status_brightness = 63;
@@ -38,23 +39,70 @@ static digitalio_digitalinout_obj_t status_neopixel;
#if defined(MICROPY_HW_APA102_MOSI) && defined(MICROPY_HW_APA102_SCK)
-uint8_t rgb_status_brightness = 255;
-static uint8_t status_apa102_color[12] = {0, 0, 0, 0, 0xff, 0, 0, 0};
-#ifdef CIRCUITPY_BITBANG_APA102
+uint8_t rgb_status_brightness = 50;
+
+#define APA102_BUFFER_LENGTH 12
+static uint8_t status_apa102_color[APA102_BUFFER_LENGTH] = {0, 0, 0, 0, 0xff, 0, 0, 0, 0xff, 0xff, 0xff, 0xff};
+
+#if CIRCUITPY_BITBANG_APA102
#include "shared-bindings/bitbangio/SPI.h"
#include "shared-module/bitbangio/types.h"
-static bitbangio_spi_obj_t status_apa102;
+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;
+busio_spi_obj_t status_apa102 = {
+ .base = {
+ .type = &busio_spi_type,
+ },
+};
+#endif
#endif
+
+#if defined(CP_RGB_STATUS_R) || defined(CP_RGB_STATUS_G) || defined(CP_RGB_STATUS_B)
+#define CP_RGB_STATUS_LED
+
+#include "shared-bindings/pulseio/PWMOut.h"
+#include "shared-bindings/microcontroller/Pin.h"
+
+pulseio_pwmout_obj_t rgb_status_r = {
+ .base = {
+ .type = &pulseio_pwmout_type,
+ },
+};
+pulseio_pwmout_obj_t rgb_status_g = {
+ .base = {
+ .type = &pulseio_pwmout_type,
+ },
+};
+pulseio_pwmout_obj_t rgb_status_b = {
+ .base = {
+ .type = &pulseio_pwmout_type,
+ },
+};
+
+uint8_t rgb_status_brightness = 0xFF;
+
+uint16_t status_rgb_color[3] = {
+ 0 /* red */, 0 /* green */, 0 /* blue */
+};
#endif
-#if defined(MICROPY_HW_NEOPIXEL) || (defined(MICROPY_HW_APA102_MOSI) && defined(MICROPY_HW_APA102_SCK))
+#if defined(MICROPY_HW_NEOPIXEL) || (defined(MICROPY_HW_APA102_MOSI) && defined(MICROPY_HW_APA102_SCK)) || (defined(CP_RGB_STATUS_LED))
static uint32_t current_status_color = 0;
#endif
+static bool rgb_led_status_init_in_progress = false;
void rgb_led_status_init() {
+ if (rgb_led_status_init_in_progress) {
+ // Avoid recursion.
+ return;
+ }
+ rgb_led_status_init_in_progress = true;
+
#ifdef MICROPY_HW_NEOPIXEL
common_hal_digitalio_digitalinout_construct(&status_neopixel, MICROPY_HW_NEOPIXEL);
// Pretend we aren't using the pins. digitalio.DigitalInOut
@@ -63,56 +111,90 @@ void rgb_led_status_init() {
common_hal_digitalio_digitalinout_switch_to_output(&status_neopixel, false, DRIVE_MODE_PUSH_PULL);
#endif
#if defined(MICROPY_HW_APA102_MOSI) && defined(MICROPY_HW_APA102_SCK)
- #ifdef CIRCUITPY_BITBANG_APA102
+ #if CIRCUITPY_BITBANG_APA102
shared_module_bitbangio_spi_construct(&status_apa102,
MICROPY_HW_APA102_SCK,
MICROPY_HW_APA102_MOSI,
- mp_const_none);
+ NULL);
#else
if (!common_hal_busio_spi_deinited(&status_apa102)) {
- // Don't use spi_deinit because that leads to infinite
- // recursion because reset_pin_number may call
- // rgb_led_status_init.
- spi_m_sync_disable(&status_apa102.spi_desc);
+ // This may call us recursively if common_hal_reset_pin() is called,
+ // The rgb_led_status_init_in_progress guard will prevent further recursion.
+ common_hal_busio_spi_deinit(&status_apa102);
}
common_hal_busio_spi_construct(&status_apa102,
MICROPY_HW_APA102_SCK,
MICROPY_HW_APA102_MOSI,
- mp_const_none);
+ NULL);
+ common_hal_busio_spi_never_reset(&status_apa102);
#endif
// Pretend we aren't using the pins. bitbangio.SPI will
// mark them as used.
apa102_mosi_in_use = false;
apa102_sck_in_use = false;
- #ifdef CIRCUITPY_BITBANG_APA102
+ #if CIRCUITPY_BITBANG_APA102
shared_module_bitbangio_spi_try_lock(&status_apa102);
- shared_module_bitbangio_spi_configure(&status_apa102, 100000, 0, 1, 8);
+ // 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, 100000, 0, 1, 8);
+ common_hal_busio_spi_configure(&status_apa102, 1000000, 0, 0, 8);
#endif
#endif
- #if defined(MICROPY_HW_NEOPIXEL) || (defined(MICROPY_HW_APA102_MOSI) && defined(MICROPY_HW_APA102_SCK))
+
+ #if defined(CP_RGB_STATUS_LED)
+ if (common_hal_mcu_pin_is_free(CP_RGB_STATUS_R)) {
+ pwmout_result_t red_result = common_hal_pulseio_pwmout_construct(&rgb_status_r, CP_RGB_STATUS_R, 0, 50000, false);
+
+ if (PWMOUT_OK == red_result) {
+ common_hal_pulseio_pwmout_never_reset(&rgb_status_r);
+ }
+ }
+
+ if (common_hal_mcu_pin_is_free(CP_RGB_STATUS_G)) {
+ pwmout_result_t green_result = common_hal_pulseio_pwmout_construct(&rgb_status_g, CP_RGB_STATUS_G, 0, 50000, false);
+
+ if (PWMOUT_OK == green_result) {
+ common_hal_pulseio_pwmout_never_reset(&rgb_status_g);
+ }
+ }
+
+ if (common_hal_mcu_pin_is_free(CP_RGB_STATUS_B)) {
+ pwmout_result_t blue_result = common_hal_pulseio_pwmout_construct(&rgb_status_b, CP_RGB_STATUS_B, 0, 50000, false);
+
+ if (PWMOUT_OK == blue_result) {
+ common_hal_pulseio_pwmout_never_reset(&rgb_status_b);
+ }
+ }
+ #endif
+
+ #if defined(MICROPY_HW_NEOPIXEL) || (defined(MICROPY_HW_APA102_MOSI) && defined(MICROPY_HW_APA102_SCK)) || (defined(CP_RGB_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
+
+ rgb_led_status_init_in_progress = false;
}
void reset_status_led() {
#ifdef MICROPY_HW_NEOPIXEL
- reset_pin_number(MICROPY_HW_NEOPIXEL->number);
+ common_hal_reset_pin(MICROPY_HW_NEOPIXEL);
#endif
#if defined(MICROPY_HW_APA102_MOSI) && defined(MICROPY_HW_APA102_SCK)
- reset_pin_number(MICROPY_HW_APA102_MOSI->number);
- reset_pin_number(MICROPY_HW_APA102_SCK->number);
+ common_hal_reset_pin(MICROPY_HW_APA102_MOSI);
+ common_hal_reset_pin(MICROPY_HW_APA102_SCK);
+ #endif
+ #if defined(CP_RGB_STATUS_LED)
+ // TODO: Support sharing status LED with user.
#endif
}
void new_status_color(uint32_t rgb) {
- #if defined(MICROPY_HW_NEOPIXEL) || (defined(MICROPY_HW_APA102_MOSI) && defined(MICROPY_HW_APA102_SCK))
+ #if defined(MICROPY_HW_NEOPIXEL) || (defined(MICROPY_HW_APA102_MOSI) && defined(MICROPY_HW_APA102_SCK)) || (defined(CP_RGB_STATUS_LED))
if (current_status_color == rgb) {
return;
}
@@ -137,16 +219,36 @@ void new_status_color(uint32_t rgb) {
status_apa102_color[6] = (rgb_adjusted >> 8) & 0xff;
status_apa102_color[7] = (rgb_adjusted >> 16) & 0xff;
- #ifdef CIRCUITPY_BITBANG_APA102
- shared_module_bitbangio_spi_write(&status_apa102, status_apa102_color, 8);
+ #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, 8);
+ common_hal_busio_spi_write(&status_apa102, status_apa102_color, APA102_BUFFER_LENGTH);
#endif
#endif
+
+ #if defined(CP_RGB_STATUS_LED)
+ uint8_t red_u8 = (rgb_adjusted >> 16) & 0xFF;
+ uint8_t green_u8 = (rgb_adjusted >> 8) & 0xFF;
+ uint8_t blue_u8 = rgb_adjusted & 0xFF;
+
+ #if defined(CP_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_pulseio_pwmout_set_duty_cycle(&rgb_status_r, status_rgb_color[0]);
+ common_hal_pulseio_pwmout_set_duty_cycle(&rgb_status_g, status_rgb_color[1]);
+ common_hal_pulseio_pwmout_set_duty_cycle(&rgb_status_b, status_rgb_color[2]);
+ #endif
}
void temp_status_color(uint32_t rgb) {
- #if defined(MICROPY_HW_NEOPIXEL) || (defined(MICROPY_HW_APA102_MOSI) && defined(MICROPY_HW_APA102_SCK))
+ #if defined(MICROPY_HW_NEOPIXEL) || (defined(MICROPY_HW_APA102_MOSI) && defined(MICROPY_HW_APA102_SCK)) || (defined(CP_RGB_STATUS_LED))
uint32_t rgb_adjusted = rgb;
rgb_adjusted = color_brightness(rgb, rgb_status_brightness);
#endif
@@ -161,30 +263,77 @@ void temp_status_color(uint32_t rgb) {
if (apa102_mosi_in_use || apa102_sck_in_use) {
return;
}
- uint8_t colors[12] = {0, 0, 0, 0, 0xff, rgb_adjusted & 0xff, (rgb_adjusted >> 8) & 0xff, (rgb_adjusted >> 16) & 0xff, 0x0, 0x0, 0x0, 0x0};
- #ifdef CIRCUITPY_BITBANG_APA102
- shared_module_bitbangio_spi_write(&status_apa102, colors, 12);
+ uint8_t colors[APA102_BUFFER_LENGTH] = {0, 0, 0, 0, 0xff, rgb_adjusted & 0xff, (rgb_adjusted >> 8) & 0xff, (rgb_adjusted >> 16) & 0xff, 0xff, 0xff, 0xff, 0xff};
+ #if CIRCUITPY_BITBANG_APA102
+ shared_module_bitbangio_spi_write(&status_apa102, colors, APA102_BUFFER_LENGTH);
#else
- common_hal_busio_spi_write(&status_apa102, colors, 12);
+ common_hal_busio_spi_write(&status_apa102, colors, APA102_BUFFER_LENGTH);
#endif
#endif
+ #if defined(CP_RGB_STATUS_LED)
+ uint8_t red_u8 = (rgb_adjusted >> 16) & 0xFF;
+ uint8_t green_u8 = (rgb_adjusted >> 8) & 0xFF;
+ uint8_t blue_u8 = rgb_adjusted & 0xFF;
+
+ uint16_t temp_status_color_rgb[3] = {0};
+
+ #if defined(CP_RGB_STATUS_INVERTED_PWM)
+ temp_status_color_rgb[0] = (1 << 16) - 1 - ((uint16_t) (red_u8 << 8) + red_u8);
+ temp_status_color_rgb[1] = (1 << 16) - 1 - ((uint16_t) (green_u8 << 8) + green_u8);
+ temp_status_color_rgb[2] = (1 << 16) - 1 - ((uint16_t) (blue_u8 << 8) + blue_u8);
+ #else
+ temp_status_color_rgb[0] = (uint16_t) (red_u8 << 8) + red_u8;
+ temp_status_color_rgb[1] = (uint16_t) (green_u8 << 8) + green_u8;
+ temp_status_color_rgb[2] = (uint16_t) (blue_u8 << 8) + blue_u8;
+ #endif
+
+ common_hal_pulseio_pwmout_set_duty_cycle(&rgb_status_r, temp_status_color_rgb[0]);
+ common_hal_pulseio_pwmout_set_duty_cycle(&rgb_status_g, temp_status_color_rgb[1]);
+ common_hal_pulseio_pwmout_set_duty_cycle(&rgb_status_b, temp_status_color_rgb[2]);
+ #endif
}
void clear_temp_status() {
#ifdef MICROPY_HW_NEOPIXEL
+ if (neopixel_in_use) {
+ return;
+ }
common_hal_neopixel_write(&status_neopixel, status_neopixel_color, 3);
#endif
#if defined(MICROPY_HW_APA102_MOSI) && defined(MICROPY_HW_APA102_SCK)
- #ifdef CIRCUITPY_BITBANG_APA102
- shared_module_bitbangio_spi_write(&status_apa102, status_apa102_color, 8);
+ if (apa102_mosi_in_use || apa102_sck_in_use) {
+ return;
+ }
+ #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, 8);
+ common_hal_busio_spi_write(&status_apa102, status_apa102_color, APA102_BUFFER_LENGTH);
#endif
#endif
+ #if defined(CP_RGB_STATUS_LED)
+
+ uint16_t red = 0;
+ uint16_t green = 0;
+ uint16_t blue = 0;
+
+ #if defined(CP_RGB_STATUS_INVERTED_PWM)
+ red = (1 << 16) - 1 - status_rgb_color[0];
+ green = (1 << 16) - 1 - status_rgb_color[1];
+ blue = (1 << 16) - 1 - status_rgb_color[2];
+ #else
+ red = status_rgb_color[0];
+ green = status_rgb_color[1];
+ blue = status_rgb_color[2];
+ #endif
+
+ common_hal_pulseio_pwmout_set_duty_cycle(&rgb_status_r, red);
+ common_hal_pulseio_pwmout_set_duty_cycle(&rgb_status_g, green);
+ common_hal_pulseio_pwmout_set_duty_cycle(&rgb_status_b, blue);
+ #endif
}
uint32_t color_brightness(uint32_t color, uint8_t brightness) {
- #if defined(MICROPY_HW_NEOPIXEL) || (defined(MICROPY_HW_APA102_MOSI) && defined(MICROPY_HW_APA102_SCK))
+ #if defined(MICROPY_HW_NEOPIXEL) || (defined(MICROPY_HW_APA102_MOSI) && defined(MICROPY_HW_APA102_SCK)) || (defined(CP_RGB_STATUS_LED))
uint32_t result = ((color & 0xff0000) * brightness / 255) & 0xff0000;
result += ((color & 0xff00) * brightness / 255) & 0xff00;
result += ((color & 0xff) * brightness / 255) & 0xff;
@@ -195,8 +344,14 @@ uint32_t color_brightness(uint32_t color, uint8_t brightness) {
}
void set_rgb_status_brightness(uint8_t level){
- #if defined(MICROPY_HW_NEOPIXEL) || (defined(MICROPY_HW_APA102_MOSI) && defined(MICROPY_HW_APA102_SCK))
+ #if defined(MICROPY_HW_NEOPIXEL) || (defined(MICROPY_HW_APA102_MOSI) && defined(MICROPY_HW_APA102_SCK)) || (defined(CP_RGB_STATUS_LED))
rgb_status_brightness = level;
+ uint32_t current_color = current_status_color;
+ // Temporarily change the current color global to force the new_status_color call to update the
+ // LED. Usually duplicate calls of the same color are ignored without regard to brightness
+ // changes.
+ current_status_color = 0;
+ new_status_color(current_color);
#endif
}
@@ -204,9 +359,9 @@ void prep_rgb_status_animation(const pyexec_result_t* result,
bool found_main,
safe_mode_t safe_mode,
rgb_status_animation_t* status) {
- #if defined(MICROPY_HW_NEOPIXEL) || (defined(MICROPY_HW_APA102_MOSI) && defined(MICROPY_HW_APA102_SCK))
+ #if defined(MICROPY_HW_NEOPIXEL) || (defined(MICROPY_HW_APA102_MOSI) && defined(MICROPY_HW_APA102_SCK)) || (defined(CP_RGB_STATUS_LED))
new_status_color(ALL_DONE);
- status->pattern_start = ticks_ms;
+ status->pattern_start = supervisor_ticks_ms32();
status->safe_mode = safe_mode;
status->found_main = found_main;
status->total_exception_cycle = 0;
@@ -241,6 +396,8 @@ void prep_rgb_status_animation(const pyexec_result_t* result,
status->exception_color = OS_ERROR;
} else if (mp_obj_is_subclass_fast(result->exception_type, &mp_type_ValueError)) {
status->exception_color = VALUE_ERROR;
+ } else if (mp_obj_is_subclass_fast(result->exception_type, &mp_type_MpyError)) {
+ status->exception_color = MPY_ERROR;
} else {
status->exception_color = OTHER_ERROR;
}
@@ -248,12 +405,12 @@ void prep_rgb_status_animation(const pyexec_result_t* result,
}
void tick_rgb_status_animation(rgb_status_animation_t* status) {
- #if defined(MICROPY_HW_NEOPIXEL) || (defined(MICROPY_HW_APA102_MOSI) && defined(MICROPY_HW_APA102_SCK))
- uint32_t tick_diff = ticks_ms - status->pattern_start;
+ #if defined(MICROPY_HW_NEOPIXEL) || (defined(MICROPY_HW_APA102_MOSI) && defined(MICROPY_HW_APA102_SCK)) || (defined(CP_RGB_STATUS_LED))
+ uint32_t tick_diff = supervisor_ticks_ms32() - status->pattern_start;
if (status->ok) {
// All is good. Ramp ALL_DONE up and down.
if (tick_diff > ALL_GOOD_CYCLE_MS) {
- status->pattern_start = ticks_ms;
+ status->pattern_start = supervisor_ticks_ms32();
tick_diff = 0;
}
@@ -268,7 +425,7 @@ void tick_rgb_status_animation(rgb_status_animation_t* status) {
}
} else {
if (tick_diff > status->total_exception_cycle) {
- status->pattern_start = ticks_ms;
+ status->pattern_start = supervisor_ticks_ms32();
tick_diff = 0;
}
// First flash the file color.
diff --git a/supervisor/shared/rgb_led_status.h b/supervisor/shared/rgb_led_status.h
index 9542b3197..e4e1981a2 100644
--- a/supervisor/shared/rgb_led_status.h
+++ b/supervisor/shared/rgb_led_status.h
@@ -36,13 +36,15 @@
#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.
-#if defined(MICROPY_HW_APA102_MOSI) && defined(MICROPY_HW_APA102_SCK) && !defined(CIRCUITPY_BITBANG_APA102)
+#if defined(MICROPY_HW_APA102_MOSI) && defined(MICROPY_HW_APA102_SCK) && !CIRCUITPY_BITBANG_APA102
#include "common-hal/busio/SPI.h"
extern busio_spi_obj_t status_apa102;
#endif
diff --git a/supervisor/shared/safe_mode.c b/supervisor/shared/safe_mode.c
new file mode 100644
index 000000000..a167ab392
--- /dev/null
+++ b/supervisor/shared/safe_mode.c
@@ -0,0 +1,171 @@
+/*
+ * 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"
+
+#include "shared-bindings/digitalio/DigitalInOut.h"
+
+#include "supervisor/serial.h"
+#include "supervisor/shared/rgb_led_colors.h"
+#include "supervisor/shared/rgb_led_status.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;
+ }
+ port_set_saved_word(SAFE_MODE_DATA_GUARD | (MANUAL_SAFE_MODE << 8));
+ // Wait for a while to allow for reset.
+ temp_status_color(SAFE_MODE);
+ #ifdef MICROPY_HW_LED_STATUS
+ digitalio_digitalinout_obj_t status_led;
+ common_hal_digitalio_digitalinout_construct(&status_led, MICROPY_HW_LED_STATUS);
+ common_hal_digitalio_digitalinout_switch_to_output(&status_led, true, DRIVE_MODE_PUSH_PULL);
+ #endif
+ uint64_t start_ticks = supervisor_ticks_ms64();
+ uint64_t diff = 0;
+ while (diff < 700) {
+ #ifdef MICROPY_HW_LED_STATUS
+ // Blink on for 100, off for 100, on for 100, off for 100 and on for 200
+ common_hal_digitalio_digitalinout_set_value(&status_led, diff > 100 && diff / 100 != 2 && diff / 100 != 4);
+ #endif
+ diff = supervisor_ticks_ms64() - start_ticks;
+ }
+ #ifdef MICROPY_HW_LED_STATUS
+ common_hal_digitalio_digitalinout_deinit(&status_led);
+ #endif
+ clear_temp_status();
+ port_set_saved_word(SAFE_MODE_DATA_GUARD);
+ 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();
+}
+
+
+
+#define FILE_AN_ISSUE translate("\nPlease file an issue with the contents of your CIRCUITPY drive at \nhttps://github.com/adafruit/circuitpython/issues\n")
+
+void print_safe_mode_message(safe_mode_t reason) {
+ if (reason == NO_SAFE_MODE) {
+ return;
+ }
+ serial_write("\n");
+ // Output a user safe mode string if it's set.
+ #ifdef BOARD_USER_SAFE_MODE
+ if (reason == USER_SAFE_MODE) {
+ serial_write_compressed(translate("You requested starting safe mode by "));
+ serial_write(BOARD_USER_SAFE_MODE_ACTION);
+ serial_write_compressed(translate("\nTo exit, please reset the board without "));
+ serial_write(BOARD_USER_SAFE_MODE_ACTION);
+ serial_write("\n");
+ } else
+ #endif
+ switch (reason) {
+ case MANUAL_SAFE_MODE:
+ serial_write_compressed(translate("CircuitPython is in safe mode because you pressed the reset button during boot. Press again to exit safe mode.\n"));
+ return;
+ case PROGRAMMATIC_SAFE_MODE:
+ serial_write_compressed(translate("The `microcontroller` module was used to boot into safe mode. Press reset to exit safe mode.\n"));
+ return;
+ default:
+ break;
+ }
+
+ serial_write_compressed(translate("You are in safe mode: something unanticipated happened.\n"));
+ switch (reason) {
+ case BROWNOUT:
+ serial_write_compressed(translate("The microcontroller's power dipped. Make sure your power supply provides\nenough power for the whole circuit and press reset (after ejecting CIRCUITPY).\n"));
+ return;
+ case HEAP_OVERWRITTEN:
+ serial_write_compressed(translate("The CircuitPython heap was corrupted because the stack was too small.\nPlease increase the stack size if you know how, or if not:"));
+ serial_write_compressed(FILE_AN_ISSUE);
+ return;
+ default:
+ break;
+ }
+
+ serial_write_compressed(translate("CircuitPython core code crashed hard. Whoops!\n"));
+ switch (reason) {
+ case HARD_CRASH:
+ serial_write_compressed(translate("Crash into the HardFault_Handler."));
+ return;
+ case MICROPY_NLR_JUMP_FAIL:
+ serial_write_compressed(translate("MicroPython NLR jump failed. Likely memory corruption."));
+ return;
+ case MICROPY_FATAL_ERROR:
+ serial_write_compressed(translate("MicroPython fatal error."));
+ break;
+ case GC_ALLOC_OUTSIDE_VM:
+ serial_write_compressed(translate("Attempted heap allocation when MicroPython VM not running."));
+ break;
+ case NORDIC_SOFT_DEVICE_ASSERT:
+ serial_write_compressed(translate("Nordic Soft Device failure assertion."));
+ break;
+ case FLASH_WRITE_FAIL:
+ serial_write_compressed(translate("Failed to write internal flash."));
+ break;
+ case MEM_MANAGE:
+ serial_write_compressed(translate("Invalid memory access."));
+ break;
+ case WATCHDOG_RESET:
+ serial_write_compressed(translate("Watchdog timer expired."));
+ break;
+ default:
+ serial_write_compressed(translate("Unknown reason."));
+ break;
+ }
+ serial_write_compressed(FILE_AN_ISSUE);
+}
diff --git a/supervisor/shared/safe_mode.h b/supervisor/shared/safe_mode.h
new file mode 100644
index 000000000..c160739ae
--- /dev/null
+++ b/supervisor/shared/safe_mode.h
@@ -0,0 +1,54 @@
+/*
+ * 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
+
+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,
+} 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);
+
+void print_safe_mode_message(safe_mode_t reason);
+
+#endif // MICROPY_INCLUDED_SUPERVISOR_SAFE_MODE_H
diff --git a/supervisor/shared/serial.c b/supervisor/shared/serial.c
index c57688ddc..513022667 100644
--- a/supervisor/shared/serial.c
+++ b/supervisor/shared/serial.c
@@ -26,36 +26,94 @@
#include <string.h>
+#include "py/mpconfig.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 "tusb.h"
+/*
+ * Note: DEBUG_UART currently only works on STM32,
+ * enabling on another platform will cause a crash.
+ */
+
+#if defined(DEBUG_UART_TX) && defined(DEBUG_UART_RX)
+#include "shared-bindings/busio/UART.h"
+busio_uart_obj_t debug_uart;
+byte buf_array[64];
+#endif
+
+void serial_early_init(void) {
+#if defined(DEBUG_UART_TX) && defined(DEBUG_UART_RX)
+ debug_uart.base.type = &busio_uart_type;
+
+ const mcu_pin_obj_t* rx = MP_OBJ_TO_PTR(DEBUG_UART_RX);
+ const mcu_pin_obj_t* tx = MP_OBJ_TO_PTR(DEBUG_UART_TX);
+
+ common_hal_busio_uart_construct(&debug_uart, tx, rx, NULL, NULL, NULL,
+ false, 115200, 8, PARITY_NONE, 1, 1.0f, 64,
+ buf_array, true);
+ common_hal_busio_uart_never_reset(&debug_uart);
+#endif
+}
+
void serial_init(void) {
usb_init();
}
bool serial_connected(void) {
+#if defined(DEBUG_UART_TX) && defined(DEBUG_UART_RX)
+ return true;
+#else
return tud_cdc_connected();
+#endif
}
char serial_read(void) {
+#if defined(DEBUG_UART_TX) && defined(DEBUG_UART_RX)
+ if (tud_cdc_connected() && tud_cdc_available() > 0) {
+ return (char) tud_cdc_read_char();
+ }
+ int uart_errcode;
+ char text;
+ common_hal_busio_uart_read(&debug_uart, (uint8_t*) &text, 1, &uart_errcode);
+ return text;
+#else
return (char) tud_cdc_read_char();
+#endif
}
bool serial_bytes_available(void) {
+#if defined(DEBUG_UART_TX) && defined(DEBUG_UART_RX)
+ return common_hal_busio_uart_rx_characters_available(&debug_uart) || (tud_cdc_available() > 0);
+#else
return tud_cdc_available() > 0;
+#endif
}
void serial_write_substring(const char* text, uint32_t length) {
- if (!tud_cdc_connected()) {
+ if (length == 0) {
return;
}
+#if CIRCUITPY_DISPLAYIO
+ int errcode;
+ common_hal_terminalio_terminal_write(&supervisor_terminal, (const uint8_t*) text, length, &errcode);
+#endif
+
uint32_t count = 0;
- while (count < length) {
+ while (count < length && tud_cdc_connected()) {
count += tud_cdc_write(text + count, length - count);
usb_background();
}
+
+#if defined(DEBUG_UART_TX) && defined(DEBUG_UART_RX)
+ int uart_errcode;
+ common_hal_busio_uart_write(&debug_uart, (const uint8_t*) text, length, &uart_errcode);
+#endif
}
void serial_write(const char* text) {
diff --git a/supervisor/shared/stack.c b/supervisor/shared/stack.c
index 6b74742eb..e7aa956b0 100755
--- a/supervisor/shared/stack.c
+++ b/supervisor/shared/stack.c
@@ -27,7 +27,10 @@
#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;
@@ -38,17 +41,34 @@ supervisor_allocation* stack_alloc = NULL;
#define EXCEPTION_STACK_SIZE 1024
void allocate_stack(void) {
- mp_uint_t regs[10];
- mp_uint_t sp = cpu_get_regs_and_sp(regs);
- mp_uint_t c_size = (uint32_t) &_estack - sp;
-
- stack_alloc = allocate_memory(c_size + next_stack_size + EXCEPTION_STACK_SIZE, true);
- if (stack_alloc == NULL) {
- stack_alloc = allocate_memory(c_size + CIRCUITPY_DEFAULT_STACK_SIZE + EXCEPTION_STACK_SIZE, true);
- current_stack_size = CIRCUITPY_DEFAULT_STACK_SIZE;
+ if (port_fixed_stack() != NULL) {
+ stack_alloc = port_fixed_stack();
+ current_stack_size = stack_alloc->length;
} else {
- current_stack_size = next_stack_size;
+ mp_uint_t regs[10];
+ mp_uint_t sp = cpu_get_regs_and_sp(regs);
+
+ mp_uint_t c_size = (uint32_t) port_stack_get_top() - sp;
+ stack_alloc = allocate_memory(c_size + next_stack_size + EXCEPTION_STACK_SIZE, true);
+ if (stack_alloc == NULL) {
+ stack_alloc = allocate_memory(c_size + CIRCUITPY_DEFAULT_STACK_SIZE + EXCEPTION_STACK_SIZE, true);
+ current_stack_size = CIRCUITPY_DEFAULT_STACK_SIZE;
+ } else {
+ current_stack_size = next_stack_size;
+ }
+ }
+
+ *stack_alloc->ptr = STACK_CANARY_VALUE;
+}
+
+inline bool stack_ok(void) {
+ return stack_alloc == NULL || *stack_alloc->ptr == STACK_CANARY_VALUE;
+}
+
+inline void assert_heap_ok(void) {
+ if (!stack_ok()) {
+ reset_into_safe_mode(HEAP_OVERWRITTEN);
}
}
@@ -57,7 +77,11 @@ void stack_init(void) {
}
void stack_resize(void) {
+ if (stack_alloc == NULL) {
+ return;
+ }
if (next_stack_size == current_stack_size) {
+ *stack_alloc->ptr = STACK_CANARY_VALUE;
return;
}
free_memory(stack_alloc);
diff --git a/supervisor/shared/stack.h b/supervisor/shared/stack.h
index 3a5c18e7d..7096f0b3e 100755
--- a/supervisor/shared/stack.h
+++ b/supervisor/shared/stack.h
@@ -37,5 +37,14 @@ void stack_init(void);
void stack_resize(void);
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/supervisor/shared/status_leds.c b/supervisor/shared/status_leds.c
index d99853ceb..672242a96 100644
--- a/supervisor/shared/status_leds.c
+++ b/supervisor/shared/status_leds.c
@@ -26,8 +26,10 @@
#include "supervisor/shared/status_leds.h"
+#if CIRCUITPY_DIGITALIO
#include "common-hal/digitalio/DigitalInOut.h"
#include "shared-bindings/digitalio/DigitalInOut.h"
+#endif
#ifdef MICROPY_HW_LED_RX
digitalio_digitalinout_obj_t rx_led;
diff --git a/supervisor/shared/tick.c b/supervisor/shared/tick.c
new file mode 100644
index 000000000..47395bd60
--- /dev/null
+++ b/supervisor/shared/tick.c
@@ -0,0 +1,142 @@
+/*
+ * 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 "py/mpstate.h"
+#include "supervisor/linker.h"
+#include "supervisor/filesystem.h"
+#include "supervisor/port.h"
+#include "supervisor/shared/autoreload.h"
+
+static volatile uint64_t PLACE_IN_DTCM_BSS(background_ticks);
+
+#if CIRCUITPY_GAMEPAD
+#include "shared-module/gamepad/__init__.h"
+#endif
+
+#if CIRCUITPY_GAMEPADSHIFT
+#include "shared-module/gamepadshift/__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
+
+void supervisor_tick(void) {
+#if CIRCUITPY_FILESYSTEM_FLUSH_INTERVAL_MS > 0
+ filesystem_tick();
+#endif
+#ifdef CIRCUITPY_AUTORELOAD_DELAY_MS
+ autoreload_tick();
+#endif
+#ifdef CIRCUITPY_GAMEPAD_TICKS
+ if (!(port_get_raw_ticks(NULL) & CIRCUITPY_GAMEPAD_TICKS)) {
+ #if CIRCUITPY_GAMEPAD
+ gamepad_tick();
+ #endif
+ #if CIRCUITPY_GAMEPADSHIFT
+ gamepadshift_tick();
+ #endif
+ }
+#endif
+}
+
+uint64_t supervisor_ticks_ms64() {
+ uint64_t result;
+ common_hal_mcu_disable_interrupts();
+ result = port_get_raw_ticks(NULL);
+ common_hal_mcu_enable_interrupts();
+ result = result * 1000 / 1024;
+ return result;
+}
+
+uint32_t supervisor_ticks_ms32() {
+ return supervisor_ticks_ms64();
+}
+
+extern void run_background_tasks(void);
+
+void PLACE_IN_ITCM(supervisor_run_background_tasks_if_tick)() {
+ // TODO: Add a global that can be set by anyone to indicate we should run background tasks. That
+ // way we can short circuit the background tasks early. We used to do it based on time but it
+ // breaks cases where we wake up for a short period and then sleep. If we skipped the last
+ // background task or more before sleeping we may end up starving a task like USB.
+ run_background_tasks();
+}
+
+void mp_hal_delay_ms(mp_uint_t delay) {
+ uint64_t start_tick = port_get_raw_ticks(NULL);
+ // Adjust the delay to ticks vs ms.
+ delay = delay * 1024 / 1000;
+ uint64_t end_tick = start_tick + delay;
+ int64_t remaining = delay;
+ while (remaining > 0) {
+ RUN_BACKGROUND_TASKS;
+ // Check to see if we've been CTRL-Ced by autoreload or the user.
+ if(MP_STATE_VM(mp_pending_exception) == MP_OBJ_FROM_PTR(&MP_STATE_VM(mp_kbd_exception)) ||
+ MP_STATE_VM(mp_pending_exception) == MP_OBJ_FROM_PTR(&MP_STATE_VM(mp_reload_exception)) ||
+ WATCHDOG_EXCEPTION_CHECK()) {
+ break;
+ }
+ 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);
+ // Sleep until an interrupt happens.
+ port_sleep_until_interrupt();
+ remaining = end_tick - port_get_raw_ticks(NULL);
+ }
+}
+
+volatile size_t tick_enable_count = 0;
+extern 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();
+}
+
+extern 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/supervisor/shared/tick.h b/supervisor/shared/tick.h
new file mode 100644
index 000000000..e7e808058
--- /dev/null
+++ b/supervisor/shared/tick.h
@@ -0,0 +1,70 @@
+/*
+ * 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>
+
+/** @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 Cause background tasks to be called soon
+ *
+ * Normally, background tasks are only run once per tick. For other cases where
+ * an event noticed from an interrupt context needs to be completed by a background
+ * task activity, the interrupt can call supervisor_fake_tick.
+ */
+extern void supervisor_fake_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_ms64 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);
+
+#endif
diff --git a/supervisor/shared/translate.c b/supervisor/shared/translate.c
index 1fa361c15..606f8fa91 100644
--- a/supervisor/shared/translate.c
+++ b/supervisor/shared/translate.c
@@ -34,12 +34,46 @@
#include "genhdr/compression.generated.h"
#endif
+#include "supervisor/serial.h"
+
+void serial_write_compressed(const compressed_string_t* compressed) {
+ char decompressed[decompress_length(compressed)];
+ decompress(compressed, decompressed);
+ serial_write(decompressed);
+}
+
+STATIC int put_utf8(char *buf, int u) {
+ if(u <= 0x7f) {
+ *buf = u;
+ return 1;
+ } else if(u <= 0x07ff) {
+ *buf++ = 0b11000000 | (u >> 6);
+ *buf = 0b10000000 | (u & 0b00111111);
+ return 2;
+ } else { // u <= 0xffff)
+ *buf++ = 0b11000000 | (u >> 12);
+ *buf = 0b10000000 | ((u >> 6) & 0b00111111);
+ *buf = 0b10000000 | (u & 0b00111111);
+ return 3;
+ }
+}
+
+uint16_t decompress_length(const compressed_string_t* compressed) {
+ 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));
+ }
+}
+
char* decompress(const compressed_string_t* compressed, char* decompressed) {
- uint8_t this_byte = 0;
- uint8_t this_bit = 7;
- uint8_t b = compressed->data[this_byte];
+ 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];
+ uint16_t length = decompress_length(compressed);
+
// Stop one early because the last byte is always NULL.
- for (uint16_t i = 0; i < compressed->length - 1; i++) {
+ for (uint16_t i = 0; i < length - 1;) {
uint32_t bits = 0;
uint8_t bit_length = 0;
uint32_t max_code = lengths[0];
@@ -54,7 +88,7 @@ char* decompress(const compressed_string_t* compressed, char* decompressed) {
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.
+ b = (&compressed->data)[this_byte]; // This may read past the end but its never used.
} else {
this_bit -= 1;
}
@@ -64,17 +98,17 @@ char* decompress(const compressed_string_t* compressed, char* decompressed) {
max_code = (max_code << 1) + lengths[bit_length];
searched_length += lengths[bit_length];
}
- decompressed[i] = values[searched_length + bits - max_code];
+ i += put_utf8(decompressed + i, values[searched_length + bits - max_code]);
}
- decompressed[compressed->length-1] = '\0';
+ decompressed[length-1] = '\0';
return decompressed;
}
inline __attribute__((always_inline)) const compressed_string_t* translate(const char* original) {
#ifndef NO_QSTR
#define QDEF(id, str)
- #define TRANSLATION(id, len, compressed...) if (strcmp(original, id) == 0) { static const compressed_string_t v = {.length = len, .data = compressed}; return &v; } else
+ #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
diff --git a/supervisor/shared/translate.h b/supervisor/shared/translate.h
index 3a1ba4076..731b26d12 100644
--- a/supervisor/shared/translate.h
+++ b/supervisor/shared/translate.h
@@ -29,13 +29,41 @@
#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)
+//
+// 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 {
- uint16_t length;
- const uint8_t data[];
+ 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);
#endif // MICROPY_INCLUDED_SUPERVISOR_TRANSLATE_H
diff --git a/supervisor/shared/usb/tusb_config.h b/supervisor/shared/usb/tusb_config.h
index 649c390ac..5b7230983 100644
--- a/supervisor/shared/usb/tusb_config.h
+++ b/supervisor/shared/usb/tusb_config.h
@@ -49,10 +49,14 @@
//--------------------------------------------------------------------+
#define CFG_TUSB_RHPORT0_MODE OPT_MODE_DEVICE
+#ifndef CFG_TUSB_DEBUG
#define CFG_TUSB_DEBUG 0
+#endif
/*------------- RTOS -------------*/
+#ifndef CFG_TUSB_OS
#define CFG_TUSB_OS OPT_OS_NONE
+#endif
//#define CFG_TUD_TASK_QUEUE_SZ 16
//#define CFG_TUD_TASK_PRIO 0
//#define CFG_TUD_TASK_STACK_SZ 150
@@ -74,7 +78,8 @@
//------------- CLASS -------------//
#define CFG_TUD_CDC 1
#define CFG_TUD_MSC 1
-#define CFG_TUD_HID 1
+#define CFG_TUD_HID CIRCUITPY_USB_HID
+#define CFG_TUD_MIDI CIRCUITPY_USB_MIDI
#define CFG_TUD_CUSTOM_CLASS 0
/*------------------------------------------------------------------*/
@@ -106,7 +111,7 @@
// USB RAM PLACEMENT
//--------------------------------------------------------------------+
#define CFG_TUSB_ATTR_USBRAM
-#define CFG_TUSB_MEM_ALIGN ATTR_ALIGNED(4)
+#define CFG_TUSB_MEM_ALIGN __attribute__ ((aligned(4)))
#ifdef __cplusplus
diff --git a/supervisor/shared/usb/usb.c b/supervisor/shared/usb/usb.c
index 1aa34e9e6..edf810118 100644
--- a/supervisor/shared/usb/usb.c
+++ b/supervisor/shared/usb/usb.c
@@ -24,8 +24,9 @@
* THE SOFTWARE.
*/
-#include "tick.h"
+#include "py/objstr.h"
#include "shared-bindings/microcontroller/Processor.h"
+#include "shared-module/usb_midi/__init__.h"
#include "supervisor/port.h"
#include "supervisor/usb.h"
#include "lib/utils/interrupt_char.h"
@@ -42,21 +43,17 @@ void load_serial_number(void) {
uint8_t raw_id[COMMON_HAL_MCU_PROCESSOR_UID_LENGTH];
common_hal_mcu_processor_get_uid(raw_id);
- const char nibble_to_hex[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
- 'A', 'B', 'C', 'D', 'E', 'F'};
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;
// Strings are UTF-16-LE encoded.
- usb_serial_number[1 + i * 2 + j] = nibble_to_hex[nibble];
+ usb_serial_number[1 + i * 2 + j] = nibble_to_hex_upper[nibble];
}
}
}
-bool _usb_enabled = false;
-
bool usb_enabled(void) {
- return _usb_enabled;
+ return tusb_inited();
}
void usb_init(void) {
@@ -64,18 +61,23 @@ void usb_init(void) {
load_serial_number();
tusb_init();
- _usb_enabled = true;
#if MICROPY_KBD_EXCEPTION
// Set Ctrl+C as wanted char, tud_cdc_rx_wanted_cb() callback will be invoked when Ctrl+C is received
// This callback always got invoked regardless of mp_interrupt_char value since we only set it once here
tud_cdc_set_wanted_char(CHAR_CTRL_C);
#endif
+
+#if CIRCUITPY_USB_MIDI
+ usb_midi_init();
+#endif
}
void usb_background(void) {
if (usb_enabled()) {
- tusb_task();
+ #if CFG_TUSB_OS == OPT_OS_NONE
+ tud_task();
+ #endif
tud_cdc_write_flush();
}
}
@@ -86,19 +88,23 @@ void usb_background(void) {
// Invoked when device is mounted
void tud_mount_cb(void) {
+ usb_msc_mount();
}
// Invoked when device is unmounted
void tud_umount_cb(void) {
+ usb_msc_umount();
}
-uint32_t tusb_hal_millis(void) {
- uint64_t ms;
- uint32_t us;
- current_tick(&ms, &us);
- return (uint32_t) ms;
+// 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
diff --git a/supervisor/shared/usb/usb_desc.c b/supervisor/shared/usb/usb_desc.c
index 5a0de1ffc..080187ebc 100644
--- a/supervisor/shared/usb/usb_desc.c
+++ b/supervisor/shared/usb/usb_desc.c
@@ -24,23 +24,35 @@
* THE SOFTWARE.
*/
-#include "supervisor/shared/usb/usb_desc.h"
+#include "lib/tinyusb/src/tusb.h"
#include "shared-module/usb_hid/Device.h"
#include "genhdr/autogen_usb_descriptor.h"
-// tud_desc_set is required by tinyusb stack
-tud_desc_set_t tud_desc_set =
-{
- .device = &usb_desc_dev,
- .config = &usb_desc_cfg,
- .string_arr = (uint8_t const **) string_desc_arr,
- .string_count = sizeof(string_desc_arr)/sizeof(string_desc_arr[0]),
+// Invoked when received GET DEVICE DESCRIPTOR
+// Application return pointer to descriptor
+uint8_t const * tud_descriptor_device_cb(void) {
+ return usb_desc_dev;
+}
- .hid_report =
- {
- .generic = hid_report_descriptor,
- .boot_keyboard = NULL,
- .boot_mouse = NULL
- }
-};
+// Invoked when received GET CONFIGURATION DESCRIPTOR
+// 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 usb_desc_cfg;
+}
+
+// Invoked when received GET HID REPORT DESCRIPTOR
+// Application return pointer to descriptor
+// Descriptor contents must exist long enough for transfer to complete
+uint8_t const * tud_hid_descriptor_report_cb(void) {
+ return hid_report_descriptor;
+}
+
+// Invoked when received GET STRING DESCRIPTOR request
+// 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) {
+ uint8_t const max_index = sizeof(string_desc_arr)/sizeof(string_desc_arr[0]);
+ return (index < max_index) ? string_desc_arr[index] : NULL;
+}
diff --git a/supervisor/shared/usb/usb_msc_flash.c b/supervisor/shared/usb/usb_msc_flash.c
index 658aab0d8..7532b6aea 100644
--- a/supervisor/shared/usb/usb_msc_flash.c
+++ b/supervisor/shared/usb/usb_msc_flash.c
@@ -34,11 +34,32 @@
#include "lib/oofatfs/ff.h"
#include "py/mpstate.h"
+#include "supervisor/filesystem.h"
#include "supervisor/shared/autoreload.h"
#define MSC_FLASH_BLOCK_SIZE 512
-static bool ejected[1];
+static bool ejected[1] = {true};
+
+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.
+ for (uint8_t i = 0; i < sizeof(ejected); i++) {
+ ejected[i] = 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;
+}
// The root FS is always at the end of the list.
static fs_user_mount_t* get_vfs(int lun) {
@@ -58,59 +79,18 @@ static fs_user_mount_t* get_vfs(int lun) {
}
// Callback invoked when received an SCSI command not in built-in list below
-// - READ_CAPACITY10, READ_FORMAT_CAPACITY, INQUIRY, MODE_SENSE6, REQUEST_SENSE
+// - 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_TEST_UNIT_READY:
- // Command that host uses to check our readiness before sending other commands
- resplen = 0;
- if (lun > 1) {
- resplen = -1;
- } else {
- fs_user_mount_t* current_mount = get_vfs(lun);
- if (current_mount == NULL) {
- resplen = -1;
- }
- if (ejected[lun]) {
- resplen = -1;
- }
- }
- break;
-
case SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL:
// Host is about to read/write etc ... better not to disconnect disk
resplen = 0;
break;
- case SCSI_CMD_START_STOP_UNIT:
- {
- // Host try to eject/safe remove/poweroff us. We could safely disconnect with disk storage, or go into lower power
- const scsi_start_stop_unit_t* start_stop = (const scsi_start_stop_unit_t*) scsi_cmd;
- // Start bit = 0 : low power mode, if load_eject = 1 : unmount disk storage as well
- // Start bit = 1 : Ready mode, if load_eject = 1 : mount disk storage
- resplen = 0;
- if (start_stop->load_eject == 1) {
- if (lun > 1) {
- resplen = -1;
- } else {
- fs_user_mount_t* current_mount = get_vfs(lun);
- if (current_mount == NULL) {
- resplen = -1;
- }
- if (disk_ioctl(current_mount, CTRL_SYNC, NULL) != RES_OK) {
- resplen = -1;
- } else {
- ejected[lun] = true;
- }
- }
- }
- }
- break;
-
default:
// Set Sense = Invalid Command Operation
tud_msc_set_sense(lun, SCSI_SENSE_ILLEGAL_REQUEST, 0x20, 0x00);
@@ -126,7 +106,7 @@ int32_t tud_msc_scsi_cb (uint8_t lun, const uint8_t scsi_cmd[16], void* buffer,
}
// copy response to stack's buffer if any
- if ( response && resplen ) {
+ if ( response && (resplen > 0) ) {
memcpy(buffer, response, resplen);
}
@@ -148,8 +128,7 @@ bool tud_msc_is_writable_cb(uint8_t lun) {
if (vfs == NULL) {
return false;
}
- if (vfs->writeblocks[0] == MP_OBJ_NULL ||
- (vfs->flags & FSUSER_USB_WRITABLE) == 0) {
+ if (vfs->writeblocks[0] == MP_OBJ_NULL || !filesystem_is_writable_by_usb(vfs)) {
return false;
}
return true;
@@ -206,3 +185,69 @@ void tud_msc_write10_complete_cb (uint8_t lun) {
// This write is complete, start the autoreload clock.
autoreload_start();
}
+
+// 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/supervisor/spi_flash_api.h b/supervisor/spi_flash_api.h
index 28cccb1b1..f59346e25 100644
--- a/supervisor/spi_flash_api.h
+++ b/supervisor/spi_flash_api.h
@@ -31,6 +31,10 @@
#include "supervisor/shared/external_flash/devices.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);
diff --git a/supervisor/stub/filesystem.c b/supervisor/stub/filesystem.c
index 12fe9fb10..1c4a3e12d 100644
--- a/supervisor/stub/filesystem.c
+++ b/supervisor/stub/filesystem.c
@@ -26,13 +26,17 @@
#include "supervisor/filesystem.h"
-void filesystem_init(void) {
+void filesystem_init(bool create_allowed, bool force_create) {
+ (void) create_allowed;
+ (void) force_create;
}
void filesystem_flush(void) {
}
-void filesystem_writable_by_python(bool writable) {
+bool filesystem_is_writable_by_python(fs_user_mount_t *vfs) {
+ (void) vfs;
+ return true;
}
bool filesystem_present(void) {
diff --git a/supervisor/stub/internal_flash.c b/supervisor/stub/internal_flash.c
new file mode 100644
index 000000000..5a82f81f7
--- /dev/null
+++ b/supervisor/stub/internal_flash.c
@@ -0,0 +1,65 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * 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/internal_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;
+}
+
+void supervisor_flash_flush(void) {
+}
+
+mp_uint_t supervisor_flash_read_blocks(uint8_t *dest, uint32_t block, uint32_t num_blocks) {
+ return 0; // success
+}
+
+bool supervisor_flash_write_block(const uint8_t *src, uint32_t block) {
+ return true;
+}
+
+mp_uint_t supervisor_flash_write_blocks(const uint8_t *src, uint32_t block_num, uint32_t num_blocks) {
+ return 0; // success
+}
+
+void supervisor_flash_release_cache(void) {
+}
diff --git a/supervisor/stub/safe_mode.c b/supervisor/stub/safe_mode.c
new file mode 100644
index 000000000..1a792becd
--- /dev/null
+++ b/supervisor/stub/safe_mode.c
@@ -0,0 +1,39 @@
+/*
+ * 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"
+
+safe_mode_t wait_for_safe_mode_reset(void) {
+ return NO_SAFE_MODE;
+}
+
+void reset_into_safe_mode(safe_mode_t reason) {
+ (void) reason;
+}
+
+void print_safe_mode_message(safe_mode_t reason) {
+ (void) reason;
+}
diff --git a/supervisor/stub/serial.c b/supervisor/stub/serial.c
index 4f0e11873..34b433e53 100644
--- a/supervisor/stub/serial.c
+++ b/supervisor/stub/serial.c
@@ -26,6 +26,10 @@
#include "supervisor/serial.h"
+void serial_early_init(void) {
+
+}
+
void serial_init(void) {
}
@@ -43,4 +47,5 @@ bool serial_bytes_available(void) {
}
void serial_write(const char* text) {
+ (void) text;
}
diff --git a/supervisor/stub/stack.c b/supervisor/stub/stack.c
new file mode 100644
index 000000000..9a9ecd32f
--- /dev/null
+++ b/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/supervisor/supervisor.mk b/supervisor/supervisor.mk
index 73c76ebb7..195e4a9b2 100644
--- a/supervisor/supervisor.mk
+++ b/supervisor/supervisor.mk
@@ -2,21 +2,42 @@ SRC_SUPERVISOR = \
main.c \
supervisor/port.c \
supervisor/shared/autoreload.c \
+ supervisor/shared/board.c \
supervisor/shared/filesystem.c \
supervisor/shared/flash.c \
supervisor/shared/micropython.c \
supervisor/shared/rgb_led_status.c \
+ supervisor/shared/safe_mode.c \
supervisor/shared/stack.c \
supervisor/shared/status_leds.c \
+ supervisor/shared/tick.c \
supervisor/shared/translate.c
ifndef $(NO_USB)
-NO_USB = $(wildcard supervisor/usb.c)
+ NO_USB = $(wildcard supervisor/usb.c)
endif
+ifndef INTERNAL_FLASH_FILESYSTEM
+INTERNAL_FLASH_FILESYSTEM = 0
+endif
+CFLAGS += -DINTERNAL_FLASH_FILESYSTEM=$(INTERNAL_FLASH_FILESYSTEM)
+
+ifndef QSPI_FLASH_FILESYSTEM
+QSPI_FLASH_FILESYSTEM = 0
+endif
+CFLAGS += -DQSPI_FLASH_FILESYSTEM=$(QSPI_FLASH_FILESYSTEM)
+
+ifndef SPI_FLASH_FILESYSTEM
+SPI_FLASH_FILESYSTEM = 0
+endif
+CFLAGS += -DSPI_FLASH_FILESYSTEM=$(SPI_FLASH_FILESYSTEM)
+
+ifeq ($(CIRCUITPY_BLEIO),1)
+ SRC_SUPERVISOR += supervisor/shared/bluetooth.c
+endif
# Choose which flash filesystem impl to use.
-# (Right now INTERNAL_FLASH_FILESYSTEM and SPI_FLASH_FILESYSTEM are mutually exclusive.
+# (Right now INTERNAL_FLASH_FILESYSTEM and (Q)SPI_FLASH_FILESYSTEM are mutually exclusive.
# But that might not be true in the future.)
ifdef EXTERNAL_FLASH_DEVICES
CFLAGS += -DEXTERNAL_FLASH_DEVICES=$(EXTERNAL_FLASH_DEVICES) \
@@ -24,15 +45,18 @@ ifdef EXTERNAL_FLASH_DEVICES
SRC_SUPERVISOR += supervisor/shared/external_flash/external_flash.c
ifeq ($(SPI_FLASH_FILESYSTEM),1)
- CFLAGS += -DSPI_FLASH_FILESYSTEM
SRC_SUPERVISOR += supervisor/shared/external_flash/spi_flash.c
+ else
endif
ifeq ($(QSPI_FLASH_FILESYSTEM),1)
- CFLAGS += -DQSPI_FLASH_FILESYSTEM
SRC_SUPERVISOR += supervisor/qspi_flash.c supervisor/shared/external_flash/qspi_flash.c
endif
else
- SRC_SUPERVISOR += supervisor/internal_flash.c
+ ifeq ($(DISABLE_FILESYSTEM),1)
+ SRC_SUPERVISOR += supervisor/stub/internal_flash.c
+ else
+ SRC_SUPERVISOR += supervisor/internal_flash.c
+ endif
endif
ifeq ($(USB),FALSE)
@@ -42,28 +66,129 @@ ifeq ($(USB),FALSE)
SRC_SUPERVISOR += supervisor/serial.c
endif
else
- SRC_SUPERVISOR += lib/tinyusb/src/common/tusb_fifo.c \
- lib/tinyusb/src/device/usbd.c \
- lib/tinyusb/src/device/usbd_control.c \
- lib/tinyusb/src/class/msc/msc_device.c \
- lib/tinyusb/src/class/cdc/cdc_device.c \
- lib/tinyusb/src/class/hid/hid_device.c \
- lib/tinyusb/src/tusb.c \
- supervisor/shared/serial.c \
- supervisor/usb.c \
- supervisor/shared/usb/usb_desc.c \
- supervisor/shared/usb/usb.c \
- supervisor/shared/usb/usb_msc_flash.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 \
- $(BUILD)/autogen_usb_descriptor.c
+ SRC_SUPERVISOR += \
+ lib/tinyusb/src/common/tusb_fifo.c \
+ lib/tinyusb/src/device/usbd.c \
+ lib/tinyusb/src/device/usbd_control.c \
+ lib/tinyusb/src/class/msc/msc_device.c \
+ lib/tinyusb/src/class/cdc/cdc_device.c \
+ lib/tinyusb/src/tusb.c \
+ supervisor/shared/serial.c \
+ supervisor/usb.c \
+ supervisor/shared/usb/usb_desc.c \
+ supervisor/shared/usb/usb.c \
+ supervisor/shared/usb/usb_msc_flash.c \
+ $(BUILD)/autogen_usb_descriptor.c
+
+ 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
+
CFLAGS += -DUSB_AVAILABLE
endif
SUPERVISOR_O = $(addprefix $(BUILD)/, $(SRC_SUPERVISOR:.c=.o))
+ifeq ($(CIRCUITPY_DISPLAYIO), 1)
+ SRC_SUPERVISOR += \
+ supervisor/shared/display.c
+
+ SUPERVISOR_O += $(BUILD)/autogen_display_resources.o
+endif
+ifndef USB_INTERFACE_NAME
+USB_INTERFACE_NAME = "CircuitPython"
+endif
+
+ifndef USB_DEVICES
+USB_DEVICES = "CDC,MSC,AUDIO,HID"
+endif
+
+ifndef USB_HID_DEVICES
+USB_HID_DEVICES = "KEYBOARD,MOUSE,CONSUMER,GAMEPAD"
+endif
+
+ifndef USB_MSC_MAX_PACKET_SIZE
+USB_MSC_MAX_PACKET_SIZE = 64
+endif
+
+ifndef USB_CDC_EP_NUM_NOTIFICATION
+USB_CDC_EP_NUM_NOTIFICATION = 0
+endif
+
+ifndef USB_CDC_EP_NUM_DATA_OUT
+USB_CDC_EP_NUM_DATA_OUT = 0
+endif
+
+ifndef USB_CDC_EP_NUM_DATA_IN
+USB_CDC_EP_NUM_DATA_IN = 0
+endif
+
+ifndef USB_MSC_EP_NUM_OUT
+USB_MSC_EP_NUM_OUT = 0
+endif
+
+ifndef USB_MSC_EP_NUM_IN
+USB_MSC_EP_NUM_IN = 0
+endif
+
+ifndef USB_HID_EP_NUM_OUT
+USB_HID_EP_NUM_OUT = 0
+endif
+
+ifndef USB_HID_EP_NUM_IN
+USB_HID_EP_NUM_IN = 0
+endif
+
+ifndef USB_MIDI_EP_NUM_OUT
+USB_MIDI_EP_NUM_OUT = 0
+endif
+
+ifndef USB_MIDI_EP_NUM_IN
+USB_MIDI_EP_NUM_IN = 0
+endif
+
+USB_DESCRIPTOR_ARGS = \
+ --manufacturer $(USB_MANUFACTURER)\
+ --product $(USB_PRODUCT)\
+ --vid $(USB_VID)\
+ --pid $(USB_PID)\
+ --serial_number_length $(USB_SERIAL_NUMBER_LENGTH)\
+ --interface_name $(USB_INTERFACE_NAME)\
+ --devices $(USB_DEVICES)\
+ --hid_devices $(USB_HID_DEVICES)\
+ --msc_max_packet_size $(USB_MSC_MAX_PACKET_SIZE)\
+ --cdc_ep_num_notification $(USB_CDC_EP_NUM_NOTIFICATION)\
+ --cdc_ep_num_data_out $(USB_CDC_EP_NUM_DATA_OUT)\
+ --cdc_ep_num_data_in $(USB_CDC_EP_NUM_DATA_IN)\
+ --msc_ep_num_out $(USB_MSC_EP_NUM_OUT)\
+ --msc_ep_num_in $(USB_MSC_EP_NUM_IN)\
+ --hid_ep_num_out $(USB_HID_EP_NUM_OUT)\
+ --hid_ep_num_in $(USB_HID_EP_NUM_IN)\
+ --midi_ep_num_out $(USB_MIDI_EP_NUM_OUT)\
+ --midi_ep_num_in $(USB_MIDI_EP_NUM_IN)\
+ --output_c_file $(BUILD)/autogen_usb_descriptor.c\
+ --output_h_file $(BUILD)/genhdr/autogen_usb_descriptor.h
+
+ifeq ($(USB_RENUMBER_ENDPOINTS), 0)
+USB_DESCRIPTOR_ARGS += --no-renumber_endpoints
+endif
+
$(BUILD)/supervisor/shared/translate.o: $(HEADER_BUILD)/qstrdefs.generated.h
$(BUILD)/autogen_usb_descriptor.c $(BUILD)/genhdr/autogen_usb_descriptor.h: autogen_usb_descriptor.intermediate
@@ -73,11 +198,14 @@ $(BUILD)/autogen_usb_descriptor.c $(BUILD)/genhdr/autogen_usb_descriptor.h: auto
autogen_usb_descriptor.intermediate: ../../tools/gen_usb_descriptor.py Makefile | $(HEADER_BUILD)
$(STEPECHO) "GEN $@"
$(Q)install -d $(BUILD)/genhdr
- $(Q)$(PYTHON3) ../../tools/gen_usb_descriptor.py \
- --manufacturer $(USB_MANUFACTURER)\
- --product $(USB_PRODUCT)\
- --vid $(USB_VID)\
- --pid $(USB_PID)\
- --serial_number_length $(USB_SERIAL_NUMBER_LENGTH)\
- --output_c_file $(BUILD)/autogen_usb_descriptor.c\
- --output_h_file $(BUILD)/genhdr/autogen_usb_descriptor.h
+ $(Q)$(PYTHON3) ../../tools/gen_usb_descriptor.py $(USB_DESCRIPTOR_ARGS)
+
+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)$(PYTHON3) ../../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/supervisor/usb.h b/supervisor/usb.h
index c87540d40..29280c725 100644
--- a/supervisor/usb.h
+++ b/supervisor/usb.h
@@ -41,4 +41,9 @@ void init_usb_hardware(void);
bool usb_enabled(void);
void usb_init(void);
+// Propagate plug/unplug events to the MSC logic.
+void usb_msc_mount(void);
+void usb_msc_umount(void);
+bool usb_msc_ejected(void);
+
#endif // MICROPY_INCLUDED_SUPERVISOR_USB_H