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}