1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
//! 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()));
    }
}