//! Ports and memory-mapped IO.
#![allow(dead_code)]
use core::arch::asm;
use core::marker::PhantomData;
/// An IO port that can read/write register values.
///
/// This class is generic and supports any type that implements [PortValue]
/// and can be converted into a 1, 2 or 4 byte integer.
#[derive(Copy, Clone, Debug)]
pub struct Port<T: PortValue>(pub u16, PhantomData<T>);
impl<T: PortValue> Port<T> {
/// Create a new port.
pub const fn new(addr: u16) -> Self {
Self(addr, PhantomData)
}
/// Read from the port.
pub unsafe fn read(self) -> T {
T::I::read(self.0).into()
}
/// Write to the port.
pub unsafe fn write(self, val: T) {
T::I::write(val.into(), self.0);
}
/// Read-modify-update the ports value.
pub unsafe fn update(self, f: impl FnOnce(T) -> T) {
self.write(f(self.read()));
}
}
/// A value that can be written/read to/from IO ports.
///
/// It has to be convertible from/into the raw integer specified in [PortValue::I].
pub trait PortValue: Sized + Copy + From<Self::I> + Into<Self::I> {
/// The underlying raw integer type, which can be [u8], [u16], or [u32].
type I: PortRaw;
}
// Implement port value for all raw integers
impl<I: PortRaw> PortValue for I {
type I = Self;
}
/// A raw value that can be written/read to/from IO ports.
///
/// It is implemented for [u8], [u16], and [u32].
pub trait PortRaw: Sized + Copy {
/// Write to the specified port `addr`
unsafe fn read(addr: u16) -> Self;
/// Read from the specified port `addr`
unsafe fn write(self, addr: u16);
}
impl PortRaw for u8 {
unsafe fn write(self, addr: u16) {
asm!("out dx, al", in("dx") addr, in("al") self, options(nomem, nostack));
}
unsafe fn read(addr: u16) -> Self {
let out: Self;
asm!("in al, dx", out("al") out, in("dx") addr, options(nomem, nostack));
out
}
}
impl PortRaw for u16 {
unsafe fn write(self, addr: u16) {
asm!("out dx, ax", in("dx") addr, in("ax") self, options(nomem, nostack));
}
unsafe fn read(addr: u16) -> Self {
let out: Self;
asm!("in ax, dx", out("ax") out, in("dx") addr, options(nomem, nostack));
out
}
}
impl PortRaw for u32 {
unsafe fn write(self, addr: u16) {
asm!("out dx, eax", in("dx") addr, in("eax") self, options(nomem, nostack));
}
unsafe fn read(addr: u16) -> Self {
let out: Self;
asm!("in eax, dx", out("eax") out, in("dx") addr, options(nomem, nostack));
out
}
}
/// Abstraction for memory mapped IO registers
pub trait IOMem {
const OFFSET: usize;
}
/// Extension for pointers, that provides a volatile update function (read->modify->write)
pub trait VolatileUpdate<T> {
/// Reads from the memory address, passes the value to `f`, and writes the value it returns back to memory.
unsafe fn update_volatile(self, f: impl FnOnce(T) -> T);
}
impl<T> VolatileUpdate<T> for *mut T {
unsafe fn update_volatile(self, f: impl FnOnce(T) -> T) {
self.write_volatile(f(self.read_volatile()));
}
}