mirror of
https://github.com/LegitCamper/picocalc-os-rs.git
synced 2025-12-27 15:55:25 +00:00
WIP scsi :(
This commit is contained in:
1
Cargo.lock
generated
1
Cargo.lock
generated
@@ -1299,6 +1299,7 @@ dependencies = [
|
|||||||
"heapless",
|
"heapless",
|
||||||
"kolibri-embedded-gui",
|
"kolibri-embedded-gui",
|
||||||
"num_enum 0.7.4",
|
"num_enum 0.7.4",
|
||||||
|
"once_cell",
|
||||||
"panic-probe",
|
"panic-probe",
|
||||||
"portable-atomic",
|
"portable-atomic",
|
||||||
"shared",
|
"shared",
|
||||||
|
|||||||
@@ -78,6 +78,7 @@ embedded-text = "0.7.2"
|
|||||||
embedded-layout = "0.4.2"
|
embedded-layout = "0.4.2"
|
||||||
kolibri-embedded-gui = "0.1.0"
|
kolibri-embedded-gui = "0.1.0"
|
||||||
|
|
||||||
|
once_cell = { version = "1.21.3", default-features = false }
|
||||||
static_cell = "2.1.1"
|
static_cell = "2.1.1"
|
||||||
bitflags = "2.9.1"
|
bitflags = "2.9.1"
|
||||||
heapless = "0.8.0"
|
heapless = "0.8.0"
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ use embassy_time::{Delay, Timer};
|
|||||||
use embedded_graphics::{
|
use embedded_graphics::{
|
||||||
draw_target::DrawTarget,
|
draw_target::DrawTarget,
|
||||||
pixelcolor::{Rgb565, RgbColor},
|
pixelcolor::{Rgb565, RgbColor},
|
||||||
prelude::Dimensions,
|
|
||||||
};
|
};
|
||||||
use embedded_hal_bus::spi::ExclusiveDevice;
|
use embedded_hal_bus::spi::ExclusiveDevice;
|
||||||
use st7365p_lcd::ST7365P;
|
use st7365p_lcd::ST7365P;
|
||||||
|
|||||||
@@ -78,12 +78,12 @@ static ALLOCATOR: Talck<spin::Mutex<()>, ClaimOnOom> =
|
|||||||
Talc::new(unsafe { ClaimOnOom::new(Span::from_array(core::ptr::addr_of!(ARENA).cast_mut())) })
|
Talc::new(unsafe { ClaimOnOom::new(Span::from_array(core::ptr::addr_of!(ARENA).cast_mut())) })
|
||||||
.lock();
|
.lock();
|
||||||
|
|
||||||
static TASK_STATE: Mutex<CriticalSectionRawMutex, TaskState> = Mutex::new(TaskState::Ui);
|
static TASK_STATE: Mutex<CriticalSectionRawMutex, TaskState> = Mutex::new(TaskState::Selection);
|
||||||
static TASK_STATE_CHANGED: Signal<CriticalSectionRawMutex, ()> = Signal::new();
|
static TASK_STATE_CHANGED: Signal<CriticalSectionRawMutex, ()> = Signal::new();
|
||||||
|
|
||||||
#[derive(Copy, Clone, PartialEq)]
|
#[derive(Copy, Clone, PartialEq)]
|
||||||
enum TaskState {
|
enum TaskState {
|
||||||
Ui,
|
Selection,
|
||||||
Kernel,
|
Kernel,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -154,7 +154,7 @@ async fn userland_task() {
|
|||||||
// enable kernel ui
|
// enable kernel ui
|
||||||
{
|
{
|
||||||
let mut state = TASK_STATE.lock().await;
|
let mut state = TASK_STATE.lock().await;
|
||||||
*state = TaskState::Ui;
|
*state = TaskState::Selection;
|
||||||
TASK_STATE_CHANGED.signal(());
|
TASK_STATE_CHANGED.signal(());
|
||||||
// clear_fb();
|
// clear_fb();
|
||||||
}
|
}
|
||||||
@@ -244,7 +244,7 @@ async fn kernel_task(
|
|||||||
spawner.spawn(key_handler()).unwrap();
|
spawner.spawn(key_handler()).unwrap();
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
if let TaskState::Ui = *TASK_STATE.lock().await {
|
if let TaskState::Selection = *TASK_STATE.lock().await {
|
||||||
let ui_fut = ui_handler();
|
let ui_fut = ui_handler();
|
||||||
let binary_search_fut = prog_search_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 files = sd.list_files_by_extension(".bin").unwrap();
|
||||||
let mut select = SELECTIONS.lock().await;
|
let mut select = SELECTIONS.lock().await;
|
||||||
|
|
||||||
if select.selections != files {
|
if *select.selections() != files {
|
||||||
select.selections = files;
|
select.update_selections(files);
|
||||||
select.reset();
|
select.reset();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,8 @@
|
|||||||
|
use core::sync::atomic::AtomicBool;
|
||||||
|
|
||||||
|
use embassy_futures::select::select;
|
||||||
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
|
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
|
||||||
|
use embassy_sync::lazy_lock::LazyLock;
|
||||||
use embassy_sync::signal::Signal;
|
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};
|
||||||
@@ -14,13 +18,22 @@ const BULK_ENDPOINT_PACKET_SIZE: usize = 64;
|
|||||||
|
|
||||||
pub static MSC_SHUTDOWN: Signal<CriticalSectionRawMutex, ()> = Signal::new();
|
pub static MSC_SHUTDOWN: Signal<CriticalSectionRawMutex, ()> = 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>> {
|
pub struct MassStorageClass<'d, D: Driver<'d>> {
|
||||||
|
temp_sd: Option<SdCard>, // temporarly owns sdcard when scsi is running
|
||||||
|
ejected: bool,
|
||||||
|
pending_eject: bool,
|
||||||
bulk_out: D::EndpointOut,
|
bulk_out: D::EndpointOut,
|
||||||
bulk_in: D::EndpointIn,
|
bulk_in: D::EndpointIn,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'d, 's, D: Driver<'d>> MassStorageClass<'d, D> {
|
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<SdCard>) -> Self {
|
||||||
let mut function = builder.function(0x08, SUBCLASS_SCSI, 0x50); // Mass Storage class
|
let mut function = builder.function(0x08, SUBCLASS_SCSI, 0x50); // Mass Storage class
|
||||||
let mut interface = function.interface();
|
let mut interface = function.interface();
|
||||||
let mut alt = interface.alt_setting(0x08, SUBCLASS_SCSI, 0x50, None);
|
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_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);
|
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) {
|
pub async fn poll(&mut self) {
|
||||||
loop {
|
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() {
|
if MSC_SHUTDOWN.signaled() {
|
||||||
defmt::info!("MSC shutting down");
|
defmt::info!("MSC shutting down");
|
||||||
return; // or break
|
|
||||||
|
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) {
|
async fn handle_cbw(&mut self) {
|
||||||
let mut cbw_buf = [0u8; 31];
|
let mut cbw_buf = [0u8; 31];
|
||||||
if let Ok(n) = self.bulk_out.read(&mut cbw_buf).await {
|
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 n == 31 {
|
||||||
if let Some(cbw) = CommandBlockWrapper::parse(&cbw_buf[..n]) {
|
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
|
self.send_csw_success(cbw.dCBWTag).await
|
||||||
} else {
|
} else {
|
||||||
self.send_csw_fail(cbw.dCBWTag).await
|
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<u8, BULK_ENDPOINT_PACKET_SIZE> = Vec::new();
|
let mut response: Vec<u8, BULK_ENDPOINT_PACKET_SIZE> = Vec::new();
|
||||||
let mut block = [Block::new(); 1];
|
|
||||||
|
|
||||||
match parse_cb(cbw) {
|
match command {
|
||||||
ScsiCommand::Unknown => {
|
ScsiCommand::Unknown => Err(()),
|
||||||
#[cfg(feature = "defmt")]
|
|
||||||
defmt::warn!("Got unexpected scsi command: {}", cbw);
|
|
||||||
Err(())
|
|
||||||
}
|
|
||||||
ScsiCommand::Inquiry {
|
ScsiCommand::Inquiry {
|
||||||
evpd,
|
evpd,
|
||||||
page_code,
|
page_code,
|
||||||
@@ -144,9 +186,7 @@ impl<'d, 's, D: Driver<'d>> MassStorageClass<'d, D> {
|
|||||||
self.bulk_in.write(&response[..len]).await.map_err(|_| ())
|
self.bulk_in.write(&response[..len]).await.map_err(|_| ())
|
||||||
}
|
}
|
||||||
ScsiCommand::TestUnitReady => {
|
ScsiCommand::TestUnitReady => {
|
||||||
let guard = SDCARD.get().lock().await;
|
if self.temp_sd.as_ref().unwrap().is_attached() {
|
||||||
let sdcard = guard.as_ref().unwrap();
|
|
||||||
if sdcard.is_attached() {
|
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
Err(())
|
Err(())
|
||||||
@@ -192,11 +232,8 @@ impl<'d, 's, D: Driver<'d>> MassStorageClass<'d, D> {
|
|||||||
self.bulk_in.write(&response[..len]).await.map_err(|_| ())
|
self.bulk_in.write(&response[..len]).await.map_err(|_| ())
|
||||||
}
|
}
|
||||||
ScsiCommand::ReadCapacity10 => {
|
ScsiCommand::ReadCapacity10 => {
|
||||||
let guard = SDCARD.get().lock().await;
|
|
||||||
let sdcard = guard.as_ref().unwrap();
|
|
||||||
|
|
||||||
let block_size = SdCard::BLOCK_SIZE as u64;
|
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);
|
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(|_| ())
|
self.bulk_in.write(&response).await.map_err(|_| ())
|
||||||
}
|
}
|
||||||
ScsiCommand::ReadCapacity16 { alloc_len } => {
|
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 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);
|
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(|_| ())
|
self.bulk_in.write(&response[..len]).await.map_err(|_| ())
|
||||||
}
|
}
|
||||||
ScsiCommand::Read { lba, len } => {
|
ScsiCommand::Read { lba, len } => {
|
||||||
let guard = SDCARD.get().lock().await;
|
let sdcard = self.temp_sd.as_ref().unwrap();
|
||||||
let sdcard = guard.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 {
|
while blocks > 0 {
|
||||||
let block_idx = BlockIdx(lba as u32 + i as u32);
|
if blocks >= block_buf.len() as u64 {
|
||||||
sdcard.read_blocks(&mut block, block_idx)?;
|
sdcard.read_blocks(block_buf, BlockIdx(idx as u32))?;
|
||||||
for chunk in block[0].contents.chunks(BULK_ENDPOINT_PACKET_SIZE.into()) {
|
|
||||||
self.bulk_in.write(chunk).await.map_err(|_| ())?;
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
ScsiCommand::Write { lba, len } => {
|
ScsiCommand::Write { lba, len } => {
|
||||||
let guard = SDCARD.get().lock().await;
|
let sdcard = self.temp_sd.as_ref().unwrap();
|
||||||
let sdcard = guard.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 {
|
while blocks > 0 {
|
||||||
let block_idx = BlockIdx(lba as u32 + i as u32);
|
if blocks >= block_buf.len() as u64 {
|
||||||
for chunk in block[0]
|
for block in block_buf.as_mut() {
|
||||||
.contents
|
for chunk in block.contents.chunks_mut(BULK_ENDPOINT_PACKET_SIZE.into())
|
||||||
.chunks_mut(BULK_ENDPOINT_PACKET_SIZE.into())
|
{
|
||||||
{
|
self.bulk_out.read(chunk).await.map_err(|_| ())?;
|
||||||
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
ScsiCommand::ReadFormatCapacities { alloc_len } => {
|
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 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];
|
let mut response = [0u8; 12];
|
||||||
|
|
||||||
@@ -274,7 +348,12 @@ impl<'d, 's, D: Driver<'d>> MassStorageClass<'d, D> {
|
|||||||
.map_err(|_| ())
|
.map_err(|_| ())
|
||||||
}
|
}
|
||||||
ScsiCommand::PreventAllowMediumRemoval { prevent: _prevent } => Ok(()),
|
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) {
|
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
|
self.send_csw(tag, 0x01, 0).await; // 0x01 = Command Failed
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,35 +1,12 @@
|
|||||||
|
use core::sync::atomic::Ordering;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
BINARY_CH, TASK_STATE, TaskState,
|
BINARY_CH, display::FRAMEBUFFER, elf::load_binary, peripherals::keyboard, storage::FileName,
|
||||||
display::{FRAMEBUFFER, SCREEN_HEIGHT, SCREEN_WIDTH},
|
usb::USB_ACTIVE,
|
||||||
elf::load_binary,
|
|
||||||
format,
|
|
||||||
peripherals::keyboard,
|
|
||||||
storage::FileName,
|
|
||||||
};
|
};
|
||||||
use alloc::{string::String, vec::Vec};
|
use alloc::vec::Vec;
|
||||||
use core::{fmt::Debug, str::FromStr, sync::atomic::Ordering};
|
use embassy_sync::{blocking_mutex::raw::CriticalSectionRawMutex, mutex::Mutex};
|
||||||
use embassy_sync::{
|
use embedded_graphics::mono_font::ascii;
|
||||||
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 kolibri_embedded_gui::{label::Label, style::medsize_rgb565_style, ui::Ui};
|
use kolibri_embedded_gui::{label::Label, style::medsize_rgb565_style, ui::Ui};
|
||||||
use shared::keyboard::{KeyCode, KeyState};
|
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));
|
ui.add(Label::new(&file.long_name).with_font(ascii::FONT_10X20));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let mut sel = SELECTIONS.lock().await;
|
||||||
|
sel.changed = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct SelectionList {
|
pub struct SelectionList {
|
||||||
current_selection: u16,
|
current_selection: u16,
|
||||||
pub selections: Vec<FileName>,
|
selections: Vec<FileName>,
|
||||||
|
changed: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SelectionList {
|
impl SelectionList {
|
||||||
@@ -96,11 +79,22 @@ impl SelectionList {
|
|||||||
Self {
|
Self {
|
||||||
selections: Vec::new(),
|
selections: Vec::new(),
|
||||||
current_selection: 0,
|
current_selection: 0,
|
||||||
|
changed: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn update_selections(&mut self, selections: Vec<FileName>) {
|
||||||
|
self.selections = selections;
|
||||||
|
self.changed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn selections(&self) -> &Vec<FileName> {
|
||||||
|
&self.selections
|
||||||
|
}
|
||||||
|
|
||||||
pub fn reset(&mut self) {
|
pub fn reset(&mut self) {
|
||||||
self.current_selection = 1
|
self.current_selection = 1;
|
||||||
|
self.changed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn down(&mut self) {
|
fn down(&mut self) {
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
use crate::scsi::MassStorageClass;
|
use crate::{scsi::MassStorageClass, storage::SdCard};
|
||||||
use embassy_futures::join::join;
|
use core::sync::atomic::{AtomicBool, Ordering};
|
||||||
|
use embassy_futures::{join::join, select::select};
|
||||||
use embassy_rp::{peripherals::USB, usb::Driver};
|
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]
|
#[embassy_executor::task]
|
||||||
pub async fn usb_handler(driver: Driver<'static, USB>) {
|
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,
|
&mut control_buf,
|
||||||
);
|
);
|
||||||
|
|
||||||
let mut scsi = MassStorageClass::new(&mut builder);
|
let temp_sd: Option<SdCard> = None;
|
||||||
let mut usb = builder.build();
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user