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(clippy::must_use_candidate, clippy::upper_case_acronyms)]
10
11#[cfg(feature = "writer")]
12pub mod writer;
13
14#[cfg(any(target_arch = "x86_64", target_arch = "x86"))]
15pub use x86::*;
16#[cfg(any(target_arch = "x86_64", target_arch = "x86"))]
17mod x86 {
18 use super::UartAddress;
19
20 pub const COM1: UartAddress = UartAddress::Io(0x3F8);
23 pub const COM2: UartAddress = UartAddress::Io(0x2F8);
26 pub const COM3: UartAddress = UartAddress::Io(0x3E8);
29 pub const COM4: UartAddress = UartAddress::Io(0x2E8);
32}
33
34use bitflags::bitflags;
35use core::marker::PhantomData;
36
37bitflags! {
38 #[repr(transparent)]
39 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
40 pub struct InterruptEnable: u8 {
41 const RECEIVED_DATA = 1 << 0;
43
44 const TRANSMIT_EMPTY = 1 << 1;
46
47 const RECEIVE_STATUS = 1 << 2;
49
50 const MODEM_STATUS = 1 << 3;
52
53 }
55}
56
57bitflags! {
58 #[repr(transparent)]
59 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
60 pub struct InterruptIdent: u8 {
61 const INTERRUPT_PENDING = 1 << 0;
63
64 const RECV_LINE_STATUS = 0b011 << 1;
68
69 const RECV_DATA_AVAIL = 0b010 << 1;
73
74 const TIMEOUT = 0b110 << 1;
79
80 const TX_EMPTY = 0b001 << 1;
84
85 const MODEM_STATUS = 0b000 << 1;
89
90 }
93}
94
95#[repr(u16)]
97#[derive(Debug, Copy, Clone, Eq, PartialEq)]
98pub enum Baud {
99 B115200 = 1,
100 B57600 = 2,
101 B38400 = 3,
102 B19200 = 6,
103 B9600 = 12,
104 B4800 = 24,
105 B2400 = 48,
106 B1200 = 96,
107 B300 = 384,
108 B50 = 2304,
109}
110
111bitflags! {
112 #[repr(transparent)]
113 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
114 pub struct FifoControl: u8 {
115 const ENABLE = 1 << 0;
117
118 const CLEAR_RX = 1 << 1;
120
121 const CLEAR_TX = 1 << 2;
123
124 const INT_LVL_1 = 0b00 << 5;
126
127 const INT_LVL_4 = 0b01 << 5;
129
130 const INT_LVL_8 = 0b10 << 5;
132
133 const INT_LVL_14 = 0b11 << 5;
135 }
136}
137
138bitflags! {
139 #[repr(transparent)]
140 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
141 pub struct LineControl: u8 {
142 const BITS_5 = 0b00;
144
145 const BITS_6 = 0b01;
147
148 const BITS_7 = 0b10;
150
151 const BITS_8 = 0b11;
153
154 const EXTRA_STOP = 1 << 2;
156
157 const PARITY_ENABLE = 1 << 3;
159
160 const EVEN_PARITY = 1 << 4;
162
163 const STICK_PARITY = 1 << 5;
167
168 const BREAK_SIGNAL = 1 << 6;
171
172 const DLAB = 1 << 7;
174 }
175}
176
177bitflags! {
178 #[repr(transparent)]
179 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
180 pub struct ModemControl: u8 {
181 const TERMINAL_READY = 1 << 0;
183
184 const REQUEST_TO_SEND = 1 << 1;
186
187 const AUXILIARY_OUTPUT_1 = 1 << 2;
189
190 const AUXILIARY_OUTPUT_2 = 1 << 3;
192
193 const LOOPBACK_MODE = 1 << 4;
196 }
197}
198
199bitflags! {
200 #[repr(transparent)]
201 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
202 pub struct LineStatus: u8 {
203 const DATA_AVAILABLE = 1 << 0;
205
206 const OVERRUN_ERROR = 1 << 1;
208
209 const PARITY_ERROR = 1 << 2;
211
212 const FRAMING_ERROR = 1 << 3;
214
215 const BREAK_INDICATOR = 1 << 4;
217
218 const THR_EMPTY = 1 << 5;
220
221 const THR_SHR_EMPTY = 1 << 6;
223
224 const IMPENDING_ERROR = 1 << 7;
226 }
227}
228
229bitflags! {
230 #[repr(transparent)]
231 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
232 pub struct ModemStatus: u8 {
233 const CLEAR_TO_SEND_CHANGED = 1 << 0;
235
236 const DATA_SET_READY_CHANGED = 1 << 1;
238
239 const TRAILING_EDGE_RING_INDICATOR = 1 << 2;
242
243 const CARRIER_DETECT_CHANGE = 1 << 3;
245
246 const CLEAR_TO_SEND = 1 << 4;
248
249 const DATA_SET_READY = 1 << 5;
251
252 const RING_INDICATOR = 1 << 6;
254
255 const CARRIER_DETECT = 1 << 7;
257 }
258}
259
260pub enum UartAddress {
261 Io(u16),
262 Mmio(*mut u8),
263}
264
265#[repr(usize)]
266#[allow(dead_code)]
267enum ReadOffset {
268 RHR = 0x0,
270
271 IER = 0x1,
273
274 IIR = 0x2,
276
277 LCR = 0x3,
279
280 LSR = 0x5,
282
283 MSR = 0x6,
285}
286
287#[repr(usize)]
288enum WriteOffset {
289 THR = 0x0,
291
292 IER = 0x1,
294
295 FCR = 0x2,
297
298 LCR = 0x3,
300
301 MCR = 0x4,
303}
304
305pub trait Mode {}
306pub struct Data;
307impl Mode for Data {}
308pub struct Configure;
309impl Mode for Configure {}
310
311pub struct Uart<M: Mode>(UartAddress, PhantomData<M>);
312
313impl<M: Mode> Uart<M> {
314 fn read(&self, offset: ReadOffset) -> u8 {
315 unsafe {
317 match self.0 {
318 UartAddress::Io(port) => {
319 let value: u8;
320
321 #[cfg(target_arch = "x86_64")]
322 core::arch::asm!("in al, dx", out("al") value, in("dx") port + (offset as u16), options(nostack, nomem, preserves_flags));
323
324 #[cfg(not(target_arch = "x86_64"))]
325 unimplemented!();
326
327 value
328 }
329
330 UartAddress::Mmio(ptr) => ptr.add(offset as usize).read_volatile(),
331 }
332 }
333 }
334
335 fn write(&mut self, offset: WriteOffset, value: u8) {
336 unsafe {
338 match self.0 {
339 UartAddress::Io(port) => {
340 #[cfg(target_arch = "x86_64")]
341 core::arch::asm!("out dx, al", in("dx") port + (offset as u16), in("al") value, options(nostack, nomem, preserves_flags));
342
343 #[cfg(not(target_arch = "x86_64"))]
344 unimplemented!();
345 }
346
347 UartAddress::Mmio(ptr) => ptr.add(offset as usize).write_volatile(value),
348 }
349 }
350 }
351
352 pub fn write_fifo_control(&mut self, fifo_control: FifoControl) {
353 self.write(WriteOffset::FCR, fifo_control.bits());
354 }
355
356 pub fn read_line_control(&self) -> LineControl {
357 unsafe { LineControl::from_bits(self.read(ReadOffset::LCR)).unwrap_unchecked() }
359 }
360
361 pub fn write_line_control(&mut self, value: LineControl) {
362 self.write(WriteOffset::LCR, value.bits());
363 }
364
365 pub fn write_modem_control(&mut self, value: ModemControl) {
366 self.write(WriteOffset::MCR, value.bits());
367 }
368
369 pub fn read_line_status(&self) -> LineStatus {
370 LineStatus::from_bits_truncate(self.read(ReadOffset::LSR))
371 }
372
373 pub fn read_modem_status(&self) -> ModemStatus {
374 ModemStatus::from_bits_truncate(self.read(ReadOffset::MSR))
375 }
376}
377
378impl Uart<Configure> {
379 fn read_divisor_latch(&self) -> u16 {
380 let lsb = u16::from(self.read(ReadOffset::RHR));
384 let msb = u16::from(self.read(ReadOffset::IER));
385
386 (msb << 8) | lsb
387 }
388
389 fn write_divisor_latch(&mut self, value: u16) {
390 let value_le_bytes = value.to_le_bytes();
393 self.write(WriteOffset::THR, value_le_bytes[0]);
394 self.write(WriteOffset::IER, value_le_bytes[1]);
395 }
396
397 pub fn get_baud(&self) -> Baud {
398 match self.read_divisor_latch() {
399 1 => Baud::B115200,
400 2 => Baud::B57600,
401 3 => Baud::B38400,
402 6 => Baud::B19200,
403 12 => Baud::B9600,
404 24 => Baud::B4800,
405 48 => Baud::B2400,
406 96 => Baud::B1200,
407 384 => Baud::B300,
408 2304 => Baud::B50,
409 _ => unimplemented!(),
410 }
411 }
412
413 pub fn set_baud(&mut self, baud: Baud) {
414 self.write_divisor_latch(baud as u16);
415 }
416
417 pub fn into_data_mode(mut self) -> Uart<Data> {
419 let mut line_control = self.read_line_control();
420 line_control.remove(LineControl::DLAB);
421
422 self.write(WriteOffset::LCR, line_control.bits());
423
424 Uart::<Data>(self.0, PhantomData)
425 }
426}
427
428impl Uart<Data> {
429 pub unsafe fn new(address: UartAddress) -> Self {
434 Self(address, PhantomData)
435 }
436
437 pub fn read_byte(&self) -> u8 {
438 self.read(ReadOffset::RHR)
439 }
440
441 pub fn write_byte(&mut self, byte: u8) {
442 self.write(WriteOffset::THR, byte);
443 }
444
445 pub fn read_interrupt_enable(&self) -> InterruptEnable {
446 InterruptEnable::from_bits_truncate(self.read(ReadOffset::IER))
447 }
448
449 pub fn write_interrupt_enable(&mut self, value: InterruptEnable) {
450 self.write(WriteOffset::IER, value.bits());
451 }
452
453 pub fn into_configure_mode(mut self) -> Uart<Configure> {
455 let mut line_control = self.read_line_control();
456 line_control.insert(LineControl::DLAB);
457
458 self.write(WriteOffset::LCR, line_control.bits());
459
460 Uart::<Configure>(self.0, PhantomData)
461 }
462}