From 5616fc2a3c31eebfe97e974b01353e86c0beb7cc Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Wed, 5 Mar 2025 11:39:06 -0800 Subject: [PATCH] Add support for saves partition Suggested in #10045 --- extmod/vfs.h | 4 + extmod/vfs_blockdev.c | 4 - main.c | 6 +- .../boards/adafruit_fruit_jam/mpconfigboard.h | 2 + py/circuitpy_mpconfig.h | 4 + supervisor/shared/filesystem.c | 115 +++++++++++----- supervisor/shared/flash.c | 123 +++++++++++------- supervisor/shared/usb/usb_msc_flash.c | 41 ++++-- 8 files changed, 198 insertions(+), 101 deletions(-) diff --git a/extmod/vfs.h b/extmod/vfs.h index c6c471d476c0f..65cf4ece9851c 100644 --- a/extmod/vfs.h +++ b/extmod/vfs.h @@ -71,6 +71,10 @@ typedef struct _mp_vfs_proto_t { typedef struct _mp_vfs_blockdev_t { uint16_t flags; size_t block_size; + #if CIRCUITPY_SAVES_PARTITION_SIZE > 0 + size_t offset; + int size; + #endif mp_obj_t readblocks[5]; mp_obj_t writeblocks[5]; // new protocol uses just ioctl, old uses sync (optional) and count diff --git a/extmod/vfs_blockdev.c b/extmod/vfs_blockdev.c index eda5cecc147a9..0ec46453cf9a0 100644 --- a/extmod/vfs_blockdev.c +++ b/extmod/vfs_blockdev.c @@ -78,8 +78,6 @@ void mp_vfs_blockdev_init(mp_vfs_blockdev_t *self, mp_obj_t bdev) { int mp_vfs_blockdev_read(mp_vfs_blockdev_t *self, size_t block_num, size_t num_blocks, uint8_t *buf) { if (self->flags & MP_BLOCKDEV_FLAG_NATIVE) { - // CIRCUITPY-CHANGE: Pass the blockdev object into native readblocks so - // it has the corresponding state. mp_uint_t (*f)(mp_obj_t self, uint8_t *, uint32_t, uint32_t) = (void *)(uintptr_t)self->readblocks[2]; return f(self->readblocks[1], buf, block_num, num_blocks); } else { @@ -112,8 +110,6 @@ int mp_vfs_blockdev_write(mp_vfs_blockdev_t *self, size_t block_num, size_t num_ } if (self->flags & MP_BLOCKDEV_FLAG_NATIVE) { - // CIRCUITPY-CHANGE: Pass the blockdev object into native readblocks so - // it has the corresponding state. mp_uint_t (*f)(mp_obj_t self, const uint8_t *, uint32_t, uint32_t) = (void *)(uintptr_t)self->writeblocks[2]; return f(self->writeblocks[1], buf, block_num, num_blocks); } else { diff --git a/main.c b/main.c index 763a3c1e0c197..c9c7acb11f7ba 100644 --- a/main.c +++ b/main.c @@ -214,6 +214,10 @@ static void stop_mp(void) { vfs = vfs->next; } MP_STATE_VM(vfs_mount_table) = vfs; + // The last vfs is CIRCUITPY and the root directory. + while (vfs->next != NULL) { + vfs = vfs->next; + } MP_STATE_VM(vfs_cur) = vfs; #endif @@ -863,7 +867,7 @@ static void __attribute__ ((noinline)) run_boot_py(safe_mode_t safe_mode) { #ifdef CIRCUITPY_BOOT_OUTPUT_FILE // Get the base filesystem. - fs_user_mount_t *vfs = (fs_user_mount_t *)MP_STATE_VM(vfs_mount_table)->obj; + fs_user_mount_t *vfs = filesystem_circuitpy(); FATFS *fs = &vfs->fatfs; boot_output = NULL; diff --git a/ports/raspberrypi/boards/adafruit_fruit_jam/mpconfigboard.h b/ports/raspberrypi/boards/adafruit_fruit_jam/mpconfigboard.h index 77159015ef5f4..fb643deead23d 100644 --- a/ports/raspberrypi/boards/adafruit_fruit_jam/mpconfigboard.h +++ b/ports/raspberrypi/boards/adafruit_fruit_jam/mpconfigboard.h @@ -36,3 +36,5 @@ // #define CIRCUITPY_CONSOLE_UART_RX (&pin_GPIO45) // #define CIRCUITPY_DEBUG_TINYUSB 0 + +#define CIRCUITPY_SAVES_PARTITION_SIZE (2 * 1024 * 1024) diff --git a/py/circuitpy_mpconfig.h b/py/circuitpy_mpconfig.h index aaead443e0bf3..4d366a37c7fab 100644 --- a/py/circuitpy_mpconfig.h +++ b/py/circuitpy_mpconfig.h @@ -615,6 +615,10 @@ void background_callback_run_all(void); #define CIRCUITPY_MIN_GCC_VERSION 13 #endif +#ifndef CIRCUITPY_SAVES_PARTITION_SIZE +#define CIRCUITPY_SAVES_PARTITION_SIZE 0 +#endif + #if defined(__GNUC__) && !defined(__ZEPHYR__) #if __GNUC__ < CIRCUITPY_MIN_GCC_VERSION // (the 3 level scheme here is required to get expansion & stringization diff --git a/supervisor/shared/filesystem.c b/supervisor/shared/filesystem.c index e5edb148e4145..3532db4bdce61 100644 --- a/supervisor/shared/filesystem.c +++ b/supervisor/shared/filesystem.c @@ -15,8 +15,13 @@ #include "supervisor/flash.h" #include "supervisor/linker.h" -static mp_vfs_mount_t _mp_vfs; -static fs_user_mount_t _internal_vfs; +static mp_vfs_mount_t _circuitpy_vfs; +static fs_user_mount_t _circuitpy_usermount; + +#if CIRCUITPY_SAVES_PARTITION_SIZE > 0 +static mp_vfs_mount_t _saves_vfs; +static fs_user_mount_t _saves_usermount; +#endif static volatile uint32_t filesystem_flush_interval_ms = CIRCUITPY_FILESYSTEM_FLUSH_INTERVAL_MS; volatile bool filesystem_flush_requested = false; @@ -73,15 +78,29 @@ static void make_file_with_contents(FATFS *fatfs, const char *filename, const by // want it to be executed without using stack within main() function bool filesystem_init(bool create_allowed, bool force_create) { // init the vfs object - fs_user_mount_t *vfs_fat = &_internal_vfs; - vfs_fat->blockdev.flags = 0; - supervisor_flash_init_vfs(vfs_fat); + fs_user_mount_t *circuitpy = &_circuitpy_usermount; + circuitpy->blockdev.flags = 0; + supervisor_flash_init_vfs(circuitpy); + + #if CIRCUITPY_SAVES_PARTITION_SIZE > 0 + // SAVES is placed before CIRCUITPY so that CIRCUITPY takes up the remaining space. + circuitpy->blockdev.offset = CIRCUITPY_SAVES_PARTITION_SIZE; + circuitpy->blockdev.size = -1; + + fs_user_mount_t *saves = &_saves_usermount; + saves->blockdev.flags = 0; + saves->blockdev.offset = 0; + saves->blockdev.size = CIRCUITPY_SAVES_PARTITION_SIZE; + supervisor_flash_init_vfs(saves); + filesystem_set_concurrent_write_protection(saves, true); + filesystem_set_writable_by_usb(saves, false); + #endif - mp_vfs_mount_t *vfs = &_mp_vfs; - vfs->len = 0; + mp_vfs_mount_t *circuitpy_vfs = &_circuitpy_vfs; + circuitpy_vfs->len = 0; // try to mount the flash - FRESULT res = f_mount(&vfs_fat->fatfs); + FRESULT res = f_mount(&circuitpy->fatfs); if ((res == FR_NO_FILESYSTEM && create_allowed) || force_create) { // No filesystem so create a fresh one, or reformat has been requested. uint8_t working_buf[FF_MAX_SS]; @@ -89,7 +108,7 @@ bool filesystem_init(bool create_allowed, bool force_create) { #if FF_FS_EXFAT formats |= FM_EXFAT | FM_FAT32; #endif - res = f_mkfs(&vfs_fat->fatfs, formats, 0, working_buf, sizeof(working_buf)); + res = f_mkfs(&circuitpy->fatfs, formats, 0, working_buf, sizeof(working_buf)); if (res != FR_OK) { return false; } @@ -98,9 +117,9 @@ bool filesystem_init(bool create_allowed, bool force_create) { // set label #ifdef CIRCUITPY_DRIVE_LABEL - res = f_setlabel(&vfs_fat->fatfs, CIRCUITPY_DRIVE_LABEL); + res = f_setlabel(&circuitpy->fatfs, CIRCUITPY_DRIVE_LABEL); #else - res = f_setlabel(&vfs_fat->fatfs, "CIRCUITPY"); + res = f_setlabel(&circuitpy->fatfs, "CIRCUITPY"); #endif if (res != FR_OK) { return false; @@ -108,36 +127,56 @@ bool filesystem_init(bool create_allowed, bool force_create) { #if CIRCUITPY_USB_DEVICE // inhibit file indexing on MacOS - res = f_mkdir(&vfs_fat->fatfs, "/.fseventsd"); + res = f_mkdir(&circuitpy->fatfs, "/.fseventsd"); if (res != FR_OK) { return false; } - make_empty_file(&vfs_fat->fatfs, "/.fseventsd/no_log"); - make_empty_file(&vfs_fat->fatfs, "/.metadata_never_index"); + make_empty_file(&circuitpy->fatfs, "/.fseventsd/no_log"); + make_empty_file(&circuitpy->fatfs, "/.metadata_never_index"); // Prevent storing trash on all OSes. - make_empty_file(&vfs_fat->fatfs, "/.Trashes"); // MacOS - make_empty_file(&vfs_fat->fatfs, "/.Trash-1000"); // Linux, XDG trash spec: + make_empty_file(&circuitpy->fatfs, "/.Trashes"); // MacOS + make_empty_file(&circuitpy->fatfs, "/.Trash-1000"); // Linux, XDG trash spec: // https://specifications.freedesktop.org/trash-spec/trashspec-latest.html #endif #if CIRCUITPY_SDCARDIO || CIRCUITPY_SDIOIO - res = f_mkdir(&vfs_fat->fatfs, "/sd"); + res = f_mkdir(&circuitpy->fatfs, "/sd"); #if CIRCUITPY_FULL_BUILD - MAKE_FILE_WITH_OPTIONAL_CONTENTS(&vfs_fat->fatfs, "/sd/placeholder.txt", + MAKE_FILE_WITH_OPTIONAL_CONTENTS(&circuitpy->fatfs, "/sd/placeholder.txt", "SD cards mounted at /sd will hide this file from Python." " SD cards are not visible via USB CIRCUITPY.\n"); #endif #endif + #if CIRCUITPY_SAVES_PARTITION_SIZE > 0 + res = f_mkfs(&saves->fatfs, formats, 0, working_buf, sizeof(working_buf)); + if (res == FR_OK) { + // Flush the new file system to make sure it's repaired immediately. + supervisor_flash_flush(); + res = f_setlabel(&saves->fatfs, "CPSAVES"); + } + + if (res == FR_OK) { + res = f_mkdir(&circuitpy->fatfs, "/saves"); + } + #if CIRCUITPY_FULL_BUILD + if (res == FR_OK) { + MAKE_FILE_WITH_OPTIONAL_CONTENTS(&circuitpy->fatfs, "/saves/placeholder.txt", + "A separate filesystem mounted at /saves will hide this file from Python." + " Saves are visible via USB CPSAVES.\n"); + } + #endif + #endif + #if CIRCUITPY_OS_GETENV - make_empty_file(&vfs_fat->fatfs, "/settings.toml"); + make_empty_file(&circuitpy->fatfs, "/settings.toml"); #endif // make a sample code.py file - MAKE_FILE_WITH_OPTIONAL_CONTENTS(&vfs_fat->fatfs, "/code.py", "print(\"Hello World!\")\n"); + MAKE_FILE_WITH_OPTIONAL_CONTENTS(&circuitpy->fatfs, "/code.py", "print(\"Hello World!\")\n"); // create empty lib directory - res = f_mkdir(&vfs_fat->fatfs, "/lib"); + res = f_mkdir(&circuitpy->fatfs, "/lib"); if (res != FR_OK) { return false; } @@ -148,16 +187,28 @@ bool filesystem_init(bool create_allowed, bool force_create) { return false; } - vfs->str = "/"; - vfs->len = 1; - vfs->obj = MP_OBJ_FROM_PTR(vfs_fat); - vfs->next = NULL; - - MP_STATE_VM(vfs_mount_table) = vfs; + circuitpy_vfs->str = "/"; + circuitpy_vfs->len = 1; + circuitpy_vfs->obj = MP_OBJ_FROM_PTR(circuitpy); + circuitpy_vfs->next = NULL; + + MP_STATE_VM(vfs_mount_table) = circuitpy_vfs; + + #if CIRCUITPY_SAVES_PARTITION_SIZE > 0 + res = f_mount(&saves->fatfs); + if (res == FR_OK) { + mp_vfs_mount_t *saves_vfs = &_saves_vfs; + saves_vfs->str = "/saves"; + saves_vfs->len = 6; + saves_vfs->obj = MP_OBJ_FROM_PTR(&_saves_usermount); + saves_vfs->next = MP_STATE_VM(vfs_mount_table); + MP_STATE_VM(vfs_mount_table) = saves_vfs; + } + #endif // The current directory is used as the boot up directory. // It is set to the internal flash filesystem by default. - MP_STATE_PORT(vfs_cur) = vfs; + MP_STATE_PORT(vfs_cur) = circuitpy_vfs; #if CIRCUITPY_STORAGE_EXTEND supervisor_flash_update_extended(); @@ -175,7 +226,7 @@ void PLACE_IN_ITCM(filesystem_flush)(void) { } void filesystem_set_internal_writable_by_usb(bool writable) { - fs_user_mount_t *vfs = &_internal_vfs; + fs_user_mount_t *vfs = &_circuitpy_usermount; filesystem_set_writable_by_usb(vfs, writable); } @@ -199,7 +250,7 @@ bool filesystem_is_writable_by_usb(fs_user_mount_t *vfs) { } void filesystem_set_internal_concurrent_write_protection(bool concurrent_write_protection) { - filesystem_set_concurrent_write_protection(&_internal_vfs, concurrent_write_protection); + filesystem_set_concurrent_write_protection(&_circuitpy_usermount, concurrent_write_protection); } void filesystem_set_concurrent_write_protection(fs_user_mount_t *vfs, bool concurrent_write_protection) { @@ -211,14 +262,14 @@ void filesystem_set_concurrent_write_protection(fs_user_mount_t *vfs, bool concu } bool filesystem_present(void) { - return _mp_vfs.len > 0; + return _circuitpy_vfs.len > 0; } fs_user_mount_t *filesystem_circuitpy(void) { if (!filesystem_present()) { return NULL; } - return &_internal_vfs; + return &_circuitpy_usermount; } fs_user_mount_t *filesystem_for_path(const char *path_in, const char **path_under_mount) { diff --git a/supervisor/shared/flash.c b/supervisor/shared/flash.c index 37fba60ae8b34..ccb60efe32192 100644 --- a/supervisor/shared/flash.c +++ b/supervisor/shared/flash.c @@ -7,6 +7,7 @@ #include "extmod/vfs_fat.h" #include "py/runtime.h" +#include "lib/oofatfs/diskio.h" #include "lib/oofatfs/ff.h" #include "supervisor/flash.h" #include "supervisor/shared/tick.h" @@ -27,9 +28,54 @@ static mp_obj_t supervisor_flash_obj_make_new(const mp_obj_type_t *type, size_t return (mp_obj_t)&supervisor_flash_obj; } -static uint32_t flash_get_block_count(void) { - return PART1_START_BLOCK + supervisor_flash_get_block_count(); + +static bool flash_ioctl(mp_obj_t self_in, size_t cmd, size_t arg, mp_int_t *out_value) { + if (out_value != NULL) { + *out_value = 0; + } + + mp_vfs_blockdev_t *self = (mp_vfs_blockdev_t *)self_in; + switch (cmd) { + case MP_BLOCKDEV_IOCTL_INIT: + supervisor_flash_init(); + break; + case MP_BLOCKDEV_IOCTL_DEINIT: + supervisor_flash_flush(); + break; // TODO properly + case MP_BLOCKDEV_IOCTL_SYNC: + supervisor_flash_flush(); + break; + case MP_BLOCKDEV_IOCTL_BLOCK_COUNT: + *out_value = PART1_START_BLOCK; + #if CIRCUITPY_SAVES_PARTITION_SIZE > 0 + if (self->size > 0) { + *out_value += self->size / self->block_size; + } else { + *out_value += supervisor_flash_get_block_count() - (self->offset / self->block_size); + } + #else + *out_value += supervisor_flash_get_block_count(); + #endif + break; + case MP_BLOCKDEV_IOCTL_BLOCK_SIZE: + *out_value = self->block_size; + break; + default: + return false; + } + return true; +} + +static mp_obj_t supervisor_flash_obj_ioctl(mp_obj_t self, mp_obj_t cmd_in, mp_obj_t arg_in) { + mp_int_t cmd = mp_obj_get_int(cmd_in); + mp_int_t arg = mp_obj_get_int(arg_in); + mp_int_t out_value; + if (flash_ioctl(self, cmd, arg, &out_value)) { + return MP_OBJ_NEW_SMALL_INT(out_value); + } + return mp_const_none; } +static MP_DEFINE_CONST_FUN_OBJ_3(supervisor_flash_obj_ioctl_obj, supervisor_flash_obj_ioctl); static void build_partition(uint8_t *buf, int boot, int type, uint32_t start_block, uint32_t num_blocks) { buf[0] = boot; @@ -67,7 +113,7 @@ static void build_partition(uint8_t *buf, int boot, int type, uint32_t start_blo buf[15] = num_blocks >> 24; } -static mp_uint_t flash_read_blocks(mp_obj_t self, uint8_t *dest, uint32_t block_num, uint32_t num_blocks) { +static mp_uint_t flash_read_blocks(mp_obj_t self_in, uint8_t *dest, uint32_t block_num, uint32_t num_blocks) { if (block_num == 0) { // fake the MBR so we can decide on our own partition table @@ -75,8 +121,11 @@ static mp_uint_t flash_read_blocks(mp_obj_t self, uint8_t *dest, uint32_t block_ dest[i] = 0; } + mp_int_t block_count; + flash_ioctl(self_in, MP_BLOCKDEV_IOCTL_BLOCK_COUNT, 0, &block_count); + // Specifying "Big FAT12/16 CHS" allows mounting by Android - build_partition(dest + 446, 0, 0x06 /* Big FAT12/16 CHS */, PART1_START_BLOCK, supervisor_flash_get_block_count()); + build_partition(dest + 446, 0, 0x06 /* Big FAT12/16 CHS */, PART1_START_BLOCK, block_count - PART1_START_BLOCK); build_partition(dest + 462, 0, 0, 0, 0); build_partition(dest + 478, 0, 0, 0, 0); build_partition(dest + 494, 0, 0, 0, 0); @@ -92,12 +141,17 @@ static mp_uint_t flash_read_blocks(mp_obj_t self, uint8_t *dest, uint32_t block_ return 0; // Done and ok. } } - return supervisor_flash_read_blocks(dest, block_num - PART1_START_BLOCK, num_blocks); + block_num -= PART1_START_BLOCK; + #if CIRCUITPY_SAVES_PARTITION_SIZE > 0 + mp_vfs_blockdev_t *self = (mp_vfs_blockdev_t *)self_in; + block_num += self->offset / self->block_size; + #endif + return supervisor_flash_read_blocks(dest, block_num, num_blocks); } static volatile bool filesystem_dirty = false; -static mp_uint_t flash_write_blocks(mp_obj_t self, const uint8_t *src, uint32_t block_num, uint32_t num_blocks) { +static mp_uint_t flash_write_blocks(mp_obj_t self_in, const uint8_t *src, uint32_t block_num, uint32_t num_blocks) { if (block_num == 0) { if (num_blocks > 1) { return 1; // error @@ -110,7 +164,12 @@ static mp_uint_t flash_write_blocks(mp_obj_t self, const uint8_t *src, uint32_t supervisor_enable_tick(); filesystem_dirty = true; } - return supervisor_flash_write_blocks(src, block_num - PART1_START_BLOCK, num_blocks); + block_num -= PART1_START_BLOCK; + #if CIRCUITPY_SAVES_PARTITION_SIZE > 0 + mp_vfs_blockdev_t *self = (mp_vfs_blockdev_t *)self_in; + block_num += self->offset / self->block_size; + #endif + return supervisor_flash_write_blocks(src, block_num, num_blocks); } } @@ -143,43 +202,6 @@ static mp_obj_t supervisor_flash_obj_writeblocks(mp_obj_t self, mp_obj_t block_n } static MP_DEFINE_CONST_FUN_OBJ_3(supervisor_flash_obj_writeblocks_obj, supervisor_flash_obj_writeblocks); -static bool flash_ioctl(mp_obj_t self_in, size_t cmd, size_t arg, mp_int_t *out_value) { - if (out_value != NULL) { - *out_value = 0; - } - switch (cmd) { - case MP_BLOCKDEV_IOCTL_INIT: - supervisor_flash_init(); - break; - case MP_BLOCKDEV_IOCTL_DEINIT: - supervisor_flash_flush(); - break; // TODO properly - case MP_BLOCKDEV_IOCTL_SYNC: - supervisor_flash_flush(); - break; - case MP_BLOCKDEV_IOCTL_BLOCK_COUNT: - *out_value = flash_get_block_count(); - break; - case MP_BLOCKDEV_IOCTL_BLOCK_SIZE: - *out_value = supervisor_flash_get_block_size(); - break; - default: - return false; - } - return true; -} - -static mp_obj_t supervisor_flash_obj_ioctl(mp_obj_t self, mp_obj_t cmd_in, mp_obj_t arg_in) { - mp_int_t cmd = mp_obj_get_int(cmd_in); - mp_int_t arg = mp_obj_get_int(arg_in); - mp_int_t out_value; - if (flash_ioctl(self, cmd, arg, &out_value)) { - return MP_OBJ_NEW_SMALL_INT(out_value); - } - return mp_const_none; -} -static MP_DEFINE_CONST_FUN_OBJ_3(supervisor_flash_obj_ioctl_obj, supervisor_flash_obj_ioctl); - static const mp_rom_map_elem_t supervisor_flash_obj_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_readblocks), MP_ROM_PTR(&supervisor_flash_obj_readblocks_obj) }, { MP_ROM_QSTR(MP_QSTR_writeblocks), MP_ROM_PTR(&supervisor_flash_obj_writeblocks_obj) }, @@ -199,15 +221,16 @@ MP_DEFINE_CONST_OBJ_TYPE( void supervisor_flash_init_vfs(fs_user_mount_t *vfs) { vfs->base.type = &mp_fat_vfs_type; vfs->blockdev.flags |= MP_BLOCKDEV_FLAG_NATIVE | MP_BLOCKDEV_FLAG_HAVE_IOCTL; + vfs->blockdev.block_size = supervisor_flash_get_block_size(); vfs->fatfs.drv = vfs; vfs->fatfs.part = 1; // flash filesystem lives on first fake partition - vfs->blockdev.readblocks[0] = (mp_obj_t)&supervisor_flash_obj_readblocks_obj; - vfs->blockdev.readblocks[1] = (mp_obj_t)&supervisor_flash_obj; + vfs->blockdev.readblocks[0] = mp_const_none; + vfs->blockdev.readblocks[1] = (mp_obj_t)&vfs->blockdev; vfs->blockdev.readblocks[2] = (mp_obj_t)flash_read_blocks; // native version - vfs->blockdev.writeblocks[0] = (mp_obj_t)&supervisor_flash_obj_writeblocks_obj; - vfs->blockdev.writeblocks[1] = (mp_obj_t)&supervisor_flash_obj; + vfs->blockdev.writeblocks[0] = mp_const_none; + vfs->blockdev.writeblocks[1] = (mp_obj_t)&vfs->blockdev; vfs->blockdev.writeblocks[2] = (mp_obj_t)flash_write_blocks; // native version - vfs->blockdev.u.ioctl[0] = (mp_obj_t)&supervisor_flash_obj_ioctl_obj; - vfs->blockdev.u.ioctl[1] = (mp_obj_t)&supervisor_flash_obj; + vfs->blockdev.u.ioctl[0] = mp_const_none; + vfs->blockdev.u.ioctl[1] = (mp_obj_t)&vfs->blockdev; vfs->blockdev.u.ioctl[2] = (mp_obj_t)flash_ioctl; // native version } diff --git a/supervisor/shared/usb/usb_msc_flash.c b/supervisor/shared/usb/usb_msc_flash.c index 53a591f7ad177..2b92035d97199 100644 --- a/supervisor/shared/usb/usb_msc_flash.c +++ b/supervisor/shared/usb/usb_msc_flash.c @@ -20,8 +20,14 @@ #define MSC_FLASH_BLOCK_SIZE 512 -static bool ejected[1] = {true}; -static bool locked[1] = {false}; +#if CIRCUITPY_SAVES_PARTITION_SIZE > 0 +#define LUN_COUNT 2 +#else +#define LUN_COUNT 1 +#endif + +static bool ejected[LUN_COUNT]; +static bool locked[LUN_COUNT]; #include "tusb.h" @@ -99,23 +105,26 @@ size_t usb_msc_add_descriptor(uint8_t *descriptor_buf, descriptor_counts_t *desc // The root FS is always at the end of the list. static fs_user_mount_t *get_vfs(int lun) { - // TODO(tannewt): Return the mount which matches the lun where 0 is the end - // and is counted in reverse. - if (lun > 0) { - return NULL; - } + // Keep a history of the mounts we pass so we can search back. + mp_vfs_mount_t *mounts[LUN_COUNT]; mp_vfs_mount_t *current_mount = MP_STATE_VM(vfs_mount_table); if (current_mount == NULL) { return NULL; } + // i is the last entry filled + size_t i = 0; + mounts[i] = current_mount; while (current_mount->next != NULL) { current_mount = current_mount->next; + i = (i + 1) % LUN_COUNT; + mounts[i] = current_mount; } - return current_mount->obj; + fs_user_mount_t *vfs = mounts[(i - lun) % LUN_COUNT]->obj; + return vfs; } static void _usb_msc_uneject(void) { - for (uint8_t i = 0; i < sizeof(ejected); i++) { + for (uint8_t i = 0; i < LUN_COUNT; i++) { ejected[i] = false; locked[i] = false; } @@ -126,7 +135,7 @@ void usb_msc_mount(void) { } void usb_msc_umount(void) { - for (uint8_t i = 0; i < sizeof(ejected); i++) { + for (uint8_t i = 0; i < LUN_COUNT; i++) { fs_user_mount_t *vfs = get_vfs(i + 1); if (vfs == NULL) { continue; @@ -138,12 +147,16 @@ void usb_msc_umount(void) { bool usb_msc_ejected(void) { bool all_ejected = true; - for (uint8_t i = 0; i < sizeof(ejected); i++) { + for (uint8_t i = 0; i < LUN_COUNT; i++) { all_ejected &= ejected[i]; } return all_ejected; } +uint8_t tud_msc_get_maxlun_cb(void) { + return LUN_COUNT; +} + // Callback invoked when received an SCSI command not in built-in list below // - READ_CAPACITY10, READ_FORMAT_CAPACITY, INQUIRY, TEST_UNIT_READY, START_STOP_UNIT, MODE_SENSE6, REQUEST_SENSE // - READ10 and WRITE10 have their own callbacks @@ -186,7 +199,7 @@ void tud_msc_capacity_cb(uint8_t lun, uint32_t *block_count, uint16_t *block_siz } bool tud_msc_is_writable_cb(uint8_t lun) { - if (lun > 1) { + if (lun >= LUN_COUNT) { return false; } @@ -278,7 +291,7 @@ void tud_msc_inquiry_cb(uint8_t lun, uint8_t vendor_id[8], uint8_t product_id[16 // 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) { + if (lun >= LUN_COUNT) { return false; } @@ -299,7 +312,7 @@ bool tud_msc_test_unit_ready_cb(uint8_t lun) { // - 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) { + if (lun >= LUN_COUNT) { return false; } fs_user_mount_t *current_mount = get_vfs(lun);