use std::collections::VecDeque;
use std::io::{Error, Result, Write};
use vmm_sys_util::eventfd::EventFd;
const DATA_OFFSET: u8 = 0;
const IER_OFFSET: u8 = 1;
const IIR_OFFSET: u8 = 2;
const LCR_OFFSET: u8 = 3;
const MCR_OFFSET: u8 = 4;
const LSR_OFFSET: u8 = 5;
const MSR_OFFSET: u8 = 6;
const SCR_OFFSET: u8 = 7;
const DLAB_LOW_OFFSET: u8 = 0;
const DLAB_HIGH_OFFSET: u8 = 1;
const FIFO_SIZE: usize = 0x40;
const IER_RDA_BIT: u8 = 0b0000_0001;
const IER_THR_EMPTY_BIT: u8 = 0b0000_0010;
const IER_UART_VALID_BITS: u8 = 0b0000_1111;
const IIR_FIFO_BITS: u8 = 0b1100_0000;
const IIR_NONE_BIT: u8 = 0b0000_0001;
const IIR_THR_EMPTY_BIT: u8 = 0b0000_0010;
const IIR_RDA_BIT: u8 = 0b0000_0100;
const LCR_DLAB_BIT: u8 = 0b1000_0000;
const LSR_DATA_READY_BIT: u8 = 0b0000_0001;
const LSR_EMPTY_THR_BIT: u8 = 0b0010_0000;
const LSR_IDLE_BIT: u8 = 0b0100_0000;
const MCR_DTR_BIT: u8 = 0b0000_0001;
const MCR_RTS_BIT: u8 = 0b0000_0010;
const MCR_OUT1_BIT: u8 = 0b0000_0100;
const MCR_OUT2_BIT: u8 = 0b0000_1000;
const MCR_LOOP_BIT: u8 = 0b0001_0000;
const MSR_CTS_BIT: u8 = 0b0001_0000;
const MSR_DSR_BIT: u8 = 0b0010_0000;
const MSR_RI_BIT: u8 = 0b0100_0000;
const MSR_DCD_BIT: u8 = 0b1000_0000;
const DEFAULT_BAUD_DIVISOR_HIGH: u8 = 0x00;
const DEFAULT_BAUD_DIVISOR_LOW: u8 = 0x0C;
const DEFAULT_INTERRUPT_ENABLE: u8 = 0x00;
const DEFAULT_INTERRUPT_IDENTIFICATION: u8 = IIR_NONE_BIT;
const DEFAULT_LINE_STATUS: u8 = LSR_EMPTY_THR_BIT | LSR_IDLE_BIT;
const DEFAULT_LINE_CONTROL: u8 = 0b0000_0011;
const DEFAULT_MODEM_CONTROL: u8 = MCR_OUT2_BIT;
const DEFAULT_MODEM_STATUS: u8 = MSR_DSR_BIT | MSR_CTS_BIT | MSR_DCD_BIT;
const DEFAULT_SCRATCH: u8 = 0x00;
pub struct Serial<W: Write> {
baud_divisor_low: u8,
baud_divisor_high: u8,
interrupt_enable: u8,
interrupt_identification: u8,
line_control: u8,
line_status: u8,
modem_control: u8,
modem_status: u8,
scratch: u8,
in_buffer: VecDeque<u8>,
interrupt_evt: EventFd,
out: W,
}
impl<W: Write> Serial<W> {
pub fn new(interrupt_evt: EventFd, out: W) -> Serial<W> {
Serial {
baud_divisor_low: DEFAULT_BAUD_DIVISOR_LOW,
baud_divisor_high: DEFAULT_BAUD_DIVISOR_HIGH,
interrupt_enable: DEFAULT_INTERRUPT_ENABLE,
interrupt_identification: DEFAULT_INTERRUPT_IDENTIFICATION,
line_control: DEFAULT_LINE_CONTROL,
line_status: DEFAULT_LINE_STATUS,
modem_control: DEFAULT_MODEM_CONTROL,
modem_status: DEFAULT_MODEM_STATUS,
scratch: DEFAULT_SCRATCH,
in_buffer: VecDeque::new(),
interrupt_evt,
out,
}
}
pub fn interrupt_evt(&self) -> &EventFd {
&self.interrupt_evt
}
fn is_dlab_set(&self) -> bool {
(self.line_control & LCR_DLAB_BIT) != 0
}
fn is_rda_interrupt_enabled(&self) -> bool {
(self.interrupt_enable & IER_RDA_BIT) != 0
}
fn is_thr_interrupt_enabled(&self) -> bool {
(self.interrupt_enable & IER_THR_EMPTY_BIT) != 0
}
fn is_in_loop_mode(&self) -> bool {
(self.modem_control & MCR_LOOP_BIT) != 0
}
fn trigger_interrupt(&mut self) -> Result<()> {
self.interrupt_evt.write(1)
}
fn set_lsr_rda_bit(&mut self) {
self.line_status |= LSR_DATA_READY_BIT
}
fn clear_lsr_rda_bit(&mut self) {
self.line_status &= !LSR_DATA_READY_BIT
}
fn add_interrupt(&mut self, interrupt_bits: u8) {
self.interrupt_identification &= !IIR_NONE_BIT;
self.interrupt_identification |= interrupt_bits;
}
fn del_interrupt(&mut self, interrupt_bits: u8) {
self.interrupt_identification &= !interrupt_bits;
if self.interrupt_identification == 0x00 {
self.interrupt_identification = IIR_NONE_BIT;
}
}
fn thr_empty_interrupt(&mut self) -> Result<()> {
if self.is_thr_interrupt_enabled() {
if self.interrupt_identification & IIR_THR_EMPTY_BIT == 0 {
self.add_interrupt(IIR_THR_EMPTY_BIT);
self.trigger_interrupt()?
}
}
Ok(())
}
fn received_data_interrupt(&mut self) -> Result<()> {
if self.is_rda_interrupt_enabled() {
if self.interrupt_identification & IIR_RDA_BIT == 0 {
self.add_interrupt(IIR_RDA_BIT);
self.trigger_interrupt()?
}
}
Ok(())
}
fn reset_iir(&mut self) {
self.interrupt_identification = DEFAULT_INTERRUPT_IDENTIFICATION
}
pub fn write(&mut self, offset: u8, value: u8) -> Result<()> {
match offset {
DLAB_LOW_OFFSET if self.is_dlab_set() => self.baud_divisor_low = value,
DLAB_HIGH_OFFSET if self.is_dlab_set() => self.baud_divisor_high = value,
DATA_OFFSET => {
if self.is_in_loop_mode() {
if self.in_buffer.len() < FIFO_SIZE {
self.in_buffer.push_back(value);
self.set_lsr_rda_bit();
self.received_data_interrupt()?;
}
} else {
self.out.write_all(&[value])?;
self.out.flush()?;
self.thr_empty_interrupt()?;
}
}
IER_OFFSET => self.interrupt_enable = value & IER_UART_VALID_BITS,
LCR_OFFSET => self.line_control = value,
MCR_OFFSET => self.modem_control = value,
SCR_OFFSET => self.scratch = value,
_ => {}
}
Ok(())
}
pub fn read(&mut self, offset: u8) -> u8 {
match offset {
DLAB_LOW_OFFSET if self.is_dlab_set() => self.baud_divisor_low,
DLAB_HIGH_OFFSET if self.is_dlab_set() => self.baud_divisor_high,
DATA_OFFSET => {
self.del_interrupt(IIR_RDA_BIT);
if self.in_buffer.len() <= 1 {
self.clear_lsr_rda_bit();
}
self.in_buffer.pop_front().unwrap_or_default()
}
IER_OFFSET => self.interrupt_enable,
IIR_OFFSET => {
let iir = self.interrupt_identification | IIR_FIFO_BITS;
self.reset_iir();
iir
}
LCR_OFFSET => self.line_control,
MCR_OFFSET => self.modem_control,
LSR_OFFSET => self.line_status,
MSR_OFFSET => {
if self.is_in_loop_mode() {
let mut msr =
self.modem_status & !(MSR_DSR_BIT | MSR_CTS_BIT | MSR_RI_BIT | MSR_DCD_BIT);
if (self.modem_control & MCR_DTR_BIT) != 0 {
msr |= MSR_DSR_BIT;
}
if (self.modem_control & MCR_RTS_BIT) != 0 {
msr |= MSR_CTS_BIT;
}
if (self.modem_control & MCR_OUT1_BIT) != 0 {
msr |= MSR_RI_BIT;
}
if (self.modem_control & MCR_OUT2_BIT) != 0 {
msr |= MSR_DCD_BIT;
}
msr
} else {
self.modem_status
}
}
SCR_OFFSET => self.scratch,
_ => 0,
}
}
#[inline]
pub fn fifo_capacity(&self) -> usize {
FIFO_SIZE - self.in_buffer.len()
}
pub fn enqueue_raw_bytes(&mut self, input: &[u8]) -> Result<usize> {
let mut write_count = 0;
if !self.is_in_loop_mode() {
if self.fifo_capacity() == 0 {
return Err(Error::from_raw_os_error(libc::ENOBUFS));
}
write_count = std::cmp::min(self.fifo_capacity(), input.len());
if write_count > 0 {
self.in_buffer.extend(&input[0..write_count]);
self.set_lsr_rda_bit();
self.received_data_interrupt()?;
}
}
Ok(write_count)
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::io::sink;
const RAW_INPUT_BUF: [u8; 3] = [b'a', b'b', b'c'];
#[test]
fn test_serial_output() {
let intr_evt = EventFd::new(libc::EFD_NONBLOCK).unwrap();
let mut serial = Serial::new(intr_evt, Vec::new());
RAW_INPUT_BUF
.iter()
.for_each(|&c| serial.write(DATA_OFFSET, c).unwrap());
assert_eq!(serial.out.as_slice(), &RAW_INPUT_BUF);
}
#[test]
fn test_serial_raw_input() {
let intr_evt = EventFd::new(libc::EFD_NONBLOCK).unwrap();
let mut serial = Serial::new(intr_evt.try_clone().unwrap(), sink());
serial.write(IER_OFFSET, IER_RDA_BIT).unwrap();
serial.enqueue_raw_bytes(&RAW_INPUT_BUF).unwrap();
assert_eq!(intr_evt.read().unwrap(), 1);
let mut lsr = serial.read(LSR_OFFSET);
assert_ne!(lsr & LSR_DATA_READY_BIT, 0);
RAW_INPUT_BUF.iter().for_each(|&c| {
lsr = serial.read(LSR_OFFSET);
assert_ne!(lsr & LSR_DATA_READY_BIT, 0);
assert_eq!(serial.read(DATA_OFFSET), c);
assert_eq!(
serial.interrupt_identification,
DEFAULT_INTERRUPT_IDENTIFICATION
);
});
lsr = serial.read(LSR_OFFSET);
assert_eq!(lsr & LSR_DATA_READY_BIT, 0);
}
#[test]
fn test_serial_thr() {
let intr_evt = EventFd::new(libc::EFD_NONBLOCK).unwrap();
let mut serial = Serial::new(intr_evt.try_clone().unwrap(), sink());
serial.write(IER_OFFSET, IER_THR_EMPTY_BIT).unwrap();
assert_eq!(
serial.interrupt_enable,
IER_THR_EMPTY_BIT & IER_UART_VALID_BITS
);
serial.write(DATA_OFFSET, b'a').unwrap();
assert_eq!(intr_evt.read().unwrap(), 1);
let ier = serial.read(IER_OFFSET);
assert_eq!(ier & IER_UART_VALID_BITS, IER_THR_EMPTY_BIT);
let iir = serial.read(IIR_OFFSET);
assert_ne!(iir & IIR_THR_EMPTY_BIT, 0);
assert_eq!(iir, IIR_THR_EMPTY_BIT | IIR_FIFO_BITS);
assert_eq!(
serial.interrupt_identification,
DEFAULT_INTERRUPT_IDENTIFICATION
);
}
#[test]
fn test_serial_loop_mode() {
let intr_evt = EventFd::new(libc::EFD_NONBLOCK).unwrap();
let mut serial = Serial::new(intr_evt.try_clone().unwrap(), sink());
serial.write(MCR_OFFSET, MCR_LOOP_BIT).unwrap();
serial.write(IER_OFFSET, IER_RDA_BIT).unwrap();
for value in 0..FIFO_SIZE as u8 {
serial.write(DATA_OFFSET, value).unwrap();
assert_eq!(intr_evt.read().unwrap(), 1);
assert_eq!(serial.in_buffer.len(), 1);
assert_eq!(serial.read(DATA_OFFSET), value);
}
assert_eq!(serial.line_status & LSR_DATA_READY_BIT, 0);
for value in 0..FIFO_SIZE as u8 {
serial.write(DATA_OFFSET, value).unwrap();
}
assert_eq!(intr_evt.read().unwrap(), 1);
assert_eq!(serial.in_buffer.len(), FIFO_SIZE);
for value in 0..FIFO_SIZE as u8 {
assert_ne!(serial.line_status & LSR_DATA_READY_BIT, 0);
assert_eq!(serial.read(DATA_OFFSET), value);
}
assert_eq!(serial.line_status & LSR_DATA_READY_BIT, 0);
}
#[test]
fn test_serial_dlab() {
let intr_evt = EventFd::new(libc::EFD_NONBLOCK).unwrap();
let mut serial = Serial::new(intr_evt, sink());
serial.write(LCR_OFFSET, LCR_DLAB_BIT).unwrap();
serial.write(DLAB_HIGH_OFFSET, 0x12).unwrap();
assert_eq!(serial.read(DLAB_LOW_OFFSET), DEFAULT_BAUD_DIVISOR_LOW);
assert_eq!(serial.read(DLAB_HIGH_OFFSET), 0x12);
serial.write(DLAB_LOW_OFFSET, 0x34).unwrap();
assert_eq!(serial.read(DLAB_LOW_OFFSET), 0x34);
assert_eq!(serial.read(DLAB_HIGH_OFFSET), 0x12);
serial.write(LCR_OFFSET, 0x00).unwrap();
assert_ne!(serial.read(DLAB_LOW_OFFSET), 0x12);
assert_ne!(serial.read(DLAB_HIGH_OFFSET), 0x34);
}
#[test]
fn test_basic_register_accesses() {
let intr_evt = EventFd::new(libc::EFD_NONBLOCK).unwrap();
let mut serial = Serial::new(intr_evt, sink());
let basic_register_accesses = [LCR_OFFSET, MCR_OFFSET, SCR_OFFSET];
for offset in basic_register_accesses.iter() {
serial.write(*offset, 0x12).unwrap();
assert_eq!(serial.read(*offset), 0x12);
}
}
#[test]
fn test_invalid_access() {
let intr_evt = EventFd::new(libc::EFD_NONBLOCK).unwrap();
let mut serial = Serial::new(intr_evt, sink());
serial.write(SCR_OFFSET + 1, 5).unwrap();
assert_eq!(serial.read(SCR_OFFSET + 1), 0);
}
#[test]
fn test_serial_msr() {
let intr_evt = EventFd::new(libc::EFD_NONBLOCK).unwrap();
let mut serial = Serial::new(intr_evt, sink());
assert_eq!(serial.read(MSR_OFFSET), DEFAULT_MODEM_STATUS);
serial.write(MCR_OFFSET, MCR_LOOP_BIT).unwrap();
assert_ne!(serial.read(MSR_OFFSET), DEFAULT_MODEM_STATUS);
assert_eq!(serial.read(MSR_OFFSET), 0x00);
serial
.write(MCR_OFFSET, DEFAULT_MODEM_CONTROL | MCR_LOOP_BIT)
.unwrap();
assert_eq!(serial.read(MSR_OFFSET), MSR_DCD_BIT);
serial
.write(MCR_OFFSET, MCR_OUT1_BIT | MCR_LOOP_BIT)
.unwrap();
assert_eq!(serial.read(MSR_OFFSET), MSR_RI_BIT);
serial
.write(MCR_OFFSET, MCR_LOOP_BIT | MCR_DTR_BIT | MCR_RTS_BIT)
.unwrap();
assert_eq!(serial.read(MSR_OFFSET), MSR_DSR_BIT | MSR_CTS_BIT);
}
#[test]
fn test_fifo_max_size() {
let event_fd = EventFd::new(libc::EFD_NONBLOCK).unwrap();
let mut serial = Serial::new(event_fd, sink());
let too_many_bytes = vec![1u8; FIFO_SIZE + 1];
let written_bytes = serial.enqueue_raw_bytes(&too_many_bytes).unwrap();
assert_eq!(written_bytes, FIFO_SIZE);
assert_eq!(serial.in_buffer.len(), FIFO_SIZE);
let one_byte_input = [1u8];
let err_code = serial
.enqueue_raw_bytes(&one_byte_input)
.unwrap_err()
.raw_os_error()
.unwrap();
assert_eq!(err_code, libc::ENOBUFS);
let _ = serial.read(DATA_OFFSET);
let written_bytes = serial.enqueue_raw_bytes(&too_many_bytes[..2]).unwrap();
assert_eq!(written_bytes, 1);
assert_eq!(serial.in_buffer.len(), FIFO_SIZE);
}
}