forked from M-Labs/artiq-zynq
parent
7e26a87aed
commit
86b9045417
@ -1,15 +0,0 @@ |
||||
[package] |
||||
name = "libconfig" |
||||
version = "0.1.0" |
||||
authors = ["M-Labs"] |
||||
edition = "2018" |
||||
|
||||
[dependencies] |
||||
libboard_zynq = { git = "https://git.m-labs.hk/M-Labs/zynq-rs.git" } |
||||
core_io = { version = "0.1", features = ["collections"] } |
||||
fatfs = { version = "0.3", features = ["core_io"], default-features = false } |
||||
log = "0.4" |
||||
|
||||
[features] |
||||
ipv6 = [] |
||||
|
@ -1,181 +0,0 @@ |
||||
use alloc::vec::Vec; |
||||
use core_io::{Error, Read, Seek, SeekFrom}; |
||||
use libboard_zynq::devc; |
||||
use log::debug; |
||||
|
||||
#[derive(Debug)] |
||||
pub enum BootgenLoadingError { |
||||
InvalidBootImageHeader, |
||||
MissingPartition, |
||||
EncryptedBitstream, |
||||
IoError(Error), |
||||
DevcError(devc::DevcError), |
||||
} |
||||
|
||||
impl From<Error> for BootgenLoadingError { |
||||
fn from(error: Error) -> Self { |
||||
BootgenLoadingError::IoError(error) |
||||
} |
||||
} |
||||
|
||||
impl From<devc::DevcError> for BootgenLoadingError { |
||||
fn from(error: devc::DevcError) -> Self { |
||||
BootgenLoadingError::DevcError(error) |
||||
} |
||||
} |
||||
|
||||
impl core::fmt::Display for BootgenLoadingError { |
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { |
||||
use BootgenLoadingError::*; |
||||
match self { |
||||
InvalidBootImageHeader => write!( |
||||
f, |
||||
"Invalid boot image header. Check if the file is correct." |
||||
), |
||||
MissingPartition => write!(f, "Partition not found. Check your compile configuration."), |
||||
EncryptedBitstream => write!(f, "Encrypted bitstream is not supported."), |
||||
IoError(e) => write!(f, "Error while reading: {}", e), |
||||
DevcError(e) => write!(f, "PCAP interface error: {}", e), |
||||
} |
||||
} |
||||
} |
||||
|
||||
#[repr(C)] |
||||
struct PartitionHeader { |
||||
pub encrypted_length: u32, |
||||
pub unencrypted_length: u32, |
||||
pub word_length: u32, |
||||
pub dest_load_addr: u32, |
||||
pub dest_exec_addr: u32, |
||||
pub data_offset: u32, |
||||
pub attribute_bits: u32, |
||||
pub section_count: u32, |
||||
pub checksum_offset: u32, |
||||
pub header_offset: u32, |
||||
pub cert_offset: u32, |
||||
pub reserved: [u32; 4], |
||||
pub checksum: u32, |
||||
} |
||||
|
||||
/// Read a u32 word from the reader.
|
||||
fn read_u32<Reader: Read>(reader: &mut Reader) -> Result<u32, BootgenLoadingError> { |
||||
let mut buffer: [u8; 4] = [0; 4]; |
||||
reader.read_exact(&mut buffer)?; |
||||
let mut result: u32 = 0; |
||||
for i in 0..4 { |
||||
result |= (buffer[i] as u32) << (i * 8); |
||||
} |
||||
Ok(result) |
||||
} |
||||
|
||||
/// Load PL partition header.
|
||||
fn load_pl_header<File: Read + Seek>( |
||||
file: &mut File, |
||||
) -> Result<Option<PartitionHeader>, BootgenLoadingError> { |
||||
let mut buffer: [u8; 0x40] = [0; 0x40]; |
||||
file.read_exact(&mut buffer)?; |
||||
let header = unsafe { core::mem::transmute::<_, PartitionHeader>(buffer) }; |
||||
if header.attribute_bits & (2 << 4) != 0 { |
||||
Ok(Some(header)) |
||||
} else { |
||||
Ok(None) |
||||
} |
||||
} |
||||
|
||||
fn load_ps_header<File: Read + Seek>( |
||||
file: &mut File, |
||||
) -> Result<Option<PartitionHeader>, BootgenLoadingError> { |
||||
let mut buffer: [u8; 0x40] = [0; 0x40]; |
||||
file.read_exact(&mut buffer)?; |
||||
let header = unsafe { core::mem::transmute::<_, PartitionHeader>(buffer) }; |
||||
if header.attribute_bits & (1 << 4) != 0 { |
||||
Ok(Some(header)) |
||||
} else { |
||||
Ok(None) |
||||
} |
||||
} |
||||
|
||||
/// Locate the partition from the image, and return the size (in bytes) of the partition if successful.
|
||||
/// This function would seek the file to the location of the partition.
|
||||
fn locate< |
||||
File: Read + Seek, |
||||
F: Fn(&mut File) -> Result<Option<PartitionHeader>, BootgenLoadingError>, |
||||
>( |
||||
file: &mut File, |
||||
f: F, |
||||
) -> Result<usize, BootgenLoadingError> { |
||||
file.seek(SeekFrom::Start(0))?; |
||||
const BOOT_HEADER_SIGN: u32 = 0x584C4E58; |
||||
// read boot header signature
|
||||
file.seek(SeekFrom::Start(0x24))?; |
||||
if read_u32(file)? != BOOT_HEADER_SIGN { |
||||
return Err(BootgenLoadingError::InvalidBootImageHeader); |
||||
} |
||||
// find fsbl offset
|
||||
file.seek(SeekFrom::Start(0x30))?; |
||||
// the length is in bytes, we have to convert it to words to compare with the partition offset
|
||||
// later
|
||||
let fsbl = read_u32(file)? / 4; |
||||
// read partition header offset
|
||||
file.seek(SeekFrom::Start(0x9C))?; |
||||
let ptr = read_u32(file)?; |
||||
debug!("Partition header pointer = {:0X}", ptr); |
||||
file.seek(SeekFrom::Start(ptr as u64))?; |
||||
|
||||
// at most 3 partition headers
|
||||
for _ in 0..3 { |
||||
if let Some(header) = f(file)? { |
||||
let encrypted_length = header.encrypted_length; |
||||
let unencrypted_length = header.unencrypted_length; |
||||
debug!("Unencrypted length = {:0X}", unencrypted_length); |
||||
if encrypted_length != unencrypted_length { |
||||
return Err(BootgenLoadingError::EncryptedBitstream); |
||||
} |
||||
|
||||
let start_addr = header.data_offset; |
||||
// skip fsbl
|
||||
if start_addr == fsbl { |
||||
continue; |
||||
} |
||||
debug!("Partition start address: {:0X}", start_addr); |
||||
file.seek(SeekFrom::Start(start_addr as u64 * 4))?; |
||||
|
||||
return Ok(unencrypted_length as usize * 4); |
||||
} |
||||
} |
||||
Err(BootgenLoadingError::MissingPartition) |
||||
} |
||||
|
||||
/// Load bitstream from bootgen file.
|
||||
/// This function parses the file, locate the bitstream and load it through the PCAP driver.
|
||||
/// It requires a large buffer, please enable the DDR RAM before using it.
|
||||
pub fn load_bitstream<File: Read + Seek>(file: &mut File) -> Result<(), BootgenLoadingError> { |
||||
let size = locate(file, load_pl_header)?; |
||||
unsafe { |
||||
// align to 64 bytes
|
||||
let ptr = alloc::alloc::alloc(alloc::alloc::Layout::from_size_align(size, 64).unwrap()); |
||||
let buffer = core::slice::from_raw_parts_mut(ptr, size); |
||||
file.read_exact(buffer).map_err(|e| { |
||||
core::ptr::drop_in_place(ptr); |
||||
e |
||||
})?; |
||||
let mut devcfg = devc::DevC::new(); |
||||
devcfg.enable(); |
||||
devcfg.program(&buffer).map_err(|e| { |
||||
core::ptr::drop_in_place(ptr); |
||||
e |
||||
})?; |
||||
core::ptr::drop_in_place(ptr); |
||||
Ok(()) |
||||
} |
||||
} |
||||
|
||||
pub fn get_runtime<File: Read + Seek>(file: &mut File) -> Result<Vec<u8>, BootgenLoadingError> { |
||||
let size = locate(file, load_ps_header)?; |
||||
let mut buffer = Vec::with_capacity(size); |
||||
unsafe { |
||||
buffer.set_len(size); |
||||
} |
||||
file.read_exact(&mut buffer)?; |
||||
Ok(buffer) |
||||
} |
@ -1,115 +0,0 @@ |
||||
#![no_std] |
||||
extern crate alloc; |
||||
|
||||
use core::fmt; |
||||
use alloc::{string::FromUtf8Error, string::String, vec::Vec, rc::Rc}; |
||||
use core_io::{self as io, BufRead, BufReader, Read}; |
||||
use libboard_zynq::sdio; |
||||
|
||||
pub mod sd_reader; |
||||
pub mod net_settings; |
||||
pub mod bootgen; |
||||
|
||||
#[derive(Debug)] |
||||
pub enum Error<'a> { |
||||
SdError(sdio::sd_card::CardInitializationError), |
||||
IoError(io::Error), |
||||
Utf8Error(FromUtf8Error), |
||||
KeyNotFoundError(&'a str), |
||||
NoConfig, |
||||
} |
||||
|
||||
pub type Result<'a, T> = core::result::Result<T, Error<'a>>; |
||||
|
||||
impl<'a> fmt::Display for Error<'a> { |
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
||||
match self { |
||||
Error::SdError(error) => write!(f, "SD error: {}", error), |
||||
Error::IoError(error) => write!(f, "I/O error: {}", error), |
||||
Error::Utf8Error(error) => write!(f, "UTF-8 error: {}", error), |
||||
Error::KeyNotFoundError(name) => write!(f, "Configuration key `{}` not found", name), |
||||
Error::NoConfig => write!(f, "Configuration not present"), |
||||
} |
||||
} |
||||
} |
||||
|
||||
impl<'a> From<sdio::sd_card::CardInitializationError> for Error<'a> { |
||||
fn from(error: sdio::sd_card::CardInitializationError) -> Self { |
||||
Error::SdError(error) |
||||
} |
||||
} |
||||
|
||||
impl<'a> From<io::Error> for Error<'a> { |
||||
fn from(error: io::Error) -> Self { |
||||
Error::IoError(error) |
||||
} |
||||
} |
||||
|
||||
impl<'a> From<FromUtf8Error> for Error<'a> { |
||||
fn from(error: FromUtf8Error) -> Self { |
||||
Error::Utf8Error(error) |
||||
} |
||||
} |
||||
|
||||
fn parse_config<'a>( |
||||
key: &'a str, |
||||
buffer: &mut Vec<u8>, |
||||
file: fatfs::File<sd_reader::SdReader>, |
||||
) -> Result<'a, ()> { |
||||
let prefix = [key, "="].concat(); |
||||
for line in BufReader::new(file).lines() { |
||||
let line = line?; |
||||
if line.starts_with(&prefix) { |
||||
buffer.extend(line[prefix.len()..].as_bytes()); |
||||
return Ok(()); |
||||
} |
||||
} |
||||
Err(Error::KeyNotFoundError(key)) |
||||
} |
||||
|
||||
pub struct Config { |
||||
fs: Option<Rc<fatfs::FileSystem<sd_reader::SdReader>>>, |
||||
} |
||||
|
||||
impl Config { |
||||
pub fn new() -> Result<'static, Self> { |
||||
let sdio = sdio::Sdio::sdio0(true); |
||||
if !sdio.is_card_inserted() { |
||||
Err(sdio::sd_card::CardInitializationError::NoCardInserted)?; |
||||
} |
||||
let sd = sdio::sd_card::SdCard::from_sdio(sdio)?; |
||||
let reader = sd_reader::SdReader::new(sd); |
||||
|
||||
let fs = reader.mount_fatfs(sd_reader::PartitionEntry::Entry1)?; |
||||
Ok(Config { fs: Some(Rc::new(fs)) }) |
||||
} |
||||
|
||||
pub fn from_fs(fs: Option<Rc<fatfs::FileSystem<sd_reader::SdReader>>>) -> Self { |
||||
Config { fs } |
||||
} |
||||
|
||||
pub fn new_dummy() -> Self { |
||||
Config { fs: None } |
||||
} |
||||
|
||||
pub fn read<'b>(&self, key: &'b str) -> Result<'b, Vec<u8>> { |
||||
if let Some(fs) = &self.fs { |
||||
let root_dir = fs.root_dir(); |
||||
let mut buffer: Vec<u8> = Vec::new(); |
||||
match root_dir.open_file(&["/CONFIG/", key, ".BIN"].concat()) { |
||||
Ok(mut f) => f.read_to_end(&mut buffer).map(|_| ())?, |
||||
Err(_) => match root_dir.open_file("/CONFIG.TXT") { |
||||
Ok(f) => parse_config(key, &mut buffer, f)?, |
||||
Err(_) => return Err(Error::KeyNotFoundError(key)), |
||||
}, |
||||
}; |
||||
Ok(buffer) |
||||
} else { |
||||
Err(Error::NoConfig) |
||||
} |
||||
} |
||||
|
||||
pub fn read_str<'b>(&self, key: &'b str) -> Result<'b, String> { |
||||
Ok(String::from_utf8(self.read(key)?)?) |
||||
} |
||||
} |
@ -1,62 +0,0 @@ |
||||
use core::fmt; |
||||
|
||||
use libboard_zynq::smoltcp::wire::{EthernetAddress, IpAddress}; |
||||
|
||||
use super::Config; |
||||
|
||||
pub struct NetAddresses { |
||||
pub hardware_addr: EthernetAddress, |
||||
pub ipv4_addr: IpAddress, |
||||
#[cfg(feature = "ipv6")] |
||||
pub ipv6_ll_addr: IpAddress, |
||||
#[cfg(feature = "ipv6")] |
||||
pub ipv6_addr: Option<IpAddress> |
||||
} |
||||
|
||||
impl fmt::Display for NetAddresses { |
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
||||
write!(f, "MAC={} IPv4={} ", |
||||
self.hardware_addr, self.ipv4_addr)?; |
||||
|
||||
#[cfg(feature = "ipv6")] |
||||
{ |
||||
write!(f, "IPv6-LL={}", self.ipv6_ll_addr)?; |
||||
match self.ipv6_addr { |
||||
Some(addr) => write!(f, " {}", addr)?, |
||||
None => write!(f, " IPv6: no configured address")? |
||||
} |
||||
} |
||||
Ok(()) |
||||
} |
||||
} |
||||
|
||||
pub fn get_adresses(cfg: &Config) -> NetAddresses { |
||||
let mut hardware_addr = EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x52]); |
||||
let mut ipv4_addr = IpAddress::v4(192, 168, 1, 52); |
||||
|
||||
if let Ok(Ok(addr)) = cfg.read_str("mac").map(|s| s.parse()) { |
||||
hardware_addr = addr; |
||||
} |
||||
if let Ok(Ok(addr)) = cfg.read_str("ip").map(|s| s.parse()) { |
||||
ipv4_addr = addr; |
||||
} |
||||
#[cfg(feature = "ipv6")] |
||||
let ipv6_addr = cfg.read_str("ipv6").ok().and_then(|s| s.parse().ok()); |
||||
|
||||
#[cfg(feature = "ipv6")] |
||||
let ipv6_ll_addr = IpAddress::v6( |
||||
0xfe80, 0x0000, 0x0000, 0x0000, |
||||
(((hardware_addr.0[0] ^ 0x02) as u16) << 8) | (hardware_addr.0[1] as u16), |
||||
((hardware_addr.0[2] as u16) << 8) | 0x00ff, |
||||
0xfe00 | (hardware_addr.0[3] as u16), |
||||
((hardware_addr.0[4] as u16) << 8) | (hardware_addr.0[5] as u16)); |
||||
|
||||
NetAddresses { |
||||
hardware_addr, |
||||
ipv4_addr, |
||||
#[cfg(feature = "ipv6")] |
||||
ipv6_ll_addr, |
||||
#[cfg(feature = "ipv6")] |
||||
ipv6_addr |
||||
} |
||||
} |
@ -1,303 +0,0 @@ |
||||
use core_io::{BufRead, Error, ErrorKind, Read, Result as IoResult, Seek, SeekFrom, Write}; |
||||
use fatfs; |
||||
use libboard_zynq::sdio::{sd_card::SdCard, CmdTransferError}; |
||||
use log::debug; |
||||
use alloc::vec::Vec; |
||||
|
||||
const MBR_SIGNATURE: [u8; 2] = [0x55, 0xAA]; |
||||
const PARTID_FAT12: u8 = 0x01; |
||||
const PARTID_FAT16_LESS32M: u8 = 0x04; |
||||
const PARTID_FAT16: u8 = 0x06; |
||||
const PARTID_FAT32: u8 = 0x0B; |
||||
const PARTID_FAT32_LBA: u8 = 0x0C; |
||||
|
||||
fn cmd_error_to_io_error(_: CmdTransferError) -> Error { |
||||
Error::new(ErrorKind::Other, "Command transfer error") |
||||
} |
||||
|
||||
const BLOCK_SIZE: usize = 512; |
||||
|
||||
/// SdReader struct implementing `Read + BufRead + Write + Seek` traits for `core_io`.
|
||||
/// Used as an adaptor for fatfs crate, but could be used directly for raw data access.
|
||||
///
|
||||
/// Implementation: all read/writes would be split into unaligned and block-aligned parts,
|
||||
/// unaligned read/writes would do a buffered read/write using a block-sized internal buffer,
|
||||
/// while aligned transactions would be sent to the SD card directly for performance reason.
|
||||
pub struct SdReader { |
||||
/// Internal SdCard handle.
|
||||
sd: SdCard, |
||||
/// Read buffer with the size of 1 block.
|
||||
buffer: Vec<u8>, |
||||
/// Address for the next byte.
|
||||
byte_addr: u32, |
||||
/// Internal index for the next byte.
|
||||
/// Normally in range `[0, BLOCK_SIZE - 1]`.
|
||||
///
|
||||
/// `index = BLOCK_SIZE` means that the `buffer` is invalid for the current `byte_addr`,
|
||||
/// the next `fill_buf` call would fill the buffer.
|
||||
index: usize, |
||||
/// Dirty flag indicating the content has to be flushed.
|
||||
dirty: bool, |
||||
/// Base offset for translation from logical address to physical address.
|
||||
offset: u32, |
||||
} |
||||
|
||||
#[derive(Copy, Clone)] |
||||
#[allow(unused)] |
||||
// Partition entry enum, normally we would use entry1.
|
||||
pub enum PartitionEntry { |
||||
Entry1 = 0x1BE, |
||||
Entry2 = 0x1CE, |
||||
Entry3 = 0x1DE, |
||||
Entry4 = 0x1EE, |
||||
} |
||||
|
||||
impl SdReader { |
||||
/// Create SdReader from SdCard
|
||||
pub fn new(sd: SdCard) -> SdReader { |
||||
let mut vec: Vec<u8> = Vec::with_capacity(BLOCK_SIZE); |
||||
unsafe { |
||||
vec.set_len(vec.capacity()); |
||||
} |
||||
SdReader { |
||||
sd, |
||||
buffer: vec, |
||||
byte_addr: 0, |
||||
index: BLOCK_SIZE, |
||||
dirty: false, |
||||
offset: 0, |
||||
} |
||||
} |
||||
|
||||
/// Internal read function for unaligned read.
|
||||
/// The read must not cross block boundary.
|
||||
fn read_unaligned(&mut self, buf: &mut [u8]) -> IoResult<usize> { |
||||
if buf.len() == 0 { |
||||
return Ok(0); |
||||
} |
||||
let filled_buffer = self.fill_buf()?; |
||||
for (dest, src) in buf.iter_mut().zip(filled_buffer.iter()) { |
||||
*dest = *src; |
||||
} |
||||
self.consume(buf.len()); |
||||
Ok(buf.len()) |
||||
} |
||||
|
||||
/// Internal write function for unaligned write.
|
||||
/// The write must not cross block boundary.
|
||||
fn write_unaligned(&mut self, buf: &[u8]) -> IoResult<usize> { |
||||
if buf.len() == 0 { |
||||
return Ok(0); |
||||
} |
||||
// update buffer if needed, as we will flush the entire block later.
|
||||
self.fill_buf()?; |
||||
self.dirty = true; |
||||
let dest_buffer = &mut self.buffer[self.index..]; |
||||
for (src, dest) in buf.iter().zip(dest_buffer.iter_mut()) { |
||||
*dest = *src; |
||||
} |
||||
self.consume(buf.len()); |
||||
Ok(buf.len()) |
||||
} |
||||
|
||||
/// Split the slice into three segments, with the middle block-aligned.
|
||||
/// Alignment depends on the current `self.byte_addr` instead of the slice pointer address
|
||||
fn block_align<'b>(&self, buf: &'b [u8]) -> (&'b [u8], &'b [u8], &'b [u8]) { |
||||
let head_len = BLOCK_SIZE - (self.byte_addr as usize % BLOCK_SIZE); |
||||
if head_len > buf.len() { |
||||
(buf, &[], &[]) |
||||
} else { |
||||
let remaining_length = buf.len() - head_len; |
||||
let mid_length = remaining_length - remaining_length % BLOCK_SIZE; |
||||
let (head, remaining) = buf.split_at(head_len); |
||||
let (mid, tail) = remaining.split_at(mid_length); |
||||
(head, mid, tail) |
||||
} |
||||
} |
||||
|
||||
/// Split the mutable slice into three segments, with the middle block-aligned.
|
||||
/// Alignment depends on the current `self.byte_addr` instead of the slice pointer address
|
||||
fn block_align_mut<'b>(&self, buf: &'b mut [u8]) -> (&'b mut [u8], &'b mut [u8], &'b mut [u8]) { |
||||
let head_len = BLOCK_SIZE - (self.byte_addr as usize % BLOCK_SIZE); |
||||
if head_len > buf.len() { |
||||
(buf, &mut [], &mut []) |
||||
} else { |
||||
let remaining_length = buf.len() - head_len; |
||||
let mid_length = remaining_length - remaining_length % BLOCK_SIZE; |
||||
let (head, remaining) = buf.split_at_mut(head_len); |
||||
let (mid, tail) = remaining.split_at_mut(mid_length); |
||||
(head, mid, tail) |
||||
} |
||||
} |
||||
|
||||
/// Invalidate the buffer, so later unaligned read/write would reload the buffer from SD card.
|
||||
fn invalidate_buffer(&mut self) { |
||||
self.index = BLOCK_SIZE; |
||||
} |
||||
|
||||
/// Set the base offset of the SD card, to transform from physical address to logical address.
|
||||
fn set_base_offset(&mut self, offset: u32) -> IoResult<u64> { |
||||
self.offset = offset; |
||||
self.seek(SeekFrom::Start(0)) |
||||
} |
||||
|
||||
/// Mount fatfs from partition entry, and return the fatfs object if success.
|
||||
/// This takes the ownership of self, so currently there is no way to recover from an error,
|
||||
/// except creating a new SD card instance.
|
||||
pub fn mount_fatfs(mut self, entry: PartitionEntry) -> IoResult<fatfs::FileSystem<Self>> { |
||||
let mut buffer: [u8; 4] = [0; 4]; |
||||
self.seek(SeekFrom::Start(0x1FE))?; |
||||
self.read_exact(&mut buffer[..2])?; |
||||
// check MBR signature
|
||||
if buffer[..2] != MBR_SIGNATURE { |
||||
return Err(Error::new( |
||||
ErrorKind::InvalidData, |
||||
"Incorrect signature for MBR sector.", |
||||
)); |
||||
} |
||||
// Read partition ID.
|
||||
self.seek(SeekFrom::Start(entry as u64 + 0x4))?; |
||||
self.read_exact(&mut buffer[..1])?; |
||||
debug!("Partition ID: {:0X}", buffer[0]); |
||||
match buffer[0] { |
||||
PARTID_FAT12 | PARTID_FAT16_LESS32M | PARTID_FAT16 | |
||||
PARTID_FAT32 | PARTID_FAT32_LBA => {} |
||||
_ => { |
||||
return Err(Error::new( |
||||
ErrorKind::InvalidData, |
||||
"No FAT partition found for the specified entry.", |
||||
)); |
||||
} |
||||
} |
||||
// Read LBA
|
||||
self.seek(SeekFrom::Current(0x3))?; |
||||
self.read_exact(&mut buffer)?; |
||||
let mut lba: u32 = 0; |
||||
// Little endian
|
||||
for i in 0..4 { |
||||
lba |= (buffer[i] as u32) << (i * 8); |
||||
} |
||||
// Set to logical address
|
||||
self.set_base_offset(lba * BLOCK_SIZE as u32)?; |
||||
// setup fatfs
|
||||
fatfs::FileSystem::new(self, fatfs::FsOptions::new()) |
||||
} |
||||
} |
||||
|
||||
impl Read for SdReader { |
||||
fn read(&mut self, buf: &mut [u8]) -> IoResult<usize> { |
||||
let total_length = buf.len(); |
||||
let (a, b, c) = self.block_align_mut(buf); |
||||
self.read_unaligned(a)?; |
||||
if b.len() > 0 { |
||||
// invalidate internal buffer
|
||||
self.invalidate_buffer(); |
||||
if let Err(_) = self.sd.read_block( |
||||
self.byte_addr / BLOCK_SIZE as u32, |
||||
(b.len() / BLOCK_SIZE) as u16, |
||||
b, |
||||
) { |
||||
// we have to allow partial read, as per the trait required
|
||||
return Ok(a.len()); |
||||
} |
||||
self.byte_addr += b.len() as u32; |
||||
} |
||||
if let Err(_) = self.read_unaligned(c) { |
||||
// we have to allow partial read, as per the trait required
|
||||
return Ok(a.len() + b.len()); |
||||
} |
||||
Ok(total_length) |
||||
} |
||||
} |
||||
|
||||
impl BufRead for SdReader { |
||||
fn fill_buf(&mut self) -> IoResult<&[u8]> { |
||||
if self.index == BLOCK_SIZE { |
||||
// flush the buffer if it is dirty before overwriting it with new data
|
||||
if self.dirty { |
||||
self.flush()?; |
||||
} |
||||
// reload buffer
|
||||
self.sd |
||||
.read_block(self.byte_addr / (BLOCK_SIZE as u32), 1, &mut self.buffer) |
||||
.map_err(cmd_error_to_io_error)?; |
||||
self.index = (self.byte_addr as usize) % BLOCK_SIZE; |
||||
} |
||||
Ok(&self.buffer[self.index..]) |
||||
} |
||||
|
||||
fn consume(&mut self, amt: usize) { |
||||
self.index += amt; |
||||
self.byte_addr += amt as u32; |
||||
} |
||||
} |
||||
|
||||
impl Write for SdReader { |
||||
fn write(&mut self, buf: &[u8]) -> IoResult<usize> { |
||||
let (a, b, c) = self.block_align(buf); |
||||
self.write_unaligned(a)?; |
||||
if b.len() > 0 { |
||||
self.flush()?; |
||||
self.invalidate_buffer(); |
||||
if let Err(_) = self.sd.write_block( |
||||
self.byte_addr / BLOCK_SIZE as u32, |
||||
(b.len() / BLOCK_SIZE) as u16, |
||||
b, |
||||
) { |
||||
return Ok(a.len()); |
||||
} |
||||
self.byte_addr += b.len() as u32; |
||||
} |
||||
if let Err(_) = self.write_unaligned(c) { |
||||
return Ok(a.len() + b.len()); |
||||
} |
||||
Ok(buf.len()) |
||||
} |
||||
|
||||
fn flush(&mut self) -> IoResult<()> { |
||||
if self.dirty { |
||||
let block_addr = (self.byte_addr - self.index as u32) / (BLOCK_SIZE as u32); |
||||
self.sd |
||||
.write_block(block_addr, 1, &self.buffer) |
||||
.map_err(cmd_error_to_io_error)?; |
||||
self.dirty = false; |
||||
} |
||||
Ok(()) |
||||
} |
||||
} |
||||
|
||||
impl Seek for SdReader { |
||||
fn seek(&mut self, pos: SeekFrom) -> IoResult<u64> { |
||||
let raw_target = match pos { |
||||
SeekFrom::Start(x) => self.offset as i64 + x as i64, |
||||
SeekFrom::Current(x) => self.byte_addr as i64 + x, |
||||
SeekFrom::End(_) => panic!("SD card does not support seek from end"), |
||||
}; |
||||
if raw_target < self.offset as i64 || raw_target > core::u32::MAX as i64 { |
||||
return Err(Error::new(ErrorKind::InvalidInput, "Invalid address")); |
||||
} |
||||
let target_byte_addr = raw_target as u32; |
||||
let address_same_block = |
||||
self.byte_addr / (BLOCK_SIZE as u32) == target_byte_addr / (BLOCK_SIZE as u32); |
||||
// if the buffer was invalidated, we consider seek as different block
|
||||
let same_block = address_same_block && self.index != BLOCK_SIZE; |
||||
if !same_block { |
||||
self.flush()?; |
||||
} |
||||
self.byte_addr = target_byte_addr; |
||||
self.index = if same_block { |
||||
target_byte_addr as usize % BLOCK_SIZE |
||||
} else { |
||||
// invalidate the buffer as we moved to a different block
|
||||
BLOCK_SIZE |
||||
}; |
||||
Ok((self.byte_addr - self.offset) as u64) |
||||
} |
||||
} |
||||
|
||||
impl Drop for SdReader { |
||||
fn drop(&mut self) { |
||||
// just try to flush it, ignore error if any
|
||||
self.flush().unwrap_or(()); |
||||
} |
||||
} |
@ -1,14 +0,0 @@ |
||||
[package] |
||||
authors = ["M-Labs"] |
||||
name = "core_io" |
||||
version = "0.1.20200410" |
||||
|
||||
[lib] |
||||
name = "core_io" |
||||
|
||||
[dependencies] |
||||
memchr = { version = "2", default-features = false, optional = true } |
||||
|
||||
[features] |
||||
alloc = [] |
||||
collections = ["alloc", "memchr"] |
File diff suppressed because it is too large
Load Diff
@ -1,896 +0,0 @@ |
||||
use crate::io::prelude::*; |
||||
|
||||
use core::cmp; |
||||
use crate::io::{self, Error, ErrorKind, Initializer, SeekFrom}; |
||||
|
||||
#[cfg(feature = "collections")] |
||||
use core::convert::TryInto; |
||||
|
||||
#[cfg(feature="collections")]
|
||||
use collections::vec::Vec; |
||||
|
||||
#[cfg(feature = "alloc")] |
||||
use alloc::boxed::Box; |
||||
|
||||
/// A `Cursor` wraps an in-memory buffer and provides it with a
|
||||
/// [`Seek`] implementation.
|
||||
///
|
||||
/// `Cursor`s are used with in-memory buffers, anything implementing
|
||||
/// `AsRef<[u8]>`, to allow them to implement [`Read`] and/or [`Write`],
|
||||
/// allowing these buffers to be used anywhere you might use a reader or writer
|
||||
/// that does actual I/O.
|
||||
///
|
||||
/// The standard library implements some I/O traits on various types which
|
||||
/// are commonly used as a buffer, like `Cursor<`[`Vec`]`<u8>>` and
|
||||
/// `Cursor<`[`&[u8]`][bytes]`>`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// We may want to write bytes to a [`File`] in our production
|
||||
/// code, but use an in-memory buffer in our tests. We can do this with
|
||||
/// `Cursor`:
|
||||
///
|
||||
/// [`Seek`]: trait.Seek.html
|
||||
/// [`Read`]: ../../std/io/trait.Read.html
|
||||
/// [`Write`]: ../../std/io/trait.Write.html
|
||||
/// [`Vec`]: ../../std/vec/struct.Vec.html
|
||||
/// [bytes]: ../../std/primitive.slice.html
|
||||
/// [`File`]: ../fs/struct.File.html
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::io::prelude::*;
|
||||
/// use std::io::{self, SeekFrom};
|
||||
/// use std::fs::File;
|
||||
///
|
||||
/// // a library function we've written
|
||||
/// fn write_ten_bytes_at_end<W: Write + Seek>(writer: &mut W) -> io::Result<()> {
|
||||
/// writer.seek(SeekFrom::End(-10))?;
|
||||
///
|
||||
/// for i in 0..10 {
|
||||
/// writer.write(&[i])?;
|
||||
/// }
|
||||
///
|
||||
/// // all went well
|
||||
/// Ok(())
|
||||
/// }
|
||||
///
|
||||
/// # fn foo() -> io::Result<()> {
|
||||
/// // Here's some code that uses this library function.
|
||||
/// //
|
||||
/// // We might want to use a BufReader here for efficiency, but let's
|
||||
/// // keep this example focused.
|
||||
/// let mut file = File::create("foo.txt")?;
|
||||
///
|
||||
/// write_ten_bytes_at_end(&mut file)?;
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
///
|
||||
/// // now let's write a test
|
||||
/// #[test]
|
||||
/// fn test_writes_bytes() {
|
||||
/// // setting up a real File is much slower than an in-memory buffer,
|
||||
/// // let's use a cursor instead
|
||||
/// use std::io::Cursor;
|
||||
/// let mut buff = Cursor::new(vec![0; 15]);
|
||||
///
|
||||
/// write_ten_bytes_at_end(&mut buff).unwrap();
|
||||
///
|
||||
/// assert_eq!(&buff.get_ref()[5..15], &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
|
||||
/// }
|
||||
/// ```
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq)] |
||||
pub struct Cursor<T> { |
||||
inner: T, |
||||
pos: u64, |
||||
} |
||||
|
||||
impl<T> Cursor<T> { |
||||
/// Creates a new cursor wrapping the provided underlying in-memory buffer.
|
||||
///
|
||||
/// Cursor initial position is `0` even if underlying buffer (e.g., `Vec`)
|
||||
/// is not empty. So writing to cursor starts with overwriting `Vec`
|
||||
/// content, not with appending to it.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::io::Cursor;
|
||||
///
|
||||
/// let buff = Cursor::new(Vec::new());
|
||||
/// # fn force_inference(_: &Cursor<Vec<u8>>) {}
|
||||
/// # force_inference(&buff);
|
||||
/// ```
|
||||
pub fn new(inner: T) -> Cursor<T> { |
||||
Cursor { pos: 0, inner } |
||||
} |
||||
|
||||
/// Consumes this cursor, returning the underlying value.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::io::Cursor;
|
||||
///
|
||||
/// let buff = Cursor::new(Vec::new());
|
||||
/// # fn force_inference(_: &Cursor<Vec<u8>>) {}
|
||||
/// # force_inference(&buff);
|
||||
///
|
||||
/// let vec = buff.into_inner();
|
||||
/// ```
|
||||
pub fn into_inner(self) -> T { |
||||
self.inner |
||||
} |
||||
|
||||
/// Gets a reference to the underlying value in this cursor.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::io::Cursor;
|
||||
///
|
||||
/// let buff = Cursor::new(Vec::new());
|
||||
/// # fn force_inference(_: &Cursor<Vec<u8>>) {}
|
||||
/// # force_inference(&buff);
|
||||
///
|
||||
/// let reference = buff.get_ref();
|
||||
/// ```
|
||||
pub fn get_ref(&self) -> &T { |
||||
&self.inner |
||||
} |
||||
|
||||
/// Gets a mutable reference to the underlying value in this cursor.
|
||||
///
|
||||
/// Care should be taken to avoid modifying the internal I/O state of the
|
||||
/// underlying value as it may corrupt this cursor's position.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::io::Cursor;
|
||||
///
|
||||
/// let mut buff = Cursor::new(Vec::new());
|
||||
/// # fn force_inference(_: &Cursor<Vec<u8>>) {}
|
||||
/// # force_inference(&buff);
|
||||
///
|
||||
/// let reference = buff.get_mut();
|
||||
/// ```
|
||||
pub fn get_mut(&mut self) -> &mut T { |
||||
&mut self.inner |
||||
} |
||||
|
||||
/// Returns the current position of this cursor.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::io::Cursor;
|
||||
/// use std::io::prelude::*;
|
||||
/// use std::io::SeekFrom;
|
||||
///
|
||||
/// let mut buff = Cursor::new(vec![1, 2, 3, 4, 5]);
|
||||
///
|
||||
/// assert_eq!(buff.position(), 0);
|
||||
///
|
||||
/// buff.seek(SeekFrom::Current(2)).unwrap();
|
||||
/// assert_eq!(buff.position(), 2);
|
||||
///
|
||||
/// buff.seek(SeekFrom::Current(-1)).unwrap();
|
||||
/// assert_eq!(buff.position(), 1);
|
||||
/// ```
|
||||
pub fn position(&self) -> u64 { |
||||
self.pos |
||||
} |
||||
|
||||
/// Sets the position of this cursor.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::io::Cursor;
|
||||
///
|
||||
/// let mut buff = Cursor::new(vec![1, 2, 3, 4, 5]);
|
||||
///
|
||||
/// assert_eq!(buff.position(), 0);
|
||||
///
|
||||
/// buff.set_position(2);
|
||||
/// assert_eq!(buff.position(), 2);
|
||||
///
|
||||
/// buff.set_position(4);
|
||||
/// assert_eq!(buff.position(), 4);
|
||||
/// ```
|
||||
pub fn set_position(&mut self, pos: u64) { |
||||
self.pos = pos; |
||||
} |
||||
} |
||||
|
||||
impl<T> io::Seek for Cursor<T> |
||||
where |
||||
T: AsRef<[u8]>, |
||||
{ |
||||
fn seek(&mut self, style: SeekFrom) -> io::Result<u64> { |
||||
let (base_pos, offset) = match style { |
||||
SeekFrom::Start(n) => { |
||||
self.pos = n; |
||||
return Ok(n); |
||||
} |
||||
SeekFrom::End(n) => (self.inner.as_ref().len() as u64, n), |
||||
SeekFrom::Current(n) => (self.pos, n), |
||||
}; |
||||
let new_pos = if offset >= 0 { |
||||
base_pos.checked_add(offset as u64) |
||||
} else { |
||||
base_pos.checked_sub((offset.wrapping_neg()) as u64) |
||||
}; |
||||
match new_pos { |
||||
Some(n) => { |
||||
self.pos = n; |
||||
Ok(self.pos) |
||||
} |
||||
None => Err(Error::new( |
||||
ErrorKind::InvalidInput, |
||||
"invalid seek to a negative or overflowing position", |
||||
)), |
||||
} |
||||
} |
||||
|
||||
fn stream_len(&mut self) -> io::Result<u64> { |
||||
Ok(self.inner.as_ref().len() as u64) |
||||
} |
||||
|
||||
fn stream_position(&mut self) -> io::Result<u64> { |
||||
Ok(self.pos) |
||||
} |
||||
} |
||||
|
||||
impl<T> Read for Cursor<T> |
||||
where |
||||
T: AsRef<[u8]>, |
||||
{ |
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { |
||||
let n = Read::read(&mut self.get_ref().as_ref(), buf)?; |
||||
self.pos += n as u64; |
||||
Ok(n) |
||||
} |
||||
|
||||
fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> { |
||||
let n = buf.len(); |
||||
Read::read_exact(&mut self.get_ref().as_ref(), buf)?; |
||||
self.pos += n as u64; |
||||
Ok(()) |
||||
} |
||||
|
||||
#[inline] |
||||
unsafe fn initializer(&self) -> Initializer { |
||||
Initializer::nop() |
||||
} |
||||
} |
||||
|
||||
#[cfg(feature = "collections")] |
||||
impl<T> BufRead for Cursor<T> |
||||
where |
||||
T: AsRef<[u8]>, |
||||
{ |
||||
fn fill_buf(&mut self) -> io::Result<&[u8]> { |
||||
let amt = cmp::min(self.pos, self.inner.as_ref().len() as u64); |
||||
Ok(&self.inner.as_ref()[(amt as usize)..]) |
||||
} |
||||
fn consume(&mut self, amt: usize) { |
||||
self.pos += amt as u64; |
||||
} |
||||
} |
||||
|
||||
// Non-resizing write implementation
|
||||
#[inline] |
||||
fn slice_write(pos_mut: &mut u64, slice: &mut [u8], buf: &[u8]) -> io::Result<usize> { |
||||
let pos = cmp::min(*pos_mut, slice.len() as u64); |
||||
let amt = (&mut slice[(pos as usize)..]).write(buf)?; |
||||
*pos_mut += amt as u64; |
||||
Ok(amt) |
||||
} |
||||
|
||||
// Resizing write implementation
|
||||
#[cfg(feature = "collections")] |
||||
fn vec_write(pos_mut: &mut u64, vec: &mut Vec<u8>, buf: &[u8]) -> io::Result<usize> { |
||||
let pos: usize = (*pos_mut).try_into().map_err(|_| { |
||||