diff --git a/components/micropython/CMakeLists.txt b/components/micropython/CMakeLists.txt index 6efb5c7b96269ad23d0ae19de0fe3b08724427ed..370031c54bb193dced9ba331fcc97fd4bea8ebe7 100644 --- a/components/micropython/CMakeLists.txt +++ b/components/micropython/CMakeLists.txt @@ -37,15 +37,6 @@ set(MICROPY_SOURCE_SHARED ${MICROPY_DIR}/shared/runtime/pyexec.c ) -set(MICROPY_SOURCE_LIB - ${MICROPY_DIR}/lib/littlefs/lfs1.c - ${MICROPY_DIR}/lib/littlefs/lfs1_util.c - ${MICROPY_DIR}/lib/littlefs/lfs2.c - ${MICROPY_DIR}/lib/littlefs/lfs2_util.c - ${MICROPY_DIR}/lib/oofatfs/ff.c - ${MICROPY_DIR}/lib/oofatfs/ffunicode.c -) - set(MICROPY_SOURCE_DRIVERS ${MICROPY_DIR}/drivers/bus/softspi.c ${MICROPY_DIR}/drivers/dht/dht.c @@ -85,7 +76,6 @@ set(MICROPY_SOURCE_QSTR "${MICROPY_SOURCE_EXTMOD}" "${MICROPY_SOURCE_USERMOD}" "${MICROPY_SOURCE_SHARED}" - "${MICROPY_SOURCE_LIB}" "${MICROPY_SOURCE_PORT}" ) @@ -138,7 +128,6 @@ idf_component_register( ${MICROPY_SOURCE_EXTMOD} ${MICROPY_SOURCE_USERMOD} ${MICROPY_SOURCE_SHARED} - ${MICROPY_SOURCE_LIB} ${MICROPY_SOURCE_DRIVERS} ${MICROPY_SOURCE_PORT} INCLUDE_DIRS @@ -182,4 +171,4 @@ target_compile_definitions(${COMPONENT_TARGET} PUBLIC include("${MICROPY_DIR}/py/mkrules.cmake") -target_link_libraries(${COMPONENT_LIB} INTERFACE "-u flow3r_startup") \ No newline at end of file +target_link_libraries(${COMPONENT_LIB} INTERFACE "-u flow3r_startup") diff --git a/components/st3m/CMakeLists.txt b/components/st3m/CMakeLists.txt index 34f3009113cd9c477e244a9bc3944d1ccf4bdcd4..408dad9876f5b3e4247bca20ce0b1e42003f208d 100644 --- a/components/st3m/CMakeLists.txt +++ b/components/st3m/CMakeLists.txt @@ -18,6 +18,7 @@ idf_component_register( st3m_ringbuffer.c st3m_tar.c st3m_fs.c + st3m_fs_flash.c INCLUDE_DIRS . REQUIRES diff --git a/components/st3m/st3m_fs.c b/components/st3m/st3m_fs.c index f774128ab447719ff91e2f16c430a7dc2401eb15..2a705a6003f3deeb6937b240ead5eef5d72bc358 100644 --- a/components/st3m/st3m_fs.c +++ b/components/st3m/st3m_fs.c @@ -1,5 +1,6 @@ #include "st3m_fs.h" +#include "st3m_fs_flash.h" #include "st3m_mode.h" #include "st3m_sys_data.h" #include "st3m_tar.h" @@ -37,9 +38,6 @@ static struct dirent _root_dirents[4] = { }, }; -// Handle of the wear levelling library instance -static wl_handle_t s_wl_handle = WL_INVALID_HANDLE; - static int _root_vfs_close(int fd) { return 0; } static int _root_vfs_fcntl(int fd, int cmd, int arg) { return 0; } @@ -117,23 +115,15 @@ static esp_vfs_t _root_vfs = { void st3m_fs_init(void) { esp_err_t err; + st3m_fs_flash_init(); + if ((err = esp_vfs_register("", &_root_vfs, NULL)) != ESP_OK) { ESP_LOGE(TAG, "Failed to mount root VFS: %s", esp_err_to_name(err)); return; } - const esp_vfs_fat_mount_config_t mount_config = { - .max_files = 4, - .format_if_mount_failed = true, - .allocation_unit_size = CONFIG_WL_SECTOR_SIZE - }; - - err = esp_vfs_fat_spiflash_mount("/flash", "vfs", &mount_config, - &s_wl_handle); - if (err != ESP_OK) { + if ((err = st3m_fs_flash_mount()) != ESP_OK) { ESP_LOGE(TAG, "Failed to mount FAT FS: %s", esp_err_to_name(err)); return; } - - ESP_LOGI(TAG, "Mounted Flash VFS Partition at /flash"); -} +} \ No newline at end of file diff --git a/components/st3m/st3m_fs_flash.c b/components/st3m/st3m_fs_flash.c new file mode 100644 index 0000000000000000000000000000000000000000..a3a9f4f5fe8a46289a3feb807ca322e97634752a --- /dev/null +++ b/components/st3m/st3m_fs_flash.c @@ -0,0 +1,259 @@ +#include "st3m_fs_flash.h" + +#include "esp_log.h" +#include "esp_system.h" +#include "esp_vfs.h" +#include "esp_vfs_fat.h" + +#include "freertos/FreeRTOS.h" +#include "freertos/semphr.h" + +#include "diskio.h" +#include "diskio_impl.h" +#include "diskio_wl.h" +#include "vfs_fat_internal.h" + +static const char *TAG = "st3m-fs-flash"; + +static SemaphoreHandle_t _mu = NULL; +static st3m_fs_flash_status_t _status = st3m_fs_flash_status_unmounted; + +// Handle of the wear levelling library instance +static wl_handle_t _wl_handle = WL_INVALID_HANDLE; + +void st3m_fs_flash_init(void) { + assert(_mu == NULL); + _mu = xSemaphoreCreateMutex(); + assert(_mu != NULL); + + const esp_partition_t *data_partition = esp_partition_find_first( + ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_ANY, "vfs"); + if (data_partition == NULL) { + ESP_LOGE(TAG, "Could not find data partition."); + return; + } + esp_err_t res = wl_mount(data_partition, &_wl_handle); + if (res != ESP_OK) { + ESP_LOGE(TAG, "Mounting wear leveling failed."); + return; + } +} + +st3m_fs_flash_status_t st3m_fs_flash_get_status(void) { + xSemaphoreTake(_mu, portMAX_DELAY); + st3m_fs_flash_status_t st = _status; + xSemaphoreGive(_mu); + return st; +} + +static esp_err_t _f_mount_rw(FATFS *fs, const char *drv, + const esp_vfs_fat_mount_config_t *mount_config) { + FRESULT fresult = f_mount(fs, drv, 1); + if (fresult != FR_OK) { + ESP_LOGW(TAG, "f_mount failed (%d)", fresult); + + bool need_mount_again = + (fresult == FR_NO_FILESYSTEM || fresult == FR_INT_ERR) && + mount_config->format_if_mount_failed; + if (!need_mount_again) { + return ESP_FAIL; + } + + const size_t workbuf_size = 4096; + void *workbuf = ff_memalloc(workbuf_size); + if (workbuf == NULL) { + return ESP_ERR_NO_MEM; + } + + size_t alloc_unit_size = esp_vfs_fat_get_allocation_unit_size( + CONFIG_WL_SECTOR_SIZE, mount_config->allocation_unit_size); + ESP_LOGI(TAG, "Formatting FATFS partition, allocation unit size=%d", + alloc_unit_size); + const MKFS_PARM opt = { (BYTE)(FM_ANY | FM_SFD), 0, 0, 0, + alloc_unit_size }; + fresult = f_mkfs(drv, &opt, workbuf, workbuf_size); + free(workbuf); + workbuf = NULL; + if (fresult != FR_OK) { + ESP_LOGE(TAG, "f_mkfs failed: %d", fresult); + return ESP_FAIL; + } + + ESP_LOGI(TAG, "Mounting again"); + fresult = f_mount(fs, drv, 0); + if (fresult != FR_OK) { + ESP_LOGE(TAG, "f_mount failed after format: %d", fresult); + return ESP_FAIL; + } + } + return ESP_OK; +} + +static esp_err_t _st3m_fs_flash_mount_unlocked(void) { + esp_err_t ret; + + if (_status == st3m_fs_flash_status_mounted) { + return ESP_OK; + } + + BYTE pdrv = 0xFF; + if ((ret = ff_diskio_get_drive(&pdrv)) != ESP_OK) { + ESP_LOGE(TAG, "Getting drive number failed: %s", esp_err_to_name(ret)); + return ret; + } + + char drv[3] = { (char)('0' + pdrv), ':', 0 }; + if ((ret = ff_diskio_register_wl_partition(pdrv, _wl_handle)) != ESP_OK) { + ESP_LOGE(TAG, "Registering WL partition failed: %s", + esp_err_to_name(ret)); + return ret; + } + + FATFS *fs; + esp_vfs_fat_mount_config_t mount_config = { .max_files = 16, + .format_if_mount_failed = true, + .allocation_unit_size = + CONFIG_WL_SECTOR_SIZE }; + + ret = esp_vfs_fat_register("/flash", drv, mount_config.max_files, &fs); + if (ret == ESP_ERR_INVALID_STATE) { + // it's okay, already registered with VFS + } else if (ret != ESP_OK) { + ESP_LOGE(TAG, "Registering FAT/WL partition failed: %s", + esp_err_to_name(ret)); + return ret; + } + + if ((ret = _f_mount_rw(fs, drv, &mount_config)) != ESP_OK) { + ESP_LOGE(TAG, "Registering FAT/WL partition failed: %s", + esp_err_to_name(ret)); + return ret; + } + + ESP_LOGI(TAG, "Mounted Flash VFS Partition at /flash"); + _status = st3m_fs_flash_status_mounted; + return ESP_OK; +} + +esp_err_t st3m_fs_flash_mount(void) { + xSemaphoreTake(_mu, portMAX_DELAY); + esp_err_t ret = _st3m_fs_flash_mount_unlocked(); + xSemaphoreGive(_mu); + return ret; +} + +static esp_err_t _st3m_fs_flash_unmount_unlocked(void) { + if (_status == st3m_fs_flash_status_unmounted) { + return ESP_OK; + } + + BYTE pdrv = ff_diskio_get_pdrv_wl(_wl_handle); + if (pdrv == 0xFF) { + ESP_LOGE(TAG, "Could not get drive number"); + return ESP_FAIL; + } + + esp_err_t ret; + char drv[3] = { (char)('0' + pdrv), ':', 0 }; + f_mount(0, drv, 0); + ff_diskio_unregister(pdrv); + ff_diskio_clear_pdrv_wl(_wl_handle); + if ((ret = esp_vfs_fat_unregister_path("/flash")) != ESP_OK) { + ESP_LOGE(TAG, "Could not unmount: %s", esp_err_to_name(ret)); + return ret; + } + + _status = st3m_fs_flash_status_unmounted; + return ESP_OK; +} + +esp_err_t st3m_fs_flash_unmount(void) { + xSemaphoreTake(_mu, portMAX_DELAY); + esp_err_t ret = _st3m_fs_flash_unmount_unlocked(); + xSemaphoreGive(_mu); + return ret; +} + +int32_t st3m_fs_flash_read(uint8_t lun, uint32_t lba, uint32_t offset, + void *buffer, uint32_t bufsize) { + size_t src_addr = lba * wl_sector_size(_wl_handle) + offset; + esp_err_t ret = wl_read(_wl_handle, src_addr, buffer, bufsize); + if (ret == ESP_OK) { + return bufsize; + } else { + return 0; + } +} + +int32_t st3m_fs_flash_write(uint8_t lun, uint32_t lba, uint32_t offset, + const void *buffer, uint32_t bufsize) { + size_t sector_size = wl_sector_size(_wl_handle); + + size_t start = lba * sector_size + offset; + size_t size = bufsize; + + size_t start_align = (start % sector_size); + if (start_align != 0) { + start -= start_align; + size += start_align; + } + + size_t size_align = (size % sector_size); + if (size_align != 0) { + size_align = sector_size - size_align; + size += size_align; + } + + uint8_t *copy = malloc(size); + assert(copy != 0); + + // Prefix, if any. + if (start_align != 0) { + esp_err_t ret = wl_read(_wl_handle, start, copy, sector_size); + if (ret != ESP_OK) { + free(copy); + return 0; + } + } + // Suffix, if any. + // TODO(q3k): skip if this is the same as the previous read. + if (size_align != 0) { + esp_err_t ret = wl_read(_wl_handle, start + size - sector_size, + copy + size - sector_size, sector_size); + if (ret != ESP_OK) { + free(copy); + return 0; + } + } + // Main data. + memcpy(copy + start_align, buffer, bufsize); + + // Erase and write. + wl_erase_range(_wl_handle, start, size); + esp_err_t ret = wl_write(_wl_handle, start, copy, size); + free(copy); + if (ret != ESP_OK) { + return 0; + } + return bufsize; +} + +esp_err_t st3m_fs_flash_erase(void) { + xSemaphoreTake(_mu, portMAX_DELAY); + if (_status != st3m_fs_flash_status_unmounted) { + xSemaphoreGive(_mu); + ESP_LOGE(TAG, "Cannot erase flash while mounted"); + return ESP_ERR_INVALID_STATE; + } + // Only erase first 32 sectors as that's enough to trigger FAT reformat on + // reboot. + esp_err_t ret = + wl_erase_range(_wl_handle, 0, wl_sector_size(_wl_handle) * 32); + xSemaphoreGive(_mu); + return ret; +} + +size_t st3m_fs_flash_get_blocksize(void) { return wl_sector_size(_wl_handle); } +size_t st3m_fs_flash_get_blockcount(void) { + return wl_size(_wl_handle) / wl_sector_size(_wl_handle); +} \ No newline at end of file diff --git a/components/st3m/st3m_fs_flash.h b/components/st3m/st3m_fs_flash.h new file mode 100644 index 0000000000000000000000000000000000000000..ce34afc9b0a4ccbe19a40bd03ccc7f3ba769e233 --- /dev/null +++ b/components/st3m/st3m_fs_flash.h @@ -0,0 +1,40 @@ +#pragma once + +// This subsystem manages the wear-leveled FAT32 partition on the built-in SPI +// flash. + +#include "esp_err.h" + +typedef enum { + // Not mounted, can be accessed via _write, _read, _erase. + st3m_fs_flash_status_unmounted = 0, + // Mounted on /flash. + st3m_fs_flash_status_mounted = 1, + st3m_fs_flash_status_error = 2, +} st3m_fs_flash_status_t; + +// Return the current state/status of the internal flash FAT32 partition. +st3m_fs_flash_status_t st3m_fs_flash_get_status(void); +// Mount the partition on /flash. No-op if already mounted. +esp_err_t st3m_fs_flash_mount(void); +// Unmount the partition. No-op if already unmounted. +esp_err_t st3m_fs_flash_unmount(void); + +// Read function for st3m_usb_msc. Should only be called if the flash is +// unmounted. +int32_t st3m_fs_flash_read(uint8_t lun, uint32_t lba, uint32_t offset, + void *buffer, uint32_t bufsize); +// Write function for st3m_usb_msc. Should only be called if the flash is +// unmounted. +int32_t st3m_fs_flash_write(uint8_t lun, uint32_t lba, uint32_t offset, + const void *buffer, uint32_t bufsize); +// Erase the FAT32 partition. Next mount will format it. Should only be called +// if the flash is ummounted. +esp_err_t st3m_fs_flash_erase(void); +// Return block size (top-level, ie. as exposed by the wear leveling layer). +size_t st3m_fs_flash_get_blocksize(void); +// Return block count (top-level, ie. as exposed by the wear leveling layer). +size_t st3m_fs_flash_get_blockcount(void); + +// Private. +void st3m_fs_flash_init(void); \ No newline at end of file diff --git a/recovery/main/rec_main.c b/recovery/main/rec_main.c index d03e5d14673e309e933aa96bdf02aa42e3952009..5729a49f30b4a5c77f0699b5754779d874da1bfd 100644 --- a/recovery/main/rec_main.c +++ b/recovery/main/rec_main.c @@ -4,10 +4,13 @@ #include "freertos/task.h" #include "flow3r_bsp.h" +#include "st3m_fs.h" +#include "st3m_fs_flash.h" #include "st3m_gfx.h" #include "st3m_io.h" #include "st3m_usb.h" +#include "rec_fatal.h" #include "rec_gui.h" #include "rec_vfs.h" @@ -37,7 +40,10 @@ static void _main_disk_mode(void) { static void _main_erase_fat32(void) { rec_erasing_draw(); - rec_vfs_wl_erase(); + esp_err_t ret = st3m_fs_flash_erase(); + if (ret != ESP_OK) { + rec_fatal("Erase failed"); + } _cur_menu = &_erasedone_menu; _cur_menu->selected = 0; } @@ -99,8 +105,7 @@ void app_main(void) { flow3r_bsp_display_set_backlight(100); flow3r_bsp_i2c_init(); st3m_io_init(); - - rec_vfs_wl_init(); + st3m_fs_init(); TickType_t last_wake; last_wake = xTaskGetTickCount(); diff --git a/recovery/main/rec_vfs.c b/recovery/main/rec_vfs.c index 5264f07f85c0bffdfa1720d8a85d08784f16c2ef..37accfbdefc3c8a4318ca04af1e0ce41f1ff6287 100644 --- a/recovery/main/rec_vfs.c +++ b/recovery/main/rec_vfs.c @@ -2,111 +2,23 @@ #include "rec_fatal.h" +#include "st3m_fs_flash.h" #include "st3m_usb.h" #include <string.h> -static wl_handle_t _wl_handle = WL_INVALID_HANDLE; - -static int32_t _flash_read(uint8_t lun, uint32_t lba, uint32_t offset, - void *buffer, uint32_t bufsize) { - size_t src_addr = lba * wl_sector_size(_wl_handle) + offset; - esp_err_t ret = wl_read(_wl_handle, src_addr, buffer, bufsize); - if (ret == ESP_OK) { - return bufsize; - } else { - return 0; - } -} - -static int32_t _flash_write(uint8_t lun, uint32_t lba, uint32_t offset, - const void *buffer, uint32_t bufsize) { - size_t sector_size = wl_sector_size(_wl_handle); - - size_t start = lba * sector_size + offset; - size_t size = bufsize; - - size_t start_align = (start % sector_size); - if (start_align != 0) { - start -= start_align; - size += start_align; - } - - size_t size_align = (size % sector_size); - if (size_align != 0) { - size_align = sector_size - size_align; - size += size_align; - } - - uint8_t *copy = malloc(size); - assert(copy != 0); - - // Prefix, if any. - if (start_align != 0) { - esp_err_t ret = wl_read(_wl_handle, start, copy, sector_size); - if (ret != ESP_OK) { - free(copy); - return 0; - } - } - // Suffix, if any. - // TODO(q3k): skip if this is the same as the previous read. - if (size_align != 0) { - esp_err_t ret = wl_read(_wl_handle, start + size - sector_size, - copy + size - sector_size, sector_size); - if (ret != ESP_OK) { - free(copy); - return 0; - } - } - // Main data. - memcpy(copy + start_align, buffer, bufsize); - - // Erase and write. - wl_erase_range(_wl_handle, start, size); - esp_err_t ret = wl_write(_wl_handle, start, copy, size); - if (ret != ESP_OK) { - free(copy); - return 0; - } - - return bufsize; -} - -void rec_vfs_wl_init(void) { - const esp_partition_t *data_partition = esp_partition_find_first( - ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_ANY, "vfs"); - if (data_partition == NULL) { - rec_fatal("no data partition"); - } - esp_err_t res = wl_mount(data_partition, &_wl_handle); - if (res != ESP_OK) { - rec_fatal("wear leveling failed"); - } -} - void rec_vfs_wl_msc_start(void) { st3m_usb_msc_conf_t msc = { - .block_size = wl_sector_size(_wl_handle), - .block_count = wl_size(_wl_handle) / wl_sector_size(_wl_handle), + .block_size = st3m_fs_flash_get_blocksize(), + .block_count = st3m_fs_flash_get_blockcount(), .product_id = { 'f', 'l', 'o', 'w', '3', 'r', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - .fn_read10 = _flash_read, - .fn_write10 = _flash_write, + .fn_read10 = st3m_fs_flash_read, + .fn_write10 = st3m_fs_flash_write, }; st3m_usb_mode_t usb_mode = { .kind = st3m_usb_mode_kind_disk, .disk = &msc, }; st3m_usb_mode_switch(&usb_mode); -} - -void rec_vfs_wl_erase(void) { - // Only erase first 32 sectors as that's enough to trigger FAT reformat on - // reboot. - esp_err_t ret = - wl_erase_range(_wl_handle, 0, wl_sector_size(_wl_handle) * 32); - if (ret != ESP_OK) { - rec_fatal("erase failed"); - } } \ No newline at end of file