use core::ops::Range;
use crate::{
mm::{
kspace::LINEAR_MAPPING_BASE_VADDR, paddr_to_vaddr, FallibleVmRead, FallibleVmWrite,
HasPaddr, Infallible, Paddr, PodOnce, Vaddr, VmIo, VmIoOnce, VmReader, VmWriter,
},
prelude::*,
Error,
};
#[derive(Debug, Clone)]
pub struct IoMem {
virtual_address: Vaddr,
limit: usize,
}
impl HasPaddr for IoMem {
fn paddr(&self) -> Paddr {
self.virtual_address - LINEAR_MAPPING_BASE_VADDR
}
}
impl IoMem {
pub(crate) unsafe fn new(range: Range<Paddr>) -> Self {
Self {
virtual_address: paddr_to_vaddr(range.start),
limit: range.len(),
}
}
pub fn paddr(&self) -> Paddr {
self.virtual_address - LINEAR_MAPPING_BASE_VADDR
}
pub fn length(&self) -> usize {
self.limit
}
pub fn slice(&self, range: Range<usize>) -> Self {
assert!(!range.is_empty() && range.end <= self.limit);
Self {
virtual_address: self.virtual_address + range.start,
limit: range.end - range.start,
}
}
}
impl IoMem {
fn reader(&self) -> VmReader<'_, Infallible> {
unsafe { VmReader::from_kernel_space(self.virtual_address as *mut u8, self.limit) }
}
fn writer(&self) -> VmWriter<'_, Infallible> {
unsafe { VmWriter::from_kernel_space(self.virtual_address as *mut u8, self.limit) }
}
}
impl VmIo for IoMem {
fn read(&self, offset: usize, writer: &mut VmWriter) -> Result<()> {
if self
.limit
.checked_sub(offset)
.is_none_or(|remain| remain < writer.avail())
{
return Err(Error::InvalidArgs);
}
self.reader()
.skip(offset)
.read_fallible(writer)
.map_err(|(e, _)| e)?;
debug_assert!(!writer.has_avail());
Ok(())
}
fn write(&self, offset: usize, reader: &mut VmReader) -> Result<()> {
if self
.limit
.checked_sub(offset)
.is_none_or(|avail| avail < reader.remain())
{
return Err(Error::InvalidArgs);
}
self.writer()
.skip(offset)
.write_fallible(reader)
.map_err(|(e, _)| e)?;
debug_assert!(!reader.has_remain());
Ok(())
}
}
impl VmIoOnce for IoMem {
fn read_once<T: PodOnce>(&self, offset: usize) -> Result<T> {
self.reader().skip(offset).read_once()
}
fn write_once<T: PodOnce>(&self, offset: usize, new_val: &T) -> Result<()> {
self.writer().skip(offset).write_once(new_val)
}
}