Merge branch 'ivan/bcm2835-sdhcid' into 'master'
add bcm2835-sdhci driver See merge request redox-os/drivers!121
This commit is contained in:
Generated
+18
@@ -158,6 +158,18 @@ version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b41b7ea54a0c9d92199de89e20e58d49f02f8e699814ef3fdf266f6f748d15c7"
|
||||
|
||||
[[package]]
|
||||
name = "bcm2835-sdhcid"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"block-io-wrapper",
|
||||
"common",
|
||||
"fdt",
|
||||
"partitionlib",
|
||||
"redox-daemon",
|
||||
"redox_syscall 0.4.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bgad"
|
||||
version = "0.1.0"
|
||||
@@ -384,6 +396,12 @@ version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
|
||||
|
||||
[[package]]
|
||||
name = "fdt"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "784a4df722dc6267a04af36895398f59d21d07dce47232adf31ec0ff2fa45e67"
|
||||
|
||||
[[package]]
|
||||
name = "fuchsia-cprng"
|
||||
version = "0.1.1"
|
||||
|
||||
@@ -31,6 +31,8 @@ members = [
|
||||
"virtio-netd",
|
||||
"virtio-gpud",
|
||||
"virtio-core",
|
||||
|
||||
"bcm2835-sdhcid",
|
||||
]
|
||||
|
||||
[profile.release]
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
[package]
|
||||
name = "bcm2835-sdhcid"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
fdt = "0.1.5"
|
||||
redox_syscall = "0.4"
|
||||
partitionlib = { git = "https://gitlab.redox-os.org/redox-os/partitionlib.git" }
|
||||
block-io-wrapper = { path = "../block-io-wrapper" }
|
||||
common = { path = "../common" }
|
||||
|
||||
redox-daemon = "0.1"
|
||||
@@ -0,0 +1,115 @@
|
||||
use std::{fs::File, io::{Read, Write}, process};
|
||||
|
||||
use fdt::{Fdt, node::FdtNode};
|
||||
use syscall::{Packet, SchemeBlockMut};
|
||||
|
||||
use crate::sd::Disk;
|
||||
use crate::scheme::DiskScheme;
|
||||
|
||||
mod sd;
|
||||
mod scheme;
|
||||
|
||||
#[cfg(target_os = "redox")]
|
||||
fn get_dtb() -> Vec<u8> {
|
||||
std::fs::read("kernel.dtb:").unwrap()
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
fn get_dtb() -> Vec<u8> {
|
||||
use std::env;
|
||||
if let Some(arg1) = env::args().nth(1) {
|
||||
std::fs::read(arg1).unwrap()
|
||||
} else {
|
||||
Vec::new()
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
redox_daemon::Daemon::new(daemon).expect("mmc:failed to daemonize");
|
||||
}
|
||||
|
||||
fn daemon(daemon: redox_daemon::Daemon) -> ! {
|
||||
let dtb_data = get_dtb();
|
||||
println!("read from OS, len = {}", dtb_data.len());
|
||||
if dtb_data.len() == 0 {
|
||||
process::exit(0);
|
||||
}
|
||||
|
||||
let fdt = Fdt::new(&dtb_data).unwrap();
|
||||
println!("DTB model = {}", fdt.root().model());
|
||||
let with = ["brcm,bcm2835-sdhcid"];
|
||||
let compat_node = fdt.find_compatible(&with).unwrap();
|
||||
let reg = compat_node.reg().unwrap().next().unwrap();
|
||||
let reg_size = reg.size.unwrap();
|
||||
println!("DeviceMemory start = 0x{:08x}, size = 0x{:08x}", reg.starting_address as usize, reg_size);
|
||||
let addr = unsafe {
|
||||
common::physmap(reg.starting_address as usize, reg_size, common::Prot::RW, common::MemoryType::DeviceMemory)
|
||||
.expect("bcm2835-sdhcid: failed to map address") as usize
|
||||
};
|
||||
println!("ioremap 0x{:08x} to 0x{:08x} 2222", reg.starting_address as usize, addr);
|
||||
let mut sdhci = sd::SdHostCtrl::new(addr);
|
||||
unsafe {
|
||||
sdhci.init();
|
||||
/*
|
||||
let mut buf1 = [0u32; 512];
|
||||
sdhci.sd_readblock(1, &mut buf1, 1);
|
||||
println!("readblock {:?}", buf1);
|
||||
buf1[0] = 0xdead_0000;
|
||||
buf1[1] = 0xdead_0000;
|
||||
buf1[2] = 0x0000_dead;
|
||||
buf1[3] = 0x0000_dead;
|
||||
sdhci.sd_writeblock(1, &buf1, 1);
|
||||
sdhci.sd_readblock(1, &mut buf1, 1);
|
||||
println!("readblock {:?}", buf1);
|
||||
*/
|
||||
/*
|
||||
let mut buf1 = [0u8; 512];
|
||||
sdhci.read(1, &mut buf1);
|
||||
println!("readblock {:?}", buf1);
|
||||
buf1[0] = 0xde;
|
||||
buf1[1] = 0xad;
|
||||
buf1[2] = 0xde;
|
||||
buf1[3] = 0xad;
|
||||
sdhci.write(1, &buf1);
|
||||
sdhci.read(1, &mut buf1);
|
||||
println!("readblock {:?}", buf1);
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
let scheme_name = ":disk.mmc";
|
||||
let mut socket = File::create(scheme_name).expect("mmc: failed to create disk scheme");
|
||||
syscall::setrens(0, 0).expect("mmc: failed to enter null namespace");
|
||||
|
||||
daemon.ready().expect("mmc: failed to notify parent");
|
||||
|
||||
let mut todo = Vec::new();
|
||||
let mut disks = Vec::new();
|
||||
|
||||
disks.push(Box::new(sdhci) as Box<dyn Disk>);
|
||||
let mut scheme = DiskScheme::new(scheme_name.to_string(), disks);
|
||||
loop {
|
||||
let mut packet = Packet::default();
|
||||
if socket.read(&mut packet).expect("mmc: failed to read event") == 0 {
|
||||
println!("zero, break");
|
||||
break;
|
||||
}
|
||||
if let Some(a) = scheme.handle(&packet) {
|
||||
packet.a = a;
|
||||
socket.write(&packet).expect("mmcd: failed to write disk scheme");
|
||||
} else {
|
||||
todo.push(packet);
|
||||
}
|
||||
let mut i = 0;
|
||||
while i < todo.len() {
|
||||
if let Some(a) = scheme.handle(&todo[i]) {
|
||||
let mut packet = todo.remove(i);
|
||||
packet.a = a;
|
||||
socket.write(&packet).expect("mmcd: failed to write disk scheme");
|
||||
} else {
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
process::exit(0);
|
||||
}
|
||||
@@ -0,0 +1,441 @@
|
||||
use std::collections::BTreeMap;
|
||||
use std::{cmp, str};
|
||||
use std::convert::{TryFrom};
|
||||
use std::fmt::Write;
|
||||
use std::io::prelude::*;
|
||||
use std::io::SeekFrom;
|
||||
use std::io;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use syscall::{
|
||||
Error, EACCES, EBADF, EINVAL, EISDIR, ENOENT, ENOLCK, EOVERFLOW, Result,
|
||||
Io, SchemeBlockMut, Stat, MODE_DIR, MODE_FILE, O_DIRECTORY,
|
||||
O_STAT, SEEK_CUR, SEEK_END, SEEK_SET};
|
||||
|
||||
use crate::sd::Disk;
|
||||
|
||||
use partitionlib::{LogicalBlockSize, PartitionTable};
|
||||
|
||||
#[derive(Clone)]
|
||||
enum Handle {
|
||||
List(Vec<u8>, usize), // Dir contents buffer, position
|
||||
Disk(usize, usize), // Disk index, position
|
||||
Partition(usize, u32, usize), // Disk index, partition index, position
|
||||
}
|
||||
|
||||
pub struct DiskWrapper {
|
||||
disk: Box<dyn Disk>,
|
||||
pt: Option<PartitionTable>,
|
||||
}
|
||||
|
||||
impl DiskWrapper {
|
||||
fn pt(disk: &mut dyn Disk) -> Option<PartitionTable> {
|
||||
let bs = match disk.block_length() {
|
||||
Ok(512) => LogicalBlockSize::Lb512,
|
||||
_ => return None,
|
||||
};
|
||||
struct Device<'a, 'b> { disk: &'a mut dyn Disk, offset: u64, block_bytes: &'b mut [u8] }
|
||||
|
||||
impl<'a, 'b> Seek for Device<'a, 'b> {
|
||||
fn seek(&mut self, from: SeekFrom) -> io::Result<u64> {
|
||||
let size = i64::try_from(self.disk.size()).or(Err(io::Error::new(io::ErrorKind::Other, "Disk larger than 2^63 - 1 bytes")))?;
|
||||
|
||||
self.offset = match from {
|
||||
SeekFrom::Start(new_pos) => cmp::min(self.disk.size(), new_pos),
|
||||
SeekFrom::Current(new_pos) => cmp::max(0, cmp::min(size, self.offset as i64 + new_pos)) as u64,
|
||||
SeekFrom::End(new_pos) => cmp::max(0, cmp::min(size + new_pos, size)) as u64,
|
||||
};
|
||||
|
||||
Ok(self.offset)
|
||||
}
|
||||
}
|
||||
// TODO: Perhaps this impl should be used in the rest of the scheme.
|
||||
impl<'a, 'b> Read for Device<'a, 'b> {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
let blksize = self.disk.block_length().map_err(|err| io::Error::from_raw_os_error(err.errno))?;
|
||||
let size_in_blocks = self.disk.size() / u64::from(blksize);
|
||||
|
||||
let disk = &mut self.disk;
|
||||
|
||||
let read_block = |block: u64, block_bytes: &mut [u8]| {
|
||||
if block >= size_in_blocks {
|
||||
return Err(io::Error::from_raw_os_error(syscall::EOVERFLOW));
|
||||
}
|
||||
loop {
|
||||
match disk.read(block, block_bytes) {
|
||||
Ok(Some(bytes)) => {
|
||||
assert_eq!(bytes, block_bytes.len());
|
||||
assert_eq!(bytes, blksize as usize);
|
||||
return Ok(());
|
||||
}
|
||||
Ok(None) => { std::thread::yield_now(); continue }
|
||||
Err(err) => return Err(io::Error::from_raw_os_error(err.errno)),
|
||||
}
|
||||
}
|
||||
};
|
||||
let bytes_read = block_io_wrapper::read(self.offset, blksize, buf, self.block_bytes, read_block)?;
|
||||
|
||||
self.offset += bytes_read as u64;
|
||||
Ok(bytes_read)
|
||||
}
|
||||
}
|
||||
|
||||
let mut block_bytes = [0u8; 4096];
|
||||
|
||||
partitionlib::get_partitions(&mut Device { disk, offset: 0, block_bytes: &mut block_bytes[..bs.into()] }, bs).ok().flatten()
|
||||
}
|
||||
fn new(mut disk: Box<dyn Disk>) -> Self {
|
||||
Self {
|
||||
pt: Self::pt(&mut *disk),
|
||||
disk,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Deref for DiskWrapper {
|
||||
type Target = dyn Disk;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&*self.disk
|
||||
}
|
||||
}
|
||||
impl std::ops::DerefMut for DiskWrapper {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut *self.disk
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DiskScheme {
|
||||
scheme_name: String,
|
||||
disks: Box<[DiskWrapper]>,
|
||||
handles: BTreeMap<usize, Handle>,
|
||||
next_id: usize
|
||||
}
|
||||
|
||||
impl DiskScheme {
|
||||
pub fn new(scheme_name: String, disks: Vec<Box<dyn Disk>>) -> DiskScheme {
|
||||
DiskScheme {
|
||||
scheme_name: scheme_name,
|
||||
disks: disks.into_iter().map(DiskWrapper::new).collect::<Vec<_>>().into_boxed_slice(),
|
||||
handles: BTreeMap::new(),
|
||||
next_id: 0
|
||||
}
|
||||
}
|
||||
|
||||
// Checks if any conflicting handles already exist
|
||||
fn check_locks(&self, disk_i: usize, part_i_opt: Option<u32>) -> Result<()> {
|
||||
for (_, handle) in self.handles.iter() {
|
||||
match handle {
|
||||
Handle::Disk(i, _) => if disk_i == *i {
|
||||
return Err(Error::new(ENOLCK));
|
||||
},
|
||||
Handle::Partition(i, p, _) => if disk_i == *i {
|
||||
match part_i_opt {
|
||||
Some(part_i) => if part_i == *p {
|
||||
return Err(Error::new(ENOLCK));
|
||||
},
|
||||
None => {
|
||||
return Err(Error::new(ENOLCK));
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl SchemeBlockMut for DiskScheme {
|
||||
fn open(&mut self, path: &str, flags: usize, uid: u32, _gid: u32) -> Result<Option<usize>> {
|
||||
if uid == 0 {
|
||||
let path_str = path.trim_matches('/');
|
||||
if path_str.is_empty() {
|
||||
if flags & O_DIRECTORY == O_DIRECTORY || flags & O_STAT == O_STAT {
|
||||
let mut list = String::new();
|
||||
|
||||
for (disk_index, disk) in self.disks.iter().enumerate() {
|
||||
write!(list, "{}\n", disk_index).unwrap();
|
||||
|
||||
if disk.pt.is_none() {
|
||||
continue
|
||||
}
|
||||
for part_index in 0..disk.pt.as_ref().unwrap().partitions.len() {
|
||||
write!(list, "{}p{}\n", disk_index, part_index).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
let id = self.next_id;
|
||||
self.next_id += 1;
|
||||
self.handles.insert(id, Handle::List(list.into_bytes(), 0));
|
||||
Ok(Some(id))
|
||||
} else {
|
||||
Err(Error::new(EISDIR))
|
||||
}
|
||||
} else if let Some(p_pos) = path_str.chars().position(|c| c == 'p') {
|
||||
let disk_id_str = &path_str[..p_pos];
|
||||
if p_pos + 1 >= path_str.len() {
|
||||
return Err(Error::new(ENOENT));
|
||||
}
|
||||
let part_id_str = &path_str[p_pos + 1..];
|
||||
let i = disk_id_str.parse::<usize>().or(Err(Error::new(ENOENT)))?;
|
||||
let p = part_id_str.parse::<u32>().or(Err(Error::new(ENOENT)))?;
|
||||
|
||||
if let Some(disk) = self.disks.get(i) {
|
||||
if disk.pt.is_none() || disk.pt.as_ref().unwrap().partitions.get(p as usize).is_none() {
|
||||
return Err(Error::new(ENOENT));
|
||||
}
|
||||
|
||||
self.check_locks(i, Some(p))?;
|
||||
|
||||
let id = self.next_id;
|
||||
self.next_id += 1;
|
||||
self.handles.insert(id, Handle::Partition(i, p, 0));
|
||||
Ok(Some(id))
|
||||
} else {
|
||||
Err(Error::new(ENOENT))
|
||||
}
|
||||
} else {
|
||||
let i = path_str.parse::<usize>().or(Err(Error::new(ENOENT)))?;
|
||||
|
||||
if self.disks.get(i).is_some() {
|
||||
self.check_locks(i, None)?;
|
||||
|
||||
let id = self.next_id;
|
||||
self.next_id += 1;
|
||||
self.handles.insert(id, Handle::Disk(i, 0));
|
||||
Ok(Some(id))
|
||||
} else {
|
||||
Err(Error::new(ENOENT))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Err(Error::new(EACCES))
|
||||
}
|
||||
}
|
||||
|
||||
fn dup(&mut self, id: usize, buf: &[u8]) -> Result<Option<usize>> {
|
||||
if ! buf.is_empty() {
|
||||
return Err(Error::new(EINVAL));
|
||||
}
|
||||
|
||||
let new_handle = {
|
||||
let handle = self.handles.get(&id).ok_or(Error::new(EBADF))?;
|
||||
handle.clone()
|
||||
};
|
||||
|
||||
let new_id = self.next_id;
|
||||
self.next_id += 1;
|
||||
self.handles.insert(new_id, new_handle);
|
||||
Ok(Some(new_id))
|
||||
}
|
||||
|
||||
fn fstat(&mut self, id: usize, stat: &mut Stat) -> Result<Option<usize>> {
|
||||
match *self.handles.get(&id).ok_or(Error::new(EBADF))? {
|
||||
Handle::List(ref data, _) => {
|
||||
stat.st_mode = MODE_DIR;
|
||||
stat.st_size = data.len() as u64;
|
||||
Ok(Some(0))
|
||||
},
|
||||
Handle::Disk(number, _) => {
|
||||
let disk = self.disks.get_mut(number).ok_or(Error::new(EBADF))?;
|
||||
stat.st_mode = MODE_FILE;
|
||||
stat.st_size = disk.size();
|
||||
stat.st_blksize = disk.block_length()?;
|
||||
Ok(Some(0))
|
||||
}
|
||||
Handle::Partition(disk_id, part_num, _) => {
|
||||
let disk = self.disks.get_mut(disk_id).ok_or(Error::new(EBADF))?;
|
||||
let size = {
|
||||
let pt = disk.pt.as_ref().ok_or(Error::new(EBADF))?;
|
||||
let partition = pt.partitions.get(part_num as usize).ok_or(Error::new(EBADF))?;
|
||||
partition.size
|
||||
};
|
||||
|
||||
stat.st_mode = MODE_FILE; // TODO: Block device?
|
||||
stat.st_size = size * u64::from(disk.block_length()?);
|
||||
stat.st_blksize = disk.block_length()?;
|
||||
stat.st_blocks = size;
|
||||
Ok(Some(0))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn fpath(&mut self, id: usize, buf: &mut [u8]) -> Result<Option<usize>> {
|
||||
let handle = self.handles.get(&id).ok_or(Error::new(EBADF))?;
|
||||
|
||||
let mut i = 0;
|
||||
|
||||
let scheme_name = self.scheme_name.as_bytes();
|
||||
let mut j = 0;
|
||||
while i < buf.len() && j < scheme_name.len() {
|
||||
buf[i] = scheme_name[j];
|
||||
i += 1;
|
||||
j += 1;
|
||||
}
|
||||
|
||||
if i < buf.len() {
|
||||
buf[i] = b':';
|
||||
i += 1;
|
||||
}
|
||||
|
||||
match *handle {
|
||||
Handle::List(_, _) => (),
|
||||
Handle::Disk(number, _) => {
|
||||
let number_str = format!("{}", number);
|
||||
let number_bytes = number_str.as_bytes();
|
||||
j = 0;
|
||||
while i < buf.len() && j < number_bytes.len() {
|
||||
buf[i] = number_bytes[j];
|
||||
i += 1;
|
||||
j += 1;
|
||||
}
|
||||
}
|
||||
Handle::Partition(disk_num, part_num, _) => {
|
||||
let path = format!("{}p{}", disk_num, part_num);
|
||||
let path_bytes = path.as_bytes();
|
||||
j = 0;
|
||||
while i < buf.len() && j < path_bytes.len() {
|
||||
buf[i] = path_bytes[j];
|
||||
i += 1;
|
||||
j += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Some(i))
|
||||
}
|
||||
|
||||
fn read(&mut self, id: usize, buf: &mut [u8]) -> Result<Option<usize>> {
|
||||
match *self.handles.get_mut(&id).ok_or(Error::new(EBADF))? {
|
||||
Handle::List(ref handle, ref mut size) => {
|
||||
let count = (&handle[*size..]).read(buf).unwrap();
|
||||
*size += count;
|
||||
Ok(Some(count))
|
||||
},
|
||||
Handle::Disk(number, ref mut size) => {
|
||||
let disk = self.disks.get_mut(number).ok_or(Error::new(EBADF))?;
|
||||
let blk_len = disk.block_length()?;
|
||||
if let Some(count) = disk.read((*size as u64)/(blk_len as u64), buf)? {
|
||||
*size += count;
|
||||
Ok(Some(count))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
Handle::Partition(disk_num, part_num, ref mut position) => {
|
||||
let disk = self.disks.get_mut(disk_num).ok_or(Error::new(EBADF))?;
|
||||
let blksize = disk.block_length()?;
|
||||
|
||||
// validate that we're actually reading within the bounds of the partition
|
||||
let rel_block = *position as u64 / blksize as u64;
|
||||
|
||||
let abs_block = {
|
||||
let pt = disk.pt.as_ref().ok_or(Error::new(EBADF))?;
|
||||
let partition = pt.partitions.get(part_num as usize).ok_or(Error::new(EBADF))?;
|
||||
|
||||
let abs_block = partition.start_lba + rel_block;
|
||||
if rel_block >= partition.size {
|
||||
return Err(Error::new(EOVERFLOW));
|
||||
}
|
||||
abs_block
|
||||
};
|
||||
|
||||
if let Some(count) = disk.read(abs_block, buf)? {
|
||||
Ok(Some(count))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn write(&mut self, id: usize, buf: &[u8]) -> Result<Option<usize>> {
|
||||
match *self.handles.get_mut(&id).ok_or(Error::new(EBADF))? {
|
||||
Handle::List(_, _) => {
|
||||
Err(Error::new(EBADF))
|
||||
},
|
||||
Handle::Disk(number, ref mut size) => {
|
||||
let disk = self.disks.get_mut(number).ok_or(Error::new(EBADF))?;
|
||||
let blk_len = disk.block_length()?;
|
||||
if let Some(count) = disk.write((*size as u64)/(blk_len as u64), buf)? {
|
||||
*size += count;
|
||||
Ok(Some(count))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
Handle::Partition(disk_num, part_num, ref mut position) => {
|
||||
let disk = self.disks.get_mut(disk_num).ok_or(Error::new(EBADF))?;
|
||||
let blksize = disk.block_length()?;
|
||||
|
||||
// validate that we're actually reading within the bounds of the partition
|
||||
let rel_block = *position as u64 / blksize as u64;
|
||||
|
||||
let abs_block = {
|
||||
let pt = disk.pt.as_ref().ok_or(Error::new(EBADF))?;
|
||||
let partition = pt.partitions.get(part_num as usize).ok_or(Error::new(EBADF))?;
|
||||
|
||||
let abs_block = partition.start_lba + rel_block;
|
||||
if rel_block >= partition.size {
|
||||
return Err(Error::new(EOVERFLOW));
|
||||
}
|
||||
abs_block
|
||||
};
|
||||
|
||||
if let Some(count) = disk.write(abs_block, buf)? {
|
||||
Ok(Some(count))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn seek(&mut self, id: usize, pos: isize, whence: usize) -> Result<Option<isize>> {
|
||||
let pos = pos as usize;
|
||||
|
||||
match *self.handles.get_mut(&id).ok_or(Error::new(EBADF))? {
|
||||
Handle::List(ref mut handle, ref mut size) => {
|
||||
let len = handle.len() as usize;
|
||||
*size = match whence {
|
||||
SEEK_SET => cmp::min(len, pos),
|
||||
SEEK_CUR => cmp::max(0, cmp::min(len as isize, *size as isize + pos as isize)) as usize,
|
||||
SEEK_END => cmp::max(0, cmp::min(len as isize, len as isize + pos as isize)) as usize,
|
||||
_ => return Err(Error::new(EINVAL))
|
||||
};
|
||||
|
||||
Ok(Some(*size as isize))
|
||||
},
|
||||
Handle::Disk(number, ref mut size) => {
|
||||
let disk = self.disks.get_mut(number).ok_or(Error::new(EBADF))?;
|
||||
let len = disk.size() as usize;
|
||||
*size = match whence {
|
||||
SEEK_SET => cmp::min(len, pos),
|
||||
SEEK_CUR => cmp::max(0, cmp::min(len as isize, *size as isize + pos as isize)) as usize,
|
||||
SEEK_END => cmp::max(0, cmp::min(len as isize, len as isize + pos as isize)) as usize,
|
||||
_ => return Err(Error::new(EINVAL))
|
||||
};
|
||||
|
||||
Ok(Some(*size as isize))
|
||||
}
|
||||
Handle::Partition(disk_num, part_num, ref mut position) => {
|
||||
let disk = self.disks.get_mut(disk_num).ok_or(Error::new(EBADF))?;
|
||||
let block_count = disk.pt.as_ref().ok_or(Error::new(EBADF))?.partitions.get(part_num as usize).ok_or(Error::new(EBADF))?.size;
|
||||
let len = u64::from(disk.block_length()?) * block_count;
|
||||
|
||||
*position = match whence {
|
||||
SEEK_SET => cmp::min(len as usize, pos) as usize, // Why isn't pos u64?
|
||||
SEEK_CUR => cmp::max(0, cmp::min(len as isize, *position as isize + pos as isize)) as usize,
|
||||
SEEK_END => cmp::max(0, cmp::min(len as isize, len as isize + pos as isize)) as usize,
|
||||
_ => return Err(Error::new(EINVAL)),
|
||||
};
|
||||
Ok(Some(*position as isize))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn close(&mut self, id: usize) -> Result<Option<usize>> {
|
||||
self.handles.remove(&id).ok_or(Error::new(EBADF)).and(Ok(Some(0)))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,776 @@
|
||||
use std::{sync::{Mutex, RwLock}, time::{self, Duration}, thread};
|
||||
use syscall::{io::Mmio, Io, Error, Result, EINVAL};
|
||||
use core::ptr::{read_volatile, write_volatile};
|
||||
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
#[inline(always)]
|
||||
pub(crate) unsafe fn wait_cycles(mut n: usize) {
|
||||
use core::arch::asm;
|
||||
|
||||
while n > 0 {
|
||||
asm!("nop");
|
||||
n -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
#[inline(always)]
|
||||
pub(crate) unsafe fn wait_msec(mut n: usize) {
|
||||
use core::arch::asm;
|
||||
|
||||
let mut f: usize;
|
||||
let mut t: usize;
|
||||
let mut r: usize;
|
||||
|
||||
asm!("mrs {0}, cntfrq_el0", out(reg) f);
|
||||
asm!("mrs {0}, cntpct_el0", out(reg) t);
|
||||
|
||||
|
||||
t += ((f / 1000) * n) / 1000;
|
||||
|
||||
loop {
|
||||
asm!("mrs {0}, cntpct_el0", out(reg) r);
|
||||
if r >= t {
|
||||
break;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
#[inline(always)]
|
||||
pub(crate) unsafe fn wait_msec(mut n: usize) {
|
||||
thread::sleep(Duration::from_millis(n as u64));
|
||||
}
|
||||
|
||||
//cmd Flags
|
||||
const CMD_NEED_APP: u32 = 0x8000_0000;
|
||||
const CMD_RSPNS_48: u32 = 0x0002_0000;
|
||||
const CMD_ERRORS_MASK: u32 = 0xfff9_c004;
|
||||
const CMD_RCA_MASK: u32 = 0xffff_0000;
|
||||
|
||||
//CMD
|
||||
const CMD_GO_IDLE: u32 = 0x0000_0000;
|
||||
const CMD_ALL_SEND_CID: u32 = 0x0201_0000;
|
||||
const CMD_SEND_CSD: u32 = 0x0901_0000;
|
||||
const CMD_SEND_REL_ADDR: u32 = 0x0302_0000;
|
||||
const CMD_CARD_SELECT: u32 = 0x0703_0000;
|
||||
const CMD_SEND_IF_COND: u32 = 0x0802_0000;
|
||||
const CMD_STOP_TRANS: u32 = 0x0c03_0000;
|
||||
const CMD_READ_SINGLE: u32 = 0x1122_0010;
|
||||
const CMD_READ_MULTI: u32 = 0x1222_0032;
|
||||
const CMD_SET_BLOCKCNT: u32 = 0x1702_0000;
|
||||
const CMD_WRITE_SINGLE:u32 = 0x1822_0000;
|
||||
const CMD_WRITE_MULTI:u32 = 0x1922_0022;
|
||||
|
||||
const CMD_APP_CMD: u32 = 0x3700_0000;
|
||||
const CMD_SET_BUS_WIDTH: u32 = 0x0602_0000|CMD_NEED_APP;
|
||||
const CMD_SEND_OP_COND: u32 = 0x2902_0000|CMD_NEED_APP;
|
||||
const CMD_SEND_SCR: u32 = 0x3322_0010|CMD_NEED_APP;
|
||||
|
||||
|
||||
|
||||
//STATUS register settings
|
||||
const SR_READ_AVAILABLE: u32 = 0x0000_0800;
|
||||
const SR_WRITE_AVAILABLE: u32 = 0x0000_0400;
|
||||
const SR_DAT_INHIBIT: u32 = 0x0000_0002;
|
||||
const SR_CMD_INHIBIT: u32 = 0x0000_0001;
|
||||
const SR_APP_CMD: u32 = 0x0000_0020;
|
||||
|
||||
//CONTROL register settings
|
||||
|
||||
const C0_SPI_MODE_EN: u32 = 0x0010_0000;
|
||||
const C0_HCTL_HS_EN: u32 = 0x0000_0004;
|
||||
const C0_HCTL_DWITDH: u32 = 0x0000_0002;
|
||||
|
||||
const C1_SRST_DATA: u32 = 0x0400_0000;
|
||||
const C1_SRST_CMD: u32 = 0x0200_0000;
|
||||
const C1_SRST_HC: u32 = 0x0100_0000;
|
||||
const C1_TOUNIT_DIS: u32 = 0x000f_0000;
|
||||
const C1_TOUNIT_MAX: u32 = 0x000e_0000;
|
||||
const C1_CLK_GENSEL: u32 = 0x0000_0020;
|
||||
const C1_CLK_EN: u32 = 0x0000_0004;
|
||||
const C1_CLK_STABLE: u32 = 0x0000_0002;
|
||||
const C1_CLK_INTLEN: u32 = 0x0000_0001;
|
||||
|
||||
|
||||
//INTERRUPT register settings
|
||||
const INT_DATA_TIMEOUT: u32 = 0x0010_0000;
|
||||
const INT_CMD_TIMEOUT: u32 = 0x0001_0000;
|
||||
const INT_READ_RDY: u32 = 0x0000_0020;
|
||||
const INT_WRITE_RDY: u32 = 0x0000_0010;
|
||||
const INT_DATA_DONE: u32 = 0x0000_0002;
|
||||
const INT_CMD_DONE: u32 = 0x0000_0001;
|
||||
const INT_ERROR_MASK: u32 = 0x017e_8000;
|
||||
|
||||
const HOST_SPEC_VERSION_OFFSET: u32 = 16;
|
||||
const HOST_SPEC_VERSION_MASK: u32 = 0x00ff_0000;
|
||||
const HOST_SPEC_V3: u32 = 2;
|
||||
const HOST_SPEC_V2: u32 = 1;
|
||||
const HOST_SPEC_V1: u32 = 0;
|
||||
|
||||
const ACMD41_VOLTAGE: u32 = 0x00ff_8000;
|
||||
const ACMD41_CMD_COMPLETE: u32 = 0x8000_0000;
|
||||
const ACMD41_CMD_CCS: u32 = 0x4000_0000;
|
||||
const ACMD41_ARG_HC: u32 = 0x51ff_8000;
|
||||
|
||||
const SCR_SD_BUS_WIDTH_4: u32 = 0x0000_0400;
|
||||
const SCR_SUPP_SET_BLKCNT: u32 = 0x0200_0000;
|
||||
//added by bztsrc driver
|
||||
const SCR_SUPP_CCS: u32 = 0x0000_0001;
|
||||
|
||||
#[repr(packed)]
|
||||
pub struct SdHostCtrlRegs {
|
||||
//LSB
|
||||
|
||||
//ACMD23 Argument
|
||||
_arg2: Mmio<u32>,
|
||||
|
||||
//Block Size and Count
|
||||
blksizecnt: Mmio<u32>,
|
||||
|
||||
//Argument
|
||||
arg1: Mmio<u32>,
|
||||
|
||||
//Command and Transfer Mode
|
||||
cmdtm: Mmio<u32>,
|
||||
|
||||
//Response bit 0-127
|
||||
resp0: Mmio<u32>,
|
||||
resp1: Mmio<u32>,
|
||||
resp2: Mmio<u32>,
|
||||
resp3: Mmio<u32>,
|
||||
|
||||
//Data
|
||||
data: Mmio<u32>,
|
||||
|
||||
//Status
|
||||
status: Mmio<u32>,
|
||||
|
||||
//Host Configuration bits
|
||||
control0: Mmio<u32>,
|
||||
|
||||
//Host Configuration bits
|
||||
control1: Mmio<u32>,
|
||||
|
||||
//Interrupt Flags
|
||||
interrupt: Mmio<u32>,
|
||||
|
||||
//Interrupt Flag Enable
|
||||
irpt_mask: Mmio<u32>,
|
||||
|
||||
//Interrupt Generation Enable
|
||||
irpt_en: Mmio<u32>,
|
||||
|
||||
//Host Configuration bits
|
||||
_control2: Mmio<u32>,
|
||||
|
||||
_rsvd: [Mmio<u32>; 47],
|
||||
|
||||
//Slot Interrupt Status and Version
|
||||
slotisr_ver: Mmio<u32>
|
||||
}
|
||||
|
||||
//TODO: refactor, sd/sdhci/bcmh2835-sdhci three different modules.
|
||||
pub struct SdHostCtrl {
|
||||
regs: RwLock<&'static mut SdHostCtrlRegs>,
|
||||
host_spec_ver: u32,
|
||||
cid: [u32; 4],
|
||||
csd: [u32; 4],
|
||||
rca: u32, //relative card address
|
||||
scr: [u32; 2],
|
||||
ocr: u32,
|
||||
size: u64
|
||||
}
|
||||
|
||||
impl SdHostCtrl {
|
||||
pub fn new(address: usize) -> Self {
|
||||
SdHostCtrl {
|
||||
regs: RwLock::new(unsafe { &mut *(address as *mut SdHostCtrlRegs)}),
|
||||
host_spec_ver: 0,
|
||||
cid: [0; 4],
|
||||
csd: [0; 4],
|
||||
rca: 0,
|
||||
scr: [0; 2],
|
||||
ocr: 0,
|
||||
size: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn init(&mut self) {
|
||||
let regs = self.regs.get_mut().unwrap();
|
||||
|
||||
let mut reg_val = regs.slotisr_ver.read();
|
||||
self.host_spec_ver = (reg_val & HOST_SPEC_VERSION_MASK) >> HOST_SPEC_VERSION_OFFSET;
|
||||
|
||||
regs.control0.write(0x0);
|
||||
reg_val = regs.control1.read();
|
||||
regs.control1.write(reg_val | C1_SRST_HC);
|
||||
let mut cnt = 1000;
|
||||
while (cnt >= 0) &&
|
||||
((regs.control1.read() & C1_SRST_HC) == C1_SRST_HC) {
|
||||
cnt -= 1;
|
||||
wait_msec(10);
|
||||
}
|
||||
|
||||
if cnt < 0 {
|
||||
println!("ERROR: failed to reset EMMC");
|
||||
return ;
|
||||
}
|
||||
println!("EMMC: reset OK");
|
||||
reg_val = regs.control1.read();
|
||||
regs.control1.write(reg_val | C1_CLK_INTLEN | C1_TOUNIT_MAX);
|
||||
|
||||
wait_msec(10);
|
||||
|
||||
{
|
||||
if let Err(_) = self.set_clock(40_0000) {
|
||||
println!("ERROR: failed to set clock {}", 40_0000);
|
||||
return ;
|
||||
}
|
||||
}
|
||||
|
||||
let regs = self.regs.get_mut().unwrap();
|
||||
regs.irpt_en.write(0xffff_ffff);
|
||||
regs.irpt_mask.write(0xffff_ffff);
|
||||
|
||||
if let Err(_) = self.sd_cmd(CMD_GO_IDLE, 0) {
|
||||
println!("failed to go idle");
|
||||
return ;
|
||||
}
|
||||
|
||||
if let Err(_) = self.sd_cmd(CMD_SEND_IF_COND, 0x0000_01aa) {
|
||||
println!("failed to send if cond");
|
||||
return ;
|
||||
}
|
||||
|
||||
cnt = 6;
|
||||
reg_val = 0;
|
||||
|
||||
while ((reg_val & ACMD41_CMD_COMPLETE) == 0) && cnt > 0 {
|
||||
wait_msec(10);
|
||||
cnt -= 1;
|
||||
|
||||
if let Ok(val) = self.sd_cmd(CMD_SEND_OP_COND, ACMD41_ARG_HC) {
|
||||
reg_val = val;
|
||||
self.ocr = reg_val;
|
||||
print!("EMMC: CMD_SEND_OP_COND returned 0x{:08x} = ", reg_val);
|
||||
|
||||
if (reg_val & ACMD41_CMD_COMPLETE) != 0 {
|
||||
print!("COMPLETE ");
|
||||
}
|
||||
if (reg_val & ACMD41_VOLTAGE) != 0 {
|
||||
print!("VOLTAGE ");
|
||||
}
|
||||
if (reg_val & ACMD41_CMD_CCS) != 0 {
|
||||
print!("CCS ");
|
||||
}
|
||||
print!("\n");
|
||||
} else {
|
||||
println!("ERROR: EMMC ACMD41 returned error");
|
||||
return ;
|
||||
}
|
||||
}
|
||||
|
||||
if (reg_val & ACMD41_CMD_COMPLETE) == 0 || cnt <= 0 {
|
||||
println!("ACMD41 TIMEOUT");
|
||||
return ;
|
||||
}
|
||||
|
||||
if (reg_val & ACMD41_VOLTAGE) == 0 {
|
||||
println!("ACMD41 VOLTAGE NOT FOUND!");
|
||||
return ;
|
||||
}
|
||||
|
||||
let ccs = if (reg_val & ACMD41_CMD_CCS) != 0 { SCR_SUPP_CCS } else { 0 };
|
||||
|
||||
if let Err(_) = self.sd_cmd(CMD_ALL_SEND_CID, 0) {
|
||||
println!("CMD_ALL_SEND_CID ERROR, IGNORE!");
|
||||
}
|
||||
|
||||
let sd_rca = self.sd_cmd(CMD_SEND_REL_ADDR, 0x0).unwrap();
|
||||
println!("CMD_SEND_REL_ADDR = 0x{:08x}", sd_rca);
|
||||
self.rca = sd_rca;
|
||||
|
||||
if let Err(_) = self.sd_cmd(CMD_SEND_CSD, sd_rca) {
|
||||
println!("failed to get csd");
|
||||
return ;
|
||||
}
|
||||
|
||||
let (csize, cmult) = if (self.ocr & ACMD41_CMD_CCS) != 0 {
|
||||
let csize = (self.csd[1] & 0x3f) << 16 | (self.csd[2] & 0xffff_0000) >> 16;
|
||||
let cmult = 8;
|
||||
(csize as u64, cmult as u64)
|
||||
} else {
|
||||
let csize = (self.csd[1] & 0x3ff) << 2 | (self.csd[2] & 0xc000_0000) >> 30;
|
||||
let cmult = (self.csd[2] & 0x0003_8000) >> 15;
|
||||
(csize as u64, cmult as u64)
|
||||
};
|
||||
self.size = ((csize + 1) << (cmult + 2)) * 512;
|
||||
println!("mmc size = 0x{:08x}", self.size);
|
||||
|
||||
|
||||
if let Err(_) = self.set_clock(2500_0000) {
|
||||
println!("failed to set clock 2500_0000 Hz");
|
||||
return ;
|
||||
}
|
||||
|
||||
if let Err(_) = self.sd_cmd(CMD_CARD_SELECT, sd_rca) {
|
||||
println!("failed to CMD_CARD_SELECT 0x{:08x}", sd_rca);
|
||||
return ;
|
||||
}
|
||||
|
||||
if let Err(_) = self.sd_status(SR_DAT_INHIBIT) {
|
||||
println!("SR_DAT_INHIBIT return");
|
||||
return ;
|
||||
}
|
||||
|
||||
let regs = self.regs.get_mut().unwrap();
|
||||
regs.blksizecnt.write(1 << 16 | 8);
|
||||
|
||||
if let Err(_) = self.sd_cmd(CMD_SEND_SCR, 0) {
|
||||
println!("failed to CMD_SEND_SCR");
|
||||
return ;
|
||||
}
|
||||
|
||||
if let Err(_) = self.sd_int(INT_READ_RDY) {
|
||||
println!("failed to INT_READ_RDY");
|
||||
return ;
|
||||
}
|
||||
|
||||
cnt = 10000;
|
||||
let mut i = 0;
|
||||
let regs = self.regs.get_mut().unwrap();
|
||||
while i < 2 && cnt > 0 {
|
||||
reg_val = regs.status.read();
|
||||
cnt -= 1;
|
||||
if (reg_val & SR_READ_AVAILABLE) != 0 {
|
||||
self.scr[i] = regs.data.read();
|
||||
i += 1;
|
||||
} else {
|
||||
wait_msec(10);
|
||||
cnt -= 1;
|
||||
}
|
||||
}
|
||||
if i != 2 {
|
||||
println!("SD TIMEOUT FOR SCR[; 2]");
|
||||
return ;
|
||||
}
|
||||
|
||||
if (self.scr[0] & SCR_SD_BUS_WIDTH_4) != 0 {
|
||||
if let Err(_) = self.sd_cmd(CMD_SET_BUS_WIDTH, sd_rca | 2) {
|
||||
println!("failed to set bus width, {}", sd_rca | 2);
|
||||
return ;
|
||||
}
|
||||
let regs = self.regs.get_mut().unwrap();
|
||||
regs.control0.write(C0_HCTL_DWITDH);
|
||||
}
|
||||
|
||||
print!("EMMC: supports ");
|
||||
|
||||
if (self.scr[0] & SCR_SUPP_SET_BLKCNT) != 0 {
|
||||
print!("SET_BLKCNT ");
|
||||
}
|
||||
|
||||
if ccs != 0 {
|
||||
print!("CCS ");
|
||||
}
|
||||
|
||||
print!("\n");
|
||||
|
||||
self.scr[0] &= !SCR_SUPP_CCS;
|
||||
self.scr[0] |= ccs;
|
||||
}
|
||||
|
||||
pub unsafe fn set_clock(&mut self, freq: u32) -> Result<()> {
|
||||
let mut cnt: i32 = 0;
|
||||
let regs = self.regs.get_mut().unwrap();
|
||||
|
||||
let mut reg_val = regs.status.read() & (SR_CMD_INHIBIT | SR_DAT_INHIBIT);
|
||||
cnt = 10_0000;
|
||||
while (cnt > 0) && reg_val != 0 {
|
||||
wait_msec(1);
|
||||
cnt -= 1;
|
||||
reg_val = regs.status.read() & (SR_CMD_INHIBIT | SR_DAT_INHIBIT);
|
||||
}
|
||||
|
||||
if cnt <= 0 {
|
||||
println!("ERROR: TIMEOUT WAITING FOR INHIBIT FLAG");
|
||||
return Err(Error::new(EINVAL));
|
||||
}
|
||||
|
||||
reg_val = regs.control1.read();
|
||||
reg_val &= !C1_CLK_EN;
|
||||
regs.control1.write(reg_val);
|
||||
wait_msec(10);
|
||||
|
||||
let c = 4166_6666 / freq;
|
||||
let mut x: u32 = c - 1;
|
||||
let mut s: u32 = 32;
|
||||
|
||||
if x == 0 {
|
||||
s = 0;
|
||||
} else {
|
||||
if (x & 0xffff_0000) == 0 { x <<= 16; s -= 16; }
|
||||
if (x & 0xff00_0000) == 0 { x <<= 8; s -= 8; }
|
||||
if (x & 0xf000_0000) == 0 { x <<= 4; s -= 4; }
|
||||
if (x & 0xc000_0000) == 0 { x <<= 2; s -= 2; }
|
||||
if (x & 0x8000_0000) == 0 { x <<= 1; s -= 1; }
|
||||
if s > 0 { s -= 1; }
|
||||
if s > 7 { s = 7; }
|
||||
}
|
||||
let mut d = 0;
|
||||
if self.host_spec_ver > HOST_SPEC_V2 {
|
||||
d = c;
|
||||
} else {
|
||||
d = 1 << s;
|
||||
}
|
||||
|
||||
if d <= 2 {
|
||||
d = 2;
|
||||
s = 0;
|
||||
}
|
||||
println!("sd clk divisor: 0x{:08x}, shift: 0x{:08x}", d, s);
|
||||
|
||||
let mut h = 0;
|
||||
if self.host_spec_ver > HOST_SPEC_V2 {
|
||||
h = (d & 0x300) >> 2;
|
||||
}
|
||||
|
||||
d = ( ((d & 0xff) << 8)| h);
|
||||
reg_val = regs.control1.read() & 0xffff_003f;
|
||||
regs.control1.write(reg_val | d);
|
||||
wait_msec(10);
|
||||
reg_val = regs.control1.read();
|
||||
regs.control1.write(reg_val | C1_CLK_EN);
|
||||
wait_msec(10);
|
||||
|
||||
reg_val = regs.control1.read() & C1_CLK_STABLE;
|
||||
cnt = 10000;
|
||||
while cnt > 0 && reg_val == 0 {
|
||||
wait_msec(10);
|
||||
cnt -= 1;
|
||||
reg_val = regs.control1.read() & C1_CLK_STABLE;
|
||||
}
|
||||
|
||||
if cnt <= 0 {
|
||||
println!("ERROR: failed to get stable clock");
|
||||
return Err(Error::new(EINVAL));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub unsafe fn sd_cmd(&mut self, mut code: u32, arg: u32) -> Result<u32> {
|
||||
if (code & CMD_NEED_APP) != 0 {
|
||||
let pre_cmd = CMD_APP_CMD | if self.rca != 0 { CMD_RSPNS_48 } else { 0 };
|
||||
match self.sd_cmd(pre_cmd, self.rca) {
|
||||
Err(_) => {
|
||||
println!("ERROR: failed to send SD APP command");
|
||||
return Err(Error::new(EINVAL));
|
||||
}
|
||||
Ok(_) => {
|
||||
code &= !CMD_NEED_APP;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Err(_) = self.sd_status(SR_CMD_INHIBIT) {
|
||||
println!("ERROR: Emmc busy");
|
||||
return Err(Error::new(EINVAL));
|
||||
}
|
||||
|
||||
//println!("EMMC: Sending command 0x{:08x}, arg 0x{:08x}", code, arg);
|
||||
|
||||
let regs = self.regs.get_mut().unwrap();
|
||||
let mut reg_val = regs.interrupt.read();
|
||||
regs.interrupt.write(reg_val);
|
||||
regs.arg1.write(arg);
|
||||
regs.cmdtm.write(code);
|
||||
|
||||
if code == CMD_SEND_OP_COND {
|
||||
wait_msec(1000);
|
||||
} else if code == CMD_SEND_IF_COND || code == CMD_APP_CMD {
|
||||
wait_msec(200);
|
||||
}
|
||||
|
||||
if let Err(_) = self.sd_int(INT_CMD_DONE) {
|
||||
println!("ERROR: failed to send EMMC command");
|
||||
return Err(Error::new(EINVAL));
|
||||
}
|
||||
|
||||
let regs = self.regs.get_mut().unwrap();
|
||||
reg_val = regs.resp0.read();
|
||||
|
||||
if code == CMD_GO_IDLE || code == CMD_APP_CMD {
|
||||
return Ok(0);
|
||||
} else if code == (CMD_APP_CMD | CMD_RSPNS_48) {
|
||||
return Ok(reg_val & SR_APP_CMD);
|
||||
} else if code == CMD_SEND_OP_COND {
|
||||
return Ok(reg_val);
|
||||
} else if code == CMD_SEND_IF_COND {
|
||||
if reg_val == arg {
|
||||
return Ok(0);
|
||||
} else {
|
||||
return Err(Error::new(EINVAL));
|
||||
}
|
||||
} else if code == CMD_ALL_SEND_CID {
|
||||
self.cid[0] = reg_val;
|
||||
self.cid[1] = regs.resp1.read();
|
||||
self.cid[2] = regs.resp2.read();
|
||||
self.cid[3] = regs.resp3.read();
|
||||
|
||||
//FIXME: wrong implement, see CMD_SEND_CSD for detail
|
||||
return Ok(reg_val);
|
||||
} else if code == CMD_SEND_CSD {
|
||||
let tmp0 = reg_val;
|
||||
let tmp1 = regs.resp1.read();
|
||||
let tmp2 = regs.resp2.read();
|
||||
let tmp3 = regs.resp3.read();
|
||||
|
||||
self.csd[0] = tmp3 << 8 | tmp2 >> 24;
|
||||
self.csd[1] = tmp2 << 8 | tmp1 >> 24;
|
||||
self.csd[2] = tmp1 << 8 | tmp0 >> 24;
|
||||
self.csd[3] = tmp0 << 8;
|
||||
|
||||
//FIXME: support variable length of result.
|
||||
return Ok(reg_val);
|
||||
} else if code == CMD_SEND_REL_ADDR {
|
||||
let mut err = reg_val & 0x1fff;
|
||||
err |= ((reg_val & 0x2000) << 6);
|
||||
err |= ((reg_val & 0x4000) << 8);
|
||||
err |= ((reg_val & 0x8000) << 8);
|
||||
err &= CMD_ERRORS_MASK;
|
||||
|
||||
if err != 0 {
|
||||
return Err(Error::new(EINVAL));
|
||||
} else {
|
||||
return Ok(reg_val & CMD_RCA_MASK);
|
||||
}
|
||||
} else {
|
||||
return Ok(reg_val & CMD_ERRORS_MASK);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
pub unsafe fn sd_status(&mut self, mask: u32) -> Result<()> {
|
||||
let regs = self.regs.get_mut().unwrap();
|
||||
let mut cnt = 500000;
|
||||
|
||||
let mut reg_val = regs.status.read() & mask;
|
||||
let mut reg_val1 = regs.interrupt.read() & INT_ERROR_MASK;
|
||||
|
||||
while cnt > 0 && reg_val != 0 && reg_val1 == 0 {
|
||||
wait_msec(1);
|
||||
cnt -= 1;
|
||||
reg_val = regs.status.read() & mask;
|
||||
reg_val1 = regs.interrupt.read() & INT_ERROR_MASK;
|
||||
}
|
||||
reg_val1 = regs.interrupt.read() & INT_ERROR_MASK;
|
||||
|
||||
if cnt <= 0 || reg_val1 != 0 {
|
||||
return Err(Error::new(EINVAL));
|
||||
} else {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
pub unsafe fn sd_int(&mut self, mask: u32) -> Result<()> {
|
||||
let regs = self.regs.get_mut().unwrap();
|
||||
let mut cnt = 100_0000;
|
||||
let m = mask | INT_ERROR_MASK;
|
||||
|
||||
let mut reg_val = regs.interrupt.read() & m;
|
||||
|
||||
while cnt > 0 && reg_val == 0 {
|
||||
wait_msec(1);
|
||||
cnt -= 1;
|
||||
reg_val = regs.interrupt.read() & m;
|
||||
}
|
||||
reg_val = regs.interrupt.read();
|
||||
let err = reg_val & (INT_CMD_TIMEOUT | INT_DATA_TIMEOUT | INT_ERROR_MASK);
|
||||
|
||||
if cnt <= 0 || err != 0 {
|
||||
regs.interrupt.write(reg_val);
|
||||
return Err(Error::new(EINVAL));
|
||||
} else {
|
||||
regs.interrupt.write(mask);
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn sd_readblock(&mut self, lba: u32, buf: &mut [u32], num: u32) -> Result<usize> {
|
||||
let num = if num < 1 { 1 } else { num };
|
||||
let mut reg_val: usize = 0;
|
||||
|
||||
//println!("sd_readblock lba 0x{:x}, num 0x{:x}", lba, num);
|
||||
|
||||
if let Err(_) = self.sd_status(SR_DAT_INHIBIT) {
|
||||
println!("SR_DAT_INHIBIT TIMEOUT");
|
||||
return Err(Error::new(EINVAL));
|
||||
}
|
||||
|
||||
if (self.scr[0] & SCR_SUPP_CCS) != 0 {
|
||||
if num > 1 && ((self.scr[0] & SCR_SUPP_SET_BLKCNT) != 0) {
|
||||
if let Err(_) = self.sd_cmd(CMD_SET_BLOCKCNT, num) {
|
||||
println!("CMD_SET_BLOCKCNT ERROR");
|
||||
return Err(Error::new(EINVAL));
|
||||
}
|
||||
}
|
||||
let regs = self.regs.get_mut().unwrap();
|
||||
regs.blksizecnt.write((num) << 16 | 512);
|
||||
if num == 1 {
|
||||
self.sd_cmd(CMD_READ_SINGLE, lba).unwrap();
|
||||
} else {
|
||||
self.sd_cmd(CMD_READ_MULTI, lba).unwrap();
|
||||
}
|
||||
} else {
|
||||
let regs = self.regs.get_mut().unwrap();
|
||||
regs.blksizecnt.write(1 << 16 | 512);
|
||||
}
|
||||
|
||||
let mut cnt = 0;
|
||||
while cnt < num {
|
||||
if (self.scr[0] & SCR_SUPP_CCS) == 0 {
|
||||
self.sd_cmd(CMD_READ_SINGLE, (lba + cnt) * 512).unwrap();
|
||||
}
|
||||
|
||||
if let Err(_) = self.sd_int(INT_READ_RDY) {
|
||||
println!("ERROR: Timeout waiting for ready to read");
|
||||
return Err(Error::new(EINVAL));
|
||||
}
|
||||
|
||||
let regs = self.regs.get_mut().unwrap();
|
||||
regs.blksizecnt.write(1 << 16 | 512);
|
||||
for d in 0..128 {
|
||||
buf[(128 * cnt + d) as usize] = regs.data.read();
|
||||
}
|
||||
cnt += 1;
|
||||
}
|
||||
|
||||
if num > 1 && (self.scr[0] & SCR_SUPP_SET_BLKCNT) == 0 && (self.scr[0] & SCR_SUPP_CCS) != 0 {
|
||||
self.sd_cmd(CMD_STOP_TRANS, 0).unwrap();
|
||||
}
|
||||
Ok((num * 512) as usize)
|
||||
}
|
||||
|
||||
pub unsafe fn sd_writeblock(&mut self, lba: u32, buf: &[u32], num: u32) -> Result<usize> {
|
||||
let num = if num < 1 { 1 } else { num };
|
||||
let mut reg_val: usize = 0;
|
||||
|
||||
//println!("sd_writelock lba 0x{:x}, num 0x{:x}", lba, num);
|
||||
|
||||
if let Err(_) = self.sd_status(SR_DAT_INHIBIT | SR_WRITE_AVAILABLE) {
|
||||
println!("SR_DAT_INHIBIT TIMEOUT");
|
||||
return Err(Error::new(EINVAL));
|
||||
}
|
||||
|
||||
if (self.scr[0] & SCR_SUPP_CCS) != 0 {
|
||||
if num > 1 && ((self.scr[0] & SCR_SUPP_SET_BLKCNT) != 0) {
|
||||
if let Err(_) = self.sd_cmd(CMD_SET_BLOCKCNT, num) {
|
||||
println!("CMD_SET_BLOCKCNT ERROR");
|
||||
return Err(Error::new(EINVAL));
|
||||
}
|
||||
}
|
||||
let regs = self.regs.get_mut().unwrap();
|
||||
regs.blksizecnt.write((num) << 16 | 512);
|
||||
if num == 1 {
|
||||
self.sd_cmd(CMD_WRITE_SINGLE, lba).unwrap();
|
||||
} else {
|
||||
self.sd_cmd(CMD_WRITE_MULTI, lba).unwrap();
|
||||
}
|
||||
} else {
|
||||
let regs = self.regs.get_mut().unwrap();
|
||||
regs.blksizecnt.write(1 << 16 | 512);
|
||||
}
|
||||
|
||||
let mut cnt = 0;
|
||||
while cnt < num {
|
||||
if (self.scr[0] & SCR_SUPP_CCS) == 0 {
|
||||
self.sd_cmd(CMD_WRITE_SINGLE, (lba + cnt) * 512).unwrap();
|
||||
}
|
||||
|
||||
if let Err(_) = self.sd_int(INT_WRITE_RDY) {
|
||||
println!("ERROR: Timeout waiting for ready to write");
|
||||
return Err(Error::new(EINVAL));
|
||||
}
|
||||
|
||||
let regs = self.regs.get_mut().unwrap();
|
||||
regs.blksizecnt.write(1 << 16 | 512);
|
||||
for d in 0..128 {
|
||||
regs.data.write(buf[(128 * cnt + d) as usize]);
|
||||
}
|
||||
cnt += 1;
|
||||
}
|
||||
|
||||
if let Err(_) = self.sd_int(INT_DATA_DONE) {
|
||||
println!("ERROR: Timeout waiting for data done");
|
||||
return Err(Error::new(EINVAL));
|
||||
}
|
||||
|
||||
if num > 1 && (self.scr[0] & SCR_SUPP_SET_BLKCNT) == 0 && (self.scr[0] & SCR_SUPP_CCS) != 0 {
|
||||
self.sd_cmd(CMD_STOP_TRANS, 0).unwrap();
|
||||
}
|
||||
Ok((num * 512) as usize)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Disk {
|
||||
fn id(&self) -> usize;
|
||||
fn size(&mut self) -> u64;
|
||||
fn read(&mut self, block:u64, buffer: &mut [u8]) -> syscall::error::Result<Option<usize>>;
|
||||
fn write(&mut self, block:u64, buffer: &[u8]) -> syscall::error::Result<Option<usize>>;
|
||||
fn block_length(&mut self) -> syscall::error::Result<u32>;
|
||||
}
|
||||
|
||||
impl Disk for SdHostCtrl {
|
||||
fn id(&self) -> usize {
|
||||
0xdead_dead
|
||||
}
|
||||
|
||||
fn size(&mut self) -> u64 {
|
||||
//assert 512MiB
|
||||
self.size
|
||||
}
|
||||
|
||||
fn read(&mut self, block:u64, buffer: &mut [u8]) -> Result<Option<usize>> {
|
||||
if (buffer.len() % 512) != 0 {
|
||||
println!("buffer.len {} should be aligned to {}",
|
||||
buffer.len(), 512);
|
||||
return Err(Error::new(EINVAL));
|
||||
}
|
||||
let u32_len = buffer.len() / core::mem::size_of::<u32>();
|
||||
let num = buffer.len() / 512;
|
||||
let u8_ptr = buffer.as_mut_ptr();
|
||||
let ret = unsafe {
|
||||
let u32_buffer = core::slice::from_raw_parts_mut(u8_ptr as *mut u32, u32_len);
|
||||
self.sd_readblock(block as u32, u32_buffer, num as u32)
|
||||
};
|
||||
match ret {
|
||||
Ok(cnt) => Ok(Some(cnt)),
|
||||
Err(err) => Err(err)
|
||||
}
|
||||
}
|
||||
|
||||
fn write(&mut self, block:u64, buffer: &[u8]) -> Result<Option<usize>> {
|
||||
if (buffer.len() % 512) != 0 {
|
||||
println!("buffer.len {} should be aligned to {}",
|
||||
buffer.len(), 512);
|
||||
return Err(Error::new(EINVAL));
|
||||
}
|
||||
let u32_len = buffer.len() / core::mem::size_of::<u32>();
|
||||
let num = buffer.len() / 512;
|
||||
let u8_ptr = buffer.as_ptr();
|
||||
let ret = unsafe {
|
||||
let u32_buffer = core::slice::from_raw_parts(u8_ptr as *const u32, u32_len);
|
||||
self.sd_writeblock(block as u32, u32_buffer, num as u32)
|
||||
};
|
||||
match ret {
|
||||
Ok(cnt) => Ok(Some(cnt)),
|
||||
Err(err) => Err(err)
|
||||
}
|
||||
}
|
||||
|
||||
fn block_length(&mut self) -> Result<u32> {
|
||||
Ok(512)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -11,6 +11,7 @@ pub enum MemoryType {
|
||||
Writeback,
|
||||
Uncacheable,
|
||||
WriteCombining,
|
||||
DeviceMemory,
|
||||
}
|
||||
impl Default for MemoryType {
|
||||
fn default() -> Self {
|
||||
@@ -35,6 +36,7 @@ pub unsafe fn physmap(base_phys: usize, len: usize, Prot { read, write }: Prot,
|
||||
MemoryType::Writeback => "wb",
|
||||
MemoryType::Uncacheable => "uc",
|
||||
MemoryType::WriteCombining => "wc",
|
||||
MemoryType::DeviceMemory => "dev",
|
||||
});
|
||||
let mode = match (read, write) {
|
||||
(true, true) => O_RDWR,
|
||||
|
||||
Reference in New Issue
Block a user