1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
use core::fmt;

use crate::arch::io::{Port, PortValue};
use bitfield_struct::bitfield;

pub const COM1: u16 = 0x3F8;

/// Serial console
pub struct Serial {
    /// Base address of the data port
    base: u16,
}

impl Serial {
    pub const fn new(base: u16) -> Self {
        Self { base }
    }

    /// Initialize the serial output with:
    /// - baud rate of 115200
    /// - 8 bit word length
    /// - 1 stop bit
    /// - no parity
    pub fn init(&self) {
        unsafe {
            // Enable DLAB -> data and int_en now set the baud rate
            self.line_ctl()
                .write(LineCtl::new().with_divisor_latch_access(true));

            // Set maximum speed to 38400 bps by configuring DLL and DLM
            let baud_rate = 1u16; // 115200
            let [l, h] = baud_rate.to_be_bytes();
            self.data().write(l);
            self.int_en().write(h);

            // Disable DLAB and configure
            self.line_ctl().write(
                LineCtl::new()
                    .with_word_length(3) // 8 bits
                    .with_stop_bits(false) // 1 stop bit
                    .with_parity(false), // no parity
            );

            // Enable and clear FIFO
            self.fifo_ctl().write(
                FifoCtl::new()
                    .with_enable(true)
                    .with_clear_receive(true)
                    .with_clear_transmit(true),
            );

            // Modem Control: OUT2 (0000 1000) must be set for interrupt
            self.modem_ctl().write(ModemCtl::new().with_out2(true));
        }
    }

    /// Write a single byte to the output channel
    fn send_byte(&mut self, b: u8) {
        unsafe {
            // Wait for the serial port's fifo to not be empty
            while !self.line_status().read().transmitter_empty() {}

            // Send the byte out the serial port
            self.data().write(b);
        }
    }

    /// Data register (or lower half of the divisor if `divisor_latch_access` is enabled)
    fn data(&self) -> Port<u8> {
        Port::new(self.base)
    }
    /// Interrupt enable register (or upper half of the divisor if `divisor_latch_access` is enabled)
    fn int_en(&self) -> Port<u8> {
        Port::new(self.base + 1)
    }
    fn fifo_ctl(&self) -> Port<FifoCtl> {
        Port::new(self.base + 2)
    }
    fn line_ctl(&self) -> Port<LineCtl> {
        Port::new(self.base + 3)
    }
    fn modem_ctl(&self) -> Port<ModemCtl> {
        Port::new(self.base + 4)
    }
    fn line_status(&self) -> Port<LineStatus> {
        Port::new(self.base + 5)
    }
}

impl fmt::Write for Serial {
    fn write_str(&mut self, s: &str) -> fmt::Result {
        for b in s.bytes() {
            self.send_byte(b);
        }
        Ok(())
    }
}

/// FIFO Control Register
#[bitfield(u8)]
struct FifoCtl {
    /// 0 means disabled ^= conforming to 8250a
    enable: bool,
    clear_receive: bool,
    clear_transmit: bool,
    dma_mode_select: bool,
    #[bits(2)]
    __: (),
    trigger_receive: bool,
    __: bool,
}
impl PortValue for FifoCtl {
    type I = u8;
}

/// Line Control Register
#[bitfield(u8)]
struct LineCtl {
    /// bits per character | 5 | 6 | 7 | 8
    /// -------------------|---|---|---|---
    /// word length value  | 0 | 1 | 2 | 3
    #[bits(2)]
    word_length: u8,
    /// 0 = one stop bit, 1 = 1.5/2 stop bits
    stop_bits: bool,
    parity: bool,
    even_parity: bool,
    stick_parity: bool,
    set_break: bool,
    divisor_latch_access: bool,
}
impl PortValue for LineCtl {
    type I = u8;
}

/// Modem Control Register
#[bitfield(u8)]
struct ModemCtl {
    data_terminal_ready: bool,
    request_to_send: bool,
    out1: bool,
    /// must be set for interrupts!
    out2: bool,
    do_loop: bool,
    #[bits(3)]
    __: (),
}
impl PortValue for ModemCtl {
    type I = u8;
}

/// Line Status Register
#[bitfield(u8)]
struct LineStatus {
    /// Set when there is a value in the receive buffer
    data_ready: bool,
    overrun_error: bool,
    parity_error: bool,
    framing_error: bool,
    break_interrupt: bool,
    transmitter_holding_register: bool,
    /// Send buffer empty (ready to send)
    transmitter_empty: bool,
    __: bool,
}
impl PortValue for LineStatus {
    type I = u8;
}