summary refs log tree commit diff
path: root/devices/src/virtio/video/command.rs
diff options
context:
space:
mode:
Diffstat (limited to 'devices/src/virtio/video/command.rs')
-rw-r--r--devices/src/virtio/video/command.rs335
1 files changed, 335 insertions, 0 deletions
diff --git a/devices/src/virtio/video/command.rs b/devices/src/virtio/video/command.rs
new file mode 100644
index 0000000..7bdb335
--- /dev/null
+++ b/devices/src/virtio/video/command.rs
@@ -0,0 +1,335 @@
+// Copyright 2020 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+//! Data structures for commands of virtio video devices.
+
+use std::convert::{TryFrom, TryInto};
+use std::fmt;
+use std::io;
+
+use data_model::Le32;
+use enumn::N;
+use sys_util::error;
+
+use crate::virtio::video::control::*;
+use crate::virtio::video::format::*;
+use crate::virtio::video::params::Params;
+use crate::virtio::video::protocol::*;
+use crate::virtio::Reader;
+
+/// An error indicating a failure while reading a request from the guest.
+#[derive(Debug)]
+pub enum ReadCmdError {
+    /// Failure while reading an object.
+    IoError(io::Error),
+    /// Invalid arguement is passed,
+    InvalidArgument,
+    /// The type of the command was invalid.
+    InvalidCmdType(u32),
+    /// The type of the requested control was unsupported.
+    UnsupportedCtrlType(u32),
+}
+
+impl fmt::Display for ReadCmdError {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        use self::ReadCmdError::*;
+        match self {
+            IoError(e) => write!(f, "failed to read an object: {}", e),
+            InvalidArgument => write!(f, "invalid arguement is passed in command"),
+            InvalidCmdType(t) => write!(f, "invalid command type: {}", t),
+            UnsupportedCtrlType(t) => write!(f, "unsupported control type: {}", t),
+        }
+    }
+}
+
+impl std::error::Error for ReadCmdError {}
+
+impl From<io::Error> for ReadCmdError {
+    fn from(e: io::Error) -> ReadCmdError {
+        ReadCmdError::IoError(e)
+    }
+}
+
+#[derive(PartialEq, Eq, PartialOrd, Ord, N, Clone, Copy, Debug)]
+#[repr(u32)]
+pub enum QueueType {
+    Input = VIRTIO_VIDEO_QUEUE_TYPE_INPUT,
+    Output = VIRTIO_VIDEO_QUEUE_TYPE_OUTPUT,
+}
+impl_try_from_le32_for_enumn!(QueueType, "queue_type");
+
+pub enum VideoCmd {
+    QueryCapability {
+        queue_type: QueueType,
+    },
+    StreamCreate {
+        stream_id: u32,
+        coded_format: Format,
+    },
+    StreamDestroy {
+        stream_id: u32,
+    },
+    StreamDrain {
+        stream_id: u32,
+    },
+    ResourceCreate {
+        stream_id: u32,
+        queue_type: QueueType,
+        resource_id: u32,
+        plane_offsets: Vec<u32>,
+        uuid: u128,
+    },
+    ResourceQueue {
+        stream_id: u32,
+        queue_type: QueueType,
+        resource_id: u32,
+        timestamp: u64,
+        data_sizes: Vec<u32>,
+    },
+    ResourceDestroyAll {
+        stream_id: u32,
+    },
+    QueueClear {
+        stream_id: u32,
+        queue_type: QueueType,
+    },
+    GetParams {
+        stream_id: u32,
+        queue_type: QueueType,
+    },
+    SetParams {
+        stream_id: u32,
+        queue_type: QueueType,
+        params: Params,
+    },
+    QueryControl {
+        query_ctrl_type: QueryCtrlType,
+    },
+    GetControl {
+        stream_id: u32,
+        ctrl_type: CtrlType,
+    },
+    SetControl {
+        stream_id: u32,
+        ctrl_val: CtrlVal,
+    },
+}
+
+impl<'a> VideoCmd {
+    /// Reads a request on virtqueue and construct a VideoCmd value.
+    pub fn from_reader(r: &'a mut Reader<'a>) -> Result<Self, ReadCmdError> {
+        use self::ReadCmdError::*;
+        use self::VideoCmd::*;
+
+        // Unlike structs in virtio_video.h in the kernel, our command structs in protocol.rs don't
+        // have a field of `struct virtio_video_cmd_hdr`. So, we first read the header here and
+        // a body below.
+        let hdr = r.read_obj::<virtio_video_cmd_hdr>()?;
+
+        Ok(match hdr.type_.into() {
+            VIRTIO_VIDEO_CMD_QUERY_CAPABILITY => {
+                let virtio_video_query_capability { queue_type, .. } = r.read_obj()?;
+                QueryCapability {
+                    queue_type: queue_type.try_into()?,
+                }
+            }
+            VIRTIO_VIDEO_CMD_STREAM_CREATE => {
+                let virtio_video_stream_create {
+                    in_mem_type,
+                    out_mem_type,
+                    coded_format,
+                    ..
+                } = r.read_obj()?;
+
+                if in_mem_type != VIRTIO_VIDEO_MEM_TYPE_VIRTIO_OBJECT
+                    || out_mem_type != VIRTIO_VIDEO_MEM_TYPE_VIRTIO_OBJECT
+                {
+                    error!("mem_type must be VIRTIO_OBJECT");
+                    return Err(InvalidArgument);
+                }
+                StreamCreate {
+                    stream_id: hdr.stream_id.into(),
+                    coded_format: coded_format.try_into()?,
+                }
+            }
+            VIRTIO_VIDEO_CMD_STREAM_DESTROY => {
+                let virtio_video_stream_destroy { .. } = r.read_obj()?;
+                StreamDestroy {
+                    stream_id: hdr.stream_id.into(),
+                }
+            }
+            VIRTIO_VIDEO_CMD_STREAM_DRAIN => {
+                let virtio_video_stream_drain { .. } = r.read_obj()?;
+                StreamDrain {
+                    stream_id: hdr.stream_id.into(),
+                }
+            }
+            VIRTIO_VIDEO_CMD_RESOURCE_CREATE => {
+                let virtio_video_resource_create {
+                    queue_type,
+                    resource_id,
+                    planes_layout,
+                    num_planes,
+                    plane_offsets,
+                    ..
+                } = r.read_obj()?;
+
+                // Assume ChromeOS-specific requirements.
+                if Into::<u32>::into(planes_layout) != VIRTIO_VIDEO_PLANES_LAYOUT_SINGLE_BUFFER {
+                    error!(
+                        "each buffer must be a single DMAbuf: {}",
+                        Into::<u32>::into(planes_layout),
+                    );
+                    return Err(InvalidArgument);
+                }
+
+                let num_planes: u32 = num_planes.into();
+                if num_planes as usize > plane_offsets.len() {
+                    error!(
+                        "num_planes must not exceed {} but {}",
+                        plane_offsets.len(),
+                        num_planes
+                    );
+                    return Err(InvalidArgument);
+                }
+                let plane_offsets = plane_offsets[0..num_planes as usize]
+                    .iter()
+                    .map(|x| Into::<u32>::into(*x))
+                    .collect::<Vec<u32>>();
+
+                let virtio_video_object_entry { uuid } = r.read_obj()?;
+
+                ResourceCreate {
+                    stream_id: hdr.stream_id.into(),
+                    queue_type: queue_type.try_into()?,
+                    resource_id: resource_id.into(),
+                    plane_offsets,
+                    uuid: u128::from_be_bytes(uuid),
+                }
+            }
+            VIRTIO_VIDEO_CMD_RESOURCE_QUEUE => {
+                let virtio_video_resource_queue {
+                    queue_type,
+                    resource_id,
+                    timestamp,
+                    num_data_sizes,
+                    data_sizes,
+                    ..
+                } = r.read_obj()?;
+
+                let num_data_sizes: u32 = num_data_sizes.into();
+                if num_data_sizes as usize > data_sizes.len() {
+                    return Err(InvalidArgument);
+                }
+                let data_sizes = data_sizes[0..num_data_sizes as usize]
+                    .iter()
+                    .map(|x| Into::<u32>::into(*x))
+                    .collect::<Vec<u32>>();
+                ResourceQueue {
+                    stream_id: hdr.stream_id.into(),
+                    queue_type: queue_type.try_into()?,
+                    resource_id: resource_id.into(),
+                    timestamp: timestamp.into(),
+                    data_sizes,
+                }
+            }
+            VIRTIO_VIDEO_CMD_RESOURCE_DESTROY_ALL => {
+                let virtio_video_resource_destroy_all {
+
+                    // `queue_type` should be ignored because destroy_all will affect both queues.
+                    // This field exists here by mistake.
+                    ..
+                } = r.read_obj()?;
+                ResourceDestroyAll {
+                    stream_id: hdr.stream_id.into(),
+                }
+            }
+            VIRTIO_VIDEO_CMD_QUEUE_CLEAR => {
+                let virtio_video_queue_clear { queue_type, .. } = r.read_obj()?;
+                QueueClear {
+                    stream_id: hdr.stream_id.into(),
+                    queue_type: queue_type.try_into()?,
+                }
+            }
+            VIRTIO_VIDEO_CMD_GET_PARAMS => {
+                let virtio_video_get_params { queue_type, .. } = r.read_obj()?;
+                GetParams {
+                    stream_id: hdr.stream_id.into(),
+                    queue_type: queue_type.try_into()?,
+                }
+            }
+            VIRTIO_VIDEO_CMD_SET_PARAMS => {
+                let virtio_video_set_params { params } = r.read_obj()?;
+                SetParams {
+                    stream_id: hdr.stream_id.into(),
+                    queue_type: params.queue_type.try_into()?,
+                    params: params.try_into()?,
+                }
+            }
+            VIRTIO_VIDEO_CMD_QUERY_CONTROL => {
+                let body = r.read_obj::<virtio_video_query_control>()?;
+                let query_ctrl_type = match body.control.into() {
+                    VIRTIO_VIDEO_CONTROL_BITRATE => QueryCtrlType::Bitrate,
+                    VIRTIO_VIDEO_CONTROL_PROFILE => QueryCtrlType::Profile(
+                        r.read_obj::<virtio_video_query_control_profile>()?
+                            .format
+                            .try_into()?,
+                    ),
+                    VIRTIO_VIDEO_CONTROL_LEVEL => QueryCtrlType::Level(
+                        r.read_obj::<virtio_video_query_control_level>()?
+                            .format
+                            .try_into()?,
+                    ),
+                    t => {
+                        return Err(ReadCmdError::UnsupportedCtrlType(t));
+                    }
+                };
+                QueryControl { query_ctrl_type }
+            }
+            VIRTIO_VIDEO_CMD_GET_CONTROL => {
+                let virtio_video_get_control { control, .. } = r.read_obj()?;
+                let ctrl_type = match control.into() {
+                    VIRTIO_VIDEO_CONTROL_BITRATE => CtrlType::Bitrate,
+                    VIRTIO_VIDEO_CONTROL_PROFILE => CtrlType::Profile,
+                    VIRTIO_VIDEO_CONTROL_LEVEL => CtrlType::Level,
+                    t => {
+                        return Err(ReadCmdError::UnsupportedCtrlType(t));
+                    }
+                };
+                GetControl {
+                    stream_id: hdr.stream_id.into(),
+                    ctrl_type,
+                }
+            }
+            VIRTIO_VIDEO_CMD_SET_CONTROL => {
+                let virtio_video_set_control { control, .. } = r.read_obj()?;
+                let ctrl_val = match control.into() {
+                    VIRTIO_VIDEO_CONTROL_BITRATE => CtrlVal::Bitrate(
+                        r.read_obj::<virtio_video_control_val_bitrate>()?
+                            .bitrate
+                            .into(),
+                    ),
+                    VIRTIO_VIDEO_CONTROL_PROFILE => CtrlVal::Profile(
+                        r.read_obj::<virtio_video_control_val_profile>()?
+                            .profile
+                            .try_into()?,
+                    ),
+                    VIRTIO_VIDEO_CONTROL_LEVEL => CtrlVal::Level(
+                        r.read_obj::<virtio_video_control_val_level>()?
+                            .level
+                            .try_into()?,
+                    ),
+                    t => {
+                        return Err(ReadCmdError::UnsupportedCtrlType(t));
+                    }
+                };
+                SetControl {
+                    stream_id: hdr.stream_id.into(),
+                    ctrl_val,
+                }
+            }
+            _ => return Err(ReadCmdError::InvalidCmdType(hdr.type_.into())),
+        })
+    }
+}