host.embedded_filesystem

On-device filesystem abstraction for EmbeddedHost.

Embedded targets are not homogeneous: a Zephyr build may expose FAT on a RAM disk, LittleFS on simulated flash, or no filesystem at all. Console file transfer, the disk-equivalent monitor parser, and lab-data validation all need to know which of those a host has — not just “embedded yes/no”. This module is otto’s typed representation of that fact.

Each subclass is a small, stateless value object: it carries the mount path, the optional fs mount command needed to bring the FS up, and the command-formation hooks (read_command, write_command, etc.) that EmbeddedFileTransfer and the embedded monitor’s disk parser call when they need to drive the device shell. The defaults assume the stock Zephyr fs shell; a custom filesystem can override any subset of the hooks.

Lab data declares the variant by string — "filesystem": "fat-ram" etc. — and otto.storage.factory.create_host_from_dict() instantiates the right class. A project can register additional types via register_filesystem(); see Extending otto for new embedded targets for the extension walkthrough.

Built-in variants

  • NoFileSystem ("none") — the host has no on-device FS. Console transfer fails fast with a clear error; the disk parser yields nothing.

  • FatRamFileSystem ("fat-ram") — FAT on a RAM disk, mounted at /RAM:. Used by the sprout test target. Requires an explicit fs mount fat /RAM: because Zephyr 3.7’s zephyr,fstab does not bind to FAT.

  • LittleFsFileSystem ("littlefs") — LittleFS on simulated flash, mounted at /lfs. Auto-mounted via zephyr,fstab, so no mount_cmd.

class otto.host.embedded_filesystem.EmbeddedFileSystem

Bases: ABC

Abstract base for on-device filesystem variants.

Concrete subclasses set the class-level constants (type_name, mount, mount_cmd) and may override the command-formation methods if their target shell uses a different syntax than stock Zephyr fs.

Default command hooks

The default *_command methods assume the Zephyr fs shell. A project introducing a vendor filesystem with different syntax (e.g. myfs read instead of fs read) should override only the relevant methods; the transfer code and monitor parser call these methods rather than hardcoding the literal command strings, so the seam is real.

type_name : --is-rst--:py:data:`~typing.ClassVar`\ \[:py:class:`str`]

Lab-data string for this variant (e.g. 'fat-ram').

Looked up against _FILESYSTEM_CLASSES by the storage factory. Must be unique across all registered subclasses.

mount : --is-rst--:py:data:`~typing.ClassVar`\ \[:py:class:`str` | :py:obj:`None`]

Mount path of this filesystem on the device, or None when the target has no FS. Also used as the default default_dest_dir for an EmbeddedHost that doesn’t override it explicitly.

mount_cmd : --is-rst--:py:data:`~typing.ClassVar`\ \[:py:class:`str` | :py:obj:`None`] = None

Optional one-shot fs mount command. Needed for filesystems Zephyr cannot auto-mount via zephyr,fstab (notably FAT, in 3.7 LTS). None for auto-mounted filesystems and for NoFileSystem.

property supports_transfer : bool

True when console file transfer can target this filesystem.

Equivalent to mount is not None — every FS with a mount path supports fs read / fs write; a NoFileSystem host does not.

property supports_disk_metric : bool

True when fs statvfs is reachable on this filesystem.

Defaults to supports_transfer because every Zephyr fs shell with a mount also exposes fs statvfs. Override to False on a custom filesystem that lacks the statvfs subcommand.

read_command(path)

Render the shell command that reads path as a hexdump.

Return type:

str

write_command(path, offset, hexbytes)

Render the chunked-write command for path.

hexbytes is the already-space-separated lower-case hex of the chunk (e.g. "41 42 43"). Zephyr 3.7’s fs write requires the -o <offset> flag for positional offset (live-verified, see otto.host.embedded_transfer); a vendor FS that uses a positional offset can override this method.

Return type:

str

rm_command(path)

Render the command that removes path.

Return type:

str

trunc_command(path, length)

Render the command that truncates path to length bytes.

Return type:

str

ls_command(path)

Render the command that lists path.

Return type:

str

statvfs_command()

Render the command that reports filesystem usage stats, or None when the filesystem cannot serve one (no FS, no statvfs builtin). The default returns None when there is no mount and fs statvfs <mount> otherwise.

Return type:

str | None

class otto.host.embedded_filesystem.NoFileSystem

Bases: EmbeddedFileSystem

The target has no on-device filesystem.

Set as the default for an EmbeddedHost when "filesystem" is absent from lab data. Console transfer and the disk parser both short-circuit to a clear no-op / clear error — never a hang or a garbled response from running fs against a target that doesn’t have it.

type_name : --is-rst--:py:data:`~typing.ClassVar`\ \[:py:class:`str`] = 'none'

Lab-data string for this variant (e.g. 'fat-ram').

Looked up against _FILESYSTEM_CLASSES by the storage factory. Must be unique across all registered subclasses.

mount : --is-rst--:py:data:`~typing.ClassVar`\ \[:py:class:`str` | :py:obj:`None`] = None

Mount path of this filesystem on the device, or None when the target has no FS. Also used as the default default_dest_dir for an EmbeddedHost that doesn’t override it explicitly.

class otto.host.embedded_filesystem.FatRamFileSystem

Bases: EmbeddedFileSystem

FAT on a RAM disk, mounted at /RAM:.

Used by the sprout test target on Zephyr 3.7. The mount_cmd is required because the zephyr,fstab binding in 3.7 LTS does not handle FAT — otto issues fs mount fat /RAM: once on first transfer.

type_name : --is-rst--:py:data:`~typing.ClassVar`\ \[:py:class:`str`] = 'fat-ram'

Lab-data string for this variant (e.g. 'fat-ram').

Looked up against _FILESYSTEM_CLASSES by the storage factory. Must be unique across all registered subclasses.

mount : --is-rst--:py:data:`~typing.ClassVar`\ \[:py:class:`str` | :py:obj:`None`] = '/RAM:'

Mount path of this filesystem on the device, or None when the target has no FS. Also used as the default default_dest_dir for an EmbeddedHost that doesn’t override it explicitly.

mount_cmd : --is-rst--:py:data:`~typing.ClassVar`\ \[:py:class:`str` | :py:obj:`None`] = 'fs mount fat /RAM:'

Optional one-shot fs mount command. Needed for filesystems Zephyr cannot auto-mount via zephyr,fstab (notably FAT, in 3.7 LTS). None for auto-mounted filesystems and for NoFileSystem.

class otto.host.embedded_filesystem.LittleFsFileSystem

Bases: EmbeddedFileSystem

LittleFS on simulated flash, mounted at /lfs.

Used by the sprout_lfs test target. Auto-mounted via zephyr,fstab at boot, so no mount_cmd is needed.

type_name : --is-rst--:py:data:`~typing.ClassVar`\ \[:py:class:`str`] = 'littlefs'

Lab-data string for this variant (e.g. 'fat-ram').

Looked up against _FILESYSTEM_CLASSES by the storage factory. Must be unique across all registered subclasses.

mount : --is-rst--:py:data:`~typing.ClassVar`\ \[:py:class:`str` | :py:obj:`None`] = '/lfs'

Mount path of this filesystem on the device, or None when the target has no FS. Also used as the default default_dest_dir for an EmbeddedHost that doesn’t override it explicitly.

otto.host.embedded_filesystem.register_filesystem(type_name, cls)

Make a custom EmbeddedFileSystem subclass available to lab data.

Call from an init module listed in .otto/settings.toml — the same pattern otto.monitor.parsers.register_host_parsers() follows. Once registered, lab-data entries can reference the subclass by type_name in the filesystem field, and otto.storage.factory.create_host_from_dict() will instantiate it.

Return type:

None

Raises

ValueError

If type_name doesn’t match cls.type_name (a likely-bug mismatch — the registry key and the class constant should agree).

otto.host.embedded_filesystem.build_filesystem(type_name)

Construct the EmbeddedFileSystem registered under type_name.

Used by otto.storage.factory.create_host_from_dict() to resolve the lab-data filesystem string into a typed instance.

Return type:

EmbeddedFileSystem

Raises

ValueError

If type_name is not registered. The error lists the currently registered types so a typo ('fatram' vs 'fat-ram') is diagnosable from the message alone.