diff --git a/Cargo.lock b/Cargo.lock index 4b506d5..27f3376 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -490,6 +490,7 @@ name = "embassy-rp-skeleton" version = "0.1.0" dependencies = [ "byte-slice-cast 1.2.2", + "byteorder", "cortex-m", "cortex-m-rt", "critical-section", diff --git a/Cargo.toml b/Cargo.toml index ede9d4c..c83203c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -49,5 +49,9 @@ pio = "0.2.1" rand = { version = "0.8.5", default-features = false } embedded-sdmmc = "0.7.0" +# Dependencies of embedded-sdmmc that I need to use directly +byteorder = {version = "1", default-features = false} + + [profile.release] debug = 2 diff --git a/src/main.rs b/src/main.rs index 5494eba..7c0704a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,6 +6,7 @@ #![no_std] #![no_main] +use byteorder::ByteOrder; use defmt::*; use embassy_embedded_hal::SetConfig; use embassy_executor::Spawner; @@ -13,6 +14,7 @@ use embassy_rp::spi::Spi; use embassy_rp::{gpio, spi}; use embedded_hal_bus::spi::ExclusiveDevice; use embedded_sdmmc::sdcard::{DummyCsPin, SdCard}; +use embedded_sdmmc::{Block, BlockCount, BlockDevice, BlockIdx, FatVolume, VolumeIdx, VolumeType}; use gpio::{Level, Output}; use {defmt_rtt as _, panic_probe as _}; @@ -52,35 +54,129 @@ async fn main(_spawner: Spawner) { let mut config = spi::Config::default(); config.frequency = 16_000_000; 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. // 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()); - 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). // 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)); // Open the root directory (mutably borrows from the volume). let mut root_dir = volume0.open_root_dir().unwrap(); - // Open a file called "MY_FILE.TXT" in the root directory - // This mutably borrows the directory. - let mut my_file = root_dir - .open_file_in_dir("MY_FILE.TXT", embedded_sdmmc::Mode::ReadOnly) - .unwrap(); + { + // Open a file called "MY_FILE.TXT" in the root directory + // This mutably borrows the directory. + let mut my_file = root_dir + .open_file_in_dir("MY_FILE.TXT", embedded_sdmmc::Mode::ReadOnly) + .unwrap(); - // Print the contents of the file - while !my_file.is_eof() { - let mut buf = [0u8; 32]; - if let Ok(n) = my_file.read(&mut buf) { - info!("{:a}", buf[..n]); + // Print the contents of the file + while !my_file.is_eof() { + let mut buf = [0u8; 32]; + if let Ok(n) = my_file.read(&mut buf) { + 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( + block_device: &mut D, + volume_idx: VolumeIdx, +) -> Result> +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")), + } +}