Jelajahi Sumber

move virtio-input from rCore

Runji Wang 5 tahun lalu
induk
melakukan
16988b462d
8 mengubah file dengan 237 tambahan dan 36 penghapusan
  1. 1 1
      README.md
  2. 2 1
      examples/riscv/Makefile
  3. 16 4
      examples/riscv/src/main.rs
  4. 4 16
      src/blk.rs
  5. 2 2
      src/gpu.rs
  6. 186 0
      src/input.rs
  7. 14 0
      src/lib.rs
  8. 12 12
      src/queue.rs

+ 1 - 1
README.md

@@ -14,7 +14,7 @@ VirtIO guest drivers in Rust. For **no_std + no_alloc** environment.
 | Block  | ✅                                                            |
 | Net    | 🚧 [TODO](https://github.com/rcore-os/rCore/blob/master/kernel/src/drivers/net/virtio_net.rs) |
 | GPU    | ⚠️ Not tested                                                 |
-| Input  | 🚧 [TODO](https://github.com/rcore-os/rCore/blob/master/kernel/src/drivers/input/virtio_input.rs) |
+| Input  |  |
 | ...    | ❌ Not implemented                                            |
 
 ## Examples & Tests

+ 2 - 1
examples/riscv/Makefile

@@ -54,7 +54,8 @@ qemu: $(bin) $(img)
 		-device loader,file=$(bin),addr=$(START_ADDR) \
 		-drive file=$(img),if=none,format=raw,id=x0 \
 		-device virtio-blk-device,drive=x0 \
-		-device virtio-gpu-device
+		-device virtio-gpu-device \
+		-device virtio-mouse-device
 
 $(img):
 	dd if=/dev/zero of=$@ bs=512 count=32

+ 16 - 4
examples/riscv/src/main.rs

@@ -12,7 +12,7 @@ extern crate opensbi_rt;
 use device_tree::util::SliceRead;
 use device_tree::{DeviceTree, Node};
 use log::LevelFilter;
-use virtio_drivers::{DeviceType, VirtIOBlk, VirtIOGpu, VirtIOHeader};
+use virtio_drivers::*;
 
 mod virtio_impl;
 
@@ -65,6 +65,7 @@ fn virtio_probe(node: &Node) {
         match header.device_type() {
             DeviceType::Block => virtio_blk(header),
             DeviceType::GPU => virtio_gpu(header),
+            DeviceType::Input => virtio_input(header),
             t => warn!("Unrecognized virtio device: {:?}", t),
         }
     }
@@ -86,8 +87,8 @@ fn virtio_blk(header: &'static mut VirtIOHeader) {
 }
 
 fn virtio_gpu(header: &'static mut VirtIOHeader) {
-    let mut blk = VirtIOGpu::new(header).expect("failed to create blk driver");
-    let fb = blk.setup_framebuffer().expect("failed to get fb");
+    let mut gpu = VirtIOGpu::new(header).expect("failed to create gpu driver");
+    let fb = gpu.setup_framebuffer().expect("failed to get fb");
     for y in 0..768 {
         for x in 0..1024 {
             let idx = (y * 1024 + x) * 4;
@@ -96,6 +97,17 @@ fn virtio_gpu(header: &'static mut VirtIOHeader) {
             fb[idx + 2] = (x + y) as u8;
         }
     }
-    blk.flush().expect("failed to flush");
+    gpu.flush().expect("failed to flush");
     info!("virtio-gpu test finished");
 }
+
+fn virtio_input(header: &'static mut VirtIOHeader) {
+    let mut event_buf = [0u64; 32];
+    let mut _input =
+        VirtIOInput::new(header, &mut event_buf).expect("failed to create input driver");
+    // loop {
+    //     input.ack_interrupt().expect("failed to ack");
+    //     info!("mouse: {:?}", input.mouse_xy());
+    // }
+    // TODO: handle external interrupt
+}

+ 4 - 16
src/blk.rs

@@ -1,5 +1,3 @@
-use core::mem::size_of;
-
 use super::*;
 use crate::header::VirtIOHeader;
 use crate::queue::VirtQueue;
@@ -63,10 +61,10 @@ impl VirtIOBlk<'_> {
         let mut resp = BlkResp::default();
         self.queue.add(&[req.as_buf()], &[buf, resp.as_buf_mut()])?;
         self.header.notify(0);
-        while !self.queue.can_get() {
+        while !self.queue.can_pop() {
             spin_loop_hint();
         }
-        self.queue.get()?;
+        self.queue.pop_used()?;
         match resp.status {
             RespStatus::Ok => Ok(()),
             _ => Err(Error::IoError),
@@ -84,10 +82,10 @@ impl VirtIOBlk<'_> {
         let mut resp = BlkResp::default();
         self.queue.add(&[req.as_buf(), buf], &[resp.as_buf_mut()])?;
         self.header.notify(0);
-        while !self.queue.can_get() {
+        while !self.queue.can_pop() {
             spin_loop_hint();
         }
-        self.queue.get()?;
+        self.queue.pop_used()?;
         match resp.status {
             RespStatus::Ok => Ok(()),
             _ => Err(Error::IoError),
@@ -205,15 +203,5 @@ bitflags! {
     }
 }
 
-/// Convert a struct into buffer.
-unsafe trait AsBuf: Sized {
-    fn as_buf(&self) -> &[u8] {
-        unsafe { core::slice::from_raw_parts(self as *const _ as _, size_of::<Self>()) }
-    }
-    fn as_buf_mut(&mut self) -> &mut [u8] {
-        unsafe { core::slice::from_raw_parts_mut(self as *mut _ as _, size_of::<Self>()) }
-    }
-}
-
 unsafe impl AsBuf for BlkReq {}
 unsafe impl AsBuf for BlkResp {}

+ 2 - 2
src/gpu.rs

@@ -144,10 +144,10 @@ impl VirtIOGpu<'_> {
         self.control_queue
             .add(&[self.queue_buf_send], &[self.queue_buf_recv])?;
         self.header.notify(QUEUE_TRANSMIT as u32);
-        while !self.control_queue.can_get() {
+        while !self.control_queue.can_pop() {
             spin_loop_hint();
         }
-        self.control_queue.get()?;
+        self.control_queue.pop_used()?;
         Ok(unsafe { (self.queue_buf_recv.as_ptr() as *const Rsp).read() })
     }
 }

+ 186 - 0
src/input.rs

@@ -0,0 +1,186 @@
+use super::*;
+use bitflags::*;
+use log::*;
+use volatile::Volatile;
+
+/// Virtual human interface devices such as keyboards, mice and tablets.
+///
+/// An instance of the virtio device represents one such input device.
+/// Device behavior mirrors that of the evdev layer in Linux,
+/// making pass-through implementations on top of evdev easy.
+pub struct VirtIOInput<'a> {
+    header: &'static mut VirtIOHeader,
+    event_queue: VirtQueue<'a>,
+    status_queue: VirtQueue<'a>,
+    event_buf: &'a mut [Event],
+    x: i32,
+    y: i32,
+}
+
+impl<'a> VirtIOInput<'a> {
+    /// Create a new VirtIO-Input driver.
+    pub fn new(header: &'static mut VirtIOHeader, event_buf: &'a mut [u64]) -> Result<Self> {
+        if event_buf.len() < QUEUE_SIZE {
+            return Err(Error::BufferTooSmall);
+        }
+        let event_buf: &mut [Event] = unsafe { core::mem::transmute(event_buf) };
+        header.begin_init(|features| {
+            let features = Feature::from_bits_truncate(features);
+            info!("Device features: {:?}", features);
+            // negotiate these flags only
+            let supported_features = Feature::empty();
+            (features & supported_features).bits()
+        });
+
+        // read configuration space
+        let config = unsafe { &mut *(header.config_space() as *mut Config) };
+        info!("Config: {:?}", config);
+
+        let mut event_queue = VirtQueue::new(header, QUEUE_EVENT, QUEUE_SIZE as u16)?;
+        let status_queue = VirtQueue::new(header, QUEUE_STATUS, QUEUE_SIZE as u16)?;
+        for (i, event) in event_buf.iter_mut().enumerate() {
+            let token = event_queue.add(&[], &[event.as_buf_mut()])?;
+            assert_eq!(token, i as u16);
+        }
+
+        header.finish_init();
+
+        Ok(VirtIOInput {
+            header,
+            event_queue,
+            status_queue,
+            event_buf,
+            x: 0,
+            y: 0,
+        })
+    }
+
+    /// Acknowledge interrupt and process events.
+    pub fn ack_interrupt(&mut self) -> Result<bool> {
+        let ack = self.header.ack_interrupt();
+        if !ack {
+            return Ok(false);
+        }
+        while let Ok((token, _)) = self.event_queue.pop_used() {
+            let event = &mut self.event_buf[token as usize];
+            match EventRepr::from(*event) {
+                EventRepr::RelX(dx) => self.x += dx,
+                EventRepr::RelY(dy) => self.y += dy,
+                r => warn!("{:?}", r),
+            }
+            // requeue
+            self.event_queue.add(&[], &[event.as_buf_mut()])?;
+        }
+        Ok(true)
+    }
+
+    /// Get the coordinate of mouse.
+    pub fn mouse_xy(&self) -> (i32, i32) {
+        (self.x, self.y)
+    }
+}
+
+#[repr(u8)]
+#[derive(Debug)]
+enum Cfg {
+    Unset = 0x00,
+    IdName = 0x01,
+    IdSerial = 0x02,
+    IdDevids = 0x03,
+    PropBits = 0x10,
+    EvBits = 0x11,
+    AbsInfo = 0x12,
+}
+
+#[repr(C)]
+#[derive(Debug)]
+struct Config {
+    select: Volatile<u8>,
+    subsel: Volatile<u8>,
+    size: u8,
+    reversed: [u8; 5],
+    data: [u8; 32],
+}
+
+#[repr(C)]
+#[derive(Debug)]
+struct AbsInfo {
+    min: u32,
+    max: u32,
+    fuzz: u32,
+    flat: u32,
+    res: u32,
+}
+
+#[repr(C)]
+#[derive(Debug)]
+struct DevIDs {
+    bustype: u16,
+    vendor: u16,
+    product: u16,
+    version: u16,
+}
+
+#[repr(C)]
+#[derive(Clone, Copy, Default)]
+struct Event {
+    event_type: u16,
+    code: u16,
+    value: u32,
+}
+
+#[derive(Debug)]
+enum EventRepr {
+    SynReport,
+    SynUnknown(u16),
+    RelX(i32),
+    RelY(i32),
+    RelUnknown(u16),
+    Unknown(u16),
+}
+
+unsafe impl AsBuf for Event {}
+
+impl From<Event> for EventRepr {
+    fn from(e: Event) -> Self {
+        // linux event codes
+        match e.event_type {
+            0 => match e.code {
+                0 => EventRepr::SynReport,
+                _ => EventRepr::SynUnknown(e.code),
+            },
+            2 => match e.code {
+                0 => EventRepr::RelX(e.value as i32),
+                1 => EventRepr::RelY(e.value as i32),
+                _ => EventRepr::RelUnknown(e.code),
+            },
+            _ => EventRepr::Unknown(e.event_type),
+        }
+    }
+}
+
+bitflags! {
+    struct Feature: u64 {
+        // device independent
+        const NOTIFY_ON_EMPTY       = 1 << 24; // legacy
+        const ANY_LAYOUT            = 1 << 27; // legacy
+        const RING_INDIRECT_DESC    = 1 << 28;
+        const RING_EVENT_IDX        = 1 << 29;
+        const UNUSED                = 1 << 30; // legacy
+        const VERSION_1             = 1 << 32; // detect legacy
+
+        // since virtio v1.1
+        const ACCESS_PLATFORM       = 1 << 33;
+        const RING_PACKED           = 1 << 34;
+        const IN_ORDER              = 1 << 35;
+        const ORDER_PLATFORM        = 1 << 36;
+        const SR_IOV                = 1 << 37;
+        const NOTIFICATION_DATA     = 1 << 38;
+    }
+}
+
+const QUEUE_EVENT: usize = 0;
+const QUEUE_STATUS: usize = 1;
+
+// a parameter that can change
+const QUEUE_SIZE: usize = 32;

+ 14 - 0
src/lib.rs

@@ -12,11 +12,15 @@ mod blk;
 mod gpu;
 mod hal;
 mod header;
+mod input;
 mod queue;
 
 pub use self::blk::VirtIOBlk;
 pub use self::gpu::VirtIOGpu;
 pub use self::header::*;
+pub use self::input::VirtIOInput;
+use self::queue::VirtQueue;
+use core::mem::size_of;
 use hal::*;
 
 const PAGE_SIZE: usize = 0x1000;
@@ -55,3 +59,13 @@ fn align_up(size: usize) -> usize {
 fn pages(size: usize) -> usize {
     (size + PAGE_SIZE - 1) / PAGE_SIZE
 }
+
+/// Convert a struct into buffer.
+unsafe trait AsBuf: Sized {
+    fn as_buf(&self) -> &[u8] {
+        unsafe { core::slice::from_raw_parts(self as *const _ as _, size_of::<Self>()) }
+    }
+    fn as_buf_mut(&mut self) -> &mut [u8] {
+        unsafe { core::slice::from_raw_parts_mut(self as *mut _ as _, size_of::<Self>()) }
+    }
+}

+ 12 - 12
src/queue.rs

@@ -77,12 +77,12 @@ impl VirtQueue<'_> {
         })
     }
 
-    /// Add buffers to the virtqueue.
+    /// Add buffers to the virtqueue, return a token.
     ///
     /// Ref: linux virtio_ring.c virtqueue_add
-    pub fn add(&mut self, inputs: &[&[u8]], outputs: &[&mut [u8]]) -> Result {
+    pub fn add(&mut self, inputs: &[&[u8]], outputs: &[&mut [u8]]) -> Result<u16> {
         if inputs.is_empty() && outputs.is_empty() {
-            return Ok(());
+            return Err(Error::InvalidParam);
         }
         if inputs.len() + outputs.len() + self.num_used as usize > self.queue_size as usize {
             return Err(Error::BufferTooSmall);
@@ -123,11 +123,11 @@ impl VirtQueue<'_> {
         // increase head of avail ring
         self.avail_idx = self.avail_idx.wrapping_add(1);
         self.avail.idx.write(self.avail_idx);
-        Ok(())
+        Ok(head)
     }
 
-    ///
-    pub fn can_get(&self) -> bool {
+    /// Whether there is a used element that can pop.
+    pub fn can_pop(&self) -> bool {
         self.last_used_idx != self.used.idx.read()
     }
 
@@ -150,24 +150,24 @@ impl VirtQueue<'_> {
         }
     }
 
-    /// Get device used buffers.
+    /// Get a token from device used buffers, return (token, len).
     ///
     /// Ref: linux virtio_ring.c virtqueue_get_buf_ctx
-    pub fn get(&mut self) -> Result<usize> {
-        if !self.can_get() {
+    pub fn pop_used(&mut self) -> Result<(u16, u32)> {
+        if !self.can_pop() {
             return Err(Error::NotReady);
         }
         // read barrier
         fence(Ordering::SeqCst);
 
         let last_used_slot = self.last_used_idx & (self.queue_size - 1);
-        let index = self.used.ring[last_used_slot as usize].id.read();
+        let index = self.used.ring[last_used_slot as usize].id.read() as u16;
         let len = self.used.ring[last_used_slot as usize].len.read();
 
-        self.recycle_descriptors(index as u16);
+        self.recycle_descriptors(index);
         self.last_used_idx = self.last_used_idx.wrapping_add(1);
 
-        Ok(len as usize)
+        Ok((index, len))
     }
 }