|
| 1 | +//! File Systems related commands for Neotron OS |
| 2 | +
|
| 3 | +use chrono::{Datelike, Timelike}; |
| 4 | +use embedded_sdmmc::VolumeIdx; |
| 5 | + |
| 6 | +use crate::{bios, print, println, Ctx, API}; |
| 7 | + |
| 8 | +pub static DIR_ITEM: menu::Item<Ctx> = menu::Item { |
| 9 | + item_type: menu::ItemType::Callback { |
| 10 | + function: dir, |
| 11 | + parameters: &[], |
| 12 | + }, |
| 13 | + command: "dir", |
| 14 | + help: Some("Dir the root directory on block device 0"), |
| 15 | +}; |
| 16 | + |
| 17 | +#[cfg(target_os = "none")] |
| 18 | +pub static LOAD_ITEM: menu::Item<Ctx> = menu::Item { |
| 19 | + item_type: menu::ItemType::Callback { |
| 20 | + function: load, |
| 21 | + parameters: &[menu::Parameter::Mandatory { |
| 22 | + parameter_name: "file", |
| 23 | + help: Some("The file to load"), |
| 24 | + }], |
| 25 | + }, |
| 26 | + command: "load", |
| 27 | + help: Some("Load a file into the application area"), |
| 28 | +}; |
| 29 | + |
| 30 | +struct BiosBlock(); |
| 31 | + |
| 32 | +impl embedded_sdmmc::BlockDevice for BiosBlock { |
| 33 | + type Error = bios::Error; |
| 34 | + |
| 35 | + fn read( |
| 36 | + &self, |
| 37 | + blocks: &mut [embedded_sdmmc::Block], |
| 38 | + start_block_idx: embedded_sdmmc::BlockIdx, |
| 39 | + _reason: &str, |
| 40 | + ) -> Result<(), Self::Error> { |
| 41 | + let api = API.get(); |
| 42 | + let byte_slice = unsafe { |
| 43 | + core::slice::from_raw_parts_mut( |
| 44 | + blocks.as_mut_ptr() as *mut u8, |
| 45 | + blocks.len() * embedded_sdmmc::Block::LEN, |
| 46 | + ) |
| 47 | + }; |
| 48 | + match (api.block_read)( |
| 49 | + 0, |
| 50 | + bios::block_dev::BlockIdx(u64::from(start_block_idx.0)), |
| 51 | + blocks.len() as u8, |
| 52 | + bios::ApiBuffer::new(byte_slice), |
| 53 | + ) { |
| 54 | + bios::Result::Ok(_) => Ok(()), |
| 55 | + bios::Result::Err(e) => Err(e), |
| 56 | + } |
| 57 | + } |
| 58 | + |
| 59 | + fn write( |
| 60 | + &self, |
| 61 | + blocks: &[embedded_sdmmc::Block], |
| 62 | + start_block_idx: embedded_sdmmc::BlockIdx, |
| 63 | + ) -> Result<(), Self::Error> { |
| 64 | + let api = API.get(); |
| 65 | + let byte_slice = unsafe { |
| 66 | + core::slice::from_raw_parts( |
| 67 | + blocks.as_ptr() as *const u8, |
| 68 | + blocks.len() * embedded_sdmmc::Block::LEN, |
| 69 | + ) |
| 70 | + }; |
| 71 | + match (api.block_write)( |
| 72 | + 0, |
| 73 | + bios::block_dev::BlockIdx(u64::from(start_block_idx.0)), |
| 74 | + blocks.len() as u8, |
| 75 | + bios::ApiByteSlice::new(byte_slice), |
| 76 | + ) { |
| 77 | + bios::Result::Ok(_) => Ok(()), |
| 78 | + bios::Result::Err(e) => Err(e), |
| 79 | + } |
| 80 | + } |
| 81 | + |
| 82 | + fn num_blocks(&self) -> Result<embedded_sdmmc::BlockCount, Self::Error> { |
| 83 | + let api = API.get(); |
| 84 | + match (api.block_dev_get_info)(0) { |
| 85 | + bios::Option::Some(info) => Ok(embedded_sdmmc::BlockCount(info.num_blocks as u32)), |
| 86 | + bios::Option::None => Err(bios::Error::InvalidDevice), |
| 87 | + } |
| 88 | + } |
| 89 | +} |
| 90 | + |
| 91 | +struct BiosTime(); |
| 92 | + |
| 93 | +impl embedded_sdmmc::TimeSource for BiosTime { |
| 94 | + fn get_timestamp(&self) -> embedded_sdmmc::Timestamp { |
| 95 | + let time = API.get_time(); |
| 96 | + embedded_sdmmc::Timestamp { |
| 97 | + year_since_1970: (time.year() - 1970) as u8, |
| 98 | + zero_indexed_month: time.month0() as u8, |
| 99 | + zero_indexed_day: time.day0() as u8, |
| 100 | + hours: time.hour() as u8, |
| 101 | + minutes: time.minute() as u8, |
| 102 | + seconds: time.second() as u8, |
| 103 | + } |
| 104 | + } |
| 105 | +} |
| 106 | + |
| 107 | +/// Called when the "dir" command is executed. |
| 108 | +fn dir(_menu: &menu::Menu<Ctx>, _item: &menu::Item<Ctx>, _args: &[&str], _ctx: &mut Ctx) { |
| 109 | + fn work() -> Result<(), embedded_sdmmc::Error<bios::Error>> { |
| 110 | + println!("Listing files on Block Device 0, /"); |
| 111 | + let bios_block = BiosBlock(); |
| 112 | + let time = BiosTime(); |
| 113 | + let mut mgr = embedded_sdmmc::VolumeManager::new(bios_block, time); |
| 114 | + // Open the first partition |
| 115 | + let volume = mgr.get_volume(VolumeIdx(0))?; |
| 116 | + let root_dir = mgr.open_root_dir(&volume)?; |
| 117 | + let mut total_bytes = 0u64; |
| 118 | + let mut num_files = 0; |
| 119 | + mgr.iterate_dir(&volume, &root_dir, |dir_entry| { |
| 120 | + let padding = 8 - dir_entry.name.base_name().len(); |
| 121 | + for b in dir_entry.name.base_name() { |
| 122 | + let ch = *b as char; |
| 123 | + print!("{}", if ch.is_ascii_graphic() { ch } else { '?' }); |
| 124 | + } |
| 125 | + for _ in 0..padding { |
| 126 | + print!(" "); |
| 127 | + } |
| 128 | + print!(" "); |
| 129 | + let padding = 3 - dir_entry.name.extension().len(); |
| 130 | + for b in dir_entry.name.extension() { |
| 131 | + let ch = *b as char; |
| 132 | + print!("{}", if ch.is_ascii_graphic() { ch } else { '?' }); |
| 133 | + } |
| 134 | + for _ in 0..padding { |
| 135 | + print!(" "); |
| 136 | + } |
| 137 | + if dir_entry.attributes.is_directory() { |
| 138 | + print!(" <DIR> "); |
| 139 | + } else { |
| 140 | + print!(" {:-13}", dir_entry.size,); |
| 141 | + } |
| 142 | + print!( |
| 143 | + " {:02}/{:02}/{:04}", |
| 144 | + dir_entry.mtime.zero_indexed_day + 1, |
| 145 | + dir_entry.mtime.zero_indexed_month + 1, |
| 146 | + u32::from(dir_entry.mtime.year_since_1970) + 1970 |
| 147 | + ); |
| 148 | + println!( |
| 149 | + " {:02}:{:02}", |
| 150 | + dir_entry.mtime.hours, dir_entry.mtime.minutes |
| 151 | + ); |
| 152 | + total_bytes += dir_entry.size as u64; |
| 153 | + num_files += 1; |
| 154 | + })?; |
| 155 | + println!("{:-9} file(s) {:-13} bytes", num_files, total_bytes); |
| 156 | + Ok(()) |
| 157 | + } |
| 158 | + |
| 159 | + match work() { |
| 160 | + Ok(_) => {} |
| 161 | + Err(e) => { |
| 162 | + println!("Error: {:?}", e); |
| 163 | + } |
| 164 | + } |
| 165 | +} |
| 166 | + |
| 167 | +/// Called when the "load" command is executed. |
| 168 | +#[cfg(target_os = "none")] |
| 169 | +fn load(_menu: &menu::Menu<Ctx>, _item: &menu::Item<Ctx>, args: &[&str], _ctx: &mut Ctx) { |
| 170 | + fn work(args: &[&str]) -> Result<(), embedded_sdmmc::Error<bios::Error>> { |
| 171 | + println!("Loading /{} from Block Device 0", args[0]); |
| 172 | + let bios_block = BiosBlock(); |
| 173 | + let time = BiosTime(); |
| 174 | + let mut mgr = embedded_sdmmc::VolumeManager::new(bios_block, time); |
| 175 | + // Open the first partition |
| 176 | + let mut volume = mgr.get_volume(VolumeIdx(0))?; |
| 177 | + let root_dir = mgr.open_root_dir(&volume)?; |
| 178 | + let mut file = mgr.open_file_in_dir( |
| 179 | + &mut volume, |
| 180 | + &root_dir, |
| 181 | + args[0], |
| 182 | + embedded_sdmmc::Mode::ReadOnly, |
| 183 | + )?; |
| 184 | + let file_length = file.length(); |
| 185 | + // Application space starts 4K into Cortex-M SRAM |
| 186 | + const APPLICATION_START_ADDR: usize = 0x2000_1000; |
| 187 | + let application_ram: &'static mut [u8] = unsafe { |
| 188 | + core::slice::from_raw_parts_mut(APPLICATION_START_ADDR as *mut u8, file_length as usize) |
| 189 | + }; |
| 190 | + mgr.read(&mut volume, &mut file, application_ram)?; |
| 191 | + Ok(()) |
| 192 | + } |
| 193 | + |
| 194 | + match work(args) { |
| 195 | + Ok(_) => {} |
| 196 | + Err(e) => { |
| 197 | + println!("Error: {:?}", e); |
| 198 | + } |
| 199 | + } |
| 200 | +} |
0 commit comments