can mount but not access

This commit is contained in:
2025-07-28 12:57:50 -06:00
parent a9e1120247
commit 60ed910a88
6 changed files with 151 additions and 108 deletions

24
Cargo.lock generated
View File

@@ -87,21 +87,6 @@ dependencies = [
"rustc_version", "rustc_version",
] ]
[[package]]
name = "bisync"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5020822f6d6f23196ccaf55e228db36f9de1cf788052b37992e17cbc96ec41a7"
dependencies = [
"bisync_macros",
]
[[package]]
name = "bisync_macros"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d21f40d350a700f6aa107e45fb26448cf489d34794b2ba4522181dc9f1173af6"
[[package]] [[package]]
name = "bit-set" name = "bit-set"
version = "0.5.3" version = "0.5.3"
@@ -818,17 +803,14 @@ dependencies = [
[[package]] [[package]]
name = "embedded-sdmmc" name = "embedded-sdmmc"
version = "0.8.0" version = "0.9.0"
source = "git+https://github.com/Be-ing/embedded-sdmmc-rs?branch=bisync#835b2e4f9d3482b6287f674d7ecf6ae5d0618c18" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce3c7f9ea039eeafc4a49597b7bd5ae3a1c8e51b2803a381cb0f29ce90fe1ec6"
dependencies = [ dependencies = [
"bisync",
"byteorder", "byteorder",
"defmt 0.3.100", "defmt 0.3.100",
"embassy-futures",
"embedded-hal 1.0.0", "embedded-hal 1.0.0",
"embedded-hal-async",
"embedded-io", "embedded-io",
"embedded-io-async",
"heapless", "heapless",
] ]

View File

@@ -71,7 +71,7 @@ defmt = { version = "0.3", optional = true }
defmt-rtt = "0.4.2" defmt-rtt = "0.4.2"
embedded-graphics = { version = "0.8.1" } embedded-graphics = { version = "0.8.1" }
embedded-sdmmc = { git = "https://github.com/Be-ing/embedded-sdmmc-rs", branch = "bisync", default-features = false } embedded-sdmmc = { version = "0.9", default-features = false }
st7365p-lcd = { git = "https://github.com/legitcamper/st7365p-lcd-rs", branch = "async" } st7365p-lcd = { git = "https://github.com/legitcamper/st7365p-lcd-rs", branch = "async" }
static_cell = "2.1.1" static_cell = "2.1.1"

View File

@@ -27,7 +27,7 @@ use embassy_sync::mutex::Mutex;
use embassy_time::{Delay, Timer}; use embassy_time::{Delay, Timer};
use embedded_graphics::primitives::Rectangle; use embedded_graphics::primitives::Rectangle;
use embedded_hal_bus::spi::ExclusiveDevice; use embedded_hal_bus::spi::ExclusiveDevice;
use embedded_sdmmc::asynchronous::SdCard as SdmmcSdCard; use embedded_sdmmc::SdCard as SdmmcSdCard;
use heapless::String; use heapless::String;
mod peripherals; mod peripherals;
@@ -66,25 +66,18 @@ async fn main(_spawner: Spawner) {
let sdcard = { let sdcard = {
let mut config = spi::Config::default(); let mut config = spi::Config::default();
config.frequency = 400_000; config.frequency = 400_000;
let mut spi = Spi::new( let spi = Spi::new_blocking(
p.SPI0, p.SPI0,
p.PIN_18, // clk p.PIN_18, // clk
p.PIN_19, // mosi p.PIN_19, // mosi
p.PIN_16, // miso p.PIN_16, // miso
p.DMA_CH2,
p.DMA_CH3,
config.clone(), config.clone(),
); );
let cs = Output::new(p.PIN_17, Level::High); let cs = Output::new(p.PIN_17, Level::High);
let det = Input::new(p.PIN_22, Pull::None); let det = Input::new(p.PIN_22, Pull::None);
let device = ExclusiveDevice::new(spi, cs, Delay).unwrap(); let device = ExclusiveDevice::new(spi, cs, Delay).unwrap();
Timer::after_millis(500).await;
let sdcard = SdmmcSdCard::new(device, Delay); let sdcard = SdmmcSdCard::new(device, Delay);
while sdcard.num_bytes().await.is_err() {
Timer::after_millis(250).await;
defmt::error!("Sd init failed, trying again");
}
config.frequency = 32_000_000; config.frequency = 32_000_000;
sdcard.spi(|dev| dev.bus_mut().set_config(&config)); sdcard.spi(|dev| dev.bus_mut().set_config(&config));

View File

@@ -2,6 +2,7 @@ use crate::format;
use embassy_usb::driver::{Driver, EndpointIn, EndpointOut}; use embassy_usb::driver::{Driver, EndpointIn, EndpointOut};
use embassy_usb::types::StringIndex; use embassy_usb::types::StringIndex;
use embassy_usb::{Builder, Config}; use embassy_usb::{Builder, Config};
use embedded_sdmmc::{Block, BlockIdx};
use heapless::Vec; use heapless::Vec;
mod scsi_types; mod scsi_types;
@@ -13,13 +14,14 @@ pub struct MassStorageClass<'d, D: Driver<'d>> {
sdcard: SdCard, sdcard: SdCard,
bulk_out: D::EndpointOut, bulk_out: D::EndpointOut,
bulk_in: D::EndpointIn, bulk_in: D::EndpointIn,
last_sense: Option<ScsiError>,
} }
impl<'d, D: Driver<'d>> MassStorageClass<'d, D> { impl<'d, D: Driver<'d>> MassStorageClass<'d, D> {
pub fn new(builder: &mut Builder<'d, D>, sdcard: SdCard) -> Self { pub fn new(builder: &mut Builder<'d, D>, sdcard: SdCard) -> Self {
let mut function = builder.function(0x08, 0x06, 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, 0x06, 0x50, None); let mut alt = interface.alt_setting(0x08, SUBCLASS_SCSI, 0x50, None);
let bulk_out = alt.endpoint_bulk_out(64); let bulk_out = alt.endpoint_bulk_out(64);
let bulk_in = alt.endpoint_bulk_in(64); let bulk_in = alt.endpoint_bulk_in(64);
@@ -28,6 +30,7 @@ impl<'d, D: Driver<'d>> MassStorageClass<'d, D> {
bulk_out, bulk_out,
bulk_in, bulk_in,
sdcard, sdcard,
last_sense: None,
} }
} }
@@ -50,6 +53,9 @@ impl<'d, D: Driver<'d>> MassStorageClass<'d, D> {
} }
async fn handle_command(&mut self, cbw: &[u8]) -> Result<(), ()> { async fn handle_command(&mut self, cbw: &[u8]) -> Result<(), ()> {
let mut response: Vec<u8, 64> = Vec::new();
let mut block = [Block::new(); 1];
match parse_cb(cbw) { match parse_cb(cbw) {
ScsiCommand::Unknown => { ScsiCommand::Unknown => {
#[cfg(feature = "defmt")] #[cfg(feature = "defmt")]
@@ -61,26 +67,17 @@ impl<'d, D: Driver<'d>> MassStorageClass<'d, D> {
page_code, page_code,
alloc_len, alloc_len,
} => { } => {
#[cfg(feature = "defmt")]
defmt::info!(
"SCSI INQUIRY: evpd={}, page_code=0x{:02x}, alloc_len={}",
evpd,
page_code,
alloc_len
);
let mut response: Vec<u8, 64> = Vec::new();
if !evpd { if !evpd {
response.push(0x00).unwrap(); // Direct-access block device response.push(0x00).map_err(|_| ())?; // Direct-access block device
response.push(0x80).unwrap(); // Removable response.push(0x80).map_err(|_| ())?; // Removable
response.push(0x05).unwrap(); // SPC-3 compliance response.push(0x05).map_err(|_| ())?; // SPC-3 compliance
response.push(0x02).unwrap(); // Response data format response.push(0x02).map_err(|_| ())?; // Response data format
response.push(0).unwrap(); // Additional length response.push(0).map_err(|_| ())?; // Additional length
// Vendor ID (8 bytes) // Vendor ID (8 bytes)
response.extend_from_slice(b"RUSTUSB ").unwrap(); response.extend_from_slice(b"LEGTCMPR")?;
// Product ID (16 bytes) // Product ID (16 bytes)
response.extend_from_slice(b"Mass Storage ").unwrap(); response.extend_from_slice(b"Pico Calc Sdcard")?;
// Product Revision (4 bytes): encode volume size in GB // Product Revision (4 bytes): encode volume size in GB
let size_bytes = self.sdcard.size(); let size_bytes = self.sdcard.size();
@@ -88,36 +85,42 @@ impl<'d, D: Driver<'d>> MassStorageClass<'d, D> {
let rev_str = format!(4, "{}", size_gb); let rev_str = format!(4, "{}", size_gb);
let rev_bytes = rev_str.as_bytes(); let rev_bytes = rev_str.as_bytes();
response response.extend_from_slice(&[
.extend_from_slice(&[ *rev_bytes.get(0).unwrap_or(&b'0'),
*rev_bytes.get(0).unwrap_or(&b'0'), *rev_bytes.get(1).unwrap_or(&b'0'),
*rev_bytes.get(1).unwrap_or(&b'0'), *rev_bytes.get(2).unwrap_or(&b'0'),
*rev_bytes.get(2).unwrap_or(&b'0'), *rev_bytes.get(3).unwrap_or(&b'0'),
*rev_bytes.get(3).unwrap_or(&b'0'), ])?;
])
.unwrap();
// Now fix up the Additional Length
let addl_len = response.len() - 5; let addl_len = response.len() - 5;
response[4] = addl_len as u8; response[4] = addl_len as u8;
} else { } else {
match page_code { match page_code {
0x00 => { 0x00 => {
response response
.extend_from_slice(&[0x00, 0x00, 0x00, 0x03, 0x00, 0x80, 0x83]) .extend_from_slice(&[
.unwrap(); 0x00, // Peripheral Qualifier + Peripheral Device Type (0x00 = Direct-access block device)
0x00, // Page Code (same as requested: 0x00)
0x00, 0x03, // Page Length: 3 bytes follow
0x00, // Supported VPD Page: 0x00 (this one — the "Supported VPD Pages" page itself)
0x80, // Supported VPD Page: 0x80 (Unit Serial Number)
0x83, // Supported VPD Page: 0x83 (Device Identification)
])
.map_err(|_| ())?
} }
0x80 => { 0x80 => {
let serial = b"RUST1234"; let serial = b"Pico Calc";
let mut data: Vec<u8, 64> = Vec::new(); response.extend_from_slice(&[
data.extend_from_slice(&[0x00, 0x80, 0x00, serial.len() as u8]) 0x00, // Peripheral Qualifier & Device Type
.unwrap(); 0x80, // Page Code = 0x80 (Unit Serial Number)
data.extend_from_slice(serial).unwrap(); 0x00, // Reserved
serial.len() as u8,
])?;
response.extend_from_slice(serial)?;
} }
0x83 => { 0x83 => {
let id = b"RUSTVOL1"; let id = b"SdCard";
let mut data: Vec<u8, 64> = Vec::new(); response.extend_from_slice(&[
data.extend_from_slice(&[
0x00, 0x00,
0x83, // Page code 0x83, // Page code
0x00, 0x00,
@@ -126,9 +129,8 @@ impl<'d, D: Driver<'d>> MassStorageClass<'d, D> {
0x01, // Identifier type 0x01, // Identifier type
0x00, // Reserved 0x00, // Reserved
id.len() as u8, id.len() as u8,
]) ])?;
.unwrap(); response.extend_from_slice(id)?;
data.extend_from_slice(id).unwrap();
} }
_ => (), _ => (),
} }
@@ -144,38 +146,87 @@ impl<'d, D: Driver<'d>> MassStorageClass<'d, D> {
Err(()) Err(())
} }
} }
ScsiCommand::RequestSense { desc, alloc_len } => todo!(), ScsiCommand::RequestSense { desc, alloc_len } => Ok(()),
ScsiCommand::ModeSense6 { ScsiCommand::ModeSense6 {
dbd, dbd,
page_control, page_control,
page_code, page_code,
subpage_code, subpage_code,
alloc_len, alloc_len,
} => todo!(), } => Ok(()),
ScsiCommand::ModeSense10 { ScsiCommand::ModeSense10 {
dbd, dbd,
page_control, page_control,
page_code, page_code,
subpage_code, subpage_code,
alloc_len, alloc_len,
} => todo!(), } => Ok(()),
ScsiCommand::ReadCapacity10 => { ScsiCommand::ReadCapacity10 => {
const block_size: u64 = 512; let block_size = SdCard::BLOCK_SIZE as u64;
let total_blocks = self.sdcard.size() / block_size; let total_blocks = self.sdcard.size() / block_size;
defmt::info!("total size: {}", self.sdcard.size());
let last_lba = total_blocks - 1; let last_lba = total_blocks - 1;
let mut resp = [0u8; 8]; response.extend_from_slice(&(last_lba as u32).to_be_bytes())?;
resp[0..4].copy_from_slice(&(last_lba as u32).to_be_bytes()); response.extend_from_slice(&(block_size as u32).to_be_bytes())?;
resp[4..8].copy_from_slice(&(block_size as u32).to_be_bytes());
self.bulk_in.write(&resp).await.map_err(|_| ()) self.bulk_in.write(&response).await.map_err(|_| ())
}
ScsiCommand::ReadCapacity16 { alloc_len } => {
let block_size = SdCard::BLOCK_SIZE as u64;
let total_blocks = self.sdcard.size() / block_size;
let last_lba = total_blocks.checked_sub(1).unwrap_or(0);
response.extend_from_slice(&last_lba.to_be_bytes()); // 8 bytes last LBA
response.extend_from_slice(&(block_size as u32).to_be_bytes()); // 4 bytes block length
response.extend_from_slice(&[0u8; 20]); // 20 reserved bytes zeroed
let len = alloc_len.min(response.len() as u32) as usize;
self.bulk_in.write(&response[..len]).await.map_err(|_| ())
}
ScsiCommand::Read { lba, len } => {
for i in 0..len {
let block_idx = BlockIdx(lba as u32 + i as u32);
self.sdcard.read_blocks(&mut block, block_idx);
self.bulk_in
.write(&block[0].contents)
.await
.map_err(|_| ())?;
}
Ok(())
}
ScsiCommand::Write { lba, len } => {
for i in 0..len {
let block_idx = BlockIdx(lba as u32 + i as u32);
self.bulk_out
.read(&mut block[0].contents)
.await
.map_err(|_| ())?;
self.sdcard.write_blocks(&mut block, block_idx);
}
Ok(())
}
ScsiCommand::ReadFormatCapacities { alloc_len } => {
let block_size = SdCard::BLOCK_SIZE as u32;
let num_blocks = (self.sdcard.size() / block_size as u64) as u32;
let mut response = [0u8; 12];
// Capacity List Length (8 bytes follows)
response[3] = 8;
// Descriptor
response[4..8].copy_from_slice(&num_blocks.to_be_bytes());
response[8] = 0x02; // formatted media
response[9..12].copy_from_slice(&block_size.to_be_bytes()[1..4]); // only 3 bytes
let response_len = alloc_len.min(response.len() as u16) as usize;
self.bulk_in
.write(&response[..response_len])
.await
.map_err(|_| ())
} }
ScsiCommand::ReadCapacity16 { alloc_len } => todo!(),
ScsiCommand::Read { lba, len } => todo!(),
ScsiCommand::Write { lba, len } => todo!(),
ScsiCommand::ReadFormatCapacities { alloc_len } => todo!(),
} }
} }
@@ -213,18 +264,18 @@ impl CommandBlockWrapper {
if buf.len() < 31 { if buf.len() < 31 {
return None; return None;
} }
let dCBWSignature = u32::from_le_bytes(buf[0..4].try_into().unwrap()); let dCBWSignature = u32::from_le_bytes(buf[0..4].try_into().ok()?);
if dCBWSignature != 0x43425355 { if dCBWSignature != 0x43425355 {
return None; // invalid signature return None; // invalid signature
} }
Some(Self { Some(Self {
dCBWSignature, dCBWSignature,
dCBWTag: u32::from_le_bytes(buf[4..8].try_into().unwrap()), dCBWTag: u32::from_le_bytes(buf[4..8].try_into().ok()?),
dCBWDataTransferLength: u32::from_le_bytes(buf[8..12].try_into().unwrap()), dCBWDataTransferLength: u32::from_le_bytes(buf[8..12].try_into().ok()?),
bmCBWFlags: buf[12], bmCBWFlags: buf[12],
bCBWLUN: buf[13], bCBWLUN: buf[13],
bCBWCBLength: buf[14], bCBWCBLength: buf[14],
CBWCB: buf[15..31].try_into().unwrap(), CBWCB: buf[15..31].try_into().ok()?,
}) })
} }
} }

View File

@@ -1,5 +1,13 @@
use num_enum::TryFromPrimitive; use num_enum::TryFromPrimitive;
#[derive(Debug, Clone, Copy)]
pub enum ScsiError {
IllegalRequest { asc: u8, ascq: u8 },
NotReady { asc: u8, ascq: u8 },
MediumError { asc: u8, ascq: u8 },
// Add others as needed
}
/// THE CODE BELOW ORIGINATES FROM: https://github.com/apohrebniak/usbd-storage/blob/master/usbd-storage/src/subclass/scsi.rs /// THE CODE BELOW ORIGINATES FROM: https://github.com/apohrebniak/usbd-storage/blob/master/usbd-storage/src/subclass/scsi.rs
/// SCSI device subclass code /// SCSI device subclass code

View File

@@ -1,19 +1,19 @@
use embassy_futures::block_on; use embassy_futures::block_on;
use embassy_rp::gpio::{Input, Output}; use embassy_rp::gpio::{Input, Output};
use embassy_rp::peripherals::SPI0; use embassy_rp::peripherals::SPI0;
use embassy_rp::spi::{Async, Spi}; use embassy_rp::spi::{Blocking, Spi};
use embedded_hal_bus::spi::ExclusiveDevice; use embedded_hal_bus::spi::ExclusiveDevice;
use embedded_sdmmc; use embedded_sdmmc::{self, Block, BlockIdx};
use embedded_sdmmc::asynchronous::{ use embedded_sdmmc::{
BlockCount, BlockDevice, Directory, SdCard as SdmmcSdCard, Volume, VolumeIdx, VolumeManager, BlockCount, BlockDevice, Directory, SdCard as SdmmcSdCard, TimeSource, Timestamp, Volume,
VolumeIdx, VolumeManager,
}; };
use embedded_sdmmc::blocking::{TimeSource, Timestamp};
pub const MAX_DIRS: usize = 4; pub const MAX_DIRS: usize = 4;
pub const MAX_FILES: usize = 5; pub const MAX_FILES: usize = 5;
pub const MAX_VOLUMES: usize = 1; pub const MAX_VOLUMES: usize = 1;
type Device = ExclusiveDevice<Spi<'static, SPI0, Async>, Output<'static>, embassy_time::Delay>; type Device = ExclusiveDevice<Spi<'static, SPI0, Blocking>, Output<'static>, embassy_time::Delay>;
type SD = SdmmcSdCard<Device, embassy_time::Delay>; type SD = SdmmcSdCard<Device, embassy_time::Delay>;
type VolMgr = VolumeManager<SD, DummyTimeSource, MAX_DIRS, MAX_FILES, MAX_VOLUMES>; type VolMgr = VolumeManager<SD, DummyTimeSource, MAX_DIRS, MAX_FILES, MAX_VOLUMES>;
type Vol<'a> = Volume<'a, SD, DummyTimeSource, MAX_DIRS, MAX_FILES, MAX_VOLUMES>; type Vol<'a> = Volume<'a, SD, DummyTimeSource, MAX_DIRS, MAX_FILES, MAX_VOLUMES>;
@@ -32,12 +32,11 @@ pub struct SdCard {
} }
impl SdCard { impl SdCard {
pub const BLOCK_SIZE: u16 = 512;
pub fn new(sdcard: SD, det: Input<'static>) -> Self { pub fn new(sdcard: SD, det: Input<'static>) -> Self {
block_on(sdcard.get_card_type()).unwrap(); sdcard.get_card_type().unwrap();
defmt::info!( defmt::info!("Card size is {} bytes", sdcard.num_bytes().unwrap());
"Card size is {} bytes",
block_on(sdcard.num_bytes()).unwrap()
);
let volume_mgr = VolumeManager::<_, _, MAX_DIRS, MAX_FILES, MAX_VOLUMES>::new_with_limits( let volume_mgr = VolumeManager::<_, _, MAX_DIRS, MAX_FILES, MAX_VOLUMES>::new_with_limits(
sdcard, sdcard,
DummyTimeSource {}, DummyTimeSource {},
@@ -55,13 +54,9 @@ impl SdCard {
self.det.is_low() self.det.is_low()
} }
pub async fn open_volume(&mut self) -> Result<Vol<'_>, ()> { pub fn open_volume(&mut self) -> Result<Vol<'_>, ()> {
if self.is_attached() { if self.is_attached() {
return Ok(self return Ok(self.volume_mgr.open_volume(VolumeIdx(0)).map_err(|_| ())?);
.volume_mgr
.open_volume(VolumeIdx(0))
.await
.map_err(|_| ())?);
} }
Err(()) Err(())
} }
@@ -70,21 +65,35 @@ impl SdCard {
let mut result = 0; let mut result = 0;
self.volume_mgr.device(|sd| { self.volume_mgr.device(|sd| {
result = block_on(sd.num_bytes()).unwrap_or(0); result = sd.num_bytes().unwrap_or(0);
DummyTimeSource {} DummyTimeSource {}
}); });
result result
} }
pub fn blocks(&self) -> u32 { pub fn num_blocks(&self) -> u32 {
let mut result = 0; let mut result = 0;
self.volume_mgr.device(|sd| { self.volume_mgr.device(|sd| {
result = block_on(sd.num_blocks()).unwrap_or(BlockCount(0)).0; result = sd.num_blocks().unwrap_or(BlockCount(0)).0;
DummyTimeSource {} DummyTimeSource {}
}); });
result result
} }
pub fn read_blocks(&self, blocks: &mut [Block], start_block_idx: BlockIdx) {
self.volume_mgr.device(|sd| {
sd.read(blocks, start_block_idx);
DummyTimeSource {}
});
}
pub fn write_blocks(&self, blocks: &mut [Block], start_block_idx: BlockIdx) {
self.volume_mgr.device(|sd| {
sd.write(blocks, start_block_idx);
DummyTimeSource {}
});
}
} }