mirror of
https://github.com/LegitCamper/picocalc-os-rs.git
synced 2025-12-27 07:45:28 +00:00
init
This commit is contained in:
@@ -24,20 +24,19 @@ mod usb;
|
||||
mod utils;
|
||||
|
||||
#[cfg(feature = "pimoroni2w")]
|
||||
use crate::{heap::init_qmi_psram_heap, psram::init_psram_qmi};
|
||||
use crate::{heap::HEAP, heap::init_qmi_psram_heap, psram::init_psram_qmi};
|
||||
|
||||
use crate::{
|
||||
abi::{KEY_CACHE, MS_SINCE_LAUNCH},
|
||||
display::{FRAMEBUFFER, display_handler, init_display},
|
||||
heap::HEAP,
|
||||
peripherals::{
|
||||
conf_peripherals,
|
||||
keyboard::{KeyState, read_keyboard_fifo},
|
||||
},
|
||||
psram::init_psram,
|
||||
scsi::MSC_SHUTDOWN,
|
||||
storage::{SDCARD, SdCard},
|
||||
ui::{SELECTIONS, clear_selection, ui_handler},
|
||||
usb::usb_handler,
|
||||
};
|
||||
use abi_sys::EntryFn;
|
||||
use bumpalo::Bump;
|
||||
@@ -188,8 +187,6 @@ async fn userland_task() {
|
||||
UI_CHANGE.signal(());
|
||||
|
||||
clear_selection().await;
|
||||
|
||||
MSC_SHUTDOWN.signal(());
|
||||
}
|
||||
|
||||
unsafe { MS_SINCE_LAUNCH = Some(Instant::now()) };
|
||||
@@ -270,6 +267,7 @@ async fn setup_display(display: Display, spawner: Spawner) {
|
||||
|
||||
// psram is kind of useless on the pico calc
|
||||
// ive opted to use the pimoroni with on onboard xip psram instead
|
||||
#[allow(unused)]
|
||||
async fn setup_psram(psram: Psram) {
|
||||
let psram = init_psram(
|
||||
psram.pio, psram.sclk, psram.mosi, psram.miso, psram.cs, psram.dma1, psram.dma2,
|
||||
@@ -322,7 +320,7 @@ async fn kernel_task(
|
||||
watchdog: Peri<'static, WATCHDOG>,
|
||||
display: Display,
|
||||
sd: Sd,
|
||||
psram: Psram,
|
||||
_psram: Psram,
|
||||
mcu: Mcu,
|
||||
usb: Peri<'static, USB>,
|
||||
) {
|
||||
@@ -343,37 +341,19 @@ async fn kernel_task(
|
||||
setup_display(display, spawner).await;
|
||||
setup_sd(sd).await;
|
||||
|
||||
let _usb = embassy_rp_usb::Driver::new(usb, Irqs);
|
||||
// spawner.spawn(usb_handler(usb)).unwrap();
|
||||
let usb_driver = embassy_rp_usb::Driver::new(usb, Irqs);
|
||||
spawner.spawn(usb_handler(usb_driver)).unwrap();
|
||||
|
||||
loop {
|
||||
let ui_enabled = ENABLE_UI.load(Ordering::Relaxed);
|
||||
if ui_enabled {
|
||||
select(join(ui_handler(), prog_search_handler()), UI_CHANGE.wait()).await;
|
||||
select(ui_handler(), UI_CHANGE.wait()).await;
|
||||
} else {
|
||||
select(key_handler(), UI_CHANGE.wait()).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn prog_search_handler() {
|
||||
loop {
|
||||
{
|
||||
let mut guard = SDCARD.get().lock().await;
|
||||
let sd = guard.as_mut().unwrap();
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
Timer::after_secs(5).await;
|
||||
}
|
||||
}
|
||||
|
||||
async fn key_handler() {
|
||||
loop {
|
||||
if let Some(event) = read_keyboard_fifo().await {
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
use embassy_futures::select::select;
|
||||
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
|
||||
use core::sync::atomic::{AtomicBool, Ordering};
|
||||
use embassy_sync::lazy_lock::LazyLock;
|
||||
use embassy_sync::signal::Signal;
|
||||
use embassy_usb::Builder;
|
||||
use embassy_usb::driver::{Driver, EndpointIn, EndpointOut};
|
||||
use embedded_sdmmc::{Block, BlockIdx};
|
||||
@@ -11,10 +9,13 @@ mod scsi_types;
|
||||
use scsi_types::*;
|
||||
|
||||
use crate::storage::{SDCARD, SdCard};
|
||||
use crate::usb::stop_usb;
|
||||
|
||||
const BULK_ENDPOINT_PACKET_SIZE: usize = 64;
|
||||
|
||||
pub static MSC_SHUTDOWN: Signal<CriticalSectionRawMutex, ()> = Signal::new();
|
||||
// indicates that a scsi transaction is occurring and is
|
||||
// NOT safe to disable usb, or acquire sdcard
|
||||
static SCSI_BUSY: AtomicBool = AtomicBool::new(false);
|
||||
|
||||
// number of blocks to read from sd at once
|
||||
// higher is better, but is larger. Size is BLOCKS * 512 bytes
|
||||
@@ -24,7 +25,6 @@ static mut BLOCK_BUF: LazyLock<[Block; BLOCKS]> =
|
||||
|
||||
pub struct MassStorageClass<'d, D: Driver<'d>> {
|
||||
temp_sd: Option<SdCard>, // temporary owns sdcard when scsi is running
|
||||
ejected: bool,
|
||||
pending_eject: bool,
|
||||
bulk_out: D::EndpointOut,
|
||||
bulk_in: D::EndpointIn,
|
||||
@@ -42,7 +42,6 @@ impl<'d, 's, D: Driver<'d>> MassStorageClass<'d, D> {
|
||||
Self {
|
||||
temp_sd,
|
||||
pending_eject: false,
|
||||
ejected: false,
|
||||
bulk_out,
|
||||
bulk_in,
|
||||
}
|
||||
@@ -50,22 +49,11 @@ impl<'d, 's, D: Driver<'d>> MassStorageClass<'d, D> {
|
||||
|
||||
pub async fn poll(&mut self) {
|
||||
loop {
|
||||
if !self.ejected {
|
||||
select(self.handle_cbw(), MSC_SHUTDOWN.wait()).await;
|
||||
self.handle_cbw().await;
|
||||
|
||||
if MSC_SHUTDOWN.signaled() {
|
||||
#[cfg(feature = "defmt")]
|
||||
defmt::info!("MSC shutting down");
|
||||
|
||||
if self.temp_sd.is_some() {
|
||||
let mut guard = SDCARD.get().lock().await;
|
||||
guard.replace(self.temp_sd.take().unwrap()).unwrap();
|
||||
}
|
||||
|
||||
self.ejected = true;
|
||||
|
||||
return;
|
||||
}
|
||||
if self.temp_sd.is_some() {
|
||||
let mut guard = SDCARD.get().lock().await;
|
||||
guard.replace(self.temp_sd.take().unwrap()).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -75,15 +63,15 @@ impl<'d, 's, D: Driver<'d>> MassStorageClass<'d, D> {
|
||||
if let Ok(n) = self.bulk_out.read(&mut cbw_buf).await {
|
||||
if n == 31 {
|
||||
if let Some(cbw) = CommandBlockWrapper::parse(&cbw_buf[..n]) {
|
||||
SCSI_BUSY.store(true, Ordering::Release);
|
||||
|
||||
// Take sdcard to increase speed
|
||||
if self.temp_sd.is_none() {
|
||||
let mut guard = SDCARD.get().lock().await;
|
||||
if let Some(sd) = guard.take() {
|
||||
self.temp_sd = Some(sd);
|
||||
} else {
|
||||
#[cfg(feature = "defmt")]
|
||||
defmt::warn!("Tried to take SDCARD but it was already taken");
|
||||
return;
|
||||
panic!("Tried to take SDCARD but it was already taken");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,9 +82,11 @@ impl<'d, 's, D: Driver<'d>> MassStorageClass<'d, D> {
|
||||
self.send_csw_fail(cbw.dCBWTag).await
|
||||
}
|
||||
|
||||
SCSI_BUSY.store(false, Ordering::Release);
|
||||
|
||||
if self.pending_eject {
|
||||
if let ScsiCommand::Write { lba: _, len: _ } = command {
|
||||
MSC_SHUTDOWN.signal(());
|
||||
stop_usb();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
177
kernel/src/ui.rs
177
kernel/src/ui.rs
@@ -1,18 +1,24 @@
|
||||
use crate::{
|
||||
BINARY_CH, display::FRAMEBUFFER, elf::load_binary, framebuffer::FB_PAUSED,
|
||||
peripherals::keyboard, storage::FileName,
|
||||
BINARY_CH,
|
||||
display::FRAMEBUFFER,
|
||||
elf::load_binary,
|
||||
framebuffer::FB_PAUSED,
|
||||
peripherals::keyboard,
|
||||
storage::{FileName, SDCARD},
|
||||
usb::{USB_ACTIVE, start_usb, stop_usb},
|
||||
};
|
||||
use abi_sys::keyboard::{KeyCode, KeyState};
|
||||
use alloc::{str::FromStr, string::String, vec::Vec};
|
||||
use core::sync::atomic::Ordering;
|
||||
use embassy_sync::{blocking_mutex::raw::CriticalSectionRawMutex, mutex::Mutex};
|
||||
use embassy_time::Timer;
|
||||
use embedded_graphics::{
|
||||
Drawable,
|
||||
mono_font::{MonoTextStyle, ascii::FONT_10X20},
|
||||
pixelcolor::Rgb565,
|
||||
prelude::{Dimensions, Point, Primitive, RgbColor, Size},
|
||||
primitives::{PrimitiveStyle, Rectangle},
|
||||
text::Text,
|
||||
text::{Alignment, Text},
|
||||
};
|
||||
use embedded_layout::{
|
||||
align::{horizontal, vertical},
|
||||
@@ -25,55 +31,149 @@ pub static SELECTIONS: Mutex<CriticalSectionRawMutex, SelectionList> =
|
||||
Mutex::new(SelectionList::new());
|
||||
|
||||
pub async fn ui_handler() {
|
||||
loop {
|
||||
if let Some(event) = keyboard::read_keyboard_fifo().await {
|
||||
if let KeyState::Pressed = event.state {
|
||||
match event.key {
|
||||
KeyCode::Up => {
|
||||
let mut selections = SELECTIONS.lock().await;
|
||||
selections.up();
|
||||
}
|
||||
KeyCode::Down => {
|
||||
let mut selections = SELECTIONS.lock().await;
|
||||
selections.down();
|
||||
}
|
||||
KeyCode::Enter | KeyCode::Right => {
|
||||
let selections = SELECTIONS.lock().await;
|
||||
let selection =
|
||||
selections.selections[selections.current_selection as usize].clone();
|
||||
update_selections().await;
|
||||
let mut scsi = Scsi { last_bounds: None };
|
||||
let mut scsi_button = ScsiButton { last_bounds: None };
|
||||
|
||||
let entry = unsafe {
|
||||
load_binary(&selection.short_name)
|
||||
.await
|
||||
.expect("unable to load binary")
|
||||
};
|
||||
BINARY_CH.send(entry).await;
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
loop {
|
||||
input_handler().await;
|
||||
|
||||
if USB_ACTIVE.load(Ordering::Acquire) {
|
||||
scsi.draw(&mut scsi_button).await;
|
||||
Timer::after_millis(100).await;
|
||||
} else {
|
||||
let changed = SELECTIONS.lock().await.changed;
|
||||
if changed {
|
||||
clear_selection().await;
|
||||
draw_selection(&mut scsi_button).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let changed = SELECTIONS.lock().await.changed;
|
||||
if changed {
|
||||
clear_selection().await;
|
||||
draw_selection().await;
|
||||
async fn input_handler() {
|
||||
if let Some(event) = keyboard::read_keyboard_fifo().await {
|
||||
if let KeyState::Pressed = event.state {
|
||||
match event.key {
|
||||
KeyCode::Up => {
|
||||
let mut selections = SELECTIONS.lock().await;
|
||||
selections.up();
|
||||
}
|
||||
KeyCode::Down => {
|
||||
let mut selections = SELECTIONS.lock().await;
|
||||
selections.down();
|
||||
}
|
||||
KeyCode::F1 => {
|
||||
match USB_ACTIVE.load(Ordering::Acquire) {
|
||||
true => stop_usb(),
|
||||
false => start_usb(),
|
||||
};
|
||||
update_selections().await;
|
||||
}
|
||||
KeyCode::Enter | KeyCode::Right => {
|
||||
stop_usb();
|
||||
let selections = SELECTIONS.lock().await;
|
||||
let selection =
|
||||
selections.selections[selections.current_selection as usize].clone();
|
||||
|
||||
let entry = unsafe {
|
||||
load_binary(&selection.short_name)
|
||||
.await
|
||||
.expect("unable to load binary")
|
||||
};
|
||||
BINARY_CH.send(entry).await;
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn clear_rect(rect: Rectangle) {
|
||||
Rectangle::new(rect.top_left, rect.size)
|
||||
.into_styled(PrimitiveStyle::with_fill(Rgb565::BLACK))
|
||||
.draw(unsafe { &mut *FRAMEBUFFER.as_mut().unwrap() })
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
async fn update_selections() {
|
||||
let mut guard = SDCARD.get().lock().await;
|
||||
let sd = guard.as_mut().unwrap();
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
struct Scsi {
|
||||
last_bounds: Option<Rectangle>,
|
||||
}
|
||||
|
||||
impl Scsi {
|
||||
async fn draw(&mut self, scsi_button: &mut ScsiButton) {
|
||||
if let Some(rect) = self.last_bounds {
|
||||
clear_rect(rect).await
|
||||
}
|
||||
|
||||
let text_style = MonoTextStyle::new(&FONT_10X20, Rgb565::WHITE);
|
||||
let text = Text::with_alignment(
|
||||
"Usb Mass Storage active",
|
||||
unsafe { FRAMEBUFFER.as_ref().unwrap() }
|
||||
.bounding_box()
|
||||
.center(),
|
||||
text_style,
|
||||
Alignment::Center,
|
||||
);
|
||||
|
||||
self.last_bounds = Some(text.bounds());
|
||||
|
||||
text.draw(unsafe { &mut *FRAMEBUFFER.as_mut().unwrap() })
|
||||
.unwrap();
|
||||
scsi_button.draw().await;
|
||||
}
|
||||
}
|
||||
|
||||
struct ScsiButton {
|
||||
last_bounds: Option<Rectangle>,
|
||||
}
|
||||
|
||||
impl ScsiButton {
|
||||
async fn draw(&mut self) {
|
||||
if let Some(rect) = self.last_bounds {
|
||||
clear_rect(rect).await
|
||||
}
|
||||
|
||||
let text_style = MonoTextStyle::new(&FONT_10X20, Rgb565::WHITE);
|
||||
let text = Text::with_alignment(
|
||||
"Press F1 to enable/disable mass storage",
|
||||
unsafe { FRAMEBUFFER.as_ref().unwrap() }
|
||||
.bounding_box()
|
||||
.bottom_right()
|
||||
.unwrap(),
|
||||
text_style,
|
||||
Alignment::Left,
|
||||
);
|
||||
|
||||
self.last_bounds = Some(text.bounds());
|
||||
|
||||
text.draw(unsafe { &mut *FRAMEBUFFER.as_mut().unwrap() })
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn clear_selection() {
|
||||
let sel = SELECTIONS.lock().await;
|
||||
|
||||
if let Some(area) = sel.last_bounds {
|
||||
Rectangle::new(area.top_left, area.size)
|
||||
.into_styled(PrimitiveStyle::with_fill(Rgb565::BLACK))
|
||||
.draw(unsafe { &mut *FRAMEBUFFER.as_mut().unwrap() })
|
||||
.unwrap();
|
||||
if let Some(rect) = sel.last_bounds {
|
||||
clear_rect(rect).await
|
||||
}
|
||||
}
|
||||
|
||||
async fn draw_selection() {
|
||||
async fn draw_selection(scsi_button: &mut ScsiButton) {
|
||||
let mut guard = SELECTIONS.lock().await;
|
||||
let file_names = &guard.selections.clone();
|
||||
|
||||
@@ -130,6 +230,7 @@ async fn draw_selection() {
|
||||
}
|
||||
|
||||
guard.changed = false;
|
||||
scsi_button.draw().await;
|
||||
FB_PAUSED.store(false, Ordering::Release); // ensure all elements show up at once
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,16 @@
|
||||
use crate::{scsi::MassStorageClass, storage::SdCard};
|
||||
use core::sync::atomic::{AtomicBool, Ordering};
|
||||
use embassy_futures::select::select;
|
||||
|
||||
use crate::{scsi::MassStorageClass, storage::SdCard};
|
||||
use embassy_futures::{join::join, select::select};
|
||||
use embassy_rp::{peripherals::USB, usb::Driver};
|
||||
use embassy_sync::{blocking_mutex::raw::CriticalSectionRawMutex, signal::Signal};
|
||||
use embassy_usb::{Builder, Config, UsbDevice};
|
||||
|
||||
static START_USB: Signal<CriticalSectionRawMutex, ()> = Signal::new();
|
||||
static STOP_USB: Signal<CriticalSectionRawMutex, ()> = Signal::new();
|
||||
|
||||
// for other tasks to query the status of usb (like ui)
|
||||
// this is read only for ALL other tasks
|
||||
pub static USB_ACTIVE: AtomicBool = AtomicBool::new(false);
|
||||
|
||||
#[embassy_executor::task]
|
||||
@@ -30,16 +37,39 @@ pub async fn usb_handler(driver: Driver<'static, USB>) {
|
||||
|
||||
let temp_sd: Option<SdCard> = None;
|
||||
let mut scsi = MassStorageClass::new(&mut builder, temp_sd);
|
||||
let usb = builder.build();
|
||||
let mut usb = builder.build();
|
||||
|
||||
select(run(usb), scsi.poll()).await;
|
||||
}
|
||||
|
||||
async fn run<'d>(mut usb: UsbDevice<'d, Driver<'d, USB>>) -> ! {
|
||||
loop {
|
||||
usb.wait_resume().await;
|
||||
USB_ACTIVE.store(true, Ordering::Release);
|
||||
usb.run_until_suspend().await;
|
||||
START_USB.wait().await;
|
||||
START_USB.reset();
|
||||
#[cfg(feature = "defmt")]
|
||||
defmt::info!("starting usb");
|
||||
select(
|
||||
// waits for cancellation signal, and then waits for
|
||||
// transfers to stop before dropping usb future
|
||||
async {
|
||||
STOP_USB.wait().await;
|
||||
STOP_USB.reset();
|
||||
},
|
||||
// runs the usb, until cancelled
|
||||
join(usb.run(), scsi.poll()),
|
||||
)
|
||||
.await;
|
||||
#[cfg(feature = "defmt")]
|
||||
defmt::info!("disabling usb");
|
||||
usb.disable().await;
|
||||
USB_ACTIVE.store(false, Ordering::Release);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn start_usb() {
|
||||
STOP_USB.reset();
|
||||
START_USB.signal(());
|
||||
USB_ACTIVE.store(true, Ordering::Release);
|
||||
}
|
||||
|
||||
pub fn stop_usb() {
|
||||
START_USB.reset();
|
||||
STOP_USB.signal(());
|
||||
USB_ACTIVE.store(false, Ordering::Release);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user