Read volume label from SD

This commit is contained in:
Frederik Menke 2024-07-06 12:35:49 +02:00
parent 07a57d9b1d
commit 02b4c12d5d
3 changed files with 115 additions and 14 deletions

1
Cargo.lock generated
View file

@ -490,6 +490,7 @@ name = "embassy-rp-skeleton"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"byte-slice-cast 1.2.2", "byte-slice-cast 1.2.2",
"byteorder",
"cortex-m", "cortex-m",
"cortex-m-rt", "cortex-m-rt",
"critical-section", "critical-section",

View file

@ -49,5 +49,9 @@ pio = "0.2.1"
rand = { version = "0.8.5", default-features = false } rand = { version = "0.8.5", default-features = false }
embedded-sdmmc = "0.7.0" embedded-sdmmc = "0.7.0"
# Dependencies of embedded-sdmmc that I need to use directly
byteorder = {version = "1", default-features = false}
[profile.release] [profile.release]
debug = 2 debug = 2

View file

@ -6,6 +6,7 @@
#![no_std] #![no_std]
#![no_main] #![no_main]
use byteorder::ByteOrder;
use defmt::*; use defmt::*;
use embassy_embedded_hal::SetConfig; use embassy_embedded_hal::SetConfig;
use embassy_executor::Spawner; use embassy_executor::Spawner;
@ -13,6 +14,7 @@ use embassy_rp::spi::Spi;
use embassy_rp::{gpio, spi}; use embassy_rp::{gpio, spi};
use embedded_hal_bus::spi::ExclusiveDevice; use embedded_hal_bus::spi::ExclusiveDevice;
use embedded_sdmmc::sdcard::{DummyCsPin, SdCard}; use embedded_sdmmc::sdcard::{DummyCsPin, SdCard};
use embedded_sdmmc::{Block, BlockCount, BlockDevice, BlockIdx, FatVolume, VolumeIdx, VolumeType};
use gpio::{Level, Output}; use gpio::{Level, Output};
use {defmt_rtt as _, panic_probe as _}; use {defmt_rtt as _, panic_probe as _};
@ -52,21 +54,27 @@ async fn main(_spawner: Spawner) {
let mut config = spi::Config::default(); let mut config = spi::Config::default();
config.frequency = 16_000_000; config.frequency = 16_000_000;
sdcard.spi(|dev| dev.bus_mut().set_config(&config)).ok(); sdcard.spi(|dev| dev.bus_mut().set_config(&config)).ok();
info!("here");
// Now let's look for volumes (also known as partitions) on our block device. // Now let's look for volumes (also known as partitions) on our block device.
// To do this we need a Volume Manager. It will take ownership of the block device. // To do this we need a Volume Manager. It will take ownership of the block device.
let mut volume_mgr = embedded_sdmmc::VolumeManager::new(sdcard, DummyTimesource()); let mut volume_mgr = embedded_sdmmc::VolumeManager::new(sdcard, DummyTimesource());
info!("here"); {
let device = volume_mgr.device();
let info = read_volume_info(device, VolumeIdx(0)).unwrap();
info!("Volume 0: name: {:?}", defmt::Debug2Format(&info));
}
// Try and access Volume 0 (i.e. the first partition). // Try and access Volume 0 (i.e. the first partition).
// The volume object holds information about the filesystem on that volume. // The volume object holds information about the filesystem on that volume.
let mut volume0 = volume_mgr.open_volume(embedded_sdmmc::VolumeIdx(0)).unwrap(); let mut volume0 = volume_mgr
.open_volume(embedded_sdmmc::VolumeIdx(0))
.unwrap();
info!("Volume 0: {:?}", defmt::Debug2Format(&volume0)); info!("Volume 0: {:?}", defmt::Debug2Format(&volume0));
// Open the root directory (mutably borrows from the volume). // Open the root directory (mutably borrows from the volume).
let mut root_dir = volume0.open_root_dir().unwrap(); let mut root_dir = volume0.open_root_dir().unwrap();
{
// Open a file called "MY_FILE.TXT" in the root directory // Open a file called "MY_FILE.TXT" in the root directory
// This mutably borrows the directory. // This mutably borrows the directory.
let mut my_file = root_dir let mut my_file = root_dir
@ -80,7 +88,95 @@ async fn main(_spawner: Spawner) {
info!("{:a}", buf[..n]); info!("{:a}", buf[..n]);
} }
} }
}
{
let mut my_file = root_dir
.open_file_in_dir("MY_FILE.TXT", embedded_sdmmc::Mode::ReadWriteAppend)
.unwrap();
loop {} my_file.write(b"Another one\n").unwrap();
} }
info!("Program end")
}
pub fn read_volume_info<D>(
block_device: &mut D,
volume_idx: VolumeIdx,
) -> Result<FatVolume, embedded_sdmmc::Error<D::Error>>
where
D: BlockDevice,
{
use byteorder::LittleEndian;
use embedded_sdmmc::Error;
const PARTITION1_START: usize = 446;
const PARTITION2_START: usize = PARTITION1_START + PARTITION_INFO_LENGTH;
const PARTITION3_START: usize = PARTITION2_START + PARTITION_INFO_LENGTH;
const PARTITION4_START: usize = PARTITION3_START + PARTITION_INFO_LENGTH;
const FOOTER_START: usize = 510;
const FOOTER_VALUE: u16 = 0xAA55;
const PARTITION_INFO_LENGTH: usize = 16;
const PARTITION_INFO_STATUS_INDEX: usize = 0;
const PARTITION_INFO_TYPE_INDEX: usize = 4;
const PARTITION_INFO_LBA_START_INDEX: usize = 8;
const PARTITION_INFO_NUM_BLOCKS_INDEX: usize = 12;
/// Marker for a FAT32 partition. Sometimes also use for FAT16 formatted
/// partitions.
const PARTITION_ID_FAT32_LBA: u8 = 0x0C;
/// Marker for a FAT16 partition with LBA. Seen on a Raspberry Pi SD card.
const PARTITION_ID_FAT16_LBA: u8 = 0x0E;
/// Marker for a FAT16 partition. Seen on a card formatted with the official
/// SD-Card formatter.
const PARTITION_ID_FAT16: u8 = 0x06;
/// Marker for a FAT32 partition. What Macosx disk utility (and also SD-Card formatter?)
/// use.
const PARTITION_ID_FAT32_CHS_LBA: u8 = 0x0B;
let (part_type, lba_start, num_blocks) = {
let mut blocks = [Block::new()];
block_device
.read(&mut blocks, BlockIdx(0), "read_mbr")
.map_err(Error::DeviceError)?;
let block = &blocks[0];
// We only support Master Boot Record (MBR) partitioned cards, not
// GUID Partition Table (GPT)
if LittleEndian::read_u16(&block[FOOTER_START..FOOTER_START + 2]) != FOOTER_VALUE {
return Err(Error::FormatError("Invalid MBR signature"));
}
let partition = match volume_idx {
VolumeIdx(0) => &block[PARTITION1_START..(PARTITION1_START + PARTITION_INFO_LENGTH)],
VolumeIdx(1) => &block[PARTITION2_START..(PARTITION2_START + PARTITION_INFO_LENGTH)],
VolumeIdx(2) => &block[PARTITION3_START..(PARTITION3_START + PARTITION_INFO_LENGTH)],
VolumeIdx(3) => &block[PARTITION4_START..(PARTITION4_START + PARTITION_INFO_LENGTH)],
_ => {
return Err(Error::NoSuchVolume);
}
};
// Only 0x80 and 0x00 are valid (bootable, and non-bootable)
if (partition[PARTITION_INFO_STATUS_INDEX] & 0x7F) != 0x00 {
return Err(Error::FormatError("Invalid partition status"));
}
let lba_start = LittleEndian::read_u32(
&partition[PARTITION_INFO_LBA_START_INDEX..(PARTITION_INFO_LBA_START_INDEX + 4)],
);
let num_blocks = LittleEndian::read_u32(
&partition[PARTITION_INFO_NUM_BLOCKS_INDEX..(PARTITION_INFO_NUM_BLOCKS_INDEX + 4)],
);
(
partition[PARTITION_INFO_TYPE_INDEX],
BlockIdx(lba_start),
BlockCount(num_blocks),
)
};
match part_type {
PARTITION_ID_FAT32_CHS_LBA
| PARTITION_ID_FAT32_LBA
| PARTITION_ID_FAT16_LBA
| PARTITION_ID_FAT16 => {
let volume = embedded_sdmmc::fat::parse_volume(block_device, lba_start, num_blocks)?;
let VolumeType::Fat(fat) = volume;
Ok(fat)
}
_ => Err(Error::FormatError("Partition type not supported")),
}
}