uart_16550/port.rs
1use core::fmt;
2
3use crate::{LineStsFlags, WouldBlockError};
4
5/// A x86 I/O port-mapped UART.
6#[cfg_attr(docsrs, doc(cfg(any(target_arch = "x86", target_arch = "x86_64"))))]
7#[derive(Debug)]
8pub struct SerialPort(u16 /* base port */);
9
10impl SerialPort {
11 /// Base port.
12 fn port_base(&self) -> u16 {
13 self.0
14 }
15
16 /// Data port.
17 ///
18 /// Read and write.
19 fn port_data(&self) -> u16 {
20 self.port_base()
21 }
22
23 /// Interrupt enable port.
24 ///
25 /// Write only.
26 fn port_int_en(&self) -> u16 {
27 self.port_base() + 1
28 }
29
30 /// Fifo control port.
31 ///
32 /// Write only.
33 fn port_fifo_ctrl(&self) -> u16 {
34 self.port_base() + 2
35 }
36
37 /// Line control port.
38 ///
39 /// Write only.
40 fn port_line_ctrl(&self) -> u16 {
41 self.port_base() + 3
42 }
43
44 /// Modem control port.
45 ///
46 /// Write only.
47 fn port_modem_ctrl(&self) -> u16 {
48 self.port_base() + 4
49 }
50
51 /// Line status port.
52 ///
53 /// Read only.
54 fn port_line_sts(&self) -> u16 {
55 self.port_base() + 5
56 }
57
58 /// Creates a new serial port interface on the given I/O base port.
59 ///
60 /// This function is unsafe because the caller must ensure that the given base address
61 /// really points to a serial port device and that the caller has the necessary rights
62 /// to perform the I/O operation.
63 pub const unsafe fn new(base: u16) -> Self {
64 Self(base)
65 }
66
67 /// Initializes the serial port.
68 ///
69 /// The default configuration of [38400/8-N-1](https://en.wikipedia.org/wiki/8-N-1) is used.
70 pub fn init(&mut self) {
71 unsafe {
72 // Disable interrupts
73 x86::io::outb(self.port_int_en(), 0x00);
74
75 // Enable DLAB
76 x86::io::outb(self.port_line_ctrl(), 0x80);
77
78 // Set maximum speed to 38400 bps by configuring DLL and DLM
79 x86::io::outb(self.port_data(), 0x03);
80 x86::io::outb(self.port_int_en(), 0x00);
81
82 // Disable DLAB and set data word length to 8 bits
83 x86::io::outb(self.port_line_ctrl(), 0x03);
84
85 // Enable FIFO, clear TX/RX queues and
86 // set interrupt watermark at 14 bytes
87 x86::io::outb(self.port_fifo_ctrl(), 0xc7);
88
89 // Mark data terminal ready, signal request to send
90 // and enable auxilliary output #2 (used as interrupt line for CPU)
91 x86::io::outb(self.port_modem_ctrl(), 0x0b);
92
93 // Enable interrupts
94 x86::io::outb(self.port_int_en(), 0x01);
95 }
96 }
97
98 fn line_sts(&mut self) -> LineStsFlags {
99 unsafe { LineStsFlags::from_bits_truncate(x86::io::inb(self.port_line_sts())) }
100 }
101
102 /// Sends a byte on the serial port.
103 /// 0x08 (backspace) and 0x7F (delete) get replaced with 0x08, 0x20, 0x08 and 0x0A (\n) gets replaced with \r\n.
104 /// If this replacement is unwanted use [SerialPort::send_raw] instead.
105 pub fn send(&mut self, data: u8) {
106 match data {
107 8 | 0x7F => {
108 self.send_raw(8);
109 self.send_raw(b' ');
110 self.send_raw(8);
111 }
112 0x0A => {
113 self.send_raw(0x0D);
114 self.send_raw(0x0A);
115 }
116 data => {
117 self.send_raw(data);
118 }
119 }
120 }
121
122 /// Sends a raw byte on the serial port, intended for binary data.
123 pub fn send_raw(&mut self, data: u8) {
124 retry_until_ok!(self.try_send_raw(data))
125 }
126
127 /// Tries to send a raw byte on the serial port, intended for binary data.
128 pub fn try_send_raw(&mut self, data: u8) -> Result<(), WouldBlockError> {
129 if self.line_sts().contains(LineStsFlags::OUTPUT_EMPTY) {
130 unsafe {
131 x86::io::outb(self.port_data(), data);
132 }
133 Ok(())
134 } else {
135 Err(WouldBlockError)
136 }
137 }
138
139 /// Receives a byte on the serial port.
140 pub fn receive(&mut self) -> u8 {
141 retry_until_ok!(self.try_receive())
142 }
143
144 /// Tries to receive a byte on the serial port.
145 pub fn try_receive(&mut self) -> Result<u8, WouldBlockError> {
146 if self.line_sts().contains(LineStsFlags::INPUT_FULL) {
147 let data = unsafe { x86::io::inb(self.port_data()) };
148 Ok(data)
149 } else {
150 Err(WouldBlockError)
151 }
152 }
153}
154
155impl fmt::Write for SerialPort {
156 fn write_str(&mut self, s: &str) -> fmt::Result {
157 for byte in s.bytes() {
158 self.send(byte);
159 }
160 Ok(())
161 }
162}