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
use super::*;

/// Represents a simple MMIO address that can be safely read or written.
///
/// Unlike with raw pointers, this type favors unsafe construction and then
/// assumes that all usage is safe once the value has been constructed.
///
/// The convention is that addresses using this type are "plain" addresses.
/// * Reads will just get the current value (without any other side effect).
/// * Writes will set a new value without changing any other memory of the
///   system (though usually there's a hardware side effect).
/// * If you perform a write, and then you immediately perform a read, you'll
///   read back exactly whatever you just wrote (the same as with normal
///   memory).
///
/// For addresses where there's anything weird going on (read-only, write-only,
/// read and write represent different effects, etc) then other volatile address
/// types are used.
#[repr(transparent)]
#[derive(Debug, Clone, Copy)]
pub struct SimpleVolAddr<T> {
  addr: NonZeroUsize,
  _marker: PhantomData<T>,
}

impl<T> SimpleVolAddr<T> {
  /// Constructs a new address.
  ///
  /// ## Safety
  /// The input must
  /// * Not be 0 (or _instant_ UB).
  /// * Be an actual MMIO location for the data type specified (or
  ///   [`read`](SimpleVolAddr::read) and [`write`](SimpleVolAddr::write) will
  ///   UB).
  #[must_use]
  #[inline(always)]
  pub(crate) const unsafe fn new(addr: usize) -> Self {
    Self { addr: NonZeroUsize::new_unchecked(addr), _marker: PhantomData }
  }

  /// Reads the current value at the address.
  #[must_use]
  #[inline(always)]
  pub fn read(&self) -> T
  where
    T: Copy,
  {
    unsafe { read_volatile(self.addr.get() as *const T) }
  }

  /// Writes a new value to the address.
  #[inline(always)]
  pub fn write(&self, t: T)
  where
    T: Copy,
  {
    unsafe { write_volatile(self.addr.get() as *mut T, t) }
  }
}