1use core::fmt;
2
3use crate::{LineStsFlags, WouldBlockError};
4
5#[cfg_attr(docsrs, doc(cfg(any(target_arch = "x86", target_arch = "x86_64"))))]
7#[derive(Debug)]
8pub struct SerialPort(u16 );
9
10impl SerialPort {
11 fn port_base(&self) -> u16 {
13 self.0
14 }
15
16 fn port_data(&self) -> u16 {
20 self.port_base()
21 }
22
23 fn port_int_en(&self) -> u16 {
27 self.port_base() + 1
28 }
29
30 fn port_fifo_ctrl(&self) -> u16 {
34 self.port_base() + 2
35 }
36
37 fn port_line_ctrl(&self) -> u16 {
41 self.port_base() + 3
42 }
43
44 fn port_modem_ctrl(&self) -> u16 {
48 self.port_base() + 4
49 }
50
51 fn port_line_sts(&self) -> u16 {
55 self.port_base() + 5
56 }
57
58 pub const unsafe fn new(base: u16) -> Self {
64 Self(base)
65 }
66
67 pub fn init(&mut self) {
71 unsafe {
72 x86::io::outb(self.port_int_en(), 0x00);
74
75 x86::io::outb(self.port_line_ctrl(), 0x80);
77
78 x86::io::outb(self.port_data(), 0x03);
80 x86::io::outb(self.port_int_en(), 0x00);
81
82 x86::io::outb(self.port_line_ctrl(), 0x03);
84
85 x86::io::outb(self.port_fifo_ctrl(), 0xc7);
88
89 x86::io::outb(self.port_modem_ctrl(), 0x0b);
92
93 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 pub fn send(&mut self, data: u8) {
104 match data {
105 8 | 0x7F => {
106 self.send_raw(8);
107 self.send_raw(b' ');
108 self.send_raw(8);
109 }
110 data => {
111 self.send_raw(data);
112 }
113 }
114 }
115
116 pub fn send_raw(&mut self, data: u8) {
118 retry_until_ok!(self.try_send_raw(data))
119 }
120
121 pub fn try_send_raw(&mut self, data: u8) -> Result<(), WouldBlockError> {
123 if self.line_sts().contains(LineStsFlags::OUTPUT_EMPTY) {
124 unsafe {
125 x86::io::outb(self.port_data(), data);
126 }
127 Ok(())
128 } else {
129 Err(WouldBlockError)
130 }
131 }
132
133 pub fn receive(&mut self) -> u8 {
135 retry_until_ok!(self.try_receive())
136 }
137
138 pub fn try_receive(&mut self) -> Result<u8, WouldBlockError> {
140 if self.line_sts().contains(LineStsFlags::INPUT_FULL) {
141 let data = unsafe { x86::io::inb(self.port_data()) };
142 Ok(data)
143 } else {
144 Err(WouldBlockError)
145 }
146 }
147}
148
149impl fmt::Write for SerialPort {
150 fn write_str(&mut self, s: &str) -> fmt::Result {
151 for byte in s.bytes() {
152 self.send(byte);
153 }
154 Ok(())
155 }
156}