forked from M-Labs/artiq-zynq
Compare commits
7 Commits
fe6700f4e0
...
e4b17f9473
Author | SHA1 | Date | |
---|---|---|---|
e4b17f9473 | |||
da6bda9a06 | |||
f52f10abc2 | |||
1138db34a2 | |||
e77cb5de7e | |||
e6b8bf5481 | |||
dc78868109 |
@ -15,7 +15,7 @@ let
|
||||
version = "0.1.0";
|
||||
|
||||
src = ./src;
|
||||
cargoSha256 = "03ax0bzkn7w0bxvq99n1h5b7x4vs1xk7z5r1701c6dz27qg78fn1";
|
||||
cargoSha256 = "0iaw4zgyajyy6swk9jlq2ky1g4ffj2230j6vli7cjl2spzkhi4sz";
|
||||
|
||||
nativeBuildInputs = [
|
||||
pkgs.gnumake
|
||||
|
47
src/Cargo.lock
generated
47
src/Cargo.lock
generated
@ -93,6 +93,18 @@ dependencies = [
|
||||
"void",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fatfs"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f6d1df9e4503954f60504a5ee4fc435cd65cc42e98b2081f7f421be5f2e68e7d"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"byteorder",
|
||||
"core_io",
|
||||
"log",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures"
|
||||
version = "0.3.5"
|
||||
@ -172,7 +184,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "libasync"
|
||||
version = "0.0.0"
|
||||
source = "git+https://git.m-labs.hk/M-Labs/zc706.git#40d5eb8232de1bec19e8bb81ad09351537926a06"
|
||||
source = "git+https://git.m-labs.hk/M-Labs/zc706.git#d3b488bfb35f752dd93d316fba6d54ce1a71e398"
|
||||
dependencies = [
|
||||
"embedded-hal",
|
||||
"libcortex_a9",
|
||||
@ -184,7 +196,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "libboard_zynq"
|
||||
version = "0.0.0"
|
||||
source = "git+https://git.m-labs.hk/M-Labs/zc706.git#40d5eb8232de1bec19e8bb81ad09351537926a06"
|
||||
source = "git+https://git.m-labs.hk/M-Labs/zc706.git#d3b488bfb35f752dd93d316fba6d54ce1a71e398"
|
||||
dependencies = [
|
||||
"bit_field",
|
||||
"embedded-hal",
|
||||
@ -200,7 +212,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "libcortex_a9"
|
||||
version = "0.0.0"
|
||||
source = "git+https://git.m-labs.hk/M-Labs/zc706.git#40d5eb8232de1bec19e8bb81ad09351537926a06"
|
||||
source = "git+https://git.m-labs.hk/M-Labs/zc706.git#d3b488bfb35f752dd93d316fba6d54ce1a71e398"
|
||||
dependencies = [
|
||||
"bit_field",
|
||||
"libregister",
|
||||
@ -209,7 +221,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "libregister"
|
||||
version = "0.0.0"
|
||||
source = "git+https://git.m-labs.hk/M-Labs/zc706.git#40d5eb8232de1bec19e8bb81ad09351537926a06"
|
||||
source = "git+https://git.m-labs.hk/M-Labs/zc706.git#d3b488bfb35f752dd93d316fba6d54ce1a71e398"
|
||||
dependencies = [
|
||||
"bit_field",
|
||||
"vcell",
|
||||
@ -219,7 +231,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "libsupport_zynq"
|
||||
version = "0.0.0"
|
||||
source = "git+https://git.m-labs.hk/M-Labs/zc706.git#40d5eb8232de1bec19e8bb81ad09351537926a06"
|
||||
source = "git+https://git.m-labs.hk/M-Labs/zc706.git#d3b488bfb35f752dd93d316fba6d54ce1a71e398"
|
||||
dependencies = [
|
||||
"compiler_builtins",
|
||||
"libboard_zynq",
|
||||
@ -246,9 +258,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "managed"
|
||||
version = "0.7.1"
|
||||
version = "0.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fdcec5e97041c7f0f1c5b7d93f12e57293c831c646f4cc7a5db59460c7ea8de6"
|
||||
checksum = "c75de51135344a4f8ed3cfe2720dc27736f7711989703a0b43aadf3753c55577"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
@ -284,18 +296,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "pin-project"
|
||||
version = "0.4.19"
|
||||
version = "0.4.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ba3a1acf4a3e70849f8a673497ef984f043f95d2d8252dcdf74d54e6a1e47e8a"
|
||||
checksum = "e75373ff9037d112bb19bc61333a06a159eaeb217660dcfbea7d88e1db823919"
|
||||
dependencies = [
|
||||
"pin-project-internal",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-internal"
|
||||
version = "0.4.19"
|
||||
version = "0.4.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "194e88048b71a3e02eb4ee36a6995fed9b8236c11a7bb9f7247a9d9835b3f265"
|
||||
checksum = "10b4b44893d3c370407a1d6a5cfde7c41ae0478e31c516c85f67eb3adc51be6d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@ -316,9 +328,9 @@ checksum = "7e0456befd48169b9f13ef0f0ad46d492cf9d2dbb918bcf38e01eed4ce3ec5e4"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-nested"
|
||||
version = "0.1.4"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e946095f9d3ed29ec38de908c22f95d9ac008e424c7bcae54c75a79c527c694"
|
||||
checksum = "0afe1bd463b9e9ed51d0e0f0b50b6b146aec855c56fd182bb242388710a9b6de"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
@ -331,9 +343,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.6"
|
||||
version = "1.0.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "54a21852a652ad6f610c9510194f398ff6f8692e334fd1145fed931f7fbe44ea"
|
||||
checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
@ -353,6 +365,7 @@ dependencies = [
|
||||
"core_io",
|
||||
"cslice",
|
||||
"dyld",
|
||||
"fatfs",
|
||||
"futures",
|
||||
"libasync",
|
||||
"libboard_zynq",
|
||||
@ -377,9 +390,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.30"
|
||||
version = "1.0.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "93a56fabc59dce20fe48b6c832cc249c713e7ed88fa28b0ee0a3bfcaae5fe4e2"
|
||||
checksum = "b5304cfdf27365b7585c25d4af91b35016ed21ef88f17ced89c7093b43dba8b6"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -24,3 +24,4 @@ libsupport_zynq = { git = "https://git.m-labs.hk/M-Labs/zc706.git" }
|
||||
libcortex_a9 = { git = "https://git.m-labs.hk/M-Labs/zc706.git" }
|
||||
libasync = { git = "https://git.m-labs.hk/M-Labs/zc706.git" }
|
||||
dyld = { path = "../libdyld" }
|
||||
fatfs = { version = "0.3", features = ["core_io", "alloc"], default-features = false }
|
||||
|
@ -1,4 +1,3 @@
|
||||
use core::mem::transmute;
|
||||
use core::fmt;
|
||||
use core::cell::RefCell;
|
||||
use core::str::Utf8Error;
|
||||
@ -42,11 +41,11 @@ pub type Result<T> = core::result::Result<T, Error>;
|
||||
impl fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
&Error::NetworkError(error) => write!(f, "network error: {}", error),
|
||||
&Error::UnexpectedPattern => write!(f, "unexpected pattern"),
|
||||
&Error::UnrecognizedPacket => write!(f, "unrecognized packet"),
|
||||
&Error::BufferExhausted => write!(f, "buffer exhausted"),
|
||||
&Error::Utf8Error(error) => write!(f, "UTF-8 error: {}", error),
|
||||
Error::NetworkError(error) => write!(f, "network error: {}", error),
|
||||
Error::UnexpectedPattern => write!(f, "unexpected pattern"),
|
||||
Error::UnrecognizedPacket => write!(f, "unrecognized packet"),
|
||||
Error::BufferExhausted => write!(f, "buffer exhausted"),
|
||||
Error::Utf8Error(error) => write!(f, "UTF-8 error: {}", error),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -249,25 +248,13 @@ const IPADDR: IpAddress = IpAddress::Ipv4(Ipv4Address([192, 168, 1, 52]));
|
||||
pub fn main(timer: GlobalTimer) {
|
||||
let eth = zynq::eth::Eth::default(HWADDR.clone());
|
||||
const RX_LEN: usize = 8;
|
||||
let mut rx_descs = (0..RX_LEN)
|
||||
.map(|_| zynq::eth::rx::DescEntry::zeroed())
|
||||
.collect::<Vec<_>>();
|
||||
let mut rx_buffers = vec![zynq::eth::Buffer::new(); RX_LEN];
|
||||
// Number of transmission buffers (minimum is two because with
|
||||
// one, duplicate packet transmission occurs)
|
||||
const TX_LEN: usize = 8;
|
||||
let mut tx_descs = (0..TX_LEN)
|
||||
.map(|_| zynq::eth::tx::DescEntry::zeroed())
|
||||
.collect::<Vec<_>>();
|
||||
let mut tx_buffers = vec![zynq::eth::Buffer::new(); TX_LEN];
|
||||
let eth = eth.start_rx(&mut rx_descs, &mut rx_buffers);
|
||||
let mut eth = eth.start_tx(
|
||||
// HACK
|
||||
unsafe { transmute(tx_descs.as_mut_slice()) },
|
||||
unsafe { transmute(tx_buffers.as_mut_slice()) },
|
||||
);
|
||||
let ethernet_addr = EthernetAddress(HWADDR);
|
||||
let eth = eth.start_rx(RX_LEN);
|
||||
let mut eth = eth.start_tx(TX_LEN);
|
||||
|
||||
let ethernet_addr = EthernetAddress(HWADDR);
|
||||
let mut ip_addrs = [IpCidr::new(IPADDR, 24)];
|
||||
let mut routes_storage = vec![None; 4];
|
||||
let routes = Routes::new(&mut routes_storage[..]);
|
||||
|
65
src/runtime/src/config.rs
Normal file
65
src/runtime/src/config.rs
Normal file
@ -0,0 +1,65 @@
|
||||
use log::info;
|
||||
use core::fmt;
|
||||
use alloc::{vec::Vec, string::String, string::FromUtf8Error};
|
||||
use core_io as io;
|
||||
|
||||
use libboard_zynq::sdio;
|
||||
|
||||
use crate::sd_reader;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
SdError(sdio::sd_card::CardInitializationError),
|
||||
IoError(io::Error),
|
||||
Utf8Error(FromUtf8Error),
|
||||
}
|
||||
|
||||
pub type Result<T> = core::result::Result<T, Error>;
|
||||
|
||||
impl fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
Error::SdError(error) => write!(f, "SD error: {:?}", error), // TODO: Display for CardInitializationError?
|
||||
Error::IoError(error) => write!(f, "I/O error: {}", error),
|
||||
Error::Utf8Error(error) => write!(f, "UTF-8 error: {}", error),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<sdio::sd_card::CardInitializationError> for Error {
|
||||
fn from(error: sdio::sd_card::CardInitializationError) -> Self {
|
||||
Error::SdError(error)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<io::Error> for Error {
|
||||
fn from(error: io::Error) -> Self {
|
||||
Error::IoError(error)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<FromUtf8Error> for Error {
|
||||
fn from(error: FromUtf8Error) -> Self {
|
||||
Error::Utf8Error(error)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read(key: &str) -> Result<Vec<u8>> {
|
||||
let sdio = sdio::SDIO::sdio0(true);
|
||||
let mut sd = sdio::sd_card::SdCard::from_sdio(sdio)?;
|
||||
let reader = sd_reader::SdReader::new(&mut sd);
|
||||
|
||||
let fs = reader.mount_fatfs(sd_reader::PartitionEntry::Entry1)?;
|
||||
let root_dir = fs.root_dir();
|
||||
for entry in root_dir.iter() {
|
||||
if let Ok(entry) = entry {
|
||||
let bytes = entry.short_file_name_as_bytes();
|
||||
info!("{}", core::str::from_utf8(bytes).unwrap());
|
||||
}
|
||||
}
|
||||
Err(sdio::sd_card::CardInitializationError::NoCardInserted)? // TODO
|
||||
}
|
||||
|
||||
pub fn read_str(key: &str) -> Result<String> {
|
||||
Ok(String::from_utf8(read(key)?)?)
|
||||
}
|
@ -152,14 +152,17 @@ macro_rules! api {
|
||||
|
||||
fn resolve(required: &[u8]) -> Option<u32> {
|
||||
let api = &[
|
||||
// timing
|
||||
api!(now_mu = rtio::now_mu),
|
||||
api!(at_mu = rtio::at_mu),
|
||||
api!(delay_mu = rtio::delay_mu),
|
||||
|
||||
// rpc
|
||||
api!(rpc_send = rpc_send),
|
||||
api!(rpc_send_async = rpc_send_async),
|
||||
api!(rpc_recv = rpc_recv),
|
||||
|
||||
// rtio
|
||||
api!(rtio_init = rtio::init),
|
||||
api!(rtio_get_destination_status = rtio::get_destination_status),
|
||||
api!(rtio_get_counter = rtio::get_counter),
|
||||
@ -169,8 +172,86 @@ fn resolve(required: &[u8]) -> Option<u32> {
|
||||
api!(rtio_input_data = rtio::input_data),
|
||||
api!(rtio_input_timestamped_data = rtio::input_timestamped_data),
|
||||
|
||||
// Double-precision floating-point arithmetic helper functions
|
||||
// RTABI chapter 4.1.2, Table 2
|
||||
api!(__aeabi_dadd),
|
||||
api!(__aeabi_ddiv),
|
||||
api!(__aeabi_dmul),
|
||||
api!(__aeabi_dsub),
|
||||
// Double-precision floating-point comparison helper functions
|
||||
// RTABI chapter 4.1.2, Table 3
|
||||
api!(__aeabi_dcmpeq),
|
||||
api!(__aeabi_dcmpeq),
|
||||
api!(__aeabi_dcmplt),
|
||||
api!(__aeabi_dcmple),
|
||||
api!(__aeabi_dcmpge),
|
||||
api!(__aeabi_dcmpgt),
|
||||
api!(__aeabi_dcmpun),
|
||||
// Single-precision floating-point arithmetic helper functions
|
||||
// RTABI chapter 4.1.2, Table 4
|
||||
api!(__aeabi_fadd),
|
||||
api!(__aeabi_fdiv),
|
||||
api!(__aeabi_fmul),
|
||||
api!(__aeabi_fsub),
|
||||
// Single-precision floating-point comparison helper functions
|
||||
// RTABI chapter 4.1.2, Table 5
|
||||
api!(__aeabi_fcmpeq),
|
||||
api!(__aeabi_fcmpeq),
|
||||
api!(__aeabi_fcmplt),
|
||||
api!(__aeabi_fcmple),
|
||||
api!(__aeabi_fcmpge),
|
||||
api!(__aeabi_fcmpgt),
|
||||
api!(__aeabi_fcmpun),
|
||||
// Floating-point to integer conversions.
|
||||
// RTABI chapter 4.1.2, Table 6
|
||||
api!(__aeabi_d2iz),
|
||||
api!(__aeabi_d2uiz),
|
||||
api!(__aeabi_d2lz),
|
||||
api!(__aeabi_d2ulz),
|
||||
api!(__aeabi_f2iz),
|
||||
api!(__aeabi_f2uiz),
|
||||
api!(__aeabi_f2lz),
|
||||
api!(__aeabi_f2ulz),
|
||||
// Conversions between floating types.
|
||||
// RTABI chapter 4.1.2, Table 7
|
||||
api!(__aeabi_f2d),
|
||||
// Integer to floating-point conversions.
|
||||
// RTABI chapter 4.1.2, Table 8
|
||||
api!(__aeabi_i2d),
|
||||
api!(__aeabi_ui2d),
|
||||
api!(__aeabi_l2d),
|
||||
api!(__aeabi_ul2d),
|
||||
api!(__aeabi_i2f),
|
||||
api!(__aeabi_ui2f),
|
||||
api!(__aeabi_l2f),
|
||||
api!(__aeabi_ul2f),
|
||||
// Long long helper functions
|
||||
// RTABI chapter 4.2, Table 9
|
||||
api!(__aeabi_lmul),
|
||||
api!(__aeabi_llsl),
|
||||
api!(__aeabi_llsr),
|
||||
api!(__aeabi_lasr),
|
||||
// Integer division functions
|
||||
// RTABI chapter 4.3.1
|
||||
api!(__aeabi_idiv),
|
||||
api!(__aeabi_ldivmod),
|
||||
api!(__aeabi_uidiv),
|
||||
api!(__aeabi_uldivmod),
|
||||
// 4.3.4 Memory copying, clearing, and setting
|
||||
api!(__aeabi_memcpy8),
|
||||
api!(__aeabi_memcpy4),
|
||||
api!(__aeabi_memcpy),
|
||||
api!(__aeabi_memmove8),
|
||||
api!(__aeabi_memmove4),
|
||||
api!(__aeabi_memmove),
|
||||
api!(__aeabi_memset8),
|
||||
api!(__aeabi_memset4),
|
||||
api!(__aeabi_memset),
|
||||
api!(__aeabi_memclr8),
|
||||
api!(__aeabi_memclr4),
|
||||
api!(__aeabi_memclr),
|
||||
|
||||
// exceptions
|
||||
api!(_Unwind_Resume = exception_unimplemented),
|
||||
api!(__artiq_personality = exception_unimplemented),
|
||||
api!(__artiq_raise = exception_unimplemented),
|
||||
|
@ -11,6 +11,8 @@ use log::info;
|
||||
use libboard_zynq::{timer::GlobalTimer, logger, devc};
|
||||
use libsupport_zynq::ram;
|
||||
|
||||
mod sd_reader;
|
||||
mod config;
|
||||
mod proto_core_io;
|
||||
mod proto_async;
|
||||
mod comms;
|
||||
@ -44,6 +46,11 @@ pub fn main_core0() {
|
||||
|
||||
ram::init_alloc_linker();
|
||||
|
||||
match config::read_str("foo") {
|
||||
Ok(val) => info!("read: {}", val),
|
||||
Err(error) => info!("failed to read config: {}", error),
|
||||
}
|
||||
|
||||
let devc = devc::DevC::new();
|
||||
if devc.is_done() {
|
||||
info!("gateware already loaded");
|
||||
|
287
src/runtime/src/sd_reader.rs
Normal file
287
src/runtime/src/sd_reader.rs
Normal file
@ -0,0 +1,287 @@
|
||||
use core_io::{BufRead, Error, ErrorKind, Read, Result as IoResult, Seek, SeekFrom, Write};
|
||||
use fatfs;
|
||||
use libboard_zynq::sdio::{sd_card::SdCard, CmdTransferError};
|
||||
|
||||
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<'a> {
|
||||
/// Internal SdCard handle.
|
||||
sd: &'a mut SdCard,
|
||||
/// Read buffer with the size of 1 block.
|
||||
buffer: [u8; BLOCK_SIZE],
|
||||
/// 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<'a> SdReader<'a> {
|
||||
/// Create SdReader from SdCard
|
||||
pub fn new(sd: &'a mut SdCard) -> SdReader<'a> {
|
||||
SdReader {
|
||||
sd,
|
||||
buffer: [0; BLOCK_SIZE],
|
||||
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[0] != 0x55 || buffer[1] != 0xAA {
|
||||
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])?;
|
||||
match buffer[0] {
|
||||
0x01 | 0x04 | 0x06 | 0x0B => (),
|
||||
_ => {
|
||||
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<'a> Read for SdReader<'a> {
|
||||
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<'a> BufRead for SdReader<'a> {
|
||||
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<'a> Write for SdReader<'a> {
|
||||
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.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<'a> Seek for SdReader<'a> {
|
||||
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<'a> Drop for SdReader<'a> {
|
||||
fn drop(&mut self) {
|
||||
// just try to flush it, ignore error if any
|
||||
self.flush().unwrap_or(());
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user