diff options
| -rw-r--r-- | docs/zephyr/quickref.rst | 14 | ||||
| -rw-r--r-- | docs/zephyr/tutorial/storage.rst | 56 | ||||
| -rw-r--r-- | ports/zephyr/Kconfig | 1 | ||||
| -rw-r--r-- | ports/zephyr/main.c | 41 | ||||
| -rw-r--r-- | ports/zephyr/modules/_boot.py | 73 | ||||
| -rw-r--r-- | ports/zephyr/zephyr_storage.c | 51 |
6 files changed, 183 insertions, 53 deletions
diff --git a/docs/zephyr/quickref.rst b/docs/zephyr/quickref.rst index 6025092a0..78d465050 100644 --- a/docs/zephyr/quickref.rst +++ b/docs/zephyr/quickref.rst @@ -135,11 +135,14 @@ the ``io-channels`` property containing all the ADC channels):: Disk Access ----------- -Use the :ref:`zephyr.DiskAccess <zephyr.DiskAccess>` class to support filesystem:: +Storage devices such as SD cards are automatically mounted at startup (e.g., at ``/sd``). +For manual mounting, use the :ref:`zephyr.DiskAccess <zephyr.DiskAccess>` class:: import vfs from zephyr import DiskAccess + print(DiskAccess.disks) # list available disk names, e.g., ('SDHC',) + block_dev = DiskAccess('SDHC') # create a block device object for an SD card vfs.VfsFat.mkfs(block_dev) # create FAT filesystem object using the disk storage block vfs.mount(block_dev, '/sd') # mount the filesystem at the SD card subdirectory @@ -152,12 +155,15 @@ Use the :ref:`zephyr.DiskAccess <zephyr.DiskAccess>` class to support filesystem Flash Area ---------- -Use the :ref:`zephyr.FlashArea <zephyr.FlashArea>` class to support filesystem:: +Flash storage is automatically mounted at ``/flash`` at startup with automatic filesystem creation. +For manual mounting, use the :ref:`zephyr.FlashArea <zephyr.FlashArea>` class:: import vfs from zephyr import FlashArea - block_dev = FlashArea(4, 4096) # creates a block device object in the frdm-k64f flash scratch partition + print(FlashArea.areas) # list available areas, e.g., {'storage': 1, 'scratch': 4} + + block_dev = FlashArea(FlashArea.areas['scratch'], 4096) # creates a block device object using the scratch partition vfs.VfsLfs2.mkfs(block_dev) # create filesystem in lfs2 format using the flash block device vfs.mount(block_dev, '/flash') # mount the filesystem at the flash subdirectory @@ -166,8 +172,6 @@ Use the :ref:`zephyr.FlashArea <zephyr.FlashArea>` class to support filesystem:: f.write('Hello world') # write to the file print(open('/flash/hello.txt').read()) # print contents of the file -The ``FlashAreas``' IDs that are available are listed in the FlashArea module, as ``ID_*``. - Sensor ------ diff --git a/docs/zephyr/tutorial/storage.rst b/docs/zephyr/tutorial/storage.rst index 7fd449226..835d239b6 100644 --- a/docs/zephyr/tutorial/storage.rst +++ b/docs/zephyr/tutorial/storage.rst @@ -8,6 +8,27 @@ Zephyr DiskAccess or FlashArea (flash map) APIs depending on which the board sup See `vfs Filesystem Mounting <https://docs.micropython.org/en/latest/library/vfs.html?highlight=vfs#filesystem-mounting>`_. +Automatic Filesystem Mounting +------------------------------ + +MicroPython on Zephyr automatically attempts to mount available storage at startup: + +1. **Flash storage** (if available): Automatically mounted at ``/flash`` using the + littlefs2 filesystem. If the filesystem doesn't exist, it will be created + automatically. + +2. **Disk storage** (such as SD cards): Automatically detected and mounted at + ``/<disk_name>`` (e.g., ``/sd`` for SD cards). + +3. **Working directory**: The working directory is set to the mounted disk if + available, otherwise to ``/flash`` if flash storage is available. + +4. **Module search path**: The ``sys.path`` is automatically updated to include + ``/<disk>/lib`` for each mounted disk and ``/flash/lib`` for flash storage. + +This automatic mounting is performed by the ``_boot.py`` module at startup. For most +use cases, you don't need to manually mount filesystems. + Disk Access ----------- @@ -19,17 +40,26 @@ For use with SD card controllers, SD cards must be present at boot & not removed be auto detected and initialized by filesystem at boot. Use the disk driver interface and a file system to access SD cards via disk access (see below). +The available disk names can be accessed via the ``DiskAccess.disks`` attribute, which contains +a tuple of available disk names (e.g., ``('SDHC',)`` or ``('SD',)``). + Example usage of FatFS with an SD card on the mimxrt1050_evk board:: import vfs from zephyr import DiskAccess - bdev = zephyr.DiskAccess('SDHC') # create block device object using DiskAccess + + # List available disks + print(DiskAccess.disks) # prints available disk names, e.g., ('SDHC',) + + bdev = DiskAccess('SDHC') # create block device object using DiskAccess vfs.VfsFat.mkfs(bdev) # create FAT filesystem object using the disk storage block vfs.mount(bdev, '/sd') # mount the filesystem at the SD card subdirectory with open('/sd/hello.txt','w') as f: # open a new file in the directory f.write('Hello world') # write to the file print(open('/sd/hello.txt').read()) # print contents of the file +Note: In most cases, disks are automatically mounted at startup and manual mounting is not necessary. + Flash Area ---------- @@ -41,16 +71,26 @@ API is recommended (see below). This class uses `Zephyr Flash map API <https://docs.zephyrproject.org/latest/reference/storage/flash_map/flash_map.html#>`_ and implements the `vfs.AbstractBlockDev` protocol. +The available flash area names can be accessed via the ``FlashArea.areas`` dictionary, which maps partition +labels to their IDs (e.g., ``{'storage': 1, 'scratch': 4}``). + Example usage with the internal flash on the reel_board or the rv32m1_vega_ri5cy board:: import vfs from zephyr import FlashArea - bdev = FlashArea(FlashArea.STORAGE, 4096) # create block device object using FlashArea - vfs.VfsLfs2.mkfs(bdev) # create Little filesystem object using the flash area block - vfs.mount(bdev, '/flash') # mount the filesystem at the flash storage subdirectory - with open('/flash/hello.txt','w') as f: # open a new file in the directory - f.write('Hello world') # write to the file - print(open('/flash/hello.txt').read()) # print contents of the file + + # List available flash areas + print(FlashArea.areas) # prints available areas, e.g., {'storage': 1, 'scratch': 4} + + bdev = FlashArea(FlashArea.areas['storage'], 4096) # create block device object using FlashArea + vfs.VfsLfs2.mkfs(bdev) # create Little filesystem object using the flash area block + vfs.mount(bdev, '/flash') # mount the filesystem at the flash storage subdirectory + with open('/flash/hello.txt','w') as f: # open a new file in the directory + f.write('Hello world') # write to the file + print(open('/flash/hello.txt').read()) # print contents of the file + +Note: In most cases, the flash storage partition is automatically mounted at ``/flash`` at startup with +automatic filesystem creation if needed, so manual mounting is not necessary. For boards such as the frdm_k64f in which the MicroPython application spills into the default flash storage -partition, use the scratch partition by replacing ``FlashArea.STORAGE`` with the integer value 4. +partition, use the scratch partition by replacing ``'storage'`` with ``'scratch'``. diff --git a/ports/zephyr/Kconfig b/ports/zephyr/Kconfig index 6db0133f4..d9e85c4fc 100644 --- a/ports/zephyr/Kconfig +++ b/ports/zephyr/Kconfig @@ -43,6 +43,7 @@ config MICROPY_VFS_LFS2 config MICROPY_FROZEN_MODULES bool "Enable Frozen Modules" + default y if FLASH_MAP || DISK_ACCESS config MICROPY_FROZEN_MANIFEST string "Path to Frozen Modules manifest.py" diff --git a/ports/zephyr/main.c b/ports/zephyr/main.c index edf2ba1a3..0fa4e97b2 100644 --- a/ports/zephyr/main.c +++ b/ports/zephyr/main.c @@ -94,39 +94,6 @@ void init_zephyr(void) { #endif } -#if MICROPY_VFS -static void vfs_init(void) { - mp_obj_t bdev = NULL; - mp_obj_t mount_point; - const char *mount_point_str = NULL; - qstr path_lib_qstr = MP_QSTRnull; - int ret = 0; - - #ifdef CONFIG_DISK_DRIVER_SDMMC - #if KERNEL_VERSION_NUMBER >= ZEPHYR_VERSION(4, 0, 0) - mp_obj_t args[] = { mp_obj_new_str_from_cstr(DT_PROP(DT_INST(0, zephyr_sdmmc_disk), disk_name)) }; - #else - mp_obj_t args[] = { mp_obj_new_str_from_cstr(CONFIG_SDMMC_VOLUME_NAME) }; - #endif - bdev = MP_OBJ_TYPE_GET_SLOT(&zephyr_disk_access_type, make_new)(&zephyr_disk_access_type, ARRAY_SIZE(args), 0, args); - mount_point_str = "/sd"; - path_lib_qstr = MP_QSTR__slash_sd_slash_lib; - #elif defined(CONFIG_FLASH_MAP) && FIXED_PARTITION_EXISTS(storage_partition) - mp_obj_t args[] = { MP_OBJ_NEW_SMALL_INT(FIXED_PARTITION_ID(storage_partition)), MP_OBJ_NEW_SMALL_INT(4096) }; - bdev = MP_OBJ_TYPE_GET_SLOT(&zephyr_flash_area_type, make_new)(&zephyr_flash_area_type, ARRAY_SIZE(args), 0, args); - mount_point_str = "/flash"; - path_lib_qstr = MP_QSTR__slash_flash_slash_lib; - #endif - - if ((bdev != NULL)) { - mount_point = mp_obj_new_str_from_cstr(mount_point_str); - ret = mp_vfs_mount_and_chdir_protected(bdev, mount_point); - mp_obj_list_append(mp_sys_path, MP_OBJ_NEW_QSTR(path_lib_qstr)); - // TODO: if this failed, make a new file system and try to mount again - } -} -#endif // MICROPY_VFS - int real_main(void) { #if MICROPY_PY_THREAD struct k_thread *z_thread = (struct k_thread *)k_current_get(); @@ -151,10 +118,12 @@ soft_reset: mp_usbd_init(); #endif - #if MICROPY_VFS - vfs_init(); + #if MICROPY_VFS && MICROPY_MODULE_FROZEN_MPY + // Mount and/or create the filesystem + pyexec_frozen_module("_boot.py", false); #endif + #if MICROPY_MODULE_FROZEN || MICROPY_VFS // Execute user scripts. int ret = pyexec_file_if_exists("boot.py"); @@ -181,7 +150,7 @@ soft_reset: } } - #if MICROPY_MODULE_FROZEN || MICROPY_VFS + #if MICROPY_MODULE_FROZEN_MPY || MICROPY_VFS soft_reset_exit: #endif diff --git a/ports/zephyr/modules/_boot.py b/ports/zephyr/modules/_boot.py new file mode 100644 index 000000000..5af302fad --- /dev/null +++ b/ports/zephyr/modules/_boot.py @@ -0,0 +1,73 @@ +# ruff: noqa: F821 +import sys +import os +from micropython import const +import vfs # vfs is always available +import zephyr + +# FlashArea depends on CONFIG_FLASH_MAP +FlashArea = getattr(zephyr, "FlashArea", None) + +# DiskAccess depends on CONFIG_DISK_ACCESS +DiskAccess = getattr(zephyr, "DiskAccess", None) + +_FLASH = const("/flash") +_FLASH_LIB = const("/flash/lib") +_STORAGE_KEY = const("storage") +_FLASH_EXISTS = False + + +def create_flash_partition(): + """Create an LFS2 filesystem on the partition labeled storage + and mount it on /flash. + Return True if successful, False otherwise. + """ + if _STORAGE_KEY in FlashArea.areas: + # TODO: get the erase block size from DTS instead of 4K. + bdev = FlashArea(FlashArea.areas[_STORAGE_KEY], 4096) + retval = True + try: + vfs.mount(bdev, _FLASH) + except OSError: + try: + vfs.VfsLfs2.mkfs(bdev) + vfs.mount(bdev, _FLASH) + except OSError: + print("Error formatting flash partition") + retval = False + return retval + return False + + +def mount_all_disks(): + """Now mount all the DiskAreas (if any).""" + retval = False + for da_name in DiskAccess.disks: + mount_name = f"/{da_name.lower()}" + da = DiskAccess(da_name) + try: + vfs.mount(da, mount_name) + sys.path.append(f"{mount_name}/lib") + os.chdir(mount_name) + retval = True + except OSError as e: + print(f"Error mounting {da_name}: {e}") + return retval + + +if FlashArea and create_flash_partition(): + _FLASH_EXISTS = True + +# Prefer disks to /flash +if not (DiskAccess and mount_all_disks()): + if _FLASH_EXISTS: + os.chdir(_FLASH) + +# Place /flash/lib last on sys.path +if _FLASH_EXISTS: + sys.path.append(_FLASH_LIB) + +# Cleanup globals for boot.py/main.py +del FlashArea, DiskAccess, zephyr +del sys, vfs, os, const +del create_flash_partition, mount_all_disks, _FLASH_EXISTS diff --git a/ports/zephyr/zephyr_storage.c b/ports/zephyr/zephyr_storage.c index 40bcef733..e72e047d0 100644 --- a/ports/zephyr/zephyr_storage.c +++ b/ports/zephyr/zephyr_storage.c @@ -40,6 +40,11 @@ #endif #ifdef CONFIG_DISK_ACCESS + +#define DISK_DEFINE_NAME(part) CONCAT(MP_QSTR_, DT_STRING_TOKEN(part, disk_name)) + +#define FOREACH_DISK(n) MP_ROM_QSTR(DISK_DEFINE_NAME(n)), + typedef struct _zephyr_disk_access_obj_t { mp_obj_base_t base; const char *pdrv; @@ -121,10 +126,43 @@ static mp_obj_t zephyr_disk_access_ioctl(mp_obj_t self_in, mp_obj_t cmd_in, mp_o } static MP_DEFINE_CONST_FUN_OBJ_3(zephyr_disk_access_ioctl_obj, zephyr_disk_access_ioctl); +static const mp_rom_obj_tuple_t zephyr_disks_tuple = { + .base = {.type = &mp_type_tuple}, + .items = { + #ifdef CONFIG_DISK_DRIVER_SDMMC + DT_FOREACH_STATUS_OKAY(zephyr_sdmmc_disk, FOREACH_DISK) + #endif + #ifdef CONFIG_DISK_DRIVER_MMC + DT_FOREACH_STATUS_OKAY(zephyr_mmc_disk, FOREACH_DISK) + #endif + #ifdef CONFIG_DISK_DRIVER_FLASH + DT_FOREACH_STATUS_OKAY(zephyr_flash_disk, FOREACH_DISK) + #endif + #ifdef CONFIG_DISK_DRIVER_RAM + DT_FOREACH_STATUS_OKAY(zephyr_ram_disk, FOREACH_DISK) + #endif + }, + .len = MP_ARRAY_SIZE((mp_rom_obj_t []) { + #ifdef CONFIG_DISK_DRIVER_SDMMC + DT_FOREACH_STATUS_OKAY(zephyr_sdmmc_disk, FOREACH_DISK) + #endif + #ifdef CONFIG_DISK_DRIVER_MMC + DT_FOREACH_STATUS_OKAY(zephyr_mmc_disk, FOREACH_DISK) + #endif + #ifdef CONFIG_DISK_DRIVER_FLASH + DT_FOREACH_STATUS_OKAY(zephyr_flash_disk, FOREACH_DISK) + #endif + #ifdef CONFIG_DISK_DRIVER_RAM + DT_FOREACH_STATUS_OKAY(zephyr_ram_disk, FOREACH_DISK) + #endif + }) +}; + static const mp_rom_map_elem_t zephyr_disk_access_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_readblocks), MP_ROM_PTR(&zephyr_disk_access_readblocks_obj) }, { MP_ROM_QSTR(MP_QSTR_writeblocks), MP_ROM_PTR(&zephyr_disk_access_writeblocks_obj) }, { MP_ROM_QSTR(MP_QSTR_ioctl), MP_ROM_PTR(&zephyr_disk_access_ioctl_obj) }, + { MP_ROM_QSTR(MP_QSTR_disks), MP_ROM_PTR(&zephyr_disks_tuple) }, }; static MP_DEFINE_CONST_DICT(zephyr_disk_access_locals_dict, zephyr_disk_access_locals_dict_table); @@ -140,8 +178,8 @@ MP_DEFINE_CONST_OBJ_TYPE( #ifdef CONFIG_FLASH_MAP -#define FLASH_AREA_DEFINE_LABEL(part) CONCAT(MP_QSTR_ID_, DT_STRING_TOKEN(part, label)) -#define FLASH_AREA_DEFINE_NB(part) CONCAT(MP_QSTR_ID_, DT_FIXED_PARTITION_ID(part)) +#define FLASH_AREA_DEFINE_LABEL(part) CONCAT(MP_QSTR_, DT_STRING_TOKEN(part, label)) +#define FLASH_AREA_DEFINE_NB(part) CONCAT(MP_QSTR_, DT_FIXED_PARTITION_ID(part)) #define FLASH_AREA_DEFINE_GETNAME(part) COND_CODE_1(DT_NODE_HAS_PROP(part, label), \ (FLASH_AREA_DEFINE_LABEL(part)), (FLASH_AREA_DEFINE_NB(part))) @@ -254,12 +292,17 @@ static mp_obj_t zephyr_flash_area_ioctl(mp_obj_t self_in, mp_obj_t cmd_in, mp_ob } static MP_DEFINE_CONST_FUN_OBJ_3(zephyr_flash_area_ioctl_obj, zephyr_flash_area_ioctl); +static const mp_rom_map_elem_t zephyr_flash_areas_table[] = { + /* Generate list of partition IDs from Zephyr Devicetree */ + DT_FOREACH_STATUS_OKAY(fixed_partitions, FOREACH_PARTITION) +}; +static MP_DEFINE_CONST_DICT(zephyr_flash_areas_dict, zephyr_flash_areas_table); + static const mp_rom_map_elem_t zephyr_flash_area_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_readblocks), MP_ROM_PTR(&zephyr_flash_area_readblocks_obj) }, { MP_ROM_QSTR(MP_QSTR_writeblocks), MP_ROM_PTR(&zephyr_flash_area_writeblocks_obj) }, { MP_ROM_QSTR(MP_QSTR_ioctl), MP_ROM_PTR(&zephyr_flash_area_ioctl_obj) }, - /* Generate list of partition IDs from Zephyr Devicetree */ - DT_FOREACH_STATUS_OKAY(fixed_partitions, FOREACH_PARTITION) + { MP_ROM_QSTR(MP_QSTR_areas), MP_ROM_PTR(&zephyr_flash_areas_dict) }, }; static MP_DEFINE_CONST_DICT(zephyr_flash_area_locals_dict, zephyr_flash_area_locals_dict_table); |
