1#![no_std]
2#![forbid(clippy::inline_asm_x86_att_syntax)]
3#![deny(
4 clippy::semicolon_if_nothing_returned,
5 clippy::debug_assert_with_mut_call,
6 clippy::float_arithmetic
7)]
8#![warn(clippy::cargo, clippy::pedantic, clippy::undocumented_unsafe_blocks)]
9#![allow(
10 clippy::cast_lossless,
11 clippy::enum_glob_use,
12 clippy::inline_always,
13 clippy::items_after_statements,
14 clippy::must_use_candidate,
15 clippy::unreadable_literal,
16 clippy::wildcard_imports
17)]
18
19#[cfg(any(target_arch = "x86_64", target_arch = "x86"))]
20pub use x86::*;
21#[cfg(any(target_arch = "x86_64", target_arch = "x86"))]
22mod x86 {
23 use super::UartAddress;
24
25 pub const COM1: UartAddress = UartAddress::Io(0x3F8);
28 pub const COM2: UartAddress = UartAddress::Io(0x2F8);
31 pub const COM3: UartAddress = UartAddress::Io(0x3E8);
34 pub const COM4: UartAddress = UartAddress::Io(0x2E8);
37}
38
39use bit_field::BitField;
40use bitflags::bitflags;
41use core::marker::PhantomData;
42
43bitflags! {
44 #[repr(transparent)]
45 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
46 pub struct InterruptEnable : u8 {
47 const RECEIVED_DATA = 1 << 0;
49 const TRANSMIT_EMPTY = 1 << 1;
51 const RECEIVE_STATUS = 1 << 2;
53 const MODEM_STATUS = 1 << 3;
55 const SLEEP_MODE = 1 << 4;
57 const LOW_POWER = 1 << 5;
59 }
62}
63
64#[repr(u8)]
65#[derive(Debug, Clone, Copy, PartialEq, Eq)]
66pub enum FifoSize {
67 Four = 0b01,
68 Eight = 0b10,
69 Fourteen = 0b11,
70}
71
72#[repr(u8)]
73#[derive(Debug, Clone, Copy, PartialEq, Eq)]
74pub enum DataBits {
75 Five = 0b00,
76 Six = 0b01,
77 Seven = 0b10,
78 Eight = 0b11,
79}
80
81#[repr(u8)]
82#[derive(Debug, Clone, Copy, PartialEq, Eq)]
83pub enum ParityMode {
84 None = 0b000,
85 Odd = 0b001,
86 Even = 0b011,
87 High = 0b101,
88 Low = 0b111,
89}
90
91#[repr(u16)]
93#[derive(Debug, Copy, Clone, Eq, PartialEq)]
94pub enum Baud {
95 B115200 = 1,
96 B57600 = 2,
97 B38400 = 3,
98 B19200 = 6,
99 B9600 = 12,
100 B4800 = 24,
101 B2400 = 48,
102 B1200 = 96,
103 B300 = 384,
104 B50 = 2304,
105}
106
107#[repr(C, packed)]
108pub struct LineControl {
109 pub bits: DataBits,
110 pub parity: ParityMode,
111 pub extra_stop: bool,
112 pub break_signal: bool,
113}
114
115impl LineControl {
116 #[inline]
117 pub const fn empty() -> Self {
118 Self {
119 bits: DataBits::Five,
120 parity: ParityMode::None,
121 extra_stop: false,
122 break_signal: false,
123 }
124 }
125
126 #[inline]
127 pub fn as_u8(self) -> u8 {
128 *0.set_bits(0..2, self.bits as u8)
129 .set_bit(2, self.extra_stop)
130 .set_bits(3..6, self.parity as u8)
131 .set_bit(6, self.break_signal)
132 }
133}
134
135bitflags! {
136 #[repr(transparent)]
137 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
138 pub struct ModemControl : u8 {
139 const TERMINAL_READY = 1 << 0;
140 const REQUEST_TO_SEND = 1 << 1;
141 const AUXILIARY_OUTPUT_1 = 1 << 2;
142 const AUXILIARY_OUTPUT_2 = 1 << 3;
143 const LOOPBACK_MODE = 1 << 4;
144 }
145}
146
147bitflags! {
148 #[repr(transparent)]
149 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
150 pub struct LineStatus : u8 {
151 const DATA_AVAILABLE = 1 << 0;
152 const OVERRUN_ERROR = 1 << 1;
153 const PARITY_ERROR = 1 << 2;
154 const FRAMING_ERROR = 1 << 3;
155 const BREAK_INDICATOR = 1 << 4;
156 const TRANSMIT_EMPTY = 1 << 5;
157 const TRANSMIT_EMPTY_IDLE = 1 << 6;
158 const IMPENDING_ERROR = 1 << 7;
159 }
160}
161
162bitflags! {
163 #[repr(transparent)]
164 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
165 pub struct ModemStatus : u8 {
166 const CLEAR_TO_SEND_CHANGED = 1 << 0;
167 const DATA_SET_READY_CHANGED= 1 << 1;
168 const TRAILING_EDGE_RING_INDICATOR = 1 << 2;
169 const CARRIER_DETECT_CHANGE = 1 << 3;
170 const CLEAR_TO_SEND = 1 << 4;
171 const DATA_SET_READY = 1 << 5;
172 const RING_INDICATOR = 1 << 6;
173 const CARRIER_DETECT = 1 << 7;
174 }
175}
176
177pub enum UartAddress {
178 Io(u16),
179 Mmio(*mut u8),
180}
181
182#[repr(usize)]
183#[allow(dead_code)]
184enum ReadOffset {
185 Data0 = 0x0,
186 Data1 = 0x1,
187 InterruptIdent = 0x2,
188 LineControl = 0x3,
189 ModemControl = 0x4,
190 LineStatus = 0x5,
191 ModemStatus = 0x6,
192}
193
194#[repr(usize)]
195enum WriteOffset {
196 Data0 = 0x0,
197 Data1 = 0x1,
198 FifoControl = 0x2,
199 LineControl = 0x3,
200 ModemControl = 0x4,
201}
202
203pub trait Mode {}
204pub struct Data;
205impl Mode for Data {}
206pub struct Configure;
207impl Mode for Configure {}
208
209pub struct Uart<M: Mode>(UartAddress, PhantomData<M>);
210
211impl<M: Mode> Uart<M> {
212 #[inline]
213 fn read(&self, offset: ReadOffset) -> u8 {
214 unsafe {
216 match self.0 {
217 UartAddress::Io(port) => {
218 let value: u8;
219
220 #[cfg(target_arch = "x86_64")]
221 core::arch::asm!("in al, dx", out("al") value, in("dx") port + (offset as u16), options(nostack, nomem, preserves_flags));
222 #[cfg(not(target_arch = "x86_64"))]
223 unimplemented!();
224
225 value
226 }
227
228 UartAddress::Mmio(ptr) => ptr.add(offset as usize).read_volatile(),
229 }
230 }
231 }
232
233 #[inline]
234 fn write(&mut self, offset: WriteOffset, value: u8) {
235 unsafe {
237 match self.0 {
238 UartAddress::Io(port) => {
239 #[cfg(target_arch = "x86_64")]
240 core::arch::asm!("out dx, al", in("dx") port + (offset as u16), in("al") value, options(nostack, nomem, preserves_flags));
241 #[cfg(not(target_arch = "x86_64"))]
242 unimplemented!();
243 }
244
245 UartAddress::Mmio(ptr) => ptr.add(offset as usize).write_volatile(value),
246 }
247 }
248 }
249
250 #[inline]
252 pub fn disable_fifo(&mut self) {
253 self.write(WriteOffset::FifoControl, 0x0);
254 }
255
256 #[inline]
258 pub fn enable_fifo(
259 &mut self,
260 clear_rx: bool,
261 clear_tx: bool,
262 dma_mode_1: bool,
263 size: FifoSize,
264 ) {
266 let mut control_bits = 1u8;
267 control_bits.set_bit(1, clear_rx);
268 control_bits.set_bit(2, clear_tx);
269 control_bits.set_bit(3, dma_mode_1);
270 control_bits.set_bits(6..8, size as u8);
271
272 self.write(WriteOffset::FifoControl, control_bits);
273 }
274
275 #[inline]
276 pub fn read_line_control(&self) -> LineControl {
277 let line_control_raw = self.read(ReadOffset::LineControl);
278 LineControl {
279 bits: match line_control_raw.get_bits(0..2) {
280 0b00 => DataBits::Five,
281 0b01 => DataBits::Six,
282 0b10 => DataBits::Seven,
283 0b11 => DataBits::Eight,
284 _ => unimplemented!(),
285 },
286 parity: match line_control_raw.get_bits(3..6) {
287 0b000 => ParityMode::None,
288 0b001 => ParityMode::Odd,
289 0b011 => ParityMode::Even,
290 0b101 | 0b111 => ParityMode::High,
291 _ => unimplemented!(),
292 },
293 extra_stop: line_control_raw.get_bit(2),
294 break_signal: line_control_raw.get_bit(6),
295 }
296 }
297
298 #[inline]
299 pub fn write_line_control(&mut self, value: LineControl) {
300 self.write(WriteOffset::LineControl, value.as_u8());
301 }
302
303 #[inline]
304 pub fn read_modem_control(&self) -> ModemControl {
305 ModemControl::from_bits_truncate(self.read(ReadOffset::ModemControl))
306 }
307
308 #[inline]
309 pub fn write_model_control(&mut self, value: ModemControl) {
310 self.write(WriteOffset::ModemControl, value.bits());
311 }
312
313 #[inline]
314 pub fn read_line_status(&self) -> LineStatus {
315 LineStatus::from_bits_truncate(self.read(ReadOffset::LineStatus))
316 }
317
318 #[inline]
319 pub fn read_modem_status(&self) -> ModemStatus {
320 ModemStatus::from_bits_truncate(self.read(ReadOffset::ModemStatus))
321 }
322}
323
324impl Uart<Configure> {
325 #[inline]
326 fn read_divisor_latch(&self) -> u16 {
327 ((self.read(ReadOffset::Data1) as u16) << 8) | (self.read(ReadOffset::Data0) as u16)
328 }
329
330 #[inline]
331 fn write_divisor_latch(&mut self, value: u16) {
332 let value_le_bytes = value.to_le_bytes();
333 self.write(WriteOffset::Data0, value_le_bytes[0]);
334 self.write(WriteOffset::Data1, value_le_bytes[1]);
335 }
336
337 pub fn get_baud(&self) -> Baud {
338 match self.read_divisor_latch() {
339 1 => Baud::B115200,
340 2 => Baud::B57600,
341 3 => Baud::B38400,
342 6 => Baud::B19200,
343 12 => Baud::B9600,
344 24 => Baud::B4800,
345 48 => Baud::B2400,
346 96 => Baud::B1200,
347 384 => Baud::B300,
348 2304 => Baud::B50,
349 _ => unimplemented!(),
350 }
351 }
352
353 pub fn set_baud(&mut self, baud: Baud) {
354 self.write_divisor_latch(baud as u16);
355 }
356
357 pub fn data_mode(mut self) -> Uart<Data> {
358 self.write(WriteOffset::LineControl, self.read_line_control().as_u8());
360
361 Uart::<Data>(self.0, PhantomData)
362 }
363}
364
365impl Uart<Data> {
366 pub unsafe fn new(address: UartAddress) -> Self {
371 Self(address, PhantomData)
372 }
373
374 #[inline]
375 pub fn read_data(&self) -> u8 {
376 self.read(ReadOffset::Data0)
377 }
378
379 #[inline]
380 pub fn write_data(&mut self, data: u8) {
381 self.write(WriteOffset::Data0, data);
382 }
383
384 #[inline]
385 pub fn read_interrupt_enable(&self) -> InterruptEnable {
386 InterruptEnable::from_bits_truncate(self.read(ReadOffset::Data1))
387 }
388
389 #[inline]
390 pub fn write_interrupt_enable(&mut self, value: InterruptEnable) {
391 self.write(WriteOffset::Data1, value.bits());
392 }
393
394 #[inline]
395 pub fn configure_mode(mut self) -> Uart<Configure> {
396 self.write(
398 WriteOffset::LineControl,
399 self.read_line_control().as_u8() | (1 << 7),
400 );
401
402 Uart::<Configure>(self.0, PhantomData)
403 }
404}
405
406pub const UART_FIFO_QUEUE_LEN: usize = 14;
407
408pub struct UartWriter {
409 uart: Uart<Data>,
410 queue_accumulator: usize,
411}
412
413impl UartWriter {
414 pub fn new(mut uart: Uart<Data>) -> Option<Self> {
415 uart.write_line_control(LineControl::empty());
417 uart.write_interrupt_enable(InterruptEnable::empty());
418
419 let mut uart = uart.configure_mode();
421 uart.set_baud(Baud::B115200);
422 let mut uart = uart.data_mode();
423
424 uart.write_line_control(LineControl {
426 bits: DataBits::Eight,
427 parity: ParityMode::None,
428 extra_stop: false,
429 break_signal: false,
430 });
431 uart.enable_fifo(true, true, false, FifoSize::Fourteen);
432
433 uart.write_model_control(
435 ModemControl::REQUEST_TO_SEND
436 | ModemControl::AUXILIARY_OUTPUT_1
437 | ModemControl::AUXILIARY_OUTPUT_2
438 | ModemControl::LOOPBACK_MODE,
439 );
440
441 const TEST_VALUE: u8 = 0x1F;
442 uart.write_data(TEST_VALUE);
443 if uart.read_data().ne(&TEST_VALUE) {
444 return None;
445 }
446
447 uart.write_model_control(
449 ModemControl::TERMINAL_READY
450 | ModemControl::REQUEST_TO_SEND
451 | ModemControl::AUXILIARY_OUTPUT_1
452 | ModemControl::AUXILIARY_OUTPUT_2,
453 );
454
455 Some(Self {
456 uart,
457 queue_accumulator: 0,
458 })
459 }
460
461 fn queue_index(&self) -> usize {
462 self.queue_accumulator % UART_FIFO_QUEUE_LEN
463 }
464
465 fn write_byte(&mut self, byte: u8) {
466 if self.queue_index() == UART_FIFO_QUEUE_LEN {
467 while !self
468 .uart
469 .read_line_status()
470 .contains(LineStatus::TRANSMIT_EMPTY_IDLE)
471 {
472 core::hint::spin_loop();
473 }
474 } else {
475 while !self
476 .uart
477 .read_line_status()
478 .contains(LineStatus::TRANSMIT_EMPTY)
479 {
480 core::hint::spin_loop();
481 }
482 }
483
484 self.uart.write_data(byte);
485 self.queue_accumulator += 1;
486 }
487}
488
489impl core::fmt::Write for UartWriter {
490 fn write_str(&mut self, s: &str) -> core::fmt::Result {
491 s.chars().try_for_each(|c| self.write_char(c))
492 }
493
494 fn write_char(&mut self, c: char) -> core::fmt::Result {
495 self.write_byte(u8::try_from(c).unwrap_or(b'?'));
496
497 Ok(())
498 }
499}