aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorViresh Kumar <viresh.kumar@linaro.org>2021-04-23 16:11:12 +0530
committerViresh Kumar <viresh.kumar@linaro.org>2021-04-28 17:06:50 +0530
commit75ab4a7639fdbc6b2f6515477a3fbf6be6ebf39f (patch)
tree1295efb8e96cf5699ec527c714e99c729361bf2e
parentd413bf954555b0d4f4ba5b3def04b3b55918ecac (diff)
vhost-user-i2c: Process virtio requests
This patch implements process_queue() to process incoming requests. Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org>
-rw-r--r--src/vhu_i2c.rs134
1 files changed, 132 insertions, 2 deletions
diff --git a/src/vhu_i2c.rs b/src/vhu_i2c.rs
index 5854ba0..b023372 100644
--- a/src/vhu_i2c.rs
+++ b/src/vhu_i2c.rs
@@ -7,11 +7,12 @@
use std::sync::{Arc, RwLock};
use std::{convert, error, fmt, io};
+use std::mem::{size_of};
use vhost::vhost_user::message::*;
use vhost_user_backend::{VhostUserBackend, Vring};
use virtio_bindings::bindings::virtio_net::*;
use virtio_bindings::bindings::virtio_ring::{VIRTIO_RING_F_EVENT_IDX, VIRTIO_RING_F_INDIRECT_DESC};
-use vm_memory::{GuestMemoryAtomic, GuestMemoryMmap};
+use vm_memory::{Bytes, ByteValued, GuestMemoryAtomic, GuestMemoryMmap, Le16, Le32};
use crate::i2c::*;
const QUEUE_SIZE: usize = 1024;
@@ -49,6 +50,29 @@ impl convert::From<Error> for io::Error {
}
}
+
+// I2C definitions from Virtio Spec
+
+// The final status written by the device
+const VIRTIO_I2C_MSG_OK: u8 = 0;
+const VIRTIO_I2C_MSG_ERR: u8 = 1;
+
+#[derive(Copy, Clone, Default)]
+#[repr(C)]
+struct VirtioI2cOutHdr {
+ addr: Le16,
+ padding: Le16,
+ flags: Le32
+}
+unsafe impl ByteValued for VirtioI2cOutHdr {}
+
+#[derive(Copy, Clone, Default)]
+#[repr(C)]
+struct VirtioI2cInHdr {
+ status: u8
+}
+unsafe impl ByteValued for VirtioI2cInHdr {}
+
pub struct VhostUserI2c {
backend: I2cBackend,
event_idx: bool,
@@ -68,8 +92,114 @@ impl VhostUserI2c {
// Process the messages in the vring and dispatch replies
fn process_queue(
&self,
- _vring: &mut Vring,
+ vring: &mut Vring,
) -> Result<bool> {
+ let mut reqs: Vec<I2cReq> = Vec::new();
+ let mut index = 0;
+
+ // Iterate over each I2C request and push it to "reqs" vector.
+ while let Some(mut desc_chain) = vring.mut_queue().iter().unwrap().next() {
+ let desc_out_hdr = desc_chain.next().unwrap();
+ let desc_buf = desc_chain.next().unwrap();
+ let desc_in_hdr = desc_chain.next().unwrap();
+ let mut flags: u16 = 0;
+
+ if desc_in_hdr.has_next() {
+ return Err(Error::UnexpectedDescriptorFound.into());
+ }
+
+ if desc_out_hdr.is_write_only() {
+ return Err(Error::UnexpectedWriteOnlyDescriptor.into());
+ }
+
+ if desc_out_hdr.len() as usize != size_of::<VirtioI2cOutHdr>() {
+ return Err(Error::UnexpectedDescriptorSize.into());
+ }
+
+ if !desc_in_hdr.is_write_only() {
+ return Err(Error::UnexpectedReadDescriptor.into());
+ }
+
+ if desc_in_hdr.len() as usize != size_of::<u8>() {
+ return Err(Error::UnexpectedDescriptorSize.into());
+ }
+
+ if desc_buf.len() == 0 {
+ return Err(Error::UnexpectedDescriptorSize.into());
+ }
+
+ let mut buf: Vec<u8> = vec![0; desc_buf.len() as usize];
+
+ if desc_buf.is_write_only() {
+ flags = I2C_M_RD;
+ } else {
+ desc_chain
+ .memory()
+ .read(&mut buf, desc_buf.addr()).unwrap();
+ }
+
+ let out_hdr = desc_chain
+ .memory()
+ .read_obj::<VirtioI2cOutHdr>(desc_out_hdr.addr()).unwrap();
+
+ reqs.push(
+ I2cReq {
+ addr: out_hdr.addr.to_native() >> 1,
+ flags: flags,
+ len: desc_buf.len() as u16,
+ buf: buf
+ }
+ );
+
+ index += 1;
+ }
+
+ if index == 0 {
+ return Ok(true);
+ }
+
+ // Roll back to the initial position, as we need to go over them again
+ for _ in 0..index {
+ vring.mut_queue().go_to_previous_position();
+ }
+
+ let in_hdr = match vi2c_xfer(&self.backend, &mut reqs) {
+ true => VirtioI2cInHdr { status: VIRTIO_I2C_MSG_OK },
+ false => VirtioI2cInHdr { status: VIRTIO_I2C_MSG_ERR },
+ };
+
+ index = 0;
+ while let Some(mut desc_chain) = vring.mut_queue().iter().unwrap().next() {
+ let _unused = desc_chain.next().unwrap();
+ let desc_buf = desc_chain.next().unwrap();
+ let desc_in_hdr = desc_chain.next().unwrap();
+ let mut len = size_of::<VirtioI2cInHdr>() as u32;
+
+ // Write the data read from the I2C client
+ if desc_buf.is_write_only() {
+ desc_chain
+ .memory()
+ .write(&reqs[index].buf, desc_buf.addr()).unwrap();
+ }
+
+ // Write the transfer status
+ desc_chain
+ .memory()
+ .write_obj::<VirtioI2cInHdr>(in_hdr, desc_in_hdr.addr()).unwrap();
+
+ if in_hdr.status == VIRTIO_I2C_MSG_OK {
+ len += desc_buf.len();
+ }
+
+ if vring.mut_queue().add_used(desc_chain.head_index(), len).is_err() {
+ println!("Couldn't return used descriptors to the ring");
+ }
+
+ index += 1;
+ }
+
+ // Send notification once all the requests are processed
+ vring.signal_used_queue().unwrap();
Ok(true)
}
}