From ef66933a258dd4d6572da422836ea3f18715be9f Mon Sep 17 00:00:00 2001 From: sawyer bristol Date: Tue, 16 Sep 2025 16:46:24 -0600 Subject: [PATCH] WIP scsi :( --- Cargo.lock | 1 + kernel/Cargo.toml | 1 + kernel/src/display.rs | 1 - kernel/src/main.rs | 12 +-- kernel/src/scsi/mod.rs | 177 +++++++++++++++++++++++++++++------------ kernel/src/ui.rs | 60 +++++++------- kernel/src/usb.rs | 25 ++++-- 7 files changed, 182 insertions(+), 95 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7659019..2a7a7c1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1299,6 +1299,7 @@ dependencies = [ "heapless", "kolibri-embedded-gui", "num_enum 0.7.4", + "once_cell", "panic-probe", "portable-atomic", "shared", diff --git a/kernel/Cargo.toml b/kernel/Cargo.toml index 248ce91..260d754 100644 --- a/kernel/Cargo.toml +++ b/kernel/Cargo.toml @@ -78,6 +78,7 @@ embedded-text = "0.7.2" embedded-layout = "0.4.2" kolibri-embedded-gui = "0.1.0" +once_cell = { version = "1.21.3", default-features = false } static_cell = "2.1.1" bitflags = "2.9.1" heapless = "0.8.0" diff --git a/kernel/src/display.rs b/kernel/src/display.rs index d00a859..1c7e369 100644 --- a/kernel/src/display.rs +++ b/kernel/src/display.rs @@ -9,7 +9,6 @@ use embassy_time::{Delay, Timer}; use embedded_graphics::{ draw_target::DrawTarget, pixelcolor::{Rgb565, RgbColor}, - prelude::Dimensions, }; use embedded_hal_bus::spi::ExclusiveDevice; use st7365p_lcd::ST7365P; diff --git a/kernel/src/main.rs b/kernel/src/main.rs index 6dd3154..a919984 100644 --- a/kernel/src/main.rs +++ b/kernel/src/main.rs @@ -78,12 +78,12 @@ static ALLOCATOR: Talck, ClaimOnOom> = Talc::new(unsafe { ClaimOnOom::new(Span::from_array(core::ptr::addr_of!(ARENA).cast_mut())) }) .lock(); -static TASK_STATE: Mutex = Mutex::new(TaskState::Ui); +static TASK_STATE: Mutex = Mutex::new(TaskState::Selection); static TASK_STATE_CHANGED: Signal = Signal::new(); #[derive(Copy, Clone, PartialEq)] enum TaskState { - Ui, + Selection, Kernel, } @@ -154,7 +154,7 @@ async fn userland_task() { // enable kernel ui { let mut state = TASK_STATE.lock().await; - *state = TaskState::Ui; + *state = TaskState::Selection; TASK_STATE_CHANGED.signal(()); // clear_fb(); } @@ -244,7 +244,7 @@ async fn kernel_task( spawner.spawn(key_handler()).unwrap(); loop { - if let TaskState::Ui = *TASK_STATE.lock().await { + if let TaskState::Selection = *TASK_STATE.lock().await { let ui_fut = ui_handler(); let binary_search_fut = prog_search_handler(); @@ -264,8 +264,8 @@ async fn prog_search_handler() { let files = sd.list_files_by_extension(".bin").unwrap(); let mut select = SELECTIONS.lock().await; - if select.selections != files { - select.selections = files; + if *select.selections() != files { + select.update_selections(files); select.reset(); } } diff --git a/kernel/src/scsi/mod.rs b/kernel/src/scsi/mod.rs index c4df16c..c10a97f 100644 --- a/kernel/src/scsi/mod.rs +++ b/kernel/src/scsi/mod.rs @@ -1,4 +1,8 @@ +use core::sync::atomic::AtomicBool; + +use embassy_futures::select::select; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; +use embassy_sync::lazy_lock::LazyLock; use embassy_sync::signal::Signal; use embassy_usb::Builder; use embassy_usb::driver::{Driver, EndpointIn, EndpointOut}; @@ -14,13 +18,22 @@ const BULK_ENDPOINT_PACKET_SIZE: usize = 64; pub static MSC_SHUTDOWN: Signal = Signal::new(); +// number of blocks to read from sd at once +// higher is better, but is larger. Size is BLOCKS * 512 bytes +const BLOCKS: usize = 32; +static mut BLOCK_BUF: LazyLock<[Block; BLOCKS]> = + LazyLock::new(|| core::array::from_fn(|_| Block::new())); + pub struct MassStorageClass<'d, D: Driver<'d>> { + temp_sd: Option, // temporarly owns sdcard when scsi is running + ejected: bool, + pending_eject: bool, bulk_out: D::EndpointOut, bulk_in: D::EndpointIn, } impl<'d, 's, D: Driver<'d>> MassStorageClass<'d, D> { - pub fn new(builder: &mut Builder<'d, D>) -> Self { + pub fn new(builder: &mut Builder<'d, D>, temp_sd: Option) -> Self { let mut function = builder.function(0x08, SUBCLASS_SCSI, 0x50); // Mass Storage class let mut interface = function.interface(); let mut alt = interface.alt_setting(0x08, SUBCLASS_SCSI, 0x50, None); @@ -28,16 +41,32 @@ impl<'d, 's, D: Driver<'d>> MassStorageClass<'d, D> { let bulk_out = alt.endpoint_bulk_out(None, BULK_ENDPOINT_PACKET_SIZE as u16); let bulk_in = alt.endpoint_bulk_in(None, BULK_ENDPOINT_PACKET_SIZE as u16); - Self { bulk_out, bulk_in } + Self { + temp_sd, + pending_eject: false, + ejected: false, + bulk_out, + bulk_in, + } } pub async fn poll(&mut self) { loop { - embassy_futures::select::select(self.handle_cbw(), MSC_SHUTDOWN.wait()).await; + if !self.ejected { + select(self.handle_cbw(), MSC_SHUTDOWN.wait()).await; - if MSC_SHUTDOWN.signaled() { - defmt::info!("MSC shutting down"); - return; // or break + if MSC_SHUTDOWN.signaled() { + 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; + } } } } @@ -45,28 +74,41 @@ impl<'d, 's, D: Driver<'d>> MassStorageClass<'d, D> { async fn handle_cbw(&mut self) { let mut cbw_buf = [0u8; 31]; if let Ok(n) = self.bulk_out.read(&mut cbw_buf).await { + // 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 { + defmt::warn!("Tried to take SDCARD but it was already taken"); + return; + } + } + if n == 31 { if let Some(cbw) = CommandBlockWrapper::parse(&cbw_buf[..n]) { - if self.handle_command(&cbw.CBWCB).await.is_ok() { + let command = parse_cb(&cbw.CBWCB); + if self.handle_command(command).await.is_ok() { self.send_csw_success(cbw.dCBWTag).await } else { self.send_csw_fail(cbw.dCBWTag).await } + + if self.pending_eject { + if let ScsiCommand::Write { lba: _, len: _ } = command { + MSC_SHUTDOWN.signal(()); + } + } } } } } - async fn handle_command(&mut self, cbw: &[u8]) -> Result<(), ()> { + async fn handle_command(&mut self, command: ScsiCommand) -> Result<(), ()> { let mut response: Vec = Vec::new(); - let mut block = [Block::new(); 1]; - match parse_cb(cbw) { - ScsiCommand::Unknown => { - #[cfg(feature = "defmt")] - defmt::warn!("Got unexpected scsi command: {}", cbw); - Err(()) - } + match command { + ScsiCommand::Unknown => Err(()), ScsiCommand::Inquiry { evpd, page_code, @@ -144,9 +186,7 @@ impl<'d, 's, D: Driver<'d>> MassStorageClass<'d, D> { self.bulk_in.write(&response[..len]).await.map_err(|_| ()) } ScsiCommand::TestUnitReady => { - let guard = SDCARD.get().lock().await; - let sdcard = guard.as_ref().unwrap(); - if sdcard.is_attached() { + if self.temp_sd.as_ref().unwrap().is_attached() { Ok(()) } else { Err(()) @@ -192,11 +232,8 @@ impl<'d, 's, D: Driver<'d>> MassStorageClass<'d, D> { self.bulk_in.write(&response[..len]).await.map_err(|_| ()) } ScsiCommand::ReadCapacity10 => { - let guard = SDCARD.get().lock().await; - let sdcard = guard.as_ref().unwrap(); - let block_size = SdCard::BLOCK_SIZE as u64; - let total_blocks = sdcard.size() / block_size; + let total_blocks = self.temp_sd.as_ref().unwrap().size() / block_size; let last_lba = total_blocks.checked_sub(1).unwrap_or(0); @@ -206,11 +243,8 @@ impl<'d, 's, D: Driver<'d>> MassStorageClass<'d, D> { self.bulk_in.write(&response).await.map_err(|_| ()) } ScsiCommand::ReadCapacity16 { alloc_len } => { - let guard = SDCARD.get().lock().await; - let sdcard = guard.as_ref().unwrap(); - let block_size = SdCard::BLOCK_SIZE as u64; - let total_blocks = sdcard.size() / block_size; + let total_blocks = self.temp_sd.as_ref().unwrap().size() / block_size; let last_lba = total_blocks.checked_sub(1).unwrap_or(0); @@ -222,40 +256,80 @@ impl<'d, 's, D: Driver<'d>> MassStorageClass<'d, D> { self.bulk_in.write(&response[..len]).await.map_err(|_| ()) } ScsiCommand::Read { lba, len } => { - let guard = SDCARD.get().lock().await; - let sdcard = guard.as_ref().unwrap(); + let sdcard = self.temp_sd.as_ref().unwrap(); + let block_buf = unsafe { &mut *BLOCK_BUF.get_mut() }; + let mut blocks = len; + let mut idx = lba; - for i in 0..len { - let block_idx = BlockIdx(lba as u32 + i as u32); - sdcard.read_blocks(&mut block, block_idx)?; - for chunk in block[0].contents.chunks(BULK_ENDPOINT_PACKET_SIZE.into()) { - self.bulk_in.write(chunk).await.map_err(|_| ())?; + while blocks > 0 { + if blocks >= block_buf.len() as u64 { + sdcard.read_blocks(block_buf, BlockIdx(idx as u32))?; + + for block in &mut *block_buf { + for chunk in block.contents.chunks(BULK_ENDPOINT_PACKET_SIZE.into()) { + self.bulk_in.write(chunk).await.map_err(|_| ())?; + } + } + + blocks -= block_buf.len() as u64; + idx += block_buf.len() as u64; + } else { + sdcard + .read_blocks(&mut block_buf[..blocks as usize], BlockIdx(idx as u32))?; + + for block in &block_buf[..blocks as usize] { + for chunk in block.contents.chunks(BULK_ENDPOINT_PACKET_SIZE.into()) { + self.bulk_in.write(chunk).await.map_err(|_| ())?; + } + } + + idx += blocks; + blocks = 0; } } Ok(()) } ScsiCommand::Write { lba, len } => { - let guard = SDCARD.get().lock().await; - let sdcard = guard.as_ref().unwrap(); + let sdcard = self.temp_sd.as_ref().unwrap(); + let block_buf = unsafe { &mut *BLOCK_BUF.get_mut() }; + let mut blocks = len; + let mut idx = lba; - for i in 0..len { - let block_idx = BlockIdx(lba as u32 + i as u32); - for chunk in block[0] - .contents - .chunks_mut(BULK_ENDPOINT_PACKET_SIZE.into()) - { - self.bulk_out.read(chunk).await.map_err(|_| ())?; + while blocks > 0 { + if blocks >= block_buf.len() as u64 { + for block in block_buf.as_mut() { + for chunk in block.contents.chunks_mut(BULK_ENDPOINT_PACKET_SIZE.into()) + { + self.bulk_out.read(chunk).await.map_err(|_| ())?; + } + } + + sdcard.read_blocks(block_buf, BlockIdx(idx as u32))?; + + blocks -= block_buf.len() as u64; + idx += block_buf.len() as u64; + } else { + for block in block_buf[..blocks as usize].as_mut() { + for chunk in block.contents.chunks_mut(BULK_ENDPOINT_PACKET_SIZE.into()) + { + self.bulk_out.read(chunk).await.map_err(|_| ())?; + } + } + + sdcard.write_blocks( + &mut block_buf[..blocks as usize], + BlockIdx(idx as u32), + )?; + + idx += blocks; + blocks = 0; } - sdcard.write_blocks(&mut block, block_idx)?; } Ok(()) } ScsiCommand::ReadFormatCapacities { alloc_len } => { - let guard = SDCARD.get().lock().await; - let sdcard = guard.as_ref().unwrap(); - let block_size = SdCard::BLOCK_SIZE as u32; - let num_blocks = (sdcard.size() / block_size as u64) as u32; + let num_blocks = (self.temp_sd.as_ref().unwrap().size() / block_size as u64) as u32; let mut response = [0u8; 12]; @@ -274,7 +348,12 @@ impl<'d, 's, D: Driver<'d>> MassStorageClass<'d, D> { .map_err(|_| ()) } ScsiCommand::PreventAllowMediumRemoval { prevent: _prevent } => Ok(()), - ScsiCommand::StartStopUnit { start, load_eject } => Ok(()), + ScsiCommand::StartStopUnit { start, load_eject } => { + if !start && load_eject { + self.pending_eject = true; + } + Ok(()) + } } } @@ -283,7 +362,7 @@ impl<'d, 's, D: Driver<'d>> MassStorageClass<'d, D> { } pub async fn send_csw_fail(&mut self, tag: u32) { - defmt::error!("Command Failed"); + defmt::error!("Command Failed: {}", tag); self.send_csw(tag, 0x01, 0).await; // 0x01 = Command Failed } diff --git a/kernel/src/ui.rs b/kernel/src/ui.rs index 22ac827..51ec2a1 100644 --- a/kernel/src/ui.rs +++ b/kernel/src/ui.rs @@ -1,35 +1,12 @@ +use core::sync::atomic::Ordering; + use crate::{ - BINARY_CH, TASK_STATE, TaskState, - display::{FRAMEBUFFER, SCREEN_HEIGHT, SCREEN_WIDTH}, - elf::load_binary, - format, - peripherals::keyboard, - storage::FileName, + BINARY_CH, display::FRAMEBUFFER, elf::load_binary, peripherals::keyboard, storage::FileName, + usb::USB_ACTIVE, }; -use alloc::{string::String, vec::Vec}; -use core::{fmt::Debug, str::FromStr, sync::atomic::Ordering}; -use embassy_sync::{ - blocking_mutex::raw::{CriticalSectionRawMutex, ThreadModeRawMutex}, - mutex::Mutex, -}; -use embedded_graphics::{ - Drawable, - mono_font::{ - MonoTextStyle, - ascii::{self, FONT_9X15}, - }, - pixelcolor::Rgb565, - prelude::{Dimensions, Point, RgbColor, Size}, - primitives::Rectangle, - text::Text, -}; -use embedded_layout::{ - align::{horizontal, vertical}, - layout::linear::LinearLayout, - object_chain::Chain, - prelude::*, -}; -use embedded_text::TextBox; +use alloc::vec::Vec; +use embassy_sync::{blocking_mutex::raw::CriticalSectionRawMutex, mutex::Mutex}; +use embedded_graphics::mono_font::ascii; use kolibri_embedded_gui::{label::Label, style::medsize_rgb565_style, ui::Ui}; use shared::keyboard::{KeyCode, KeyState}; @@ -63,7 +40,9 @@ pub async fn ui_handler() { } } - draw_selection().await; + if SELECTIONS.lock().await.changed { + draw_selection().await; + } } } @@ -83,12 +62,16 @@ async fn draw_selection() { ui.add(Label::new(&file.long_name).with_font(ascii::FONT_10X20)); } } + + let mut sel = SELECTIONS.lock().await; + sel.changed = false; } #[derive(Clone)] pub struct SelectionList { current_selection: u16, - pub selections: Vec, + selections: Vec, + changed: bool, } impl SelectionList { @@ -96,11 +79,22 @@ impl SelectionList { Self { selections: Vec::new(), current_selection: 0, + changed: false, } } + pub fn update_selections(&mut self, selections: Vec) { + self.selections = selections; + self.changed = true; + } + + pub fn selections(&self) -> &Vec { + &self.selections + } + pub fn reset(&mut self) { - self.current_selection = 1 + self.current_selection = 1; + self.changed = true; } fn down(&mut self) { diff --git a/kernel/src/usb.rs b/kernel/src/usb.rs index b61766a..56e31b8 100644 --- a/kernel/src/usb.rs +++ b/kernel/src/usb.rs @@ -1,7 +1,10 @@ -use crate::scsi::MassStorageClass; -use embassy_futures::join::join; +use crate::{scsi::MassStorageClass, storage::SdCard}; +use core::sync::atomic::{AtomicBool, Ordering}; +use embassy_futures::{join::join, select::select}; use embassy_rp::{peripherals::USB, usb::Driver}; -use embassy_usb::{Builder, Config}; +use embassy_usb::{Builder, Config, UsbDevice}; + +pub static USB_ACTIVE: AtomicBool = AtomicBool::new(false); #[embassy_executor::task] pub async fn usb_handler(driver: Driver<'static, USB>) { @@ -25,8 +28,18 @@ pub async fn usb_handler(driver: Driver<'static, USB>) { &mut control_buf, ); - let mut scsi = MassStorageClass::new(&mut builder); - let mut usb = builder.build(); + let temp_sd: Option = None; + let mut scsi = MassStorageClass::new(&mut builder, temp_sd); + let usb = builder.build(); - join(usb.run(), scsi.poll()).await; + 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; + USB_ACTIVE.store(false, Ordering::Release); + } }