WIP keyboard syscall

This commit is contained in:
2025-08-31 22:41:09 -06:00
parent 1bbd988ef7
commit 5d0a3608d1
8 changed files with 210 additions and 113 deletions

39
Cargo.lock generated
View File

@@ -17,8 +17,11 @@ name = "abi"
version = "0.1.0"
dependencies = [
"abi_sys",
"embassy-time 0.5.0",
"embedded-graphics",
"shared",
"spin",
"talc",
]
[[package]]
@@ -186,7 +189,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f377753756ec12e76b52d2dd657437be0448cc9736402ffadd0b8b8b9602c8a1"
dependencies = [
"embassy-sync 0.6.2",
"embassy-time",
"embassy-time 0.4.0",
"embedded-io",
"embedded-io-async",
"futures-intrusive",
@@ -320,7 +323,7 @@ dependencies = [
"embassy-futures",
"embassy-net-driver-channel",
"embassy-sync 0.6.2",
"embassy-time",
"embassy-time 0.4.0",
"embedded-hal 1.0.0",
"embedded-io-async",
"futures",
@@ -494,7 +497,7 @@ dependencies = [
"embassy-futures",
"embassy-hal-internal 0.3.0",
"embassy-sync 0.7.0",
"embassy-time",
"embassy-time 0.4.0",
"embedded-hal 0.2.7",
"embedded-hal 1.0.0",
"embedded-hal-async",
@@ -588,7 +591,7 @@ dependencies = [
"embassy-futures",
"embassy-hal-internal 0.2.0",
"embassy-sync 0.6.2",
"embassy-time",
"embassy-time 0.4.0",
"embassy-usb-driver",
"embedded-hal 0.2.7",
"embedded-hal 1.0.0",
@@ -626,7 +629,7 @@ dependencies = [
"embassy-futures",
"embassy-hal-internal 0.2.0",
"embassy-sync 0.6.2",
"embassy-time",
"embassy-time 0.4.0",
"embassy-time-driver",
"embassy-time-queue-utils",
"embassy-usb-driver",
@@ -696,10 +699,26 @@ dependencies = [
]
[[package]]
name = "embassy-time-driver"
version = "0.2.0"
name = "embassy-time"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d45f5d833b6d98bd2aab0c2de70b18bfaa10faf661a1578fd8e5dfb15eb7eba"
checksum = "f4fa65b9284d974dad7a23bb72835c4ec85c0b540d86af7fc4098c88cff51d65"
dependencies = [
"cfg-if",
"critical-section",
"document-features",
"embassy-time-driver",
"embedded-hal 0.2.7",
"embedded-hal 1.0.0",
"embedded-hal-async",
"futures-core",
]
[[package]]
name = "embassy-time-driver"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a0a244c7dc22c8d0289379c8d8830cae06bb93d8f990194d0de5efb3b5ae7ba6"
dependencies = [
"document-features",
]
@@ -1187,7 +1206,7 @@ dependencies = [
"embassy-futures",
"embassy-rp 0.4.0",
"embassy-sync 0.7.0",
"embassy-time",
"embassy-time 0.4.0",
"embassy-usb",
"embedded-graphics",
"embedded-hal 0.2.7",
@@ -2086,7 +2105,7 @@ dependencies = [
"bt-hci",
"embassy-futures",
"embassy-sync 0.6.2",
"embassy-time",
"embassy-time 0.4.0",
"embedded-io",
"futures",
"heapless",

View File

@@ -7,3 +7,6 @@ edition = "2024"
embedded-graphics = "0.8.1"
shared = { path = "../shared" }
abi_sys = { path = "../abi_sys" }
talc = "4.4.3"
spin = "0.10.0"
embassy-time = "0.5.0"

View File

@@ -1,20 +1,20 @@
#![no_std]
use abi_sys::{Syscall, call_abi};
use shared::keyboard::{KeyCode, KeyEvent, KeyState, Modifiers};
use abi_sys::draw_iter;
pub use abi_sys::{get_key, print};
pub use embassy_time;
pub use shared::keyboard::{KeyCode, KeyEvent, KeyState, Modifiers};
use talc::*;
pub fn print(msg: &str) {
let syscall = Syscall::Print {
msg: msg.as_ptr(),
len: msg.len(),
};
unsafe {
call_abi(&syscall);
}
}
static mut ARENA: [u8; 10000] = [0; 10000];
#[global_allocator]
static ALLOCATOR: Talck<spin::Mutex<()>, ClaimOnOom> =
Talc::new(unsafe { ClaimOnOom::new(Span::from_array(core::ptr::addr_of!(ARENA).cast_mut())) })
.lock();
pub mod display {
use crate::{Syscall, call_abi};
use crate::draw_iter;
use embedded_graphics::{
Pixel,
geometry::{Dimensions, Point},
@@ -30,18 +30,6 @@ pub mod display {
pub struct Display;
impl Display {
fn syscall_draw(&self, pixels: &[Pixel565]) {
let syscall = Syscall::DrawIter {
pixels: pixels.as_ptr(),
len: pixels.len(),
};
unsafe {
call_abi(&syscall);
}
}
}
impl Dimensions for Display {
fn bounding_box(&self) -> Rectangle {
Rectangle {
@@ -71,13 +59,13 @@ pub mod display {
count += 1;
if count == BUF_SIZE {
self.syscall_draw(&buf[..count]);
draw_iter(&buf[..count]);
count = 0;
}
}
if count > 0 {
self.syscall_draw(&buf[..count]);
draw_iter(&buf[..count]);
}
Ok(())

View File

@@ -1,5 +1,9 @@
#![no_std]
extern crate alloc;
use alloc::boxed::Box;
use core::pin::Pin;
pub use embedded_graphics::{
Pixel,
geometry::Point,
@@ -7,26 +11,50 @@ pub use embedded_graphics::{
};
use shared::keyboard::{KeyCode, KeyEvent, KeyState, Modifiers};
// Instead of extern, declare a static pointer in a dedicated section
pub type EntryFn = fn() -> Pin<Box<dyn Future<Output = ()>>>;
#[unsafe(no_mangle)]
#[unsafe(link_section = ".user_reloc")]
#[allow(non_upper_case_globals)]
pub static mut call_abi_ptr: usize = 0;
#[unsafe(link_section = ".userapp")]
pub static mut CALL_ABI_TABLE: [usize; CallAbiTable::COUNT] = [0; CallAbiTable::COUNT];
// Helper to call it
pub unsafe fn call_abi(call: *const Syscall) {
let f: extern "C" fn(*const Syscall) = unsafe { core::mem::transmute(call_abi_ptr) };
f(call);
#[repr(usize)]
#[derive(Clone, Copy)]
pub enum CallAbiTable {
Print = 0,
DrawIter = 1,
GetKey = 2,
}
#[repr(C)]
pub enum Syscall {
DrawIter {
pixels: *const Pixel<Rgb565>,
len: usize,
},
Print {
msg: *const u8,
len: usize,
},
impl CallAbiTable {
pub const COUNT: usize = 3;
}
pub type PrintAbi = extern "Rust" fn(msg: &str);
pub fn print(msg: &str) {
unsafe {
let ptr = CALL_ABI_TABLE[CallAbiTable::Print as usize];
let f: PrintAbi = core::mem::transmute(ptr);
f(msg);
}
}
pub type DrawIterAbi = extern "Rust" fn(pixels: &[Pixel<Rgb565>]);
pub fn draw_iter(pixels: &[Pixel<Rgb565>]) {
unsafe {
let ptr = CALL_ABI_TABLE[CallAbiTable::DrawIter as usize];
let f: DrawIterAbi = core::mem::transmute(ptr);
f(pixels);
}
}
pub type GetKeyAbi = extern "Rust" fn() -> Option<KeyEvent>;
pub fn get_key() -> Option<KeyEvent> {
unsafe {
let ptr = CALL_ABI_TABLE[CallAbiTable::GetKey as usize];
let f: GetKeyAbi = core::mem::transmute(ptr);
f()
}
}

View File

@@ -1,4 +1,7 @@
use abi_sys::Syscall;
use core::pin::Pin;
use abi_sys::{DrawIterAbi, GetKeyAbi, Pixel, PrintAbi};
use alloc::boxed::Box;
use defmt::info;
use embassy_futures::block_on;
use embedded_graphics::{
@@ -8,35 +11,30 @@ use embedded_graphics::{
prelude::{Point, RgbColor, Size},
primitives::{PrimitiveStyle, Rectangle, StyledDrawable},
};
use shared::keyboard::KeyEvent;
use crate::display::FRAMEBUFFER;
use crate::{KEY_CACHE, display::FRAMEBUFFER};
#[allow(unused)]
pub extern "C" fn call_abi(call: *const Syscall) {
info!("called abi");
let call = unsafe { &*call };
match call {
Syscall::DrawIter { pixels, len } => {
// SAFETY: we're trusting the user program here
let slice = unsafe { core::slice::from_raw_parts(*pixels, *len) };
// ensure the abi and the kernel fn signatures are the same
const _: PrintAbi = print;
const _: DrawIterAbi = draw_iter;
const _: GetKeyAbi = get_key;
let framebuffer = block_on(FRAMEBUFFER.lock());
framebuffer
.borrow_mut()
.as_mut()
.unwrap()
.draw_iter(slice.iter().copied())
.unwrap();
}
Syscall::Print { msg, len } => {
// SAFETY: we're trusting the user program here
let slice = unsafe { core::slice::from_raw_parts(*msg, *len) };
if let Ok(str) = str::from_utf8(slice) {
defmt::info!("{:?}", str);
} else {
defmt::error!("Failed to parse user print str")
}
}
}
pub extern "Rust" fn print(msg: &str) {
defmt::info!("{:?}", msg);
}
pub extern "Rust" fn draw_iter(pixels: &[Pixel<Rgb565>]) {
let framebuffer = block_on(FRAMEBUFFER.lock());
framebuffer
.borrow_mut()
.as_mut()
.unwrap()
.draw_iter(pixels.iter().copied())
.unwrap();
}
pub extern "Rust" fn get_key() -> Option<KeyEvent> {
defmt::info!("get key called");
unsafe { KEY_CACHE.dequeue() }
}

View File

@@ -1,5 +1,15 @@
#![allow(static_mut_refs)]
use core::{alloc::Layout, ffi::c_void, ptr::NonNull, slice::from_raw_parts_mut};
use crate::abi;
use abi_sys::{CallAbiTable, EntryFn};
use alloc::boxed::Box;
use core::{
alloc::Layout,
ffi::c_void,
pin::Pin,
ptr::NonNull,
slice::from_raw_parts_mut,
task::{Context, Poll},
};
use goblin::elf::{Elf, header::ET_DYN, program_header::PT_LOAD, sym};
// userland ram region defined in memory.x
@@ -8,8 +18,6 @@ unsafe extern "C" {
static __userapp_end__: u8;
}
type EntryFn = extern "C" fn();
pub unsafe fn load_binary(bytes: &[u8]) -> Result<EntryFn, &str> {
let elf = Elf::parse(&bytes).expect("Failed to parse ELF");
@@ -55,16 +63,22 @@ pub unsafe fn load_binary(bytes: &[u8]) -> Result<EntryFn, &str> {
let call_abi_sym = elf
.syms
.iter()
.find(|s| elf.strtab.get_at(s.st_name).unwrap() == "call_abi_ptr")
.expect("call_abi_ptr not found");
.find(|s| elf.strtab.get_at(s.st_name).unwrap() == "CALL_ABI_TABLE")
.expect("syscall table not found");
// Virtual address inside user RAM
let addr = call_abi_sym.st_value as *mut usize;
let table_base = call_abi_sym.st_value as *mut usize;
// Patch it
unsafe {
core::ptr::write(addr, crate::abi::call_abi as usize);
let entries: &[(CallAbiTable, usize)] = &[
(CallAbiTable::Print, abi::print as usize),
(CallAbiTable::DrawIter, abi::draw_iter as usize),
(CallAbiTable::GetKey, abi::get_key as usize),
];
assert!(entries.len() == CallAbiTable::COUNT);
for &(abi_idx, func_ptr) in entries {
unsafe {
table_base.add(abi_idx as usize).write(func_ptr);
}
}
Ok(unsafe { core::mem::transmute(elf.entry as u32) })
}

View File

@@ -1,7 +1,11 @@
#![feature(impl_trait_in_assoc_type)]
#![feature(type_alias_impl_trait)]
#![feature(ascii_char)]
#![cfg_attr(not(test), no_std)]
#![cfg_attr(not(test), no_main)]
#![allow(static_mut_refs)]
extern crate alloc;
mod abi;
mod display;
@@ -23,12 +27,13 @@ use crate::{
storage::{SDCARD, SdCard},
usb::{ENABLE_SCSI, usb_handler},
};
use alloc::vec::Vec;
use {defmt_rtt as _, panic_probe as _};
use defmt::unwrap;
use embassy_executor::{Executor, Spawner};
use embassy_futures::join::join;
use embassy_futures::join::{join, join3};
use embassy_rp::{
gpio::{Input, Level, Output, Pull},
i2c::{self, I2c},
@@ -40,10 +45,15 @@ use embassy_rp::{
spi::{self, Spi},
usb as embassy_rp_usb,
};
use embassy_sync::{blocking_mutex::raw::CriticalSectionRawMutex, signal::Signal};
use embassy_sync::{
blocking_mutex::{Mutex, raw::CriticalSectionRawMutex},
signal::Signal,
};
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::*;
@@ -118,7 +128,7 @@ async fn userland_task() {
defmt::info!("Running binary");
let entry = unsafe { load_binary(binary_data).unwrap() };
entry();
entry().await;
}
struct Display {
@@ -194,5 +204,21 @@ async fn kernel_task(display: Display, sd: Sd, mcu: Mcu, usb: USB) {
let usb_fut = usb_handler(usb);
ENABLE_SCSI.store(true, core::sync::atomic::Ordering::Relaxed);
join(usb_fut, display_fut).await;
join3(usb_fut, display_fut, async {
loop {
Timer::after_millis(100).await;
get_keys().await
}
})
.await;
}
static mut KEY_CACHE: Queue<KeyEvent, 32> = Queue::new();
async fn get_keys() {
if let Some(event) = read_keyboard_fifo().await {
unsafe {
let _ = KEY_CACHE.enqueue(event);
}
}
}

View File

@@ -1,8 +1,10 @@
#![no_std]
#![no_main]
use abi::{display::Display, print};
use core::panic::PanicInfo;
extern crate alloc;
use abi::{KeyCode, display::Display, embassy_time, get_key, print};
use alloc::{boxed::Box, string::String, vec};
use core::{panic::PanicInfo, pin::Pin};
use embedded_graphics::{
Drawable,
geometry::{Dimensions, Point},
@@ -17,21 +19,40 @@ fn panic(_info: &PanicInfo) -> ! {
loop {}
}
#[unsafe(no_mangle)]
pub extern "C" fn _start() {
print("Starting Calculator app");
pub async fn main() {
print("Starting Async Calculator app");
let mut display = Display;
let character_style = MonoTextStyle::new(&FONT_6X10, Rgb565::RED);
// Draw centered text.
let text = "embedded-graphics";
Text::with_alignment(
text,
display.bounding_box().center() + Point::new(0, 15),
character_style,
Alignment::Center,
)
.draw(&mut display)
.unwrap();
let mut text = vec!['H', 'E', 'L', 'L', 'O'];
loop {
Text::with_alignment(
&text.iter().cloned().collect::<String>(),
display.bounding_box().center() + Point::new(0, 15),
character_style,
Alignment::Center,
)
.draw(&mut display)
.unwrap();
if let Some(event) = get_key() {
print("User got event");
match event.key {
KeyCode::Char(ch) => {
text.push(ch);
}
KeyCode::Backspace => {
text.pop();
}
_ => (),
}
}
}
}
#[unsafe(no_mangle)]
pub extern "Rust" fn _start() -> Pin<Box<dyn Future<Output = ()>>> {
Box::pin(async { main().await })
}