rstubs/device/
serial.rs

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
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
//! Serial Console

use core::fmt;

use bitfield_struct::bitfield;

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

/// Serial port 1 base address
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(&mut self) {
        unsafe {
            // Disable interrupts
            self.int_en().write(0);

            // Enable DLAB -> data and int_en now set the baud rate
            self.line_ctl()
                .write(LineCtl::new().with_divisor_latch_access(true));

            let baud_rate = 1u16; // 115200
            let [l, h] = baud_rate.to_le_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 1011) must be set for interrupt
            self.modem_ctl().write(
                ModemCtl::new()
                    .with_data_terminal_ready(true)
                    .with_request_to_send(true)
                    .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 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)
    const fn data(&self) -> Port<u8> {
        Port::new(self.base)
    }
    /// Interrupt enable register (or upper half of the divisor if `divisor_latch_access` is enabled)
    const fn int_en(&self) -> Port<u8> {
        Port::new(self.base + 1)
    }
    const fn fifo_ctl(&self) -> Port<FifoCtl> {
        Port::new(self.base + 2)
    }
    const fn line_ctl(&self) -> Port<LineCtl> {
        Port::new(self.base + 3)
    }
    const fn modem_ctl(&self) -> Port<ModemCtl> {
        Port::new(self.base + 4)
    }
    const 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 the receive buffer
    clear_receive: bool,
    /// Clear the transmit buffer
    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,
    /// false = one stop bit, true = 1.5/2 stop bits
    stop_bits: bool,
    /// If enabled, add a parity bit
    parity: bool,
    even_parity: bool,
    stick_parity: bool,
    set_break: bool,
    /// If enabled, the [Serial::data] and [Serial::int_en] registers
    /// set the divisor (baud rate) instead of their normal function
    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;
}