#![cfg_attr(target_os = "none", no_std)]
use core::{fmt::Display, ops::Deref, ptr::NonNull, sync::atomic::Ordering};
#[derive(thiserror::Error, Debug)]
pub enum MapError {
#[error("Invalid MMIO address or size")]
Invalid,
#[error("Failed to allocate memory for MMIO mapping")]
NoMemory,
#[error("MMIO address is already in use")]
Busy,
}
pub trait MmioOp: Sync + Send + 'static {
fn ioremap(&self, addr: MmioAddr, size: usize) -> Result<MmioRaw, MapError>;
fn iounmap(&self, mmio: &MmioRaw);
}
static mut MMIO_OP: Option<&'static dyn MmioOp> = None;
static INIT: core::sync::atomic::AtomicBool = core::sync::atomic::AtomicBool::new(false);
pub fn init(mmio_op: &'static dyn MmioOp) {
if INIT
.compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed)
.is_err()
{
return;
}
unsafe {
MMIO_OP = Some(mmio_op);
}
}
pub unsafe fn ioremap_raw(addr: MmioAddr, size: usize) -> Result<MmioRaw, MapError> {
let mmio_op = unsafe { MMIO_OP.expect("MmioOp is not initialized") };
mmio_op.ioremap(addr, size)
}
pub unsafe fn iounmap(mmio: &MmioRaw) {
let mmio_op = unsafe { MMIO_OP.expect("MmioOp is not initialized") };
mmio_op.iounmap(mmio);
}
pub fn ioremap(addr: MmioAddr, size: usize) -> Result<Mmio, MapError> {
let mmio = unsafe { ioremap_raw(addr, size)? };
Ok(Mmio(mmio))
}
#[derive(
Default,
derive_more::From,
derive_more::Into,
Clone,
Copy,
derive_more::Debug,
derive_more::Display,
PartialEq,
Eq,
PartialOrd,
Ord,
Hash,
)]
#[repr(transparent)]
#[debug("PhysAddr({_0:#x})")]
#[display("{_0:#x}")]
pub struct MmioAddr(usize);
impl MmioAddr {
pub fn as_usize(&self) -> usize {
self.0
}
}
impl From<u64> for MmioAddr {
fn from(value: u64) -> Self {
MmioAddr(value as usize)
}
}
#[derive(Debug, Clone)]
pub struct MmioRaw {
phys: MmioAddr,
virt: NonNull<u8>,
size: usize,
}
impl MmioRaw {
pub unsafe fn new(phys: MmioAddr, virt: NonNull<u8>, size: usize) -> Self {
MmioRaw { phys, virt, size }
}
pub fn phys_addr(&self) -> MmioAddr {
self.phys
}
pub fn as_slice(&self) -> &[u8] {
unsafe { core::slice::from_raw_parts(self.virt.as_ptr(), self.size) }
}
pub fn as_ptr(&self) -> *mut u8 {
self.virt.as_ptr()
}
pub fn as_nonnull_ptr(&self) -> NonNull<u8> {
self.virt
}
pub fn size(&self) -> usize {
self.size
}
pub fn read<T>(&self, offset: usize) -> T {
assert!(offset < self.size);
unsafe { self.virt.add(offset).cast::<T>().read_volatile() }
}
pub fn write<T>(&self, offset: usize, value: T) {
assert!(offset < self.size);
unsafe { self.virt.add(offset).cast::<T>().write_volatile(value) }
}
}
pub struct Mmio(MmioRaw);
impl Deref for Mmio {
type Target = MmioRaw;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl Drop for Mmio {
fn drop(&mut self) {
let mmio_op = unsafe { MMIO_OP.expect("MmioOp is not initialized") };
mmio_op.iounmap(self);
}
}
unsafe impl Send for MmioRaw {}
unsafe impl Sync for MmioRaw {}
impl Display for MmioRaw {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(
f,
"Mmio [{}, {:#x}) -> virt: {:#p}",
self.phys,
self.phys.0 + self.size,
self.virt
)
}
}
#[cfg(all(test, not(target_os = "none")))]
mod tests {
use super::MmioRaw;
struct DummyMmioOp;
impl super::MmioOp for DummyMmioOp {
fn ioremap(&self, addr: super::MmioAddr, size: usize) -> Result<MmioRaw, super::MapError> {
Ok(MmioRaw {
phys: addr,
virt: core::ptr::NonNull::dangling(),
size,
})
}
fn iounmap(&self, _mmio: &MmioRaw) {}
}
#[test]
fn test_mmio_new() {
super::init(&DummyMmioOp);
let addr = MmioRaw {
phys: super::MmioAddr(0x1000),
virt: core::ptr::NonNull::dangling(),
size: 0x100,
};
println!("Mmio address: {:?}", addr);
println!("Mmio address display: {}", addr);
}
}