#![cfg_attr(docsrs, feature(doc_cfg))]
#![cfg_attr(
any(target_arch = "x86", target_arch = "x86_64"),
doc = "```rust,no_run"
)]
#![cfg_attr(
not(any(target_arch = "x86", target_arch = "x86_64")),
doc = "```rust,ignore"
)]
#![no_std]
#![deny(
clippy::all,
clippy::cargo,
clippy::nursery,
clippy::must_use_candidate,
clippy::missing_safety_doc,
clippy::undocumented_unsafe_blocks,
clippy::needless_pass_by_value
)]
#![deny(missing_docs)]
#![deny(missing_debug_implementations)]
#![deny(rustdoc::all)]
#[cfg(test)]
extern crate std;
pub use crate::config::*;
pub use crate::error::*;
pub use crate::tty::*;
use crate::backend::{Backend, MmioAddress, MmioBackend};
#[cfg(any(target_arch = "x86", target_arch = "x86_64", doc))]
use crate::backend::{PioBackend, PortIoAddress};
use crate::spec::registers::{DLL, DLM, FCR, IER, ISR, LCR, LSR, MCR, MSR, SPR, offsets};
use crate::spec::{FIFO_SIZE, NUM_REGISTERS, calc_baud_rate, calc_divisor};
use core::cmp;
use core::hint;
use core::num::NonZeroU8;
use core::ptr::NonNull;
pub mod backend;
pub mod spec;
mod config;
#[cfg(feature = "embedded-io")]
mod embedded_io;
mod error;
mod tty;
#[cfg_attr(
any(target_arch = "x86", target_arch = "x86_64"),
doc = "```rust,no_run"
)]
#[cfg_attr(
not(any(target_arch = "x86", target_arch = "x86_64")),
doc = "```rust,ignore"
)]
#[derive(Debug)]
pub struct Uart16550<B: Backend> {
backend: B,
base_address: B::Address,
config: Config,
}
#[cfg(any(target_arch = "x86", target_arch = "x86_64", doc))]
impl Uart16550<PioBackend> {
pub unsafe fn new_port(base_port: u16) -> Result<Self, InvalidAddressError<PortIoAddress>> {
let base_address = PortIoAddress(base_port);
if base_port.checked_add(NUM_REGISTERS as u16 - 1).is_none() {
return Err(InvalidAddressError::InvalidBaseAddress(base_address));
}
let backend = PioBackend(base_address);
Ok(Self {
backend,
base_address,
config: Config::default(),
})
}
}
impl Uart16550<MmioBackend> {
pub unsafe fn new_mmio(
base_address: NonNull<u8>,
stride: u8,
) -> Result<Self, InvalidAddressError<MmioAddress>> {
let base_address = MmioAddress(base_address);
if stride == 0 || !stride.is_power_of_two() {
return Err(InvalidAddressError::InvalidStride(stride));
}
if (base_address.0.as_ptr() as usize)
.checked_add((NUM_REGISTERS - 1) * stride as usize)
.is_none()
{
return Err(InvalidAddressError::InvalidBaseAddress(base_address));
}
let stride = NonZeroU8::new(stride).unwrap();
let backend = MmioBackend {
base_address,
stride,
};
Ok(Self {
backend,
base_address,
config: Config::default(),
})
}
}
impl<B: Backend> Uart16550<B> {
pub fn init(&mut self, config: Config) -> Result<(), InitError> {
self.config = config;
{
let mut check_fn = |write| {
let read = unsafe {
self.backend.write(offsets::SPR as u8, write);
self.backend.read(offsets::SPR as u8)
};
if read != write {
return Err(InitError::DeviceNotPresent);
}
Ok(())
};
check_fn(0x42)?;
check_fn(0x73)?;
}
unsafe {
self.backend.write(offsets::IER as u8, 0);
}
unsafe {
self.backend.write(offsets::LCR as u8, LCR::DLAB.bits());
let divisor = calc_divisor(
self.config.frequency,
self.config.baud_rate.to_integer(),
self.config.prescaler_division_factor,
)
.map_err(InitError::InvalidBaudRate)?;
let low = (divisor & 0xff) as u8;
let high = ((divisor >> 8) & 0xff) as u8;
self.backend.write(offsets::DLL as u8, low);
self.backend.write(offsets::DLM as u8, high);
self.backend.write(offsets::LCR as u8, 0);
}
unsafe {
let mut lcr = LCR::from_bits_retain(0);
lcr = lcr.set_word_length(self.config.data_bits);
if self.config.extra_stop_bits {
lcr |= LCR::MORE_STOP_BITS;
}
lcr = lcr.set_parity(self.config.parity);
self.backend.write(offsets::LCR as u8, lcr.bits());
}
unsafe {
self.configure_fcr();
}
unsafe {
let mut mcr = MCR::from_bits_retain(0);
mcr |= MCR::DTR;
mcr |= MCR::RTS;
mcr |= MCR::OUT_2_INT_ENABLE;
self.backend.write(offsets::MCR as u8, mcr.bits());
}
unsafe {
self.backend
.write(offsets::IER as u8, self.config.interrupts.bits());
}
loop {
let lsr = self.lsr();
if lsr.contains(LSR::TRANSMITTER_EMPTY) {
break;
} else {
hint::spin_loop()
}
}
Ok(())
}
pub fn test_loopback(&mut self) -> Result<(), LoopbackError> {
const TEST_BYTE: u8 = 0x42;
const TEST_MESSAGE: [u8; FIFO_SIZE] = *b"hello world!1337";
unsafe {
let old_mcr = self.mcr();
self.backend
.write(offsets::MCR as u8, MCR::LOOP_BACK.bits());
self.configure_fcr();
while self.receive_bytes(&mut [0]) > 0 {}
{
self.try_send_byte(TEST_BYTE)
.map_err(LoopbackError::SendError)?;
let mut read_buf = [0];
self.receive_bytes_exact(&mut read_buf);
let read = read_buf[0];
if read != TEST_BYTE {
return Err(LoopbackError::UnexpectedLoopbackByte {
expected: TEST_BYTE,
actual: read,
});
}
}
{
self.send_bytes_exact(&TEST_MESSAGE);
let mut read_buffer = [0_u8; TEST_MESSAGE.len()];
self.receive_bytes_exact(&mut read_buffer);
let read = read_buffer;
if read != TEST_MESSAGE {
return Err(LoopbackError::UnexpectedLoopbackMsg {
expected: TEST_MESSAGE,
actual: read_buffer,
});
}
}
self.backend.write(offsets::MCR as u8, old_mcr.bits());
}
Ok(())
}
pub fn check_connected(&mut self) -> Result<(), RemoteReadyToReceiveError> {
let msr = unsafe { self.backend.read(offsets::MSR as u8) };
let msr = MSR::from_bits_retain(msr);
if !msr.contains(MSR::DSR) {
return Err(RemoteReadyToReceiveError::NoRemoteConnectedNoDSR);
}
if !msr.contains(MSR::CTS) {
return Err(RemoteReadyToReceiveError::RemoteNotClearToSend);
}
Ok(())
}
pub fn ready_to_receive(&mut self) -> Result<(), ByteReceiveError> {
let lsr = self.lsr();
if !lsr.contains(LSR::DATA_READY) {
return Err(ByteReceiveError);
}
Ok(())
}
pub fn ready_to_send(&mut self) -> Result<(), ByteSendError> {
let lsr = self.lsr();
let msr = self.msr();
let mcr = self.mcr();
if !lsr.contains(LSR::THR_EMPTY) {
return Err(ByteSendError::NoCapacity);
}
if !mcr.contains(MCR::LOOP_BACK) && !msr.contains(MSR::CTS) {
return Err(ByteSendError::RemoteNotClearToSend);
}
Ok(())
}
pub fn try_receive_byte(&mut self) -> Result<u8, ByteReceiveError> {
self.ready_to_receive()?;
let byte = unsafe { self.backend.read(offsets::DATA as u8) };
Ok(byte)
}
#[inline]
pub fn try_send_byte(&mut self, byte: u8) -> Result<(), ByteSendError> {
match self.send_bytes(&[byte]) {
0 => Err(ByteSendError::NoCapacity),
_ => Ok(()),
}
}
pub fn receive_bytes(&mut self, buffer: &mut [u8]) -> usize {
buffer
.iter_mut()
.map_while(|slot: &mut u8| {
self.try_receive_byte().ok().map(|byte| {
*slot = byte;
})
})
.count()
}
pub fn send_bytes(&mut self, buffer: &[u8]) -> usize {
if buffer.is_empty() {
return 0;
}
if self.ready_to_send().is_err() {
return 0;
}
let fifo_enabled = self.config.fifo_trigger_level.is_some();
let bytes = if fifo_enabled {
let max_index = cmp::min(FIFO_SIZE, buffer.len());
&buffer[..max_index]
} else {
&buffer[..1]
};
for &byte in bytes {
unsafe {
self.backend.write(offsets::DATA as u8, byte);
}
}
bytes.len()
}
pub fn receive_bytes_exact(&mut self, buffer: &mut [u8]) {
for slot in buffer {
loop {
if let Ok(byte) = self.try_receive_byte() {
*slot = byte;
break;
} else {
hint::spin_loop()
}
}
}
}
pub fn send_bytes_exact(&mut self, bytes: &[u8]) {
let mut remaining_bytes = bytes;
while !remaining_bytes.is_empty() {
let n = self.send_bytes(remaining_bytes);
remaining_bytes = &remaining_bytes[n..];
if n > 0 {
continue;
} else {
hint::spin_loop()
}
}
}
pub fn ier(&mut self) -> IER {
let val = unsafe { self.backend.read(offsets::IER as u8) };
IER::from_bits_retain(val)
}
pub fn isr(&mut self) -> ISR {
let val = unsafe { self.backend.read(offsets::ISR as u8) };
ISR::from_bits_retain(val)
}
pub fn lcr(&mut self) -> LCR {
let val = unsafe { self.backend.read(offsets::LCR as u8) };
LCR::from_bits_retain(val)
}
pub fn mcr(&mut self) -> MCR {
let val = unsafe { self.backend.read(offsets::MCR as u8) };
MCR::from_bits_retain(val)
}
pub fn lsr(&mut self) -> LSR {
let val = unsafe { self.backend.read(offsets::LSR as u8) };
LSR::from_bits_retain(val)
}
pub fn msr(&mut self) -> MSR {
let val = unsafe { self.backend.read(offsets::MSR as u8) };
MSR::from_bits_retain(val)
}
pub fn spr(&mut self) -> SPR {
unsafe { self.backend.read(offsets::SPR as u8) }
}
pub fn dll_dlm(&mut self) -> (DLL, DLM) {
let old_lcr = self.lcr();
unsafe {
self.backend
.write(offsets::LCR as u8, (old_lcr | LCR::DLAB).bits());
let dll = self.backend.read(offsets::DLL as u8);
let dlm = self.backend.read(offsets::DLM as u8);
self.backend.write(offsets::LCR as u8, old_lcr.bits());
(dll, dlm)
}
}
unsafe fn configure_fcr(&mut self) {
unsafe {
let mut fcr = FCR::from_bits_retain(0);
if self.config.fifo_trigger_level.is_some() {
fcr |= FCR::FIFO_ENABLE;
}
fcr |= FCR::RX_FIFO_RESET;
fcr |= FCR::TX_FIFO_RESET;
if let Some(level) = self.config.fifo_trigger_level {
fcr = fcr.set_fifo_trigger_level(level);
}
self.backend.write(offsets::FCR as u8, fcr.bits());
}
}
pub const fn config(&self) -> (&Config, B::Address) {
(&self.config, self.base_address)
}
pub fn config_register_dump(&mut self) -> ConfigRegisterDump {
let (dll, dlm) = self.dll_dlm();
ConfigRegisterDump {
ier: self.ier(),
isr: self.isr(),
lcr: self.lcr(),
mcr: self.mcr(),
lsr: self.lsr(),
msr: self.msr(),
spr: self.spr(),
dll,
dlm,
}
}
}
unsafe impl<B: Backend> Send for Uart16550<B> {}
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
pub struct ConfigRegisterDump {
pub ier: IER,
pub isr: ISR,
pub lcr: LCR,
pub mcr: MCR,
pub lsr: LSR,
pub msr: MSR,
pub spr: SPR,
pub dll: DLL,
pub dlm: DLM,
}
impl ConfigRegisterDump {
#[must_use]
pub const fn divisor(&self) -> u16 {
let dll = self.dll as u16;
let dlm = self.dlm as u16;
(dlm << 8) | dll
}
#[must_use]
pub fn baud_rate(&self, config: &Config) -> BaudRate {
let divisor = self.divisor();
let baud_rate = calc_baud_rate(
config.frequency,
divisor as u32,
config.prescaler_division_factor,
)
.expect("should be able to calculate baud rate from the given valid values");
BaudRate::from_integer(baud_rate)
}
}
#[cfg(test)]
mod tests {
use super::*;
use core::ptr;
#[test]
fn constructors() {
#[cfg(any(target_arch = "x86", target_arch = "x86_64", doc))]
unsafe {
assert2::assert!(let Ok(_) = Uart16550::new_port(0x3f8));
assert2::assert!(let Ok(_) = Uart16550::new_port(u16::MAX - NUM_REGISTERS as u16));
assert2::assert!(let Ok(_) = Uart16550::new_port(u16::MAX - 7));
assert2::assert!(let Err(InvalidAddressError::InvalidBaseAddress(PortIoAddress(_))) = Uart16550::new_port(u16::MAX - 6));
assert2::assert!(let Err(InvalidAddressError::InvalidBaseAddress(PortIoAddress(_))) = Uart16550::new_port(u16::MAX));
}
unsafe {
let mmio_address = ptr::with_exposed_provenance_mut::<u8>(0x1000);
let mmio_address = NonNull::new(mmio_address).unwrap();
assert2::assert!(let Ok(_) = Uart16550::new_mmio(mmio_address, 1));
assert2::assert!(let Ok(_) = Uart16550::new_mmio(mmio_address, 2));
assert2::assert!(let Ok(_) = Uart16550::new_mmio(mmio_address, 4));
assert2::assert!(let Ok(_) = Uart16550::new_mmio(mmio_address, 8));
assert2::assert!(
let Err(InvalidAddressError::InvalidStride(0)) =
Uart16550::new_mmio(mmio_address, 0)
);
assert2::assert!(
let Err(InvalidAddressError::InvalidStride(3)) =
Uart16550::new_mmio(mmio_address, 3)
);
assert2::assert!(
let Err(InvalidAddressError::InvalidStride(5)) =
Uart16550::new_mmio(mmio_address, 5)
);
assert2::assert!(
let Err(InvalidAddressError::InvalidStride(6)) =
Uart16550::new_mmio(mmio_address, 6)
);
assert2::assert!(
let Err(InvalidAddressError::InvalidStride(7)) =
Uart16550::new_mmio(mmio_address, 7)
);
assert2::assert!(
let Err(InvalidAddressError::InvalidStride(9)) =
Uart16550::new_mmio(mmio_address, 9)
);
}
}
#[test]
fn is_send() {
fn accept<T: Send>() {}
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
accept::<Uart16550<PioBackend>>();
accept::<Uart16550<MmioBackend>>();
}
#[test]
fn mmio_dummy() {
let config = Config {
baud_rate: BaudRate::Baud38400,
..Default::default()
};
{
const STRIDE: usize = 1;
let mut memory = [0_u8; NUM_REGISTERS * STRIDE];
memory[offsets::LSR] = LSR::TRANSMITTER_EMPTY.bits();
let mmio_address = NonNull::new(memory.as_mut_ptr()).unwrap();
let mut uart = unsafe { Uart16550::new_mmio(mmio_address, STRIDE as u8) }
.expect("should be able to create the dummy MMIO");
uart.init(config.clone())
.expect("should be able to initialize the dummy MMIO");
let divisor = uart.config_register_dump().divisor();
let divisor = divisor & 0xff;
assert2::check!(divisor == 3);
}
{
const STRIDE: usize = 4;
let mut memory = [0_u8; NUM_REGISTERS * STRIDE];
memory[offsets::LSR * STRIDE] = LSR::TRANSMITTER_EMPTY.bits();
let mmio_address = NonNull::new(memory.as_mut_ptr()).unwrap();
let mut uart = unsafe { Uart16550::new_mmio(mmio_address, STRIDE as u8) }
.expect("should be able to create the dummy MMIO");
uart.init(config)
.expect("should be able to initialize the dummy MMIO");
let divisor = uart.config_register_dump().divisor();
let divisor = divisor & 0xff;
assert2::check!(divisor == 3);
}
}
}