1use crate::hal::Hal;
4use crate::queue::VirtQueue;
5use crate::transport::Transport;
6use crate::volatile::{volread, ReadOnly, WriteOnly};
7use crate::{Result, PAGE_SIZE};
8use alloc::boxed::Box;
9use bitflags::bitflags;
10use core::ptr::NonNull;
11
12const QUEUE_RECEIVEQ_PORT_0: u16 = 0;
13const QUEUE_TRANSMITQ_PORT_0: u16 = 1;
14const QUEUE_SIZE: usize = 2;
15const SUPPORTED_FEATURES: Features = Features::RING_EVENT_IDX.union(Features::RING_INDIRECT_DESC);
16
17pub struct VirtIOConsole<H: Hal, T: Transport> {
43 transport: T,
44 config_space: NonNull<Config>,
45 receiveq: VirtQueue<H, QUEUE_SIZE>,
46 transmitq: VirtQueue<H, QUEUE_SIZE>,
47 queue_buf_rx: Box<[u8; PAGE_SIZE]>,
48 cursor: usize,
49 pending_len: usize,
50 receive_token: Option<u16>,
52}
53
54unsafe impl<H: Hal, T: Transport + Send> Send for VirtIOConsole<H, T> where
56 VirtQueue<H, QUEUE_SIZE>: Send
57{
58}
59
60unsafe impl<H: Hal, T: Transport + Sync> Sync for VirtIOConsole<H, T> where
62 VirtQueue<H, QUEUE_SIZE>: Sync
63{
64}
65
66#[derive(Clone, Debug, Eq, PartialEq)]
68pub struct ConsoleInfo {
69 pub rows: u16,
71 pub columns: u16,
73 pub max_ports: u32,
75}
76
77impl<H: Hal, T: Transport> VirtIOConsole<H, T> {
78 pub fn new(mut transport: T) -> Result<Self> {
80 let negotiated_features = transport.begin_init(SUPPORTED_FEATURES);
81 let config_space = transport.config_space::<Config>()?;
82 let receiveq = VirtQueue::new(
83 &mut transport,
84 QUEUE_RECEIVEQ_PORT_0,
85 negotiated_features.contains(Features::RING_INDIRECT_DESC),
86 negotiated_features.contains(Features::RING_EVENT_IDX),
87 )?;
88 let transmitq = VirtQueue::new(
89 &mut transport,
90 QUEUE_TRANSMITQ_PORT_0,
91 negotiated_features.contains(Features::RING_INDIRECT_DESC),
92 negotiated_features.contains(Features::RING_EVENT_IDX),
93 )?;
94
95 let queue_buf_rx = Box::new([0; PAGE_SIZE]);
99
100 transport.finish_init();
101 let mut console = VirtIOConsole {
102 transport,
103 config_space,
104 receiveq,
105 transmitq,
106 queue_buf_rx,
107 cursor: 0,
108 pending_len: 0,
109 receive_token: None,
110 };
111 console.poll_retrieve()?;
112 Ok(console)
113 }
114
115 pub fn info(&self) -> ConsoleInfo {
117 unsafe {
119 let columns = volread!(self.config_space, cols);
120 let rows = volread!(self.config_space, rows);
121 let max_ports = volread!(self.config_space, max_nr_ports);
122 ConsoleInfo {
123 rows,
124 columns,
125 max_ports,
126 }
127 }
128 }
129
130 fn poll_retrieve(&mut self) -> Result<()> {
133 if self.receive_token.is_none() && self.cursor == self.pending_len {
134 self.receive_token = Some(unsafe {
137 self.receiveq
138 .add(&[], &mut [self.queue_buf_rx.as_mut_slice()])
139 }?);
140 if self.receiveq.should_notify() {
141 self.transport.notify(QUEUE_RECEIVEQ_PORT_0);
142 }
143 }
144 Ok(())
145 }
146
147 pub fn ack_interrupt(&mut self) -> Result<bool> {
152 if !self.transport.ack_interrupt() {
153 return Ok(false);
154 }
155
156 self.finish_receive()
157 }
158
159 fn finish_receive(&mut self) -> Result<bool> {
163 let mut flag = false;
164 if let Some(receive_token) = self.receive_token {
165 if self.receive_token == self.receiveq.peek_used() {
166 let len = unsafe {
169 self.receiveq.pop_used(
170 receive_token,
171 &[],
172 &mut [self.queue_buf_rx.as_mut_slice()],
173 )?
174 };
175 flag = true;
176 assert_ne!(len, 0);
177 self.cursor = 0;
178 self.pending_len = len as usize;
179 self.receive_token.take();
182 }
183 }
184 Ok(flag)
185 }
186
187 pub fn recv(&mut self, pop: bool) -> Result<Option<u8>> {
191 self.finish_receive()?;
192 if self.cursor == self.pending_len {
193 return Ok(None);
194 }
195 let ch = self.queue_buf_rx[self.cursor];
196 if pop {
197 self.cursor += 1;
198 self.poll_retrieve()?;
199 }
200 Ok(Some(ch))
201 }
202
203 pub fn send(&mut self, chr: u8) -> Result<()> {
205 let buf: [u8; 1] = [chr];
206 self.transmitq
207 .add_notify_wait_pop(&[&buf], &mut [], &mut self.transport)?;
208 Ok(())
209 }
210}
211
212impl<H: Hal, T: Transport> Drop for VirtIOConsole<H, T> {
213 fn drop(&mut self) {
214 self.transport.queue_unset(QUEUE_RECEIVEQ_PORT_0);
217 self.transport.queue_unset(QUEUE_TRANSMITQ_PORT_0);
218 }
219}
220
221#[repr(C)]
222struct Config {
223 cols: ReadOnly<u16>,
224 rows: ReadOnly<u16>,
225 max_nr_ports: ReadOnly<u32>,
226 emerg_wr: WriteOnly<u32>,
227}
228
229bitflags! {
230 #[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
231 struct Features: u64 {
232 const SIZE = 1 << 0;
233 const MULTIPORT = 1 << 1;
234 const EMERG_WRITE = 1 << 2;
235
236 const NOTIFY_ON_EMPTY = 1 << 24; const ANY_LAYOUT = 1 << 27; const RING_INDIRECT_DESC = 1 << 28;
240 const RING_EVENT_IDX = 1 << 29;
241 const UNUSED = 1 << 30; const VERSION_1 = 1 << 32; const ACCESS_PLATFORM = 1 << 33;
246 const RING_PACKED = 1 << 34;
247 const IN_ORDER = 1 << 35;
248 const ORDER_PLATFORM = 1 << 36;
249 const SR_IOV = 1 << 37;
250 const NOTIFICATION_DATA = 1 << 38;
251 }
252}
253
254#[cfg(test)]
255mod tests {
256 use super::*;
257 use crate::{
258 hal::fake::FakeHal,
259 transport::{
260 fake::{FakeTransport, QueueStatus, State},
261 DeviceType,
262 },
263 };
264 use alloc::{sync::Arc, vec};
265 use core::ptr::NonNull;
266 use std::{sync::Mutex, thread};
267
268 #[test]
269 fn receive() {
270 let mut config_space = Config {
271 cols: ReadOnly::new(0),
272 rows: ReadOnly::new(0),
273 max_nr_ports: ReadOnly::new(0),
274 emerg_wr: WriteOnly::default(),
275 };
276 let state = Arc::new(Mutex::new(State {
277 queues: vec![QueueStatus::default(), QueueStatus::default()],
278 ..Default::default()
279 }));
280 let transport = FakeTransport {
281 device_type: DeviceType::Console,
282 max_queue_size: 2,
283 device_features: 0,
284 config_space: NonNull::from(&mut config_space),
285 state: state.clone(),
286 };
287 let mut console = VirtIOConsole::<FakeHal, FakeTransport<Config>>::new(transport).unwrap();
288
289 assert_eq!(console.recv(false).unwrap(), None);
291 assert_eq!(console.recv(true).unwrap(), None);
292
293 assert_eq!(console.ack_interrupt(), Ok(false));
295 assert_eq!(console.recv(false).unwrap(), None);
296
297 {
299 let mut state = state.lock().unwrap();
300 state.write_to_queue::<QUEUE_SIZE>(QUEUE_RECEIVEQ_PORT_0, &[42]);
301
302 state.interrupt_pending = true;
303 }
304 assert_eq!(console.ack_interrupt(), Ok(true));
305 assert_eq!(state.lock().unwrap().interrupt_pending, false);
306
307 assert_eq!(console.recv(false).unwrap(), Some(42));
309 assert_eq!(console.recv(true).unwrap(), Some(42));
310 assert_eq!(console.recv(true).unwrap(), None);
311 }
312
313 #[test]
314 fn send() {
315 let mut config_space = Config {
316 cols: ReadOnly::new(0),
317 rows: ReadOnly::new(0),
318 max_nr_ports: ReadOnly::new(0),
319 emerg_wr: WriteOnly::default(),
320 };
321 let state = Arc::new(Mutex::new(State {
322 queues: vec![QueueStatus::default(), QueueStatus::default()],
323 ..Default::default()
324 }));
325 let transport = FakeTransport {
326 device_type: DeviceType::Console,
327 max_queue_size: 2,
328 device_features: 0,
329 config_space: NonNull::from(&mut config_space),
330 state: state.clone(),
331 };
332 let mut console = VirtIOConsole::<FakeHal, FakeTransport<Config>>::new(transport).unwrap();
333
334 let handle = thread::spawn(move || {
336 println!("Device waiting for a character.");
337 State::wait_until_queue_notified(&state, QUEUE_TRANSMITQ_PORT_0);
338 println!("Transmit queue was notified.");
339
340 let data = state
341 .lock()
342 .unwrap()
343 .read_from_queue::<QUEUE_SIZE>(QUEUE_TRANSMITQ_PORT_0);
344 assert_eq!(data, b"Q");
345 });
346
347 assert_eq!(console.send(b'Q'), Ok(()));
348
349 handle.join().unwrap();
350 }
351}