uart/
address.rs

1use core::{num::NonZero, ptr::NonNull};
2
3use crate::{ReadableRegister, UartAddress, WriteableRegister};
4
5/// A port-based UART address.
6pub struct PortAddress(NonZero<u16>);
7
8impl PortAddress {
9    /// Creates a new [`PortAddress`] with `base` as the base port address.
10    ///
11    /// # Safety
12    ///
13    /// - Base address must be a port-based UART device.
14    pub const unsafe fn new(base: NonZero<u16>) -> Self {
15        Self(base)
16    }
17}
18
19// Safety: Constructor requires that the base address be valid, and register
20//         impls are correctly offset from that.
21unsafe impl UartAddress for PortAddress {
22    unsafe fn read(&self, register: ReadableRegister) -> u8 {
23        let port_address = self.0.checked_add(register.as_index()).unwrap();
24        let value: u8;
25
26        // Safety: Caller is required to ensure that reading from port `port_address` is valid.
27        #[cfg(target_arch = "x86_64")]
28        unsafe {
29            core::arch::asm!(
30                "in al, dx",
31                out("al") value,
32                in("dx") port_address.get(),
33                options(nostack, nomem, preserves_flags)
34            );
35        }
36
37        #[cfg(not(target_arch = "x86_64"))]
38        unimplemented!();
39
40        value
41    }
42
43    unsafe fn write(&self, register: WriteableRegister, value: u8) {
44        let port_address = self.0.checked_add(register.as_index()).unwrap();
45
46        // Safety: Caller is required to ensure that writing `value` to port `port_address` is valid.
47        #[cfg(target_arch = "x86_64")]
48        unsafe {
49            core::arch::asm!(
50                "out dx, al",
51                in("dx") port_address.get(),
52                in("al") value,
53                options(nostack, nomem, preserves_flags)
54            );
55        }
56
57        #[cfg(not(target_arch = "x86_64"))]
58        unimplemented!();
59    }
60}
61
62/// An MMIO-based UART address.
63pub struct MmioAddress {
64    base: NonNull<u8>,
65    stride: usize,
66}
67
68impl MmioAddress {
69    /// Creates a new [`MmioAddress`] with `base` as the base memory address.
70    ///
71    /// # Safety
72    ///
73    /// - `base` must be a pointer to an MMIO-based UART device.
74    /// - `stride` must be the uniform distance (in bytes) between each UART register.
75    pub const unsafe fn new(base: NonNull<u8>, stride: usize) -> Self {
76        Self { base, stride }
77    }
78}
79
80// Safety: Constructor requires that the base address be valid, and register
81//         impls are correctly offset from that.
82unsafe impl UartAddress for MmioAddress {
83    unsafe fn write(&self, register: WriteableRegister, value: u8) {
84        // Safety: - `self.base` is required to be a valid base address.
85        //         - `register` is a valid offset.
86        //         - `self.stride` is required to be the distance (in bytes) between each UART register.
87        //         - Writing `value` is required to not cause undefined behaviour.
88        unsafe {
89            self.base
90                .byte_add(usize::from(register.as_index()) * self.stride)
91                .write_volatile(value);
92        }
93    }
94
95    unsafe fn read(&self, register: ReadableRegister) -> u8 {
96        // Safety: - `self.base` is required to be a valid base address.
97        //         - `register` is a valid offset.
98        //         - `self.stride` is required to be the distance (in bytes) between each UART register.
99        //         - Reading `value` is required to not cause undefined behaviour.
100        unsafe {
101            self.base
102                .byte_add(usize::from(register.as_index()) * self.stride)
103                .read_volatile()
104        }
105    }
106}