diff --git a/src/devices/virtio_serial/src/lib.rs b/src/devices/virtio_serial/src/lib.rs index 27fdba6c..b5992765 100644 --- a/src/devices/virtio_serial/src/lib.rs +++ b/src/devices/virtio_serial/src/lib.rs @@ -854,6 +854,18 @@ impl VirtioSerial { fn port_queue_pop(port_id: u32) -> Option> { RECEIVE_QUEUES.lock().get_mut(&port_id)?.pop_front() } + + fn can_recv(&self, port_id: u32) -> bool { + RECEIVE_QUEUES + .lock() + .get(&port_id) + .is_some_and(|q| !q.is_empty()) + || self + .queues + .index(Self::port_queue_index(port_id) as usize) + .borrow_mut() + .can_pop() + } } /// Align `size` up to a page. diff --git a/src/devices/virtio_serial/src/port.rs b/src/devices/virtio_serial/src/port.rs index 87526c0c..d2f661b3 100644 --- a/src/devices/virtio_serial/src/port.rs +++ b/src/devices/virtio_serial/src/port.rs @@ -64,26 +64,37 @@ impl VirtioSerialPort { pub fn recv(&mut self, data: &mut [u8]) -> Result { if self.cache.is_empty() { - let recv_bytes = SERIAL_DEVICE - .lock() - .get_mut() - .ok_or(VirtioSerialError::InvalidParameter)? - .dequeue(self.port_id, DEFAULT_TIMEOUT)?; - self.cache.push_back(recv_bytes); + loop { + let recv_bytes = SERIAL_DEVICE + .lock() + .get_mut() + .ok_or(VirtioSerialError::InvalidParameter)? + .dequeue(self.port_id, DEFAULT_TIMEOUT)?; + self.cache.push_back(recv_bytes); + + if !SERIAL_DEVICE + .lock() + .get_mut() + .ok_or(VirtioSerialError::InvalidParameter)? + .can_recv(self.port_id) + { + break; + } + } } let mut recvd = 0; - if !self.cache.is_empty() { + while !self.cache.is_empty() { let front = self.cache.front_mut().unwrap(); let expect = data.len() - recvd; if front.len() <= expect { - data[..front.len()].copy_from_slice(front); + data[recvd..recvd + front.len()].copy_from_slice(front); recvd += front.len(); self.cache.pop_front(); } else { data[recvd..].copy_from_slice(&front[..expect]); front.drain(..expect); - recvd = expect; + recvd += expect; } }