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

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);
}
}
}