# SPDX-License-Identifier: LSL-1.0 # Copyright (c) 2026 Markus Maiwald # Stewardship: Self Sovereign Society Foundation ## Nexus Membrane: LittleFS Bindings (L0 Physics) ## Provides Nim bindings to the LittleFS C library. import ../blk type LfsConfig* {.importc: "struct lfs_config", header: "lfs.h", nodecl, final.} = object context*: pointer read*: proc(cfg: ptr LfsConfig, blk: uint32, off: uint32, buf: pointer, size: uint32): cint {.cdecl.} prog*: proc(cfg: ptr LfsConfig, blk: uint32, off: uint32, buf: pointer, size: uint32): cint {.cdecl.} erase*: proc(cfg: ptr LfsConfig, blk: uint32): cint {.cdecl.} sync*: proc(cfg: ptr LfsConfig): cint {.cdecl.} read_size*: uint32 prog_size*: uint32 block_size*: uint32 block_count*: uint32 block_cycles*: int32 cache_size*: uint32 lookahead_size*: uint32 read_buffer*: pointer prog_buffer*: pointer lookahead_buffer*: pointer name_max*: uint32 file_max*: uint32 attr_max*: uint32 metadata_max*: uint32 Lfs* {.importc: "lfs_t", header: "lfs.h".} = object LfsFile* {.importc: "lfs_file_t", header: "lfs.h".} = object LfsInfo* {.importc: "struct lfs_info", header: "lfs.h".} = object lfsType*: uint8 size*: uint32 name*: array[256, char] const LFS_TYPE_REG* = 1'u8 LFS_TYPE_DIR* = 2'u8 LFS_O_RDONLY* = 1 LFS_O_WRONLY* = 2 LFS_O_RDWR* = 3 LFS_O_CREAT* = 0x0100 LFS_O_EXCL* = 0x0200 LFS_O_TRUNC* = 0x0400 LFS_O_APPEND* = 0x0800 # LittleFS C API proc lfs_mount(lfs: ptr Lfs, cfg: ptr LfsConfig): cint {.importc, header: "lfs.h", cdecl.} proc lfs_format(lfs: ptr Lfs, cfg: ptr LfsConfig): cint {.importc, header: "lfs.h", cdecl.} proc lfs_file_open(lfs: ptr Lfs, file: ptr LfsFile, path: cstring, flags: cint): cint {.importc, header: "lfs.h", cdecl.} proc lfs_file_close(lfs: ptr Lfs, file: ptr LfsFile): cint {.importc, header: "lfs.h", cdecl.} proc lfs_file_read(lfs: ptr Lfs, file: ptr LfsFile, buf: pointer, size: uint32): cint {.importc, header: "lfs.h", cdecl.} proc lfs_file_write(lfs: ptr Lfs, file: ptr LfsFile, buf: pointer, size: uint32): cint {.importc, header: "lfs.h", cdecl.} proc lfs_file_seek(lfs: ptr Lfs, file: ptr LfsFile, off: int32, whence: cint): int32 {.importc, header: "lfs.h", cdecl.} proc lfs_file_rewind(lfs: ptr Lfs, file: ptr LfsFile): int32 {.importc, header: "lfs.h", cdecl.} proc lfs_stat(lfs: ptr Lfs, path: cstring, info: ptr LfsInfo): cint {.importc, header: "lfs.h", cdecl.} proc lfs_remove(lfs: ptr Lfs, path: cstring): cint {.importc, header: "lfs.h", cdecl.} proc lfs_mkdir(lfs: ptr Lfs, path: cstring): cint {.importc, header: "lfs.h", cdecl.} # ========================================================= # LFS Wrapper Procs (Callbacks) # ========================================================= var lfs_start_sector: uint64 = 8192 proc lfs_block_read(cfg: ptr LfsConfig, blk: uint32, off: uint32, buf: pointer, size: uint32): cint {.cdecl.} = # Calculate absolute sector let sector = lfs_start_sector + uint64(blk) * (cfg.block_size div 512) + uint64(off div 512) var temp: array[512, byte] if size == 512 and off mod 512 == 0: if blk_read(sector, addr temp[0]) < 0: return -1 copyMem(buf, addr temp[0], 512) return 0 var remaining = int(size) var dst = cast[int](buf) var cur_off = off while remaining > 0: let sec = lfs_start_sector + uint64(blk) * (cfg.block_size div 512) + uint64(cur_off div 512) if blk_read(sec, addr temp[0]) < 0: return -1 let in_sec_off = int(cur_off mod 512) let copy_len = min(remaining, 512 - in_sec_off) copyMem(cast[pointer](dst), addr temp[in_sec_off], copy_len) dst += copy_len cur_off += uint32(copy_len) remaining -= copy_len return 0 proc lfs_block_prog(cfg: ptr LfsConfig, blk: uint32, off: uint32, buf: pointer, size: uint32): cint {.cdecl.} = let sector = lfs_start_sector + uint64(blk) * (cfg.block_size div 512) + uint64(off div 512) var temp: array[512, byte] if size == 512 and off mod 512 == 0: copyMem(addr temp[0], buf, 512) if blk_write(sector, addr temp[0]) < 0: return -1 return 0 var remaining = int(size) var src = cast[int](buf) var cur_off = off while remaining > 0: let sec = lfs_start_sector + uint64(blk) * (cfg.block_size div 512) + uint64(cur_off div 512) if blk_read(sec, addr temp[0]) < 0: return -1 let in_sec_off = int(cur_off mod 512) let copy_len = min(remaining, 512 - in_sec_off) copyMem(addr temp[in_sec_off], cast[pointer](src), copy_len) if blk_write(sec, addr temp[0]) < 0: return -1 src += copy_len cur_off += uint32(copy_len) remaining -= copy_len return 0 proc lfs_block_erase(cfg: ptr LfsConfig, blk: uint32): cint {.cdecl.} = var zeros: array[512, byte] let sectors_per_block = cfg.block_size div 512 for i in 0'u32 ..< sectors_per_block: let sec = lfs_start_sector + uint64(blk) * uint64(sectors_per_block) + uint64(i) if blk_write(sec, addr zeros[0]) < 0: return -1 return 0 proc lfs_block_sync(cfg: ptr LfsConfig): cint {.cdecl.} = discard blk_sync() return 0 # ========================================================= # High-Level API # ========================================================= var g_lfs: Lfs var g_cfg: LfsConfig var g_mounted: bool = false # Static buffers var lfs_read_buf: array[512, byte] var lfs_prog_buf: array[512, byte] var lfs_lookahead_buf: array[16, byte] proc lfs_nim_init*(start_sector: uint64 = 8192, block_count: uint32 = 1024) = lfs_start_sector = start_sector g_cfg.context = nil g_cfg.read = lfs_block_read g_cfg.prog = lfs_block_prog g_cfg.erase = lfs_block_erase g_cfg.sync = lfs_block_sync g_cfg.read_size = 512 g_cfg.prog_size = 512 g_cfg.block_size = 4096 g_cfg.block_count = block_count g_cfg.block_cycles = 500 g_cfg.cache_size = 512 g_cfg.lookahead_size = 16 g_cfg.read_buffer = addr lfs_read_buf g_cfg.prog_buffer = addr lfs_prog_buf g_cfg.lookahead_buffer = addr lfs_lookahead_buf proc lfs_nim_format*(): bool = lfs_nim_init() return lfs_format(addr g_lfs, addr g_cfg) == 0 proc lfs_nim_mount*(): bool = if g_mounted: return true lfs_nim_init() if lfs_mount(addr g_lfs, addr g_cfg) == 0: g_mounted = true return true return false