hal_x86_64/
serial.rs

1//! A simple 16550 UART serial port driver.
2use 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
34// #[derive(Debug)]
35pub struct Port {
36    inner: Mutex<Registers, Spinlock>,
37}
38
39// #[derive(Debug)]
40pub struct Lock<'a, B = Blocking> {
41    // This is the non-moveable part.
42    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
51// #[derive(Debug)]
52struct 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        // Disable all interrupts
100        registers.without_irqs(|registers| unsafe {
101            // Set divisor to 38400 baud
102            registers.set_baud_rate_divisor(3)?;
103
104            // 8 bits, no parity, one stop bit
105            registers.line_ctrl.writeb(0x03);
106
107            // Enable FIFO with 14-byte threshold
108            fifo_ctrl.writeb(0xC7);
109
110            // RTS/DSR set
111            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    /// Forcibly unlock the serial port, releasing any locks held by other cores
132    /// or in other functions.
133    ///
134    /// # Safety
135    ///
136    ///  /!\ only call this when oopsing!!! /!\
137    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            // set the Divisor Latch Access Bit. now, the data port and irq enable
175            // port can be used to set the least and most significant bytes of the
176            // divisor, respectively.
177            self.line_ctrl.writeb(lcr_state | Self::DLAB_BIT);
178
179            // least significant byte
180            self.data.writeb((divisor & 0x00FF) as u8);
181            // most significant byte
182            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    /// Set the serial port's baud rate for this `Lock`.
252    ///
253    /// When the `Lock` is dropped, the baud rate will be set to the previous value.
254    ///
255    /// # Errors
256    ///
257    /// This returns an `InvalidInput` error if the target baud rate exceeds the
258    /// maximum (115200), if the maximum baud rate is not divisible by the
259    /// target, or if the target is so low that the resulting baud rate divisor
260    /// is greater than `u16::MAX` (pretty unlikely!).
261    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            // Disable IRQs.
384            self.inner.without_irqs(|inner| {
385                // Reset the previous baud rate divisor.
386                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}