TikuOS: Simple. Ubiquitous. Intelligence, Everywhere.

An open-source operating system for microwatt computers.
Deploy-and-forget embedded systems that run for years on a coin cell or indefinitely on harvested energy.

API Reference / Virtual Filesystem

Module · Virtual Filesystem

Virtual Filesystem

The VFS is a static tree of named nodes that presents the entire system — peripherals, OS state, processes, and configuration — as readable/writable paths. There is no block storage and no inodes; each node holds handler functions that implement read and write in whatever way makes sense for that resource.

The same API is used by the shell (cat, ls, write) and application code. This means anything the shell can read or configure at runtime, a process can do programmatically with a few lines of C.

Headers: kernel/vfs/tiku_vfs.h, kernel/vfs/tiku_vfs_tree.h, kernel/process/tiku_proc_vfs.h.

Core API

Header: kernel/vfs/tiku_vfs.h

Types

typedef enum {
    TIKU_VFS_DIR,
    TIKU_VFS_FILE
} tiku_vfs_type_t;

typedef int (*tiku_vfs_read_fn)(char *buf, size_t max);
typedef int (*tiku_vfs_write_fn)(const char *buf, size_t len);

typedef struct tiku_vfs_node {
    const char                  *name;        /* Path component             */
    tiku_vfs_type_t              type;        /* DIR or FILE                */
    tiku_vfs_read_fn             read;        /* NULL if not readable       */
    tiku_vfs_write_fn            write;       /* NULL if not writable       */
    const struct tiku_vfs_node  *children;    /* For DIR: child array       */
    uint8_t                      child_count; /* For DIR: number of children*/
} tiku_vfs_node_t;

typedef void (*tiku_vfs_list_fn)(const tiku_vfs_node_t *node, void *ctx);

Functions

FunctionDescription
tiku_vfs_init(root)Register the root node. All subsequent resolve/read/write calls walk from this root.
tiku_vfs_resolve(path)Walk the tree to find a node by path. Returns NULL if not found. Handles leading/trailing/duplicate slashes.
tiku_vfs_read(path, buf, max)Resolve path and invoke the file's read handler. Returns bytes written, or -1 on error.
tiku_vfs_write(path, data, len)Resolve path and invoke the file's write handler. Returns 0 on success, -1 on error.
tiku_vfs_list(path, callback, ctx)List children of a directory node. Calls callback once per child with user context.

Example: Read a Path

const tiku_vfs_node_t *n = tiku_vfs_resolve("/sys/mem/free");
if (n && n->type == TIKU_VFS_FILE && n->read) {
    char buf[32];
    int len = n->read(buf, sizeof(buf));
    /* ... */
}

/* Or directly: */
char buf[32];
tiku_vfs_read("/sys/mem/free", buf, sizeof(buf));

Example: List a Directory

static void print_entry(const tiku_vfs_node_t *node, void *ctx) {
    const char *perm = (node->read && node->write) ? "rw"
                     : node->read                   ? "r-" : "--";
    SHELL_PRINTF("  %s %s%s\n", perm, node->name,
                 node->type == TIKU_VFS_DIR ? "/" : "");
}

tiku_vfs_list("/dev", print_entry, NULL);

Tree Initialization

Header: kernel/vfs/tiku_vfs_tree.h

FunctionDescription
tiku_vfs_tree_init()Build and register the production VFS tree with /sys, /dev, and /proc. Call once during boot, after processes are registered. Internally unlocks the MPU to write FRAM-backed node arrays.
tiku_vfs_set_boot_count(count)Set the boot count value exposed via /sys/boot/count. Call from the hibernate resume path after reading the marker.

Process VFS

Header: kernel/process/tiku_proc_vfs.h

FunctionDescription
tiku_proc_vfs_get()Build and return the /proc directory node. Rebuilds per-pid directories from the process registry on each call.
tiku_proc_vfs_child_count()Number of children under /proc (count + queue + catalog + one per registered process).

Complete Node Tree

All paths listed below are accessible via the shell (cat, ls, write) and programmatically via tiku_vfs_read() / tiku_vfs_write().

/
├── sys/
│   ├── version              r-   OS version string
│   ├── device               r-   MCU name
│   ├── uptime               r-   seconds since boot
│   ├── mem/
│   │   ├── sram             r-   RAM size in bytes
│   │   ├── nvm              r-   FRAM size in bytes
│   │   ├── free             r-   live stack headroom (SP - BSS end)
│   │   └── used             r-   sum of per-process SRAM allocation
│   ├── cpu/
│   │   └── freq             r-   clock frequency in Hz
│   ├── power/
│   │   ├── mode             r-   current LPM (off/LPM0/LPM3/LPM4)
│   │   └── wake             r-   active wake sources
│   ├── timer/
│   │   ├── count            r-   active software timers
│   │   ├── next             r-   ticks until next expiration
│   │   ├── fired            r-   total expirations since boot
│   │   └── list/{0..3}      r-   per-timer mode, remaining, interval
│   ├── clock/
│   │   └── ticks            r-   raw tick counter
│   ├── watchdog/
│   │   ├── mode             rw   "watchdog" or "interval"
│   │   ├── clock            rw   "aclk" or "smclk"
│   │   ├── interval         rw   divider: 64, 512, 8192, or 32768
│   │   └── kick             -w   write any value to kick the timer
│   ├── htimer/
│   │   ├── now              r-   hardware timer counter
│   │   └── scheduled        r-   1 if pending, 0 if idle
│   ├── boot/
│   │   ├── reason           r-   last reset cause
│   │   ├── count            r-   hibernate boot counter
│   │   ├── stage            r-   boot stage
│   │   ├── rstiv            r-   raw SYSRSTIV hex value
│   │   ├── clock/
│   │   │   ├── mclk         r-   live MCLK frequency in Hz
│   │   │   ├── smclk        r-   live SMCLK frequency in Hz
│   │   │   ├── aclk         r-   live ACLK frequency in Hz
│   │   │   └── fault        r-   clock fault flag (0 or 1)
│   │   └── mpu/
│   │       └── violations   r-   MPU segment violation flags (hex)
│   └── sched/
│       └── idle             r-   scheduler idle entry count
├── dev/
│   ├── led0                 rw   LED1 state (0, 1, t=toggle)
│   ├── led1                 rw   LED2 state
│   ├── console              rw   system console (UART)
│   ├── null                 rw   data sink
│   ├── zero                 r-   zero source
│   ├── gpio/{1..4}/{0..7}   rw   per-pin state
│   ├── gpio_dir/{1..4}      r-   per-port pin directions
│   ├── uart/
│   │   ├── overruns         r-   UART overrun count since boot
│   │   └── baud             r-   configured baud rate
│   ├── adc/
│   │   ├── temp             r-   on-chip temperature sensor
│   │   └── battery          r-   battery voltage
│   ├── i2c/scan             r-   list responding I2C addresses
│   └── spi/config           r-   SPI mode, bit order, prescaler
└── proc/
    ├── count                r-   number of active processes
    ├── queue/
    │   ├── length           r-   pending events in queue
    │   └── space            r-   free event slots
    ├── catalog/
    │   ├── count            r-   available-but-not-started processes
    │   └── {0..7}/name      r-   catalog entry name
    └── {0..7}/              per-process directory
        ├── name             r-   process name
        ├── state            r-   running/ready/waiting/sleeping/stopped
        ├── pid              r-   numeric process id
        ├── sram_used        r-   SRAM bytes allocated
        ├── fram_used        r-   FRAM bytes allocated
        ├── uptime           r-   seconds since start
        ├── wake_count       r-   times scheduled
        └── events           r-   pending events for this process

Character Devices

PathReadWrite
/dev/consoleDrains pending UART RX bytes (non-blocking, returns 0 when idle)Sends each byte through tiku_uart_putc()
/dev/nullAlways returns 0 bytes (EOF)Accepts and silently discards all data
/dev/zeroFills buffer with NUL bytes, returns buffer sizeNot writable (returns -1)

Adding a New VFS Node

  1. Write a read handler (and optionally a write handler):
    static int my_read(char *buf, size_t max) {
        return snprintf(buf, max, "%u\n", my_value);
    }
  2. Add a tiku_vfs_node_t entry to the appropriate children array in kernel/vfs/tiku_vfs_tree.c:
    { "my_node", TIKU_VFS_FILE, my_read, NULL, NULL, 0 },
  3. Update the parent directory's child_count.
  4. If the node lives in FRAM (.persistent section), wrap writes in tiku_mpu_unlock_nvm() / tiku_mpu_lock_nvm().