From d96b34437eacbf1806c6a185c8a1d0b959c14b13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20L=C3=B3pez?= Date: Mon, 7 Oct 2024 16:07:37 +0200 Subject: [PATCH 1/2] serial: implement receive FIFO flush via FCR MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The FIFO Control Register (FCR) controls the behavior of the receive and transmit FIFO buffers of the device. The current implementation does not emulate this register, as FIFO buffers are always enabled. However, there are two bits in this register that control flushing of said FIFOS. The transmission FIFO is already flushed by the current implementation on every write, but the receive FIFO is not. This is problematic, as some driver implementations (e.g. FreeBSD's) rely on being able to clear this buffer via the corresponding bit. Implement the correct behavior when a driver sets this bit by clearing `in_buffer`. Since there is no more data in the receive FIFO, the data-ready bit in the Line Status Register (LSR) must be cleared as well, in case it was set. Fixes: #83 Signed-off-by: Carlos López --- vm-superio/CHANGELOG.md | 5 +++++ vm-superio/src/serial.rs | 12 +++++++++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/vm-superio/CHANGELOG.md b/vm-superio/CHANGELOG.md index 62e843f..9b3d632 100644 --- a/vm-superio/CHANGELOG.md +++ b/vm-superio/CHANGELOG.md @@ -2,6 +2,11 @@ ## Upcoming Release +### Changed + +- Implemented receive FIFO flushing via the FCR register for the `Serial` + device ([#83](https://github.com/rust-vmm/vm-superio/issues/83)). + ## v0.8.1 ### Changed diff --git a/vm-superio/src/serial.rs b/vm-superio/src/serial.rs index 3876748..ed796b7 100644 --- a/vm-superio/src/serial.rs +++ b/vm-superio/src/serial.rs @@ -25,6 +25,7 @@ use crate::Trigger; const DATA_OFFSET: u8 = 0; const IER_OFFSET: u8 = 1; const IIR_OFFSET: u8 = 2; +const FCR_OFFSET: u8 = IIR_OFFSET; const LCR_OFFSET: u8 = 3; const MCR_OFFSET: u8 = 4; const LSR_OFFSET: u8 = 5; @@ -50,6 +51,8 @@ const IIR_NONE_BIT: u8 = 0b0000_0001; const IIR_THR_EMPTY_BIT: u8 = 0b0000_0010; const IIR_RDA_BIT: u8 = 0b0000_0100; +const FCR_FLUSH_IN_BIT: u8 = 0b0000_0010; + const LCR_DLAB_BIT: u8 = 0b1000_0000; const LSR_DATA_READY_BIT: u8 = 0b0000_0001; @@ -624,7 +627,14 @@ impl Serial { LCR_OFFSET => self.line_control = value, MCR_OFFSET => self.modem_control = value, SCR_OFFSET => self.scratch = value, - // We are not interested in writing to other offsets (such as FCR offset). + FCR_OFFSET => { + // Clear the receive FIFO + if value & FCR_FLUSH_IN_BIT != 0 { + self.in_buffer.clear(); + self.clear_lsr_rda_bit(); + self.events.in_buffer_empty(); + } + } _ => {} } Ok(()) From 26c43ffa180531e4d3aae4f82f80fd9f2a1be784 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20L=C3=B3pez?= Date: Mon, 7 Oct 2024 16:07:54 +0200 Subject: [PATCH 2/2] serial: add a test for receive FIFO flushing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a test that checks that flushing the receive FIFO results in the correct behavior, i.e. `in_buffer` is cleared. Signed-off-by: Carlos López --- vm-superio/src/serial.rs | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/vm-superio/src/serial.rs b/vm-superio/src/serial.rs index ed796b7..418b843 100644 --- a/vm-superio/src/serial.rs +++ b/vm-superio/src/serial.rs @@ -833,6 +833,34 @@ mod tests { assert_eq!(serial.writer().as_slice(), &RAW_INPUT_BUF); } + #[test] + fn test_serial_rx_flush() { + let intr_evt = EventFd::new(libc::EFD_NONBLOCK).unwrap(); + let mut serial = Serial::with_events(intr_evt, ExampleSerialEvents::new(), Vec::new()); + + // No data yet + let mut lsr = serial.read(LSR_OFFSET); + assert_eq!(lsr & LSR_DATA_READY_BIT, 0); + assert_eq!(serial.fifo_capacity(), FIFO_SIZE); + + // Write some bytes and check data is ready + serial.enqueue_raw_bytes(&RAW_INPUT_BUF).unwrap(); + lsr = serial.read(LSR_OFFSET); + assert_eq!(lsr & LSR_DATA_READY_BIT, 1); + assert!(serial.fifo_capacity() != FIFO_SIZE); + + // Flush the FIFO + serial.write(FCR_OFFSET, FCR_FLUSH_IN_BIT).unwrap(); + + // No data again + lsr = serial.read(LSR_OFFSET); + assert_eq!(lsr & LSR_DATA_READY_BIT, 0); + assert_eq!(serial.fifo_capacity(), FIFO_SIZE); + + // An event should have triggered + assert_eq!(serial.events.buffer_ready_event.read().unwrap(), 1); + } + #[test] fn test_serial_raw_input() { let intr_evt = EventFd::new(libc::EFD_NONBLOCK).unwrap();