ostd/arch/x86/device/
serial.rs

1// SPDX-License-Identifier: MPL-2.0
2
3//! A port-mapped UART. Copied from uart_16550.
4
5#![expect(dead_code)]
6
7use crate::{
8    arch::device::io_port::{ReadWriteAccess, WriteOnlyAccess},
9    io::IoPort,
10};
11/// A serial port.
12///
13/// Serial ports are a legacy communications port common on IBM-PC compatible computers.
14/// Ref: <https://wiki.osdev.org/Serial_Ports>
15pub struct SerialPort {
16    /// Data Register
17    data: IoPort<u8, ReadWriteAccess>,
18    /// Interrupt Enable Register
19    int_en: IoPort<u8, WriteOnlyAccess>,
20    /// First In First Out Control Register
21    fifo_ctrl: IoPort<u8, WriteOnlyAccess>,
22    /// Line control Register
23    line_ctrl: IoPort<u8, WriteOnlyAccess>,
24    /// Modem Control Register
25    modem_ctrl: IoPort<u8, WriteOnlyAccess>,
26    /// Line status Register
27    line_status: IoPort<u8, ReadWriteAccess>,
28    /// Modem Status Register
29    modem_status: IoPort<u8, ReadWriteAccess>,
30}
31
32impl SerialPort {
33    /// Creates a serial port.
34    ///
35    /// # Safety
36    ///
37    /// The caller must ensure that the base port is a valid serial base port and that it has
38    /// exclusive ownership of the serial ports.
39    pub const unsafe fn new(port: u16) -> Self {
40        // SAFETY: The safety is upheld by the caller.
41        unsafe {
42            Self {
43                data: IoPort::new(port),
44                int_en: IoPort::new(port + 1),
45                fifo_ctrl: IoPort::new(port + 2),
46                line_ctrl: IoPort::new(port + 3),
47                modem_ctrl: IoPort::new(port + 4),
48                line_status: IoPort::new(port + 5),
49                modem_status: IoPort::new(port + 6),
50            }
51        }
52    }
53
54    /// Initializes the serial port.
55    pub fn init(&self) {
56        // Disable interrupts
57        self.int_en.write(0x00);
58        // Enable DLAB
59        self.line_ctrl.write(0x80);
60        // Set maximum speed to 38400 bps by configuring DLL and DLM
61        self.data.write(0x03);
62        self.int_en.write(0x00);
63        // Disable DLAB and set data word length to 8 bits
64        self.line_ctrl.write(0x03);
65        // Enable FIFO, clear TX/RX queues and
66        // set interrupt watermark at 14 bytes
67        self.fifo_ctrl.write(0xC7);
68        // Mark data terminal ready, signal request to send
69        // and enable auxiliary output #2 (used as interrupt line for CPU)
70        self.modem_ctrl.write(0x0B);
71        // Enable interrupts
72        self.int_en.write(0x01);
73    }
74
75    /// Sends data to the data port
76    #[inline]
77    pub fn send(&self, data: u8) {
78        self.data.write(data);
79    }
80
81    /// Receives data from the data port
82    #[inline]
83    pub fn recv(&self) -> u8 {
84        self.data.read()
85    }
86
87    /// Gets line status
88    #[inline]
89    pub fn line_status(&self) -> u8 {
90        self.line_status.read()
91    }
92}