#![no_std]
#![forbid(clippy::inline_asm_x86_att_syntax)]
#![deny(
clippy::semicolon_if_nothing_returned,
clippy::debug_assert_with_mut_call,
clippy::float_arithmetic
)]
#![warn(clippy::cargo, clippy::pedantic, clippy::undocumented_unsafe_blocks)]
#![allow(clippy::must_use_candidate, clippy::upper_case_acronyms)]
#[cfg(feature = "address_impl")]
pub mod address;
#[cfg(feature = "writer_impl")]
pub mod writer;
use bitflags::bitflags;
use core::marker::PhantomData;
bitflags! {
#[repr(transparent)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct InterruptEnable: u8 {
const RECEIVED_DATA = 1 << 0;
const TRANSMIT_EMPTY = 1 << 1;
const RECEIVE_STATUS = 1 << 2;
const MODEM_STATUS = 1 << 3;
}
}
bitflags! {
#[repr(transparent)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct InterruptStatus: u8 {
const INTERRUPT_PENDING = 1 << 0;
const RECV_LINE_STATUS = 0b011 << 1;
const RECV_DATA_AVAIL = 0b010 << 1;
const TIMEOUT = 0b110 << 1;
const TX_EMPTY = 0b001 << 1;
const MODEM_STATUS = 0b000 << 1;
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum Baud {
B115200,
B57600,
B38400,
B19200,
B9600,
B4800,
B2400,
B1200,
B300,
B50,
}
impl Baud {
pub fn from_register_value(value: u16) -> Self {
match value {
1 => Self::B115200,
2 => Self::B57600,
3 => Self::B38400,
6 => Self::B19200,
12 => Self::B9600,
24 => Self::B4800,
48 => Self::B2400,
96 => Self::B1200,
384 => Self::B300,
2304 => Self::B50,
_ => unimplemented!(),
}
}
pub fn into_register_value(self) -> u16 {
match self {
Baud::B115200 => 1,
Baud::B57600 => 2,
Baud::B38400 => 3,
Baud::B19200 => 6,
Baud::B9600 => 12,
Baud::B4800 => 24,
Baud::B2400 => 48,
Baud::B1200 => 96,
Baud::B300 => 384,
Baud::B50 => 2304,
}
}
}
bitflags! {
#[repr(transparent)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct FifoControl: u8 {
const ENABLE = 1 << 0;
const CLEAR_RX = 1 << 1;
const CLEAR_TX = 1 << 2;
const INT_LVL_1 = 0b00 << 5;
const INT_LVL_4 = 0b01 << 5;
const INT_LVL_8 = 0b10 << 5;
const INT_LVL_14 = 0b11 << 5;
}
}
bitflags! {
#[repr(transparent)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct LineControl: u8 {
const BITS_5 = 0b00;
const BITS_6 = 0b01;
const BITS_7 = 0b10;
const BITS_8 = 0b11;
const EXTRA_STOP = 1 << 2;
const PARITY_ENABLE = 1 << 3;
const EVEN_PARITY = 1 << 4;
const STICK_PARITY = 1 << 5;
const BREAK_SIGNAL = 1 << 6;
const DLAB = 1 << 7;
}
}
bitflags! {
#[repr(transparent)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ModemControl: u8 {
const TERMINAL_READY = 1 << 0;
const REQUEST_TO_SEND = 1 << 1;
const OUT_1 = 1 << 2;
const OUT_2 = 1 << 3;
const LOOPBACK_MODE = 1 << 4;
}
}
bitflags! {
#[repr(transparent)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct LineStatus: u8 {
const DATA_AVAILABLE = 1 << 0;
const OVERRUN_ERROR = 1 << 1;
const PARITY_ERROR = 1 << 2;
const FRAMING_ERROR = 1 << 3;
const BREAK_INDICATOR = 1 << 4;
const THR_EMPTY = 1 << 5;
const THR_SHR_EMPTY = 1 << 6;
const IMPENDING_ERROR = 1 << 7;
}
}
bitflags! {
#[repr(transparent)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ModemStatus: u8 {
const CLEAR_TO_SEND_CHANGED = 1 << 0;
const DATA_SET_READY_CHANGED = 1 << 1;
const TRAILING_EDGE_RING_INDICATOR = 1 << 2;
const CARRIER_DETECT_CHANGE = 1 << 3;
const CLEAR_TO_SEND = 1 << 4;
const DATA_SET_READY = 1 << 5;
const RING_INDICATOR = 1 << 6;
const CARRIER_DETECT = 1 << 7;
}
}
#[derive(Debug, Clone, Copy)]
pub enum ReadableRegister {
ReceiverHolding,
InterruptEnable,
InterruptStatus,
LineControl,
LineStatus,
ModemStatus,
DivisorLatchLow,
DivisorLatchHigh,
}
impl ReadableRegister {
pub const fn as_index(self) -> u16 {
match self {
ReadableRegister::ReceiverHolding | ReadableRegister::DivisorLatchLow => 0x0,
ReadableRegister::InterruptEnable | ReadableRegister::DivisorLatchHigh => 0x1,
ReadableRegister::InterruptStatus => 0x2,
ReadableRegister::LineControl => 0x3,
ReadableRegister::LineStatus => 0x5,
ReadableRegister::ModemStatus => 0x6,
}
}
}
#[repr(u16)]
#[derive(Debug, Clone, Copy)]
pub enum WriteableRegister {
TransmitterHolding,
InterruptEnable,
FifoControl,
LineControl,
ModemControl,
DivisorLatchLow,
DivisorLatchHigh,
}
impl WriteableRegister {
pub const fn as_index(self) -> u16 {
match self {
WriteableRegister::TransmitterHolding | WriteableRegister::DivisorLatchLow => 0x0,
WriteableRegister::InterruptEnable | WriteableRegister::DivisorLatchHigh => 0x1,
WriteableRegister::FifoControl => 0x2,
WriteableRegister::LineControl => 0x3,
WriteableRegister::ModemControl => 0x4,
}
}
}
pub unsafe trait UartAddress {
unsafe fn write(&self, register: WriteableRegister, value: u8);
unsafe fn read(&self, register: ReadableRegister) -> u8;
}
pub trait Mode {}
pub struct Data;
impl Mode for Data {}
pub struct DLAB;
impl Mode for DLAB {}
pub struct Uart<A: UartAddress, M: Mode> {
base_address: A,
marker: PhantomData<M>,
}
impl<A: UartAddress, M: Mode> Uart<A, M> {
pub fn read_interrupt_status(&self) -> InterruptStatus {
let value = unsafe { self.base_address.read(ReadableRegister::InterruptStatus) };
InterruptStatus::from_bits_retain(value)
}
pub fn write_fifo_control(&mut self, fifo_control: FifoControl) {
unsafe {
self.base_address
.write(WriteableRegister::FifoControl, fifo_control.bits());
}
}
pub fn read_line_control(&self) -> LineControl {
let value = unsafe { self.base_address.read(ReadableRegister::LineControl) };
unsafe { LineControl::from_bits(value).unwrap_unchecked() }
}
pub fn write_line_control(&mut self, value: LineControl) {
unsafe {
self.base_address
.write(WriteableRegister::LineControl, value.bits());
}
}
pub fn write_modem_control(&mut self, value: ModemControl) {
unsafe {
self.base_address
.write(WriteableRegister::ModemControl, value.bits());
}
}
pub fn read_line_status(&mut self) -> LineStatus {
let value = unsafe { self.base_address.read(ReadableRegister::LineStatus) };
unsafe { LineStatus::from_bits(value).unwrap_unchecked() }
}
pub fn read_modem_status(&self) -> ModemStatus {
let value = unsafe { self.base_address.read(ReadableRegister::ModemStatus) };
ModemStatus::from_bits_retain(value)
}
}
impl<A: UartAddress> Uart<A, DLAB> {
pub unsafe fn new(base_address: A) -> Self {
Self {
base_address,
marker: PhantomData,
}
}
fn read_divisor_latch(&self) -> u16 {
unsafe {
let lsb = u16::from(self.base_address.read(ReadableRegister::ReceiverHolding));
let msb = u16::from(self.base_address.read(ReadableRegister::InterruptEnable));
(msb << 8) | lsb
}
}
fn write_divisor_latch(&mut self, value: u16) {
let value_le_bytes = value.to_le_bytes();
unsafe {
self.base_address
.write(WriteableRegister::TransmitterHolding, value_le_bytes[0]);
self.base_address
.write(WriteableRegister::InterruptEnable, value_le_bytes[1]);
}
}
#[allow(clippy::missing_panics_doc)]
pub fn get_baud(&self) -> Baud {
Baud::from_register_value(self.read_divisor_latch())
}
pub fn set_baud(&mut self, baud: Baud) {
self.write_divisor_latch(baud.into_register_value());
}
pub fn into_data_mode(self) -> Uart<A, Data> {
let mut line_control = self.read_line_control();
line_control.remove(LineControl::DLAB);
unsafe {
self.base_address
.write(WriteableRegister::LineControl, line_control.bits());
}
Uart::<A, Data> {
base_address: self.base_address,
marker: PhantomData,
}
}
}
impl<A: UartAddress> Uart<A, Data> {
pub unsafe fn new(base_address: A) -> Self {
Self {
base_address,
marker: PhantomData,
}
}
pub fn new_reset(base_address: A) -> Self {
let mut uart = unsafe { Self::new(base_address) };
uart.write_fifo_control(FifoControl::CLEAR_RX | FifoControl::CLEAR_TX);
uart.write_line_control(LineControl::empty());
uart.write_modem_control(ModemControl::empty());
uart
}
pub fn read_byte(&mut self) -> u8 {
unsafe { self.base_address.read(ReadableRegister::ReceiverHolding) }
}
pub fn write_byte(&mut self, byte: u8) {
unsafe {
self.base_address
.write(WriteableRegister::TransmitterHolding, byte);
}
}
pub fn read_interrupt_enable(&self) -> InterruptEnable {
let value = unsafe { self.base_address.read(ReadableRegister::InterruptEnable) };
InterruptEnable::from_bits_retain(value)
}
pub fn write_interrupt_enable(&mut self, value: InterruptEnable) {
unsafe {
self.base_address
.write(WriteableRegister::InterruptEnable, value.bits());
}
}
pub fn into_dlab_mode(self) -> Uart<A, DLAB> {
let mut line_control = self.read_line_control();
line_control.insert(LineControl::DLAB);
unsafe {
self.base_address
.write(WriteableRegister::LineControl, line_control.bits());
}
Uart::<A, DLAB> {
base_address: self.base_address,
marker: PhantomData,
}
}
}