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

/// Represents an MMIO address that can be safely read, but writes are
/// dangerous.
///
/// 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 reading the address will have no side effects.
/// However, writing to the address will potentially do something bad if you
/// aren't careful, and so writes are possible but `unsafe`.
#[repr(transparent)]
#[derive(Debug, Clone, Copy)]
pub struct DangerWriteVolAddr<T> {
  addr: NonZeroUsize,
  _marker: PhantomData<T>,
}

impl_vol_eq!(DangerWriteVolAddr<T>);

impl<T> DangerWriteVolAddr<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.
  ///
  /// ## Safety
  /// * The safety rules depend on what address you're writing to. Each
  ///   dangerous MMIO location has its own rules.
  #[inline(always)]
  pub unsafe fn write(&self, t: T)
  where
    T: Copy,
  {
    write_volatile(self.addr.get() as *mut T, t)
  }
}