From 2221ffdbdecd08063a150c926dcc7477a876cd51 Mon Sep 17 00:00:00 2001 From: sawyer bristol Date: Sun, 28 Sep 2025 14:07:03 -0600 Subject: [PATCH] basic fs syscall(s) --- Cargo.lock | 1 + abi_sys/Cargo.toml | 1 + abi_sys/src/lib.rs | 48 +++++++++++++++++-- kernel/src/abi.rs | 105 +++++++++++++++++++++++++++++++++++++++++- kernel/src/elf.rs | 6 ++- kernel/src/main.rs | 19 ++++---- kernel/src/storage.rs | 2 +- 7 files changed, 162 insertions(+), 20 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4f20b02..905918f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -30,6 +30,7 @@ version = "0.1.0" dependencies = [ "defmt 0.3.100", "embedded-graphics", + "embedded-sdmmc", "shared", "strum", ] diff --git a/abi_sys/Cargo.toml b/abi_sys/Cargo.toml index 502ac83..ee7bad7 100644 --- a/abi_sys/Cargo.toml +++ b/abi_sys/Cargo.toml @@ -11,3 +11,4 @@ embedded-graphics = "0.8.1" strum = { version = "0.27.2", default-features = false, features = ["derive"] } defmt = { version = "0.3", optional = true } shared = { path = "../shared" } +embedded-sdmmc = { version = "0.9.0", default-features = false } diff --git a/abi_sys/src/lib.rs b/abi_sys/src/lib.rs index dc52ba4..c8cd23f 100644 --- a/abi_sys/src/lib.rs +++ b/abi_sys/src/lib.rs @@ -8,6 +8,7 @@ use embedded_graphics::{ geometry::Point, pixelcolor::{Rgb565, RgbColor}, }; +use embedded_sdmmc::DirEntry; pub use shared::keyboard::{KeyCode, KeyEvent, KeyState, Modifiers}; use strum::{EnumCount, EnumIter}; @@ -21,19 +22,22 @@ pub static mut CALL_ABI_TABLE: [usize; CallAbiTable::COUNT] = [0; CallAbiTable:: #[derive(Clone, Copy, EnumIter, EnumCount)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum CallAbiTable { - Print = 0, - Sleep = 1, + PrintString = 0, + SleepMs = 1, LockDisplay = 2, DrawIter = 3, GetKey = 4, GenRand = 5, + ListDir = 6, + ReadFile = 7, } pub type PrintAbi = extern "C" fn(ptr: *const u8, len: usize); #[allow(unused)] pub fn print(msg: &str) { - let f: PrintAbi = unsafe { core::mem::transmute(CALL_ABI_TABLE[CallAbiTable::Print as usize]) }; + let f: PrintAbi = + unsafe { core::mem::transmute(CALL_ABI_TABLE[CallAbiTable::PrintString as usize]) }; f(msg.as_ptr(), msg.len()); } @@ -41,7 +45,8 @@ pub type SleepAbi = extern "C" fn(ms: u64); #[allow(unused)] pub fn sleep(ms: u64) { - let f: SleepAbi = unsafe { core::mem::transmute(CALL_ABI_TABLE[CallAbiTable::Sleep as usize]) }; + let f: SleepAbi = + unsafe { core::mem::transmute(CALL_ABI_TABLE[CallAbiTable::SleepMs as usize]) }; f(ms); } @@ -89,3 +94,38 @@ pub fn gen_rand(req: &mut RngRequest) { f(req) } } + +pub type ListDir = + extern "C" fn(str: *const u8, len: usize, files: *mut Option, file_len: usize); + +#[allow(unused)] +pub fn list_dir(path: &str, files: &mut [Option]) { + unsafe { + let ptr = CALL_ABI_TABLE[CallAbiTable::ListDir as usize]; + let f: ListDir = core::mem::transmute(ptr); + f(path.as_ptr(), path.len(), files.as_mut_ptr(), files.len()) + } +} + +pub type ReadFile = extern "C" fn( + str: *const u8, + len: usize, + read_from: usize, + buf: *mut u8, + buf_len: usize, +) -> usize; + +#[allow(unused)] +pub fn read_file(file: &str, read_from: usize, buf: &mut [u8]) -> usize { + unsafe { + let ptr = CALL_ABI_TABLE[CallAbiTable::ReadFile as usize]; + let f: ReadFile = core::mem::transmute(ptr); + f( + file.as_ptr(), + file.len(), + read_from, + buf.as_mut_ptr(), + buf.len(), + ) + } +} diff --git a/kernel/src/abi.rs b/kernel/src/abi.rs index 95baf20..1504af9 100644 --- a/kernel/src/abi.rs +++ b/kernel/src/abi.rs @@ -1,14 +1,18 @@ use abi_sys::{ - DrawIterAbi, GenRand, GetKeyAbi, LockDisplay, Modifiers, PrintAbi, RngRequest, SleepAbi, + DrawIterAbi, GenRand, GetKeyAbi, ListDir, LockDisplay, Modifiers, PrintAbi, ReadFile, + RngRequest, SleepAbi, }; +use alloc::vec::Vec; use core::sync::atomic::Ordering; use embassy_rp::clocks::{RoscRng, clk_sys_freq}; use embedded_graphics::{Pixel, draw_target::DrawTarget, pixelcolor::Rgb565}; +use embedded_sdmmc::{DirEntry, ShortFileName}; +use heapless::spsc::Queue; use shared::keyboard::KeyEvent; use crate::{ - KEY_CACHE, display::{FB_PAUSED, FRAMEBUFFER}, + storage::{Dir, File, SDCARD}, }; const _: PrintAbi = print; @@ -41,10 +45,13 @@ pub extern "C" fn lock_display(lock: bool) { const _: DrawIterAbi = draw_iter; // TODO: maybe return result pub extern "C" fn draw_iter(pixels: *const Pixel, len: usize) { + // SAFETY: caller guarantees `ptr` is valid for `len` bytes let pixels = unsafe { core::slice::from_raw_parts(pixels, len) }; unsafe { FRAMEBUFFER.draw_iter(pixels.iter().copied()).unwrap() } } +pub static mut KEY_CACHE: Queue = Queue::new(); + const _: GetKeyAbi = get_key; pub extern "C" fn get_key() -> KeyEvent { if let Some(event) = unsafe { KEY_CACHE.dequeue() } { @@ -66,8 +73,102 @@ pub extern "C" fn gen_rand(req: &mut RngRequest) { RngRequest::U32(i) => *i = rng.next_u32(), RngRequest::U64(i) => *i = rng.next_u64(), RngRequest::Bytes { ptr, len } => { + // SAFETY: caller guarantees `ptr` is valid for `len` bytes let slice: &mut [u8] = unsafe { core::slice::from_raw_parts_mut(*ptr, *len) }; rng.fill_bytes(slice); } } } + +fn get_dir_entries(dir: &Dir, files: &mut [Option]) { + let mut files = files.iter_mut(); + + dir.iterate_dir(|entry| { + if let Some(f) = files.next() { + *f = Some(entry.clone()) + } + }) + .unwrap() +} + +fn recurse_dir(dir: &Dir, dirs: &[&str], files: &mut [Option]) { + if dirs.is_empty() { + get_dir_entries(dir, files); + return; + } + + let dir = dir.open_dir(dirs[0]).unwrap(); + recurse_dir(&dir, &dirs[1..], files); +} + +const _: ListDir = list_dir; +pub extern "C" fn list_dir( + dir: *const u8, + len: usize, + files: *mut Option, + files_len: usize, +) { + // SAFETY: caller guarantees `ptr` is valid for `len` bytes + let files = unsafe { core::slice::from_raw_parts_mut(files, files_len) }; + // SAFETY: caller guarantees `ptr` is valid for `len` bytes + let dir = unsafe { core::str::from_raw_parts(dir, len) }; + let dirs: Vec<&str> = dir.split('/').collect(); + + let mut guard = SDCARD.get().try_lock().expect("Failed to get sdcard"); + let sd = guard.as_mut().unwrap(); + sd.access_root_dir(|root| { + if !dir.is_empty() { + if dirs[0].is_empty() { + get_dir_entries(&root, files); + } else { + recurse_dir(&root, &dirs[1..], files); + } + } + }); +} + +fn recurse_file(dir: &Dir, dirs: &[&str], mut access: impl FnMut(&mut File) -> T) -> T { + if dirs.len() == 1 { + let file_name = ShortFileName::create_from_str(dirs[0]).unwrap(); + + let mut file = dir + .open_file_in_dir(file_name, embedded_sdmmc::Mode::ReadWriteAppend) + .unwrap(); + return access(&mut file); + } + + let dir = dir.open_dir(dirs[0]).unwrap(); + recurse_file(&dir, &dirs[1..], access) +} + +const _: ReadFile = read_file; +pub extern "C" fn read_file( + str: *const u8, + len: usize, + start_from: usize, + buf: *mut u8, + buf_len: usize, +) -> usize { + // SAFETY: caller guarantees `ptr` is valid for `len` bytes + let mut buf = unsafe { core::slice::from_raw_parts_mut(buf, buf_len) }; + // SAFETY: caller guarantees `ptr` is valid for `len` bytes + let file = unsafe { core::str::from_raw_parts(str, len) }; + let file: Vec<&str> = file.split('/').collect(); + + let mut res = 0; + + let mut guard = SDCARD.get().try_lock().expect("Failed to get sdcard"); + let sd = guard.as_mut().unwrap(); + if !file.is_empty() { + if file[0].is_empty() { + } else { + sd.access_root_dir(|root| { + res = recurse_file(&root, &file[1..], |file| { + file.seek_from_start(start_from as u32).unwrap(); + file.read(&mut buf).unwrap() + }) + }); + } + } + res +} diff --git a/kernel/src/elf.rs b/kernel/src/elf.rs index 619831d..5e00074 100644 --- a/kernel/src/elf.rs +++ b/kernel/src/elf.rs @@ -195,12 +195,14 @@ fn patch_abi( for (idx, call) in CallAbiTable::iter().enumerate() { let ptr = match call { - CallAbiTable::Print => abi::print as usize, - CallAbiTable::Sleep => abi::sleep as usize, + CallAbiTable::PrintString => abi::print as usize, + CallAbiTable::SleepMs => abi::sleep as usize, CallAbiTable::LockDisplay => abi::lock_display as usize, CallAbiTable::DrawIter => abi::draw_iter as usize, CallAbiTable::GetKey => abi::get_key as usize, CallAbiTable::GenRand => abi::gen_rand as usize, + CallAbiTable::ListDir => abi::list_dir as usize, + CallAbiTable::ReadFile => abi::read_file as usize, }; unsafe { table_base.add(idx as usize).write(ptr); diff --git a/kernel/src/main.rs b/kernel/src/main.rs index 38246c8..431fc50 100644 --- a/kernel/src/main.rs +++ b/kernel/src/main.rs @@ -1,4 +1,5 @@ #![feature(impl_trait_in_assoc_type)] +#![feature(str_from_raw_parts)] #![cfg_attr(not(test), no_std)] #![cfg_attr(not(test), no_main)] #![allow(static_mut_refs)] @@ -17,6 +18,7 @@ mod usb; mod utils; use crate::{ + abi::KEY_CACHE, display::{FRAMEBUFFER, display_handler, init_display}, peripherals::{ conf_peripherals, @@ -57,8 +59,6 @@ use embassy_sync::{ use embassy_time::{Delay, Timer}; use embedded_hal_bus::spi::ExclusiveDevice; use embedded_sdmmc::SdCard as SdmmcSdCard; -use heapless::spsc::Queue; -use shared::keyboard::KeyEvent; use static_cell::StaticCell; use talc::*; @@ -251,23 +251,20 @@ async fn prog_search_handler() { loop { { let mut guard = SDCARD.get().lock().await; + let sd = guard.as_mut().unwrap(); - if let Some(sd) = guard.as_mut() { - let files = sd.list_files_by_extension(".bin").unwrap(); - let mut select = SELECTIONS.lock().await; + let files = sd.list_files_by_extension(".bin").unwrap(); + let mut select = SELECTIONS.lock().await; - if *select.selections() != files { - select.update_selections(files); - select.reset(); - } + if *select.selections() != files { + select.update_selections(files); + select.reset(); } } Timer::after_secs(5).await; } } -static mut KEY_CACHE: Queue = Queue::new(); - async fn key_handler() { loop { if let Some(event) = read_keyboard_fifo().await { diff --git a/kernel/src/storage.rs b/kernel/src/storage.rs index ad2b4a3..15093d8 100644 --- a/kernel/src/storage.rs +++ b/kernel/src/storage.rs @@ -22,7 +22,7 @@ type Device = ExclusiveDevice, Output<'static>, emb type SD = SdmmcSdCard; type VolMgr = VolumeManager; type Vol<'a> = Volume<'a, SD, DummyTimeSource, MAX_DIRS, MAX_FILES, MAX_VOLUMES>; -type Dir<'a> = Directory<'a, SD, DummyTimeSource, MAX_DIRS, MAX_FILES, MAX_VOLUMES>; +pub type Dir<'a> = Directory<'a, SD, DummyTimeSource, MAX_DIRS, MAX_FILES, MAX_VOLUMES>; pub type File<'a> = SdFile<'a, SD, DummyTimeSource, MAX_DIRS, MAX_FILES, MAX_VOLUMES>; pub static SDCARD: LazyLock>> =