1#[cfg(feature = "embedded-io")]
4mod embedded_io;
5
6use crate::config::{read_config, write_config, ReadOnly, WriteOnly};
7use crate::hal::Hal;
8use crate::queue::VirtQueue;
9use crate::transport::{InterruptStatus, Transport};
10use crate::{Error, Result, PAGE_SIZE};
11use alloc::boxed::Box;
12use bitflags::bitflags;
13use core::fmt::{self, Display, Formatter, Write};
14use log::error;
15use zerocopy::{FromBytes, Immutable, IntoBytes};
16
17const QUEUE_RECEIVEQ_PORT_0: u16 = 0;
18const QUEUE_TRANSMITQ_PORT_0: u16 = 1;
19const QUEUE_SIZE: usize = 2;
20const SUPPORTED_FEATURES: Features = Features::RING_EVENT_IDX
21 .union(Features::RING_INDIRECT_DESC)
22 .union(Features::SIZE)
23 .union(Features::EMERG_WRITE)
24 .union(Features::VERSION_1);
25
26pub struct VirtIOConsole<H: Hal, T: Transport> {
51 transport: T,
52 negotiated_features: Features,
53 receiveq: VirtQueue<H, QUEUE_SIZE>,
54 transmitq: VirtQueue<H, QUEUE_SIZE>,
55 queue_buf_rx: Box<[u8; PAGE_SIZE]>,
56 cursor: usize,
58 pending_len: usize,
60 receive_token: Option<u16>,
62}
63
64unsafe impl<H: Hal, T: Transport + Send> Send for VirtIOConsole<H, T> where
66 VirtQueue<H, QUEUE_SIZE>: Send
67{
68}
69
70unsafe impl<H: Hal, T: Transport + Sync> Sync for VirtIOConsole<H, T> where
72 VirtQueue<H, QUEUE_SIZE>: Sync
73{
74}
75
76#[derive(Copy, Clone, Debug, Eq, PartialEq)]
78pub struct Size {
79 pub columns: u16,
81 pub rows: u16,
83}
84
85impl Display for Size {
86 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
87 write!(f, "{}x{}", self.columns, self.rows)
88 }
89}
90
91impl<H: Hal, T: Transport> VirtIOConsole<H, T> {
92 pub fn new(mut transport: T) -> Result<Self> {
94 let negotiated_features = transport.begin_init(SUPPORTED_FEATURES);
95 let receiveq = VirtQueue::new(
96 &mut transport,
97 QUEUE_RECEIVEQ_PORT_0,
98 negotiated_features.contains(Features::RING_INDIRECT_DESC),
99 negotiated_features.contains(Features::RING_EVENT_IDX),
100 )?;
101 let transmitq = VirtQueue::new(
102 &mut transport,
103 QUEUE_TRANSMITQ_PORT_0,
104 negotiated_features.contains(Features::RING_INDIRECT_DESC),
105 negotiated_features.contains(Features::RING_EVENT_IDX),
106 )?;
107
108 let queue_buf_rx = Box::new([0; PAGE_SIZE]);
112
113 transport.finish_init();
114 let mut console = VirtIOConsole {
115 transport,
116 negotiated_features,
117 receiveq,
118 transmitq,
119 queue_buf_rx,
120 cursor: 0,
121 pending_len: 0,
122 receive_token: None,
123 };
124 console.poll_retrieve()?;
125 Ok(console)
126 }
127
128 pub fn size(&self) -> Result<Option<Size>> {
130 if self.negotiated_features.contains(Features::SIZE) {
131 self.transport.read_consistent(|| {
132 Ok(Some(Size {
133 columns: read_config!(self.transport, Config, cols)?,
134 rows: read_config!(self.transport, Config, rows)?,
135 }))
136 })
137 } else {
138 Ok(None)
139 }
140 }
141
142 fn poll_retrieve(&mut self) -> Result<()> {
145 if self.receive_token.is_none() && self.cursor == self.pending_len {
146 self.receive_token = Some(unsafe {
149 self.receiveq
150 .add(&[], &mut [self.queue_buf_rx.as_mut_slice()])
151 }?);
152 if self.receiveq.should_notify() {
153 self.transport.notify(QUEUE_RECEIVEQ_PORT_0);
154 }
155 }
156 Ok(())
157 }
158
159 pub fn ack_interrupt(&mut self) -> Result<bool> {
164 let status = self.transport.ack_interrupt();
165 if !status.contains(InterruptStatus::QUEUE_INTERRUPT) {
166 return Ok(false);
167 }
168
169 self.finish_receive()
170 }
171
172 fn finish_receive(&mut self) -> Result<bool> {
176 let mut flag = false;
177 if let Some(receive_token) = self.receive_token {
178 if self.receive_token == self.receiveq.peek_used() {
179 let len = unsafe {
182 self.receiveq.pop_used(
183 receive_token,
184 &[],
185 &mut [self.queue_buf_rx.as_mut_slice()],
186 )?
187 };
188 flag = true;
189 assert_ne!(len, 0);
190 self.cursor = 0;
191 self.pending_len = len as usize;
192 self.receive_token.take();
195 }
196 }
197 Ok(flag)
198 }
199
200 pub fn recv(&mut self, pop: bool) -> Result<Option<u8>> {
204 self.finish_receive()?;
205 if self.cursor == self.pending_len {
206 return Ok(None);
207 }
208 let ch = self.queue_buf_rx[self.cursor];
209 if pop {
210 self.cursor += 1;
211 self.poll_retrieve()?;
212 }
213 Ok(Some(ch))
214 }
215
216 pub fn send(&mut self, chr: u8) -> Result<()> {
218 let buf: [u8; 1] = [chr];
219 self.transmitq
220 .add_notify_wait_pop(&[&buf], &mut [], &mut self.transport)?;
221 Ok(())
222 }
223
224 pub fn send_bytes(&mut self, buffer: &[u8]) -> Result {
226 self.transmitq
227 .add_notify_wait_pop(&[buffer], &mut [], &mut self.transport)?;
228 Ok(())
229 }
230
231 fn wait_for_receive(&mut self) -> Result {
233 self.poll_retrieve()?;
234 while self.cursor == self.pending_len {
235 self.finish_receive()?;
236 }
237 Ok(())
238 }
239
240 pub fn emergency_write(&mut self, chr: u8) -> Result<()> {
244 if self.negotiated_features.contains(Features::EMERG_WRITE) {
245 write_config!(self.transport, Config, emerg_wr, chr.into())?;
246 Ok(())
247 } else {
248 Err(Error::Unsupported)
249 }
250 }
251}
252
253impl<H: Hal, T: Transport> Write for VirtIOConsole<H, T> {
254 fn write_str(&mut self, s: &str) -> fmt::Result {
255 self.send_bytes(s.as_bytes()).map_err(|e| {
256 error!("Error writing to conosel: {}", e);
257 fmt::Error
258 })
259 }
260}
261
262impl<H: Hal, T: Transport> Drop for VirtIOConsole<H, T> {
263 fn drop(&mut self) {
264 self.transport.queue_unset(QUEUE_RECEIVEQ_PORT_0);
267 self.transport.queue_unset(QUEUE_TRANSMITQ_PORT_0);
268 }
269}
270
271#[derive(FromBytes, Immutable, IntoBytes)]
272#[repr(C)]
273struct Config {
274 cols: ReadOnly<u16>,
275 rows: ReadOnly<u16>,
276 max_nr_ports: ReadOnly<u32>,
277 emerg_wr: WriteOnly<u32>,
278}
279
280bitflags! {
281 #[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
282 struct Features: u64 {
283 const SIZE = 1 << 0;
284 const MULTIPORT = 1 << 1;
285 const EMERG_WRITE = 1 << 2;
286
287 const NOTIFY_ON_EMPTY = 1 << 24; const ANY_LAYOUT = 1 << 27; const RING_INDIRECT_DESC = 1 << 28;
291 const RING_EVENT_IDX = 1 << 29;
292 const UNUSED = 1 << 30; const VERSION_1 = 1 << 32; const ACCESS_PLATFORM = 1 << 33;
297 const RING_PACKED = 1 << 34;
298 const IN_ORDER = 1 << 35;
299 const ORDER_PLATFORM = 1 << 36;
300 const SR_IOV = 1 << 37;
301 const NOTIFICATION_DATA = 1 << 38;
302 }
303}
304
305#[cfg(test)]
306mod tests {
307 use super::*;
308 use crate::{
309 hal::fake::FakeHal,
310 transport::{
311 fake::{FakeTransport, QueueStatus, State},
312 DeviceType,
313 },
314 };
315 use alloc::{sync::Arc, vec};
316 use std::{sync::Mutex, thread};
317
318 #[test]
319 fn config_info_no_features() {
320 let config_space = Config {
321 cols: ReadOnly::new(80),
322 rows: ReadOnly::new(42),
323 max_nr_ports: ReadOnly::new(0),
324 emerg_wr: WriteOnly::default(),
325 };
326 let state = Arc::new(Mutex::new(State::new(
327 vec![QueueStatus::default(), QueueStatus::default()],
328 config_space,
329 )));
330 let transport = FakeTransport {
331 device_type: DeviceType::Console,
332 max_queue_size: 2,
333 device_features: 0,
334 state: state.clone(),
335 };
336 let console = VirtIOConsole::<FakeHal, FakeTransport<Config>>::new(transport).unwrap();
337
338 assert_eq!(console.size(), Ok(None));
339 }
340
341 #[test]
342 fn config_info() {
343 let config_space = Config {
344 cols: ReadOnly::new(80),
345 rows: ReadOnly::new(42),
346 max_nr_ports: ReadOnly::new(0),
347 emerg_wr: WriteOnly::default(),
348 };
349 let state = Arc::new(Mutex::new(State::new(
350 vec![QueueStatus::default(), QueueStatus::default()],
351 config_space,
352 )));
353 let transport = FakeTransport {
354 device_type: DeviceType::Console,
355 max_queue_size: 2,
356 device_features: 0x07,
357 state: state.clone(),
358 };
359 let console = VirtIOConsole::<FakeHal, FakeTransport<Config>>::new(transport).unwrap();
360
361 assert_eq!(
362 console.size(),
363 Ok(Some(Size {
364 columns: 80,
365 rows: 42
366 }))
367 );
368 }
369
370 #[test]
371 fn emergency_write() {
372 let config_space = Config {
373 cols: ReadOnly::new(0),
374 rows: ReadOnly::new(0),
375 max_nr_ports: ReadOnly::new(0),
376 emerg_wr: WriteOnly::default(),
377 };
378 let state = Arc::new(Mutex::new(State::new(
379 vec![QueueStatus::default(), QueueStatus::default()],
380 config_space,
381 )));
382 let transport = FakeTransport {
383 device_type: DeviceType::Console,
384 max_queue_size: 2,
385 device_features: 0x07,
386 state: state.clone(),
387 };
388 let mut console = VirtIOConsole::<FakeHal, FakeTransport<Config>>::new(transport).unwrap();
389
390 console.emergency_write(42).unwrap();
391 assert_eq!(state.lock().unwrap().config_space.emerg_wr.0, 42);
392 }
393
394 #[test]
395 fn receive() {
396 let config_space = Config {
397 cols: ReadOnly::new(0),
398 rows: ReadOnly::new(0),
399 max_nr_ports: ReadOnly::new(0),
400 emerg_wr: WriteOnly::default(),
401 };
402 let state = Arc::new(Mutex::new(State::new(
403 vec![QueueStatus::default(), QueueStatus::default()],
404 config_space,
405 )));
406 let transport = FakeTransport {
407 device_type: DeviceType::Console,
408 max_queue_size: 2,
409 device_features: 0,
410 state: state.clone(),
411 };
412 let mut console = VirtIOConsole::<FakeHal, FakeTransport<Config>>::new(transport).unwrap();
413
414 assert_eq!(console.recv(false).unwrap(), None);
416 assert_eq!(console.recv(true).unwrap(), None);
417
418 assert_eq!(console.ack_interrupt(), Ok(false));
420 assert_eq!(console.recv(false).unwrap(), None);
421
422 {
424 let mut state = state.lock().unwrap();
425 state.write_to_queue::<QUEUE_SIZE>(QUEUE_RECEIVEQ_PORT_0, &[42]);
426
427 state.interrupt_pending = true;
428 }
429 assert_eq!(console.ack_interrupt(), Ok(true));
430 assert_eq!(state.lock().unwrap().interrupt_pending, false);
431
432 assert_eq!(console.recv(false).unwrap(), Some(42));
434 assert_eq!(console.recv(true).unwrap(), Some(42));
435 assert_eq!(console.recv(true).unwrap(), None);
436 }
437
438 #[test]
439 fn send() {
440 let config_space = Config {
441 cols: ReadOnly::new(0),
442 rows: ReadOnly::new(0),
443 max_nr_ports: ReadOnly::new(0),
444 emerg_wr: WriteOnly::default(),
445 };
446 let state = Arc::new(Mutex::new(State::new(
447 vec![QueueStatus::default(), QueueStatus::default()],
448 config_space,
449 )));
450 let transport = FakeTransport {
451 device_type: DeviceType::Console,
452 max_queue_size: 2,
453 device_features: 0,
454 state: state.clone(),
455 };
456 let mut console = VirtIOConsole::<FakeHal, FakeTransport<Config>>::new(transport).unwrap();
457
458 let handle = thread::spawn(move || {
460 println!("Device waiting for a character.");
461 State::wait_until_queue_notified(&state, QUEUE_TRANSMITQ_PORT_0);
462 println!("Transmit queue was notified.");
463
464 let data = state
465 .lock()
466 .unwrap()
467 .read_from_queue::<QUEUE_SIZE>(QUEUE_TRANSMITQ_PORT_0);
468 assert_eq!(data, b"Q");
469 });
470
471 assert_eq!(console.send(b'Q'), Ok(()));
472
473 handle.join().unwrap();
474 }
475}