1use crate::cpu;
3use core::{fmt, marker::PhantomData};
4use mycelium_util::{
5 io,
6 sync::{
7 blocking::{Mutex, MutexGuard},
8 spin::Spinlock,
9 Lazy,
10 },
11};
12
13static COM1: Lazy<Option<Port>> = Lazy::new(|| Port::new(0x3F8).ok());
14static COM2: Lazy<Option<Port>> = Lazy::new(|| Port::new(0x2F8).ok());
15static COM3: Lazy<Option<Port>> = Lazy::new(|| Port::new(0x3E8).ok());
16static COM4: Lazy<Option<Port>> = Lazy::new(|| Port::new(0x2E8).ok());
17
18pub fn com1() -> Option<&'static Port> {
19 COM1.as_ref()
20}
21
22pub fn com2() -> Option<&'static Port> {
23 COM2.as_ref()
24}
25
26pub fn com3() -> Option<&'static Port> {
27 COM3.as_ref()
28}
29
30pub fn com4() -> Option<&'static Port> {
31 COM4.as_ref()
32}
33
34pub struct Port {
36 inner: Mutex<Registers, Spinlock>,
37}
38
39pub struct Lock<'a, B = Blocking> {
41 inner: LockInner<'a>,
43 _is_blocking: PhantomData<B>,
44}
45
46struct LockInner<'a> {
47 inner: MutexGuard<'a, Registers, Spinlock>,
48 prev_divisor: Option<u16>,
49}
50
51struct Registers {
53 data: cpu::Port,
54 irq_enable: cpu::Port,
55 line_ctrl: cpu::Port,
56 modem_ctrl: cpu::Port,
57 status: cpu::Port,
58 baud_rate_divisor: u16,
59}
60
61#[derive(Debug, Copy, Clone, Eq, PartialEq)]
62pub struct Blocking {
63 _p: (),
64}
65
66#[derive(Debug, Copy, Clone, Eq, PartialEq)]
67pub struct Nonblocking {
68 _p: (),
69}
70
71impl Port {
72 pub const MAX_BAUD_RATE: usize = 115_200;
73
74 pub fn new(port: u16) -> io::Result<Self> {
75 let scratch_test = unsafe {
76 const TEST_BYTE: u8 = 69;
77 let scratch_port = cpu::Port::at(port + 7);
78 scratch_port.writeb(TEST_BYTE);
79 scratch_port.readb() == TEST_BYTE
80 };
81
82 if !scratch_test {
83 return Err(io::Error::new(
84 io::ErrorKind::InvalidInput,
85 "scrach port was not writeable, is there a serial port at this address?",
86 ));
87 }
88
89 let mut registers = Registers {
90 data: cpu::Port::at(port),
91 irq_enable: cpu::Port::at(port + 1),
92 line_ctrl: cpu::Port::at(port + 3),
93 modem_ctrl: cpu::Port::at(port + 4),
94 status: cpu::Port::at(port + 5),
95 baud_rate_divisor: 3,
96 };
97 let fifo_ctrl = cpu::Port::at(port + 2);
98
99 registers.without_irqs(|registers| unsafe {
101 registers.set_baud_rate_divisor(3)?;
103
104 registers.line_ctrl.writeb(0x03);
106
107 fifo_ctrl.writeb(0xC7);
109
110 registers.modem_ctrl.writeb(0x0B);
112
113 Ok::<(), io::Error>(())
114 })?;
115
116 Ok(Self {
117 inner: Mutex::new_with_raw_mutex(registers, Spinlock::new()),
118 })
119 }
120
121 pub fn lock(&self) -> Lock<'_> {
122 Lock {
123 inner: LockInner {
124 inner: self.inner.lock(),
125 prev_divisor: None,
126 },
127 _is_blocking: PhantomData,
128 }
129 }
130
131 pub unsafe fn force_unlock(&self) {
138 self.inner.force_unlock();
139 }
140}
141
142impl Registers {
143 const DLAB_BIT: u8 = 0b1000_0000;
144
145 fn without_irqs<T>(&mut self, f: impl FnOnce(&mut Self) -> T) -> T {
146 unsafe {
147 self.irq_enable.writeb(0x00);
148 }
149 let res = f(self);
150 unsafe {
151 self.irq_enable.writeb(0x01);
152 }
153 res
154 }
155
156 fn set_baud_rate_divisor(&mut self, divisor: u16) -> io::Result<u16> {
157 let prev = self.baud_rate_divisor;
158 if divisor == 0 {
159 return Err(io::Error::new(
160 io::ErrorKind::InvalidInput,
161 "baud rate divisor must be greater than 0",
162 ));
163 }
164
165 let lcr_state = unsafe { self.line_ctrl.readb() };
166 if lcr_state & Self::DLAB_BIT != 0 {
167 return Err(io::Error::new(
168 io::ErrorKind::Other,
169 "DLAB bit already set, what the heck!",
170 ));
171 }
172
173 unsafe {
174 self.line_ctrl.writeb(lcr_state | Self::DLAB_BIT);
178
179 self.data.writeb((divisor & 0x00FF) as u8);
181 self.irq_enable.writeb((divisor >> 8) as u8);
183
184 self.line_ctrl.writeb(lcr_state);
185 }
186
187 self.baud_rate_divisor = divisor;
188
189 Ok(prev)
190 }
191
192 #[inline]
193 fn line_status(&self) -> u8 {
194 unsafe { self.status.readb() }
195 }
196
197 #[inline]
198 fn is_write_ready(&self) -> bool {
199 self.line_status() & 0x20 != 0
200 }
201
202 #[inline]
203 fn is_read_ready(&self) -> bool {
204 self.line_status() & 1 != 0
205 }
206
207 #[inline]
208 fn read_blocking(&mut self) -> u8 {
209 while !self.is_read_ready() {}
210 unsafe { self.data.readb() }
211 }
212
213 #[inline]
214 fn read_nonblocking(&mut self) -> io::Result<u8> {
215 if self.is_read_ready() {
216 Ok(unsafe { self.data.readb() })
217 } else {
218 Err(io::Error::from(io::ErrorKind::WouldBlock))
219 }
220 }
221
222 #[inline]
223 fn write_blocking(&mut self, byte: u8) {
224 while !self.is_write_ready() {}
225 unsafe { self.data.writeb(byte) }
226 }
227
228 #[inline]
229 fn write_nonblocking(&mut self, byte: u8) -> io::Result<()> {
230 if self.is_write_ready() {
231 unsafe {
232 self.data.writeb(byte);
233 }
234 Ok(())
235 } else {
236 Err(io::Error::from(io::ErrorKind::WouldBlock))
237 }
238 }
239}
240
241impl<'a> Lock<'a> {
242 pub fn set_non_blocking(self) -> Lock<'a, Nonblocking> {
243 Lock {
244 inner: self.inner,
245 _is_blocking: PhantomData,
246 }
247 }
248}
249
250impl<B> Lock<'_, B> {
251 pub fn set_baud_rate(&mut self, baud: usize) -> io::Result<()> {
262 if baud > Port::MAX_BAUD_RATE {
263 return Err(io::Error::new(
264 io::ErrorKind::InvalidInput,
265 "cannot exceed max baud rate (115200)",
266 ));
267 }
268
269 if Port::MAX_BAUD_RATE % baud != 0 {
270 return Err(io::Error::new(
271 io::ErrorKind::InvalidInput,
272 "max baud rate not divisible by target",
273 ));
274 }
275
276 let divisor = Port::MAX_BAUD_RATE / baud;
277 if divisor > (u16::MAX as usize) {
278 return Err(io::Error::new(
279 io::ErrorKind::InvalidInput,
280 "divisor for target baud rate is too high!",
281 ));
282 }
283
284 self.inner.set_baud_rate_divisor(divisor as u16)
285 }
286}
287
288impl io::Read for Lock<'_, Blocking> {
289 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
290 for byte in buf.iter_mut() {
291 *byte = self.inner.read_blocking();
292 }
293 Ok(buf.len())
294 }
295}
296
297impl io::Read for Lock<'_, Nonblocking> {
298 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
299 for byte in buf.iter_mut() {
300 *byte = self.inner.read_nonblocking()?;
301 }
302 Ok(buf.len())
303 }
304}
305
306impl io::Write for Lock<'_, Blocking> {
307 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
308 for &byte in buf.iter() {
309 self.inner.write_blocking(byte)
310 }
311 Ok(buf.len())
312 }
313
314 fn flush(&mut self) -> io::Result<()> {
315 while !self.inner.is_write_ready() {}
316 Ok(())
317 }
318}
319
320impl fmt::Write for Lock<'_, Blocking> {
321 fn write_str(&mut self, s: &str) -> fmt::Result {
322 for byte in s.bytes() {
323 self.inner.write_blocking(byte)
324 }
325 Ok(())
326 }
327}
328
329impl io::Write for Lock<'_, Nonblocking> {
330 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
331 for &byte in buf.iter() {
332 self.inner.write_nonblocking(byte)?;
333 }
334 Ok(buf.len())
335 }
336
337 fn flush(&mut self) -> io::Result<()> {
338 while !self.inner.is_write_ready() {}
339 Ok(())
340 }
341}
342
343impl LockInner<'_> {
344 #[inline(always)]
345 fn is_write_ready(&self) -> bool {
346 self.inner.is_write_ready()
347 }
348
349 #[inline(always)]
350 fn write_nonblocking(&mut self, byte: u8) -> io::Result<()> {
351 self.inner.write_nonblocking(byte)
352 }
353
354 #[inline(always)]
355 fn read_nonblocking(&mut self) -> io::Result<u8> {
356 self.inner.read_nonblocking()
357 }
358
359 #[inline(always)]
360 fn write_blocking(&mut self, byte: u8) {
361 self.inner.write_blocking(byte)
362 }
363
364 #[inline(always)]
365 fn read_blocking(&mut self) -> u8 {
366 self.inner.read_blocking()
367 }
368
369 #[inline(always)]
370 fn set_baud_rate_divisor(&mut self, divisor: u16) -> io::Result<()> {
371 let prev: u16 = self
372 .inner
373 .without_irqs(|inner| inner.set_baud_rate_divisor(divisor))?;
374 self.prev_divisor = Some(prev);
375
376 Ok(())
377 }
378}
379
380impl Drop for LockInner<'_> {
381 fn drop(&mut self) {
382 if let Some(divisor) = self.prev_divisor {
383 self.inner.without_irqs(|inner| {
385 let _ = inner.set_baud_rate_divisor(divisor);
387 });
388 }
389 }
390}
391
392impl<'a> mycelium_trace::writer::MakeWriter<'a> for &Port {
393 type Writer = Lock<'a, Blocking>;
394 fn make_writer(&'a self) -> Self::Writer {
395 self.lock()
396 }
397
398 fn line_len(&self) -> usize {
399 120
400 }
401}