use crate::{
bindings,
prelude::*, };
pub mod mem;
pub mod poll;
pub mod register;
pub mod resource;
pub use crate::register;
pub use resource::Resource;
use register::LocatedRegister;
pub type PhysAddr = bindings::phys_addr_t;
pub type ResourceSize = bindings::resource_size_t;
pub struct MmioRaw<const SIZE: usize = 0> {
addr: usize,
maxsize: usize,
}
impl<const SIZE: usize> MmioRaw<SIZE> {
pub fn new(addr: usize, maxsize: usize) -> Result<Self> {
if maxsize < SIZE {
return Err(EINVAL);
}
Ok(Self { addr, maxsize })
}
#[inline]
pub fn addr(&self) -> usize {
self.addr
}
#[inline]
pub fn maxsize(&self) -> usize {
self.maxsize
}
}
#[repr(transparent)]
pub struct Mmio<const SIZE: usize = 0>(MmioRaw<SIZE>);
#[inline]
const fn offset_valid<U>(offset: usize, size: usize) -> bool {
let type_size = core::mem::size_of::<U>();
if let Some(end) = offset.checked_add(type_size) {
end <= size && offset % type_size == 0
} else {
false
}
}
pub trait IoCapable<T> {
unsafe fn io_read(&self, address: usize) -> T;
unsafe fn io_write(&self, value: T, address: usize);
}
pub trait IoLoc<T> {
type IoType: Into<T> + From<T>;
fn offset(self) -> usize;
}
macro_rules! impl_usize_ioloc {
($($ty:ty),*) => {
$(
impl IoLoc<$ty> for usize {
type IoType = $ty;
#[inline(always)]
fn offset(self) -> usize {
self
}
}
)*
}
}
impl_usize_ioloc!(u8, u16, u32, u64);
pub trait Io {
fn addr(&self) -> usize;
fn maxsize(&self) -> usize;
#[inline]
fn io_addr<U>(&self, offset: usize) -> Result<usize> {
if !offset_valid::<U>(offset, self.maxsize()) {
return Err(EINVAL);
}
self.addr().checked_add(offset).ok_or(EINVAL)
}
#[inline(always)]
fn try_read8(&self, offset: usize) -> Result<u8>
where
Self: IoCapable<u8>,
{
self.try_read(offset)
}
#[inline(always)]
fn try_read16(&self, offset: usize) -> Result<u16>
where
Self: IoCapable<u16>,
{
self.try_read(offset)
}
#[inline(always)]
fn try_read32(&self, offset: usize) -> Result<u32>
where
Self: IoCapable<u32>,
{
self.try_read(offset)
}
#[inline(always)]
fn try_read64(&self, offset: usize) -> Result<u64>
where
Self: IoCapable<u64>,
{
self.try_read(offset)
}
#[inline(always)]
fn try_write8(&self, value: u8, offset: usize) -> Result
where
Self: IoCapable<u8>,
{
self.try_write(offset, value)
}
#[inline(always)]
fn try_write16(&self, value: u16, offset: usize) -> Result
where
Self: IoCapable<u16>,
{
self.try_write(offset, value)
}
#[inline(always)]
fn try_write32(&self, value: u32, offset: usize) -> Result
where
Self: IoCapable<u32>,
{
self.try_write(offset, value)
}
#[inline(always)]
fn try_write64(&self, value: u64, offset: usize) -> Result
where
Self: IoCapable<u64>,
{
self.try_write(offset, value)
}
#[inline(always)]
fn read8(&self, offset: usize) -> u8
where
Self: IoKnownSize + IoCapable<u8>,
{
self.read(offset)
}
#[inline(always)]
fn read16(&self, offset: usize) -> u16
where
Self: IoKnownSize + IoCapable<u16>,
{
self.read(offset)
}
#[inline(always)]
fn read32(&self, offset: usize) -> u32
where
Self: IoKnownSize + IoCapable<u32>,
{
self.read(offset)
}
#[inline(always)]
fn read64(&self, offset: usize) -> u64
where
Self: IoKnownSize + IoCapable<u64>,
{
self.read(offset)
}
#[inline(always)]
fn write8(&self, value: u8, offset: usize)
where
Self: IoKnownSize + IoCapable<u8>,
{
self.write(offset, value)
}
#[inline(always)]
fn write16(&self, value: u16, offset: usize)
where
Self: IoKnownSize + IoCapable<u16>,
{
self.write(offset, value)
}
#[inline(always)]
fn write32(&self, value: u32, offset: usize)
where
Self: IoKnownSize + IoCapable<u32>,
{
self.write(offset, value)
}
#[inline(always)]
fn write64(&self, value: u64, offset: usize)
where
Self: IoKnownSize + IoCapable<u64>,
{
self.write(offset, value)
}
#[inline(always)]
fn try_read<T, L>(&self, location: L) -> Result<T>
where
L: IoLoc<T>,
Self: IoCapable<L::IoType>,
{
let address = self.io_addr::<L::IoType>(location.offset())?;
Ok(unsafe { self.io_read(address) }.into())
}
#[inline(always)]
fn try_write<T, L>(&self, location: L, value: T) -> Result
where
L: IoLoc<T>,
Self: IoCapable<L::IoType>,
{
let address = self.io_addr::<L::IoType>(location.offset())?;
let io_value = value.into();
unsafe { self.io_write(io_value, address) }
Ok(())
}
#[inline(always)]
fn try_write_reg<T, L, V>(&self, value: V) -> Result
where
L: IoLoc<T>,
V: LocatedRegister<Location = L, Value = T>,
Self: IoCapable<L::IoType>,
{
let (location, value) = value.into_io_op();
self.try_write(location, value)
}
#[inline(always)]
fn try_update<T, L, F>(&self, location: L, f: F) -> Result
where
L: IoLoc<T>,
Self: IoCapable<L::IoType>,
F: FnOnce(T) -> T,
{
let address = self.io_addr::<L::IoType>(location.offset())?;
let value: T = unsafe { self.io_read(address) }.into();
let io_value = f(value).into();
unsafe { self.io_write(io_value, address) }
Ok(())
}
#[inline(always)]
fn read<T, L>(&self, location: L) -> T
where
L: IoLoc<T>,
Self: IoKnownSize + IoCapable<L::IoType>,
{
let address = self.io_addr_assert::<L::IoType>(location.offset());
unsafe { self.io_read(address) }.into()
}
#[inline(always)]
fn write<T, L>(&self, location: L, value: T)
where
L: IoLoc<T>,
Self: IoKnownSize + IoCapable<L::IoType>,
{
let address = self.io_addr_assert::<L::IoType>(location.offset());
let io_value = value.into();
unsafe { self.io_write(io_value, address) }
}
#[inline(always)]
fn write_reg<T, L, V>(&self, value: V)
where
L: IoLoc<T>,
V: LocatedRegister<Location = L, Value = T>,
Self: IoKnownSize + IoCapable<L::IoType>,
{
let (location, value) = value.into_io_op();
self.write(location, value)
}
#[inline(always)]
fn update<T, L, F>(&self, location: L, f: F)
where
L: IoLoc<T>,
Self: IoKnownSize + IoCapable<L::IoType> + Sized,
F: FnOnce(T) -> T,
{
let address = self.io_addr_assert::<L::IoType>(location.offset());
let value: T = unsafe { self.io_read(address) }.into();
let io_value = f(value).into();
unsafe { self.io_write(io_value, address) }
}
}
pub trait IoKnownSize: Io {
const MIN_SIZE: usize;
#[inline(always)]
fn io_addr_assert<U>(&self, offset: usize) -> usize {
build_assert!(offset_valid::<U>(offset, Self::MIN_SIZE));
self.addr() + offset
}
}
macro_rules! impl_mmio_io_capable {
($mmio:ident, $(#[$attr:meta])* $ty:ty, $read_fn:ident, $write_fn:ident) => {
$(#[$attr])*
impl<const SIZE: usize> IoCapable<$ty> for $mmio<SIZE> {
unsafe fn io_read(&self, address: usize) -> $ty {
unsafe { bindings::$read_fn(address as *const c_void) }
}
unsafe fn io_write(&self, value: $ty, address: usize) {
unsafe { bindings::$write_fn(value, address as *mut c_void) }
}
}
};
}
impl_mmio_io_capable!(Mmio, u8, readb, writeb);
impl_mmio_io_capable!(Mmio, u16, readw, writew);
impl_mmio_io_capable!(Mmio, u32, readl, writel);
impl_mmio_io_capable!(
Mmio,
#[cfg(CONFIG_64BIT)]
u64,
readq,
writeq
);
impl<const SIZE: usize> Io for Mmio<SIZE> {
#[inline]
fn addr(&self) -> usize {
self.0.addr()
}
#[inline]
fn maxsize(&self) -> usize {
self.0.maxsize()
}
}
impl<const SIZE: usize> IoKnownSize for Mmio<SIZE> {
const MIN_SIZE: usize = SIZE;
}
impl<const SIZE: usize> Mmio<SIZE> {
pub unsafe fn from_raw(raw: &MmioRaw<SIZE>) -> &Self {
unsafe { &*core::ptr::from_ref(raw).cast() }
}
}
#[repr(transparent)]
pub struct RelaxedMmio<const SIZE: usize = 0>(Mmio<SIZE>);
impl<const SIZE: usize> Io for RelaxedMmio<SIZE> {
#[inline]
fn addr(&self) -> usize {
self.0.addr()
}
#[inline]
fn maxsize(&self) -> usize {
self.0.maxsize()
}
}
impl<const SIZE: usize> IoKnownSize for RelaxedMmio<SIZE> {
const MIN_SIZE: usize = SIZE;
}
impl<const SIZE: usize> Mmio<SIZE> {
pub fn relaxed(&self) -> &RelaxedMmio<SIZE> {
unsafe { core::mem::transmute(self) }
}
}
impl_mmio_io_capable!(RelaxedMmio, u8, readb_relaxed, writeb_relaxed);
impl_mmio_io_capable!(RelaxedMmio, u16, readw_relaxed, writew_relaxed);
impl_mmio_io_capable!(RelaxedMmio, u32, readl_relaxed, writel_relaxed);
impl_mmio_io_capable!(
RelaxedMmio,
#[cfg(CONFIG_64BIT)]
u64,
readq_relaxed,
writeq_relaxed
);