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}