API Reference / 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
| Function | Description |
|---|---|
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
| Function | Description |
|---|---|
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
| Function | Description |
|---|---|
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
| Path | Read | Write |
|---|---|---|
/dev/console | Drains pending UART RX bytes (non-blocking, returns 0 when idle) | Sends each byte through tiku_uart_putc() |
/dev/null | Always returns 0 bytes (EOF) | Accepts and silently discards all data |
/dev/zero | Fills buffer with NUL bytes, returns buffer size | Not writable (returns -1) |
Adding a New VFS Node
- 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); } - Add a
tiku_vfs_node_tentry to the appropriate children array inkernel/vfs/tiku_vfs_tree.c:{ "my_node", TIKU_VFS_FILE, my_read, NULL, NULL, 0 }, - Update the parent directory's
child_count. - If the node lives in FRAM (
.persistentsection), wrap writes intiku_mpu_unlock_nvm()/tiku_mpu_lock_nvm().