This repository has been archived on 2026-08-31. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
donglora/vendor/semihosting/src/fs.rs
T
swaitsandClaude Opus 4.6 3102fc747a feat: add display UI with splash screen, dashboard, and RSSI sparkline
Implement full SSD1306 OLED display support:
- Splash screen on boot (1.5s) with project name, version, board
- Dashboard showing radio state, config, counters, RSSI/SNR
- RSSI history sparkline (64-sample ring buffer, 2px-wide bars)
- DisplayOn/DisplayOff host commands routed via dedicated channel
- Pure render functions against generic DrawTarget (no hardware coupling)
- Display on/off control bypasses radio task entirely

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-29 20:42:54 -06:00

278 lines
8.8 KiB
Rust

// SPDX-License-Identifier: Apache-2.0 OR MIT
//! Host filesystem manipulation operations.
//!
//! This module contains basic methods to manipulate the contents of the host filesystem.
//!
//! This module is a subset of the [`std::fs`] module except that [`CStr`] is used for the path
//! instead of [`Path`].
//!
//! [`Path`] is not available in `core`, so this module uses [`CStr`] instead in the API where
//! `std` uses [`Path`]. When creating a path from a string literal, it is recommended to use the
//! C string literals (`c"..."`, available since Rust 1.77) or [c!](c!()) macro.
//!
//! ```no_run
//! use semihosting::{c, fs};
//!
//! // with C string literal
//! fs::write(c"a.txt", "abc")?;
//! // with c! macro
//! fs::write(c!("b.txt"), "123")?;
//! # Ok::<(), semihosting::io::Error>(())
//! ```
//!
//! # Platform-specific behavior
//!
//! The operations provided by this module are mapped to the corresponding operations of the host
//! system. The details of its operation depend on the host system.
//!
//! [`std::fs`]: https://doc.rust-lang.org/std/fs/index.html
//! [`Path`]: https://doc.rust-lang.org/std/path/struct.Path.html
use core::{ffi::CStr, fmt};
use crate::{
fd::{AsFd as _, OwnedFd},
io::{self, Write as _},
sys,
};
/// Write a slice as the entire contents of a file.
///
/// See [`std::fs::write` documentation][std] for details.
///
/// [std]: https://doc.rust-lang.org/std/fs/fn.write.html
pub fn write<P: AsRef<CStr>, C: AsRef<[u8]>>(path: P, contents: C) -> io::Result<()> {
fn inner(path: &CStr, contents: &[u8]) -> io::Result<()> {
File::create(path)?.write_all(contents)
}
inner(path.as_ref(), contents.as_ref())
}
/// Removes a file from the host filesystem.
///
/// See [`std::fs::remove_file` documentation][std] for details.
///
/// [std]: https://doc.rust-lang.org/std/fs/fn.remove_file.html
///
/// # Platform-specific behavior
///
/// The following semihosting calls are currently being used:
///
/// | Platform | Semihosting call |
/// | ------------------------------------------------------------- | ---------------- |
/// | AArch64, Arm, RISC-V, LoongArch, Xtensa (openocd-semihosting) | [SYS_REMOVE] |
/// | MIPS32, MIPS64 | UHI_unlink |
///
/// [SYS_REMOVE]: https://github.com/ARM-software/abi-aa/blob/2025Q1/semihosting/semihosting.rst#sys-remove-0x0e
///
/// **Disclaimer:** These semihosting calls might change over time.
#[doc(alias = "rm", alias = "unlink", alias = "DeleteFile")]
#[doc(alias = "SYS_REMOVE")] // arm_compat
#[doc(alias = "UHI_unlink")] // mips
pub fn remove_file<P: AsRef<CStr>>(path: P) -> io::Result<()> {
sys::fs::unlink(path.as_ref())
}
/// Rename a file or directory to a new name, replacing the original file if
/// `to` already exists.
///
/// See [`std::fs::rename` documentation][std] for details.
///
/// [std]: https://doc.rust-lang.org/std/fs/fn.rename.html
///
/// # Platform-specific behavior
///
/// The following semihosting calls are currently being used:
///
/// | Platform | Semihosting call |
/// | ------------------------------------------------------------- | ---------------- |
/// | AArch64, Arm, RISC-V, LoongArch, Xtensa (openocd-semihosting) | [SYS_RENAME] |
/// | MIPS32, MIPS64 | (Unsupported) |
///
/// [SYS_RENAME]: https://github.com/ARM-software/abi-aa/blob/2025Q1/semihosting/semihosting.rst#sys-rename-0x0f
///
/// **Disclaimer:** These semihosting calls might change over time.
#[doc(alias = "mv", alias = "MoveFile", alias = "MoveFileEx")]
#[doc(alias = "SYS_RENAME")] // arm_compat
pub fn rename<P: AsRef<CStr>, Q: AsRef<CStr>>(from: P, to: Q) -> io::Result<()> {
sys::fs::rename(from.as_ref(), to.as_ref())
}
/// An object providing access to an open file on the host filesystem.
///
/// See [`std::fs::File` documentation][std] for details.
///
/// [std]: https://doc.rust-lang.org/std/fs/struct.File.html
pub struct File(OwnedFd);
impl File {
/// Attempts to open a file in read-only mode.
pub fn open<P: AsRef<CStr>>(path: P) -> io::Result<Self> {
OpenOptions::new().read(true).open(path.as_ref())
}
/// Opens a file in write-only mode.
pub fn create<P: AsRef<CStr>>(path: P) -> io::Result<Self> {
OpenOptions::new().write(true).create(true).truncate(true).open(path.as_ref())
}
/// Returns a new OpenOptions object.
#[must_use]
pub fn options() -> OpenOptions {
OpenOptions::new()
}
/// Queries metadata about the underlying file.
///
/// # Platform-specific behavior
///
/// The following semihosting calls are currently being used:
///
/// | Platform | Semihosting call |
/// | ------------------------------------------------------------- | ---------------- |
/// | AArch64, Arm, RISC-V, LoongArch, Xtensa (openocd-semihosting) | [SYS_FLEN] |
/// | MIPS32, MIPS64 | UHI_fstat |
///
/// [SYS_FLEN]: https://github.com/ARM-software/abi-aa/blob/2025Q1/semihosting/semihosting.rst#sys-flen-0x0c
///
/// **Disclaimer:** These semihosting calls might change over time.
#[doc(alias = "SYS_FLEN")] // arm_compat
#[doc(alias = "UHI_fstat")] // mips
pub fn metadata(&self) -> io::Result<Metadata> {
sys::fs::metadata(self.as_fd()).map(Metadata)
}
}
impl_as_fd!(File);
impl_from_fd!(File);
impl fmt::Debug for File {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("File").field("fd", &self.as_fd().as_raw_fd()).finish()
}
}
impl io::Read for File {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
sys::read(self.as_fd(), buf)
}
}
impl io::Write for File {
fn write(&mut self, bytes: &[u8]) -> io::Result<usize> {
sys::write(self.as_fd(), bytes)
}
#[inline]
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}
impl io::Seek for File {
fn seek(&mut self, pos: io::SeekFrom) -> io::Result<u64> {
sys::fs::seek(self.as_fd(), pos)
}
}
impl io::Read for &File {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
sys::read(self.as_fd(), buf)
}
}
impl io::Write for &File {
fn write(&mut self, bytes: &[u8]) -> io::Result<usize> {
sys::write(self.as_fd(), bytes)
}
#[inline]
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}
impl io::Seek for &File {
fn seek(&mut self, pos: io::SeekFrom) -> io::Result<u64> {
sys::fs::seek(self.as_fd(), pos)
}
}
/// Options and flags which can be used to configure how a file is opened.
///
/// See [`std::fs::OpenOptions` documentation][std] for details.
///
/// [std]: https://doc.rust-lang.org/std/fs/struct.OpenOptions.html
#[derive(Clone, Debug)]
pub struct OpenOptions {
// generic
pub(crate) read: bool,
pub(crate) write: bool,
pub(crate) append: bool,
pub(crate) truncate: bool,
pub(crate) create: bool,
#[allow(dead_code)]
pub(crate) create_new: bool,
// system-specific
#[allow(dead_code)]
pub(crate) mode: u32,
}
#[allow(missing_docs)] // TODO
impl OpenOptions {
pub fn new() -> Self {
Self {
// generic
read: false,
write: false,
append: false,
truncate: false,
create: false,
create_new: false,
// system-specific
mode: 0o666,
}
}
pub fn read(&mut self, read: bool) -> &mut Self {
self.read = read;
self
}
pub fn write(&mut self, write: bool) -> &mut Self {
self.write = write;
self
}
pub fn append(&mut self, append: bool) -> &mut Self {
self.append = append;
self
}
pub fn truncate(&mut self, truncate: bool) -> &mut Self {
self.truncate = truncate;
self
}
pub fn create(&mut self, create: bool) -> &mut Self {
self.create = create;
self
}
// pub fn create_new(&mut self, create_new: bool) {
// self.create_new = create_new;
// }
// pub fn custom_flags(&mut self, flags: i32) {
// self.custom_flags = flags;
// }
// pub fn mode(&mut self, mode: u32) {
// self.mode = mode as mode_t;
// }
pub fn open<P: AsRef<CStr>>(&self, path: P) -> io::Result<File> {
sys::fs::open(path.as_ref(), self).map(File)
}
}
/// Metadata information about a file.
pub struct Metadata(sys::fs::Metadata);
impl Metadata {
/// Returns the size of the file, in bytes, this metadata is for.
#[must_use]
pub fn len(&self) -> u64 {
self.0.size()
}
}
impl fmt::Debug for Metadata {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Metadata").finish_non_exhaustive()
}
}