Skip to main content

kernel/arch/x86_64/drivers/tty/
uart.rs

1// SPDX-License-Identifier: GPL-3.0-only
2//! x86_64 UART driver
3//!
4//! Authors: MarioS271
5
6use crate::drivers::tty::uart::{UartError, UartOps, UartResult};
7use core::sync::atomic::{AtomicU8, Ordering};
8use x86_64::instructions::port::Port;
9
10/// x86_64 UART driver implementing [`UartOps`]
11pub struct Uart {
12    state: AtomicU8,
13    base_addr: u16
14}
15
16impl Uart {
17    const INIT_CALLED: u8 = 1 << 0;
18    const USABLE: u8 = 1 << 1;
19
20    /// Constructor; creates a new uninitialized [`Uart`] device with the given `base_addr`
21    pub fn new(base_addr: u16) -> Self {
22        Self {
23            state: AtomicU8::default(),
24            base_addr,
25        }
26    }
27
28    /// Wrapper around [`Self::new`]; creates a [`Uart`] device with a `base_addr` of `0x3F8`
29    pub fn com1() -> Self {
30        Self::new(0x3F8)
31    }
32    /// Wrapper around [`Self::new`]; creates a [`Uart`] device with a `base_addr` of `0x2F8`
33    pub fn com2() -> Self {
34        Self::new(0x2F8)
35    }
36    /// Wrapper around [`Self::new`]; creates a [`Uart`] device with a `base_addr` of `0x3E8`
37    pub fn com3() -> Self {
38        Self::new(0x3E8)
39    }
40    /// Wrapper around [`Self::new`]; creates a [`Uart`] device with a `base_addr` of `0x2E8`
41    pub fn com4() -> Self {
42        Self::new(0x2E8)
43    }
44
45    /// Returns the I/O port at `offset` from this UART device's `base_addr`
46    fn reg(&self, offset: u16) -> Port<u8> {
47        Port::new(self.base_addr + offset)
48    }
49
50    /// Checks if the given device is usable (fully initialized)
51    #[inline(always)]
52    fn is_usable(&self) -> bool {
53        self.state.load(Ordering::Acquire) & Self::USABLE == Self::USABLE
54    }
55}
56
57impl UartOps for Uart {
58    fn init(&mut self) -> UartResult<()> {
59        let prev = self.state.fetch_or(Self::INIT_CALLED, Ordering::AcqRel);
60
61        if prev & Self::INIT_CALLED != 0 {
62            core::hint::cold_path();
63            return Err(UartError::AlreadyInitialized);
64        }
65
66        // Safety:
67        // - writing to standardized x86 UART addresses via Port I/O
68        // - writes will only happen once, guaranteed by the self.state guards
69        unsafe {
70            self.reg(1).write(0x00);     // Disable Interrupts.
71            self.reg(3).write(0x80);     // Enable DLAB (divisor latch access bit)
72            self.reg(0).write(0x01);     // Baud Rate Divisor (low byte)
73            self.reg(1).write(0x00);     // Baud Rate Divisor (high byte)
74            self.reg(3).write(0x03);     // Disable DLAB and configure line
75            self.reg(2).write(0xC7);     // Enable and clear FIFO
76            self.reg(4).write(0x0B);     // Set DTR, RTS, OUT2
77        }
78
79        self.state.fetch_or(Self::USABLE, Ordering::AcqRel);
80        Ok(())
81    }
82
83    fn read_byte(&mut self) -> UartResult<u8> {
84        if !self.is_usable() {
85            core::hint::cold_path();
86            return Err(UartError::NotInitialized);
87        }
88
89        // Safety: this is a standardized x86 UART operation (spin until byte is avail, then read)
90        unsafe {
91            while self.reg(5).read() & 0x01 == 0 {
92                core::hint::spin_loop();
93            }
94            Ok(self.reg(0).read())
95        }
96    }
97
98    fn write_byte(&mut self, byte: u8) -> UartResult<()> {
99        if !self.is_usable() {
100            core::hint::cold_path();
101            return Err(UartError::NotInitialized);
102        }
103
104        // Safety: this is a standardized x86 UART operation (spin until transmitter is ready, then write)
105        unsafe {
106            while self.reg(5).read() & 0x20 == 0 {
107                core::hint::spin_loop();
108            }
109            self.reg(0).write(byte);
110        }
111
112        Ok(())
113    }
114}