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",
]
[[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]]
name = "bit-set"
version = "0.5.3"
@@ -818,17 +803,14 @@ dependencies = [
[[package]]
name = "embedded-sdmmc"
version = "0.8.0"
source = "git+https://github.com/Be-ing/embedded-sdmmc-rs?branch=bisync#835b2e4f9d3482b6287f674d7ecf6ae5d0618c18"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce3c7f9ea039eeafc4a49597b7bd5ae3a1c8e51b2803a381cb0f29ce90fe1ec6"
dependencies = [
"bisync",
"byteorder",
"defmt 0.3.100",
"embassy-futures",
"embedded-hal 1.0.0",
"embedded-hal-async",
"embedded-io",
"embedded-io-async",
"heapless",
]

View File

@@ -71,7 +71,7 @@ defmt = { version = "0.3", optional = true }
defmt-rtt = "0.4.2"
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" }
static_cell = "2.1.1"

View File

@@ -27,7 +27,7 @@ use embassy_sync::mutex::Mutex;
use embassy_time::{Delay, Timer};
use embedded_graphics::primitives::Rectangle;
use embedded_hal_bus::spi::ExclusiveDevice;
use embedded_sdmmc::asynchronous::SdCard as SdmmcSdCard;
use embedded_sdmmc::SdCard as SdmmcSdCard;
use heapless::String;
mod peripherals;
@@ -66,25 +66,18 @@ async fn main(_spawner: Spawner) {
let sdcard = {
let mut config = spi::Config::default();
config.frequency = 400_000;
let mut spi = Spi::new(
let spi = Spi::new_blocking(
p.SPI0,
p.PIN_18, // clk
p.PIN_19, // mosi
p.PIN_16, // miso
p.DMA_CH2,
p.DMA_CH3,
config.clone(),
);
let cs = Output::new(p.PIN_17, Level::High);
let det = Input::new(p.PIN_22, Pull::None);
let device = ExclusiveDevice::new(spi, cs, Delay).unwrap();
Timer::after_millis(500).await;
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;
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::types::StringIndex;
use embassy_usb::{Builder, Config};
use embedded_sdmmc::{Block, BlockIdx};
use heapless::Vec;
mod scsi_types;
@@ -13,13 +14,14 @@ pub struct MassStorageClass<'d, D: Driver<'d>> {
sdcard: SdCard,
bulk_out: D::EndpointOut,
bulk_in: D::EndpointIn,
last_sense: Option<ScsiError>,
}
impl<'d, D: Driver<'d>> MassStorageClass<'d, D> {
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 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_in = alt.endpoint_bulk_in(64);
@@ -28,6 +30,7 @@ impl<'d, D: Driver<'d>> MassStorageClass<'d, D> {
bulk_out,
bulk_in,
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<(), ()> {
let mut response: Vec<u8, 64> = Vec::new();
let mut block = [Block::new(); 1];
match parse_cb(cbw) {
ScsiCommand::Unknown => {
#[cfg(feature = "defmt")]
@@ -61,26 +67,17 @@ impl<'d, D: Driver<'d>> MassStorageClass<'d, D> {
page_code,
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 {
response.push(0x00).unwrap(); // Direct-access block device
response.push(0x80).unwrap(); // Removable
response.push(0x05).unwrap(); // SPC-3 compliance
response.push(0x02).unwrap(); // Response data format
response.push(0).unwrap(); // Additional length
response.push(0x00).map_err(|_| ())?; // Direct-access block device
response.push(0x80).map_err(|_| ())?; // Removable
response.push(0x05).map_err(|_| ())?; // SPC-3 compliance
response.push(0x02).map_err(|_| ())?; // Response data format
response.push(0).map_err(|_| ())?; // Additional length
// Vendor ID (8 bytes)
response.extend_from_slice(b"RUSTUSB ").unwrap();
response.extend_from_slice(b"LEGTCMPR")?;
// 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
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_bytes = rev_str.as_bytes();
response
.extend_from_slice(&[
*rev_bytes.get(0).unwrap_or(&b'0'),
*rev_bytes.get(1).unwrap_or(&b'0'),
*rev_bytes.get(2).unwrap_or(&b'0'),
*rev_bytes.get(3).unwrap_or(&b'0'),
])
.unwrap();
response.extend_from_slice(&[
*rev_bytes.get(0).unwrap_or(&b'0'),
*rev_bytes.get(1).unwrap_or(&b'0'),
*rev_bytes.get(2).unwrap_or(&b'0'),
*rev_bytes.get(3).unwrap_or(&b'0'),
])?;
// Now fix up the Additional Length
let addl_len = response.len() - 5;
response[4] = addl_len as u8;
} else {
match page_code {
0x00 => {
response
.extend_from_slice(&[0x00, 0x00, 0x00, 0x03, 0x00, 0x80, 0x83])
.unwrap();
.extend_from_slice(&[
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 => {
let serial = b"RUST1234";
let mut data: Vec<u8, 64> = Vec::new();
data.extend_from_slice(&[0x00, 0x80, 0x00, serial.len() as u8])
.unwrap();
data.extend_from_slice(serial).unwrap();
let serial = b"Pico Calc";
response.extend_from_slice(&[
0x00, // Peripheral Qualifier & Device Type
0x80, // Page Code = 0x80 (Unit Serial Number)
0x00, // Reserved
serial.len() as u8,
])?;
response.extend_from_slice(serial)?;
}
0x83 => {
let id = b"RUSTVOL1";
let mut data: Vec<u8, 64> = Vec::new();
data.extend_from_slice(&[
let id = b"SdCard";
response.extend_from_slice(&[
0x00,
0x83, // Page code
0x00,
@@ -126,9 +129,8 @@ impl<'d, D: Driver<'d>> MassStorageClass<'d, D> {
0x01, // Identifier type
0x00, // Reserved
id.len() as u8,
])
.unwrap();
data.extend_from_slice(id).unwrap();
])?;
response.extend_from_slice(id)?;
}
_ => (),
}
@@ -144,38 +146,87 @@ impl<'d, D: Driver<'d>> MassStorageClass<'d, D> {
Err(())
}
}
ScsiCommand::RequestSense { desc, alloc_len } => todo!(),
ScsiCommand::RequestSense { desc, alloc_len } => Ok(()),
ScsiCommand::ModeSense6 {
dbd,
page_control,
page_code,
subpage_code,
alloc_len,
} => todo!(),
} => Ok(()),
ScsiCommand::ModeSense10 {
dbd,
page_control,
page_code,
subpage_code,
alloc_len,
} => todo!(),
} => Ok(()),
ScsiCommand::ReadCapacity10 => {
const block_size: u64 = 512;
let block_size = SdCard::BLOCK_SIZE as u64;
let total_blocks = self.sdcard.size() / block_size;
defmt::info!("total size: {}", self.sdcard.size());
let last_lba = total_blocks - 1;
let mut resp = [0u8; 8];
resp[0..4].copy_from_slice(&(last_lba as u32).to_be_bytes());
resp[4..8].copy_from_slice(&(block_size as u32).to_be_bytes());
response.extend_from_slice(&(last_lba as u32).to_be_bytes())?;
response.extend_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 {
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 {
return None; // invalid signature
}
Some(Self {
dCBWSignature,
dCBWTag: u32::from_le_bytes(buf[4..8].try_into().unwrap()),
dCBWDataTransferLength: u32::from_le_bytes(buf[8..12].try_into().unwrap()),
dCBWTag: u32::from_le_bytes(buf[4..8].try_into().ok()?),
dCBWDataTransferLength: u32::from_le_bytes(buf[8..12].try_into().ok()?),
bmCBWFlags: buf[12],
bCBWLUN: buf[13],
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;
#[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
/// SCSI device subclass code

View File

@@ -1,19 +1,19 @@
use embassy_futures::block_on;
use embassy_rp::gpio::{Input, Output};
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_sdmmc;
use embedded_sdmmc::asynchronous::{
BlockCount, BlockDevice, Directory, SdCard as SdmmcSdCard, Volume, VolumeIdx, VolumeManager,
use embedded_sdmmc::{self, Block, BlockIdx};
use embedded_sdmmc::{
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_FILES: usize = 5;
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 VolMgr = VolumeManager<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 {
pub const BLOCK_SIZE: u16 = 512;
pub fn new(sdcard: SD, det: Input<'static>) -> Self {
block_on(sdcard.get_card_type()).unwrap();
defmt::info!(
"Card size is {} bytes",
block_on(sdcard.num_bytes()).unwrap()
);
sdcard.get_card_type().unwrap();
defmt::info!("Card size is {} bytes", sdcard.num_bytes().unwrap());
let volume_mgr = VolumeManager::<_, _, MAX_DIRS, MAX_FILES, MAX_VOLUMES>::new_with_limits(
sdcard,
DummyTimeSource {},
@@ -55,13 +54,9 @@ impl SdCard {
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() {
return Ok(self
.volume_mgr
.open_volume(VolumeIdx(0))
.await
.map_err(|_| ())?);
return Ok(self.volume_mgr.open_volume(VolumeIdx(0)).map_err(|_| ())?);
}
Err(())
}
@@ -70,21 +65,35 @@ impl SdCard {
let mut result = 0;
self.volume_mgr.device(|sd| {
result = block_on(sd.num_bytes()).unwrap_or(0);
result = sd.num_bytes().unwrap_or(0);
DummyTimeSource {}
});
result
}
pub fn blocks(&self) -> u32 {
pub fn num_blocks(&self) -> u32 {
let mut result = 0;
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 {}
});
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 {}
});
}
}