This commit is contained in:
2025-11-05 11:00:05 -07:00
parent 513042b561
commit 4ec5afbc65
4 changed files with 201 additions and 100 deletions

View File

@@ -24,20 +24,19 @@ mod usb;
mod utils; mod utils;
#[cfg(feature = "pimoroni2w")] #[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::{ use crate::{
abi::{KEY_CACHE, MS_SINCE_LAUNCH}, abi::{KEY_CACHE, MS_SINCE_LAUNCH},
display::{FRAMEBUFFER, display_handler, init_display}, display::{FRAMEBUFFER, display_handler, init_display},
heap::HEAP,
peripherals::{ peripherals::{
conf_peripherals, conf_peripherals,
keyboard::{KeyState, read_keyboard_fifo}, keyboard::{KeyState, read_keyboard_fifo},
}, },
psram::init_psram, psram::init_psram,
scsi::MSC_SHUTDOWN,
storage::{SDCARD, SdCard}, storage::{SDCARD, SdCard},
ui::{SELECTIONS, clear_selection, ui_handler}, ui::{SELECTIONS, clear_selection, ui_handler},
usb::usb_handler,
}; };
use abi_sys::EntryFn; use abi_sys::EntryFn;
use bumpalo::Bump; use bumpalo::Bump;
@@ -188,8 +187,6 @@ async fn userland_task() {
UI_CHANGE.signal(()); UI_CHANGE.signal(());
clear_selection().await; clear_selection().await;
MSC_SHUTDOWN.signal(());
} }
unsafe { MS_SINCE_LAUNCH = Some(Instant::now()) }; 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 // psram is kind of useless on the pico calc
// ive opted to use the pimoroni with on onboard xip psram instead // ive opted to use the pimoroni with on onboard xip psram instead
#[allow(unused)]
async fn setup_psram(psram: Psram) { async fn setup_psram(psram: Psram) {
let psram = init_psram( let psram = init_psram(
psram.pio, psram.sclk, psram.mosi, psram.miso, psram.cs, psram.dma1, psram.dma2, 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>, watchdog: Peri<'static, WATCHDOG>,
display: Display, display: Display,
sd: Sd, sd: Sd,
psram: Psram, _psram: Psram,
mcu: Mcu, mcu: Mcu,
usb: Peri<'static, USB>, usb: Peri<'static, USB>,
) { ) {
@@ -343,37 +341,19 @@ async fn kernel_task(
setup_display(display, spawner).await; setup_display(display, spawner).await;
setup_sd(sd).await; setup_sd(sd).await;
let _usb = embassy_rp_usb::Driver::new(usb, Irqs); let usb_driver = embassy_rp_usb::Driver::new(usb, Irqs);
// spawner.spawn(usb_handler(usb)).unwrap(); spawner.spawn(usb_handler(usb_driver)).unwrap();
loop { loop {
let ui_enabled = ENABLE_UI.load(Ordering::Relaxed); let ui_enabled = ENABLE_UI.load(Ordering::Relaxed);
if ui_enabled { if ui_enabled {
select(join(ui_handler(), prog_search_handler()), UI_CHANGE.wait()).await; select(ui_handler(), UI_CHANGE.wait()).await;
} else { } else {
select(key_handler(), UI_CHANGE.wait()).await; 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() { async fn key_handler() {
loop { loop {
if let Some(event) = read_keyboard_fifo().await { if let Some(event) = read_keyboard_fifo().await {

View File

@@ -1,7 +1,5 @@
use embassy_futures::select::select; use core::sync::atomic::{AtomicBool, Ordering};
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
use embassy_sync::lazy_lock::LazyLock; use embassy_sync::lazy_lock::LazyLock;
use embassy_sync::signal::Signal;
use embassy_usb::Builder; use embassy_usb::Builder;
use embassy_usb::driver::{Driver, EndpointIn, EndpointOut}; use embassy_usb::driver::{Driver, EndpointIn, EndpointOut};
use embedded_sdmmc::{Block, BlockIdx}; use embedded_sdmmc::{Block, BlockIdx};
@@ -11,10 +9,13 @@ mod scsi_types;
use scsi_types::*; use scsi_types::*;
use crate::storage::{SDCARD, SdCard}; use crate::storage::{SDCARD, SdCard};
use crate::usb::stop_usb;
const BULK_ENDPOINT_PACKET_SIZE: usize = 64; 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 // number of blocks to read from sd at once
// higher is better, but is larger. Size is BLOCKS * 512 bytes // 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>> { pub struct MassStorageClass<'d, D: Driver<'d>> {
temp_sd: Option<SdCard>, // temporary owns sdcard when scsi is running temp_sd: Option<SdCard>, // temporary owns sdcard when scsi is running
ejected: bool,
pending_eject: bool, pending_eject: bool,
bulk_out: D::EndpointOut, bulk_out: D::EndpointOut,
bulk_in: D::EndpointIn, bulk_in: D::EndpointIn,
@@ -42,7 +42,6 @@ impl<'d, 's, D: Driver<'d>> MassStorageClass<'d, D> {
Self { Self {
temp_sd, temp_sd,
pending_eject: false, pending_eject: false,
ejected: false,
bulk_out, bulk_out,
bulk_in, bulk_in,
} }
@@ -50,23 +49,12 @@ impl<'d, 's, D: Driver<'d>> MassStorageClass<'d, D> {
pub async fn poll(&mut self) { pub async fn poll(&mut self) {
loop { loop {
if !self.ejected { self.handle_cbw().await;
select(self.handle_cbw(), MSC_SHUTDOWN.wait()).await;
if MSC_SHUTDOWN.signaled() {
#[cfg(feature = "defmt")]
defmt::info!("MSC shutting down");
if self.temp_sd.is_some() { if self.temp_sd.is_some() {
let mut guard = SDCARD.get().lock().await; let mut guard = SDCARD.get().lock().await;
guard.replace(self.temp_sd.take().unwrap()).unwrap(); guard.replace(self.temp_sd.take().unwrap()).unwrap();
} }
self.ejected = true;
return;
}
}
} }
} }
@@ -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 let Ok(n) = self.bulk_out.read(&mut cbw_buf).await {
if n == 31 { if n == 31 {
if let Some(cbw) = CommandBlockWrapper::parse(&cbw_buf[..n]) { if let Some(cbw) = CommandBlockWrapper::parse(&cbw_buf[..n]) {
SCSI_BUSY.store(true, Ordering::Release);
// Take sdcard to increase speed // Take sdcard to increase speed
if self.temp_sd.is_none() { if self.temp_sd.is_none() {
let mut guard = SDCARD.get().lock().await; let mut guard = SDCARD.get().lock().await;
if let Some(sd) = guard.take() { if let Some(sd) = guard.take() {
self.temp_sd = Some(sd); self.temp_sd = Some(sd);
} else { } else {
#[cfg(feature = "defmt")] panic!("Tried to take SDCARD but it was already taken");
defmt::warn!("Tried to take SDCARD but it was already taken");
return;
} }
} }
@@ -94,9 +82,11 @@ impl<'d, 's, D: Driver<'d>> MassStorageClass<'d, D> {
self.send_csw_fail(cbw.dCBWTag).await self.send_csw_fail(cbw.dCBWTag).await
} }
SCSI_BUSY.store(false, Ordering::Release);
if self.pending_eject { if self.pending_eject {
if let ScsiCommand::Write { lba: _, len: _ } = command { if let ScsiCommand::Write { lba: _, len: _ } = command {
MSC_SHUTDOWN.signal(()); stop_usb();
} }
} }
} }

View File

@@ -1,18 +1,24 @@
use crate::{ use crate::{
BINARY_CH, display::FRAMEBUFFER, elf::load_binary, framebuffer::FB_PAUSED, BINARY_CH,
peripherals::keyboard, storage::FileName, 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 abi_sys::keyboard::{KeyCode, KeyState};
use alloc::{str::FromStr, string::String, vec::Vec}; use alloc::{str::FromStr, string::String, vec::Vec};
use core::sync::atomic::Ordering; use core::sync::atomic::Ordering;
use embassy_sync::{blocking_mutex::raw::CriticalSectionRawMutex, mutex::Mutex}; use embassy_sync::{blocking_mutex::raw::CriticalSectionRawMutex, mutex::Mutex};
use embassy_time::Timer;
use embedded_graphics::{ use embedded_graphics::{
Drawable, Drawable,
mono_font::{MonoTextStyle, ascii::FONT_10X20}, mono_font::{MonoTextStyle, ascii::FONT_10X20},
pixelcolor::Rgb565, pixelcolor::Rgb565,
prelude::{Dimensions, Point, Primitive, RgbColor, Size}, prelude::{Dimensions, Point, Primitive, RgbColor, Size},
primitives::{PrimitiveStyle, Rectangle}, primitives::{PrimitiveStyle, Rectangle},
text::Text, text::{Alignment, Text},
}; };
use embedded_layout::{ use embedded_layout::{
align::{horizontal, vertical}, align::{horizontal, vertical},
@@ -25,7 +31,27 @@ pub static SELECTIONS: Mutex<CriticalSectionRawMutex, SelectionList> =
Mutex::new(SelectionList::new()); Mutex::new(SelectionList::new());
pub async fn ui_handler() { pub async fn ui_handler() {
update_selections().await;
let mut scsi = Scsi { last_bounds: None };
let mut scsi_button = ScsiButton { last_bounds: None };
loop { 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;
}
}
}
}
async fn input_handler() {
if let Some(event) = keyboard::read_keyboard_fifo().await { if let Some(event) = keyboard::read_keyboard_fifo().await {
if let KeyState::Pressed = event.state { if let KeyState::Pressed = event.state {
match event.key { match event.key {
@@ -37,7 +63,15 @@ pub async fn ui_handler() {
let mut selections = SELECTIONS.lock().await; let mut selections = SELECTIONS.lock().await;
selections.down(); selections.down();
} }
KeyCode::F1 => {
match USB_ACTIVE.load(Ordering::Acquire) {
true => stop_usb(),
false => start_usb(),
};
update_selections().await;
}
KeyCode::Enter | KeyCode::Right => { KeyCode::Enter | KeyCode::Right => {
stop_usb();
let selections = SELECTIONS.lock().await; let selections = SELECTIONS.lock().await;
let selection = let selection =
selections.selections[selections.current_selection as usize].clone(); selections.selections[selections.current_selection as usize].clone();
@@ -53,27 +87,93 @@ pub async fn ui_handler() {
} }
} }
} }
}
let changed = SELECTIONS.lock().await.changed; async fn clear_rect(rect: Rectangle) {
if changed { Rectangle::new(rect.top_left, rect.size)
clear_selection().await; .into_styled(PrimitiveStyle::with_fill(Rgb565::BLACK))
draw_selection().await; .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() { pub async fn clear_selection() {
let sel = SELECTIONS.lock().await; let sel = SELECTIONS.lock().await;
if let Some(area) = sel.last_bounds { if let Some(rect) = sel.last_bounds {
Rectangle::new(area.top_left, area.size) clear_rect(rect).await
.into_styled(PrimitiveStyle::with_fill(Rgb565::BLACK))
.draw(unsafe { &mut *FRAMEBUFFER.as_mut().unwrap() })
.unwrap();
} }
} }
async fn draw_selection() { async fn draw_selection(scsi_button: &mut ScsiButton) {
let mut guard = SELECTIONS.lock().await; let mut guard = SELECTIONS.lock().await;
let file_names = &guard.selections.clone(); let file_names = &guard.selections.clone();
@@ -130,6 +230,7 @@ async fn draw_selection() {
} }
guard.changed = false; guard.changed = false;
scsi_button.draw().await;
FB_PAUSED.store(false, Ordering::Release); // ensure all elements show up at once FB_PAUSED.store(false, Ordering::Release); // ensure all elements show up at once
} }

View File

@@ -1,9 +1,16 @@
use crate::{scsi::MassStorageClass, storage::SdCard};
use core::sync::atomic::{AtomicBool, Ordering}; 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_rp::{peripherals::USB, usb::Driver};
use embassy_sync::{blocking_mutex::raw::CriticalSectionRawMutex, signal::Signal};
use embassy_usb::{Builder, Config, UsbDevice}; 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); pub static USB_ACTIVE: AtomicBool = AtomicBool::new(false);
#[embassy_executor::task] #[embassy_executor::task]
@@ -30,16 +37,39 @@ pub async fn usb_handler(driver: Driver<'static, USB>) {
let temp_sd: Option<SdCard> = None; let temp_sd: Option<SdCard> = None;
let mut scsi = MassStorageClass::new(&mut builder, temp_sd); 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 { loop {
usb.wait_resume().await; START_USB.wait().await;
USB_ACTIVE.store(true, Ordering::Release); START_USB.reset();
usb.run_until_suspend().await; #[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); 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);
}