1 // Copyright 2020 The Chromium OS Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 //! Data structures for commands of virtio video devices. 6 7 use std::convert::{TryFrom, TryInto}; 8 use std::fmt; 9 use std::io; 10 11 use base::error; 12 use data_model::Le32; 13 use enumn::N; 14 15 use crate::virtio::video::control::*; 16 use crate::virtio::video::format::*; 17 use crate::virtio::video::params::Params; 18 use crate::virtio::video::protocol::*; 19 use crate::virtio::Reader; 20 21 /// An error indicating a failure while reading a request from the guest. 22 #[derive(Debug)] 23 pub enum ReadCmdError { 24 /// Failure while reading an object. 25 IoError(io::Error), 26 /// Invalid arguement is passed, 27 InvalidArgument, 28 /// The type of the command was invalid. 29 InvalidCmdType(u32), 30 /// The type of the requested control was unsupported. 31 UnsupportedCtrlType(u32), 32 } 33 34 impl fmt::Display for ReadCmdError { fmt(&self, f: &mut fmt::Formatter) -> fmt::Result35 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 36 use self::ReadCmdError::*; 37 match self { 38 IoError(e) => write!(f, "failed to read an object: {}", e), 39 InvalidArgument => write!(f, "invalid arguement is passed in command"), 40 InvalidCmdType(t) => write!(f, "invalid command type: {}", t), 41 UnsupportedCtrlType(t) => write!(f, "unsupported control type: {}", t), 42 } 43 } 44 } 45 46 impl std::error::Error for ReadCmdError {} 47 48 impl From<io::Error> for ReadCmdError { from(e: io::Error) -> ReadCmdError49 fn from(e: io::Error) -> ReadCmdError { 50 ReadCmdError::IoError(e) 51 } 52 } 53 54 #[derive(PartialEq, Eq, PartialOrd, Ord, N, Clone, Copy, Debug)] 55 #[repr(u32)] 56 pub enum QueueType { 57 Input = VIRTIO_VIDEO_QUEUE_TYPE_INPUT, 58 Output = VIRTIO_VIDEO_QUEUE_TYPE_OUTPUT, 59 } 60 impl_try_from_le32_for_enumn!(QueueType, "queue_type"); 61 62 #[derive(Debug)] 63 pub enum VideoCmd { 64 QueryCapability { 65 queue_type: QueueType, 66 }, 67 StreamCreate { 68 stream_id: u32, 69 coded_format: Format, 70 }, 71 StreamDestroy { 72 stream_id: u32, 73 }, 74 StreamDrain { 75 stream_id: u32, 76 }, 77 ResourceCreate { 78 stream_id: u32, 79 queue_type: QueueType, 80 resource_id: u32, 81 plane_offsets: Vec<u32>, 82 uuid: u128, 83 }, 84 ResourceQueue { 85 stream_id: u32, 86 queue_type: QueueType, 87 resource_id: u32, 88 timestamp: u64, 89 data_sizes: Vec<u32>, 90 }, 91 ResourceDestroyAll { 92 stream_id: u32, 93 queue_type: QueueType, 94 }, 95 QueueClear { 96 stream_id: u32, 97 queue_type: QueueType, 98 }, 99 GetParams { 100 stream_id: u32, 101 queue_type: QueueType, 102 }, 103 SetParams { 104 stream_id: u32, 105 queue_type: QueueType, 106 params: Params, 107 }, 108 QueryControl { 109 query_ctrl_type: QueryCtrlType, 110 }, 111 GetControl { 112 stream_id: u32, 113 ctrl_type: CtrlType, 114 }, 115 SetControl { 116 stream_id: u32, 117 ctrl_val: CtrlVal, 118 }, 119 } 120 121 impl<'a> VideoCmd { 122 /// Reads a request on virtqueue and construct a VideoCmd value. from_reader(r: &'a mut Reader) -> Result<Self, ReadCmdError>123 pub fn from_reader(r: &'a mut Reader) -> Result<Self, ReadCmdError> { 124 use self::ReadCmdError::*; 125 use self::VideoCmd::*; 126 127 // Unlike structs in virtio_video.h in the kernel, our command structs in protocol.rs don't 128 // have a field of `struct virtio_video_cmd_hdr`. So, we first read the header here and 129 // a body below. 130 let hdr = r.read_obj::<virtio_video_cmd_hdr>()?; 131 132 Ok(match hdr.type_.into() { 133 VIRTIO_VIDEO_CMD_QUERY_CAPABILITY => { 134 let virtio_video_query_capability { queue_type, .. } = r.read_obj()?; 135 QueryCapability { 136 queue_type: queue_type.try_into()?, 137 } 138 } 139 VIRTIO_VIDEO_CMD_STREAM_CREATE => { 140 let virtio_video_stream_create { 141 in_mem_type, 142 out_mem_type, 143 coded_format, 144 .. 145 } = r.read_obj()?; 146 147 if in_mem_type != VIRTIO_VIDEO_MEM_TYPE_VIRTIO_OBJECT 148 || out_mem_type != VIRTIO_VIDEO_MEM_TYPE_VIRTIO_OBJECT 149 { 150 error!("mem_type must be VIRTIO_OBJECT"); 151 return Err(InvalidArgument); 152 } 153 StreamCreate { 154 stream_id: hdr.stream_id.into(), 155 coded_format: coded_format.try_into()?, 156 } 157 } 158 VIRTIO_VIDEO_CMD_STREAM_DESTROY => { 159 let virtio_video_stream_destroy { .. } = r.read_obj()?; 160 StreamDestroy { 161 stream_id: hdr.stream_id.into(), 162 } 163 } 164 VIRTIO_VIDEO_CMD_STREAM_DRAIN => { 165 let virtio_video_stream_drain { .. } = r.read_obj()?; 166 StreamDrain { 167 stream_id: hdr.stream_id.into(), 168 } 169 } 170 VIRTIO_VIDEO_CMD_RESOURCE_CREATE => { 171 let virtio_video_resource_create { 172 queue_type, 173 resource_id, 174 planes_layout, 175 num_planes, 176 plane_offsets, 177 .. 178 } = r.read_obj()?; 179 180 // Assume ChromeOS-specific requirements. 181 if Into::<u32>::into(planes_layout) != VIRTIO_VIDEO_PLANES_LAYOUT_SINGLE_BUFFER { 182 error!( 183 "each buffer must be a single DMAbuf: {}", 184 Into::<u32>::into(planes_layout), 185 ); 186 return Err(InvalidArgument); 187 } 188 189 let num_planes: u32 = num_planes.into(); 190 if num_planes as usize > plane_offsets.len() { 191 error!( 192 "num_planes must not exceed {} but {}", 193 plane_offsets.len(), 194 num_planes 195 ); 196 return Err(InvalidArgument); 197 } 198 let plane_offsets = plane_offsets[0..num_planes as usize] 199 .iter() 200 .map(|x| Into::<u32>::into(*x)) 201 .collect::<Vec<u32>>(); 202 203 let virtio_video_object_entry { uuid } = r.read_obj()?; 204 205 ResourceCreate { 206 stream_id: hdr.stream_id.into(), 207 queue_type: queue_type.try_into()?, 208 resource_id: resource_id.into(), 209 plane_offsets, 210 uuid: u128::from_be_bytes(uuid), 211 } 212 } 213 VIRTIO_VIDEO_CMD_RESOURCE_QUEUE => { 214 let virtio_video_resource_queue { 215 queue_type, 216 resource_id, 217 timestamp, 218 num_data_sizes, 219 data_sizes, 220 .. 221 } = r.read_obj()?; 222 223 let num_data_sizes: u32 = num_data_sizes.into(); 224 if num_data_sizes as usize > data_sizes.len() { 225 return Err(InvalidArgument); 226 } 227 let data_sizes = data_sizes[0..num_data_sizes as usize] 228 .iter() 229 .map(|x| Into::<u32>::into(*x)) 230 .collect::<Vec<u32>>(); 231 ResourceQueue { 232 stream_id: hdr.stream_id.into(), 233 queue_type: queue_type.try_into()?, 234 resource_id: resource_id.into(), 235 timestamp: timestamp.into(), 236 data_sizes, 237 } 238 } 239 VIRTIO_VIDEO_CMD_RESOURCE_DESTROY_ALL => { 240 let virtio_video_resource_destroy_all { queue_type, .. } = r.read_obj()?; 241 ResourceDestroyAll { 242 stream_id: hdr.stream_id.into(), 243 queue_type: queue_type.try_into()?, 244 } 245 } 246 VIRTIO_VIDEO_CMD_QUEUE_CLEAR => { 247 let virtio_video_queue_clear { queue_type, .. } = r.read_obj()?; 248 QueueClear { 249 stream_id: hdr.stream_id.into(), 250 queue_type: queue_type.try_into()?, 251 } 252 } 253 VIRTIO_VIDEO_CMD_GET_PARAMS => { 254 let virtio_video_get_params { queue_type, .. } = r.read_obj()?; 255 GetParams { 256 stream_id: hdr.stream_id.into(), 257 queue_type: queue_type.try_into()?, 258 } 259 } 260 VIRTIO_VIDEO_CMD_SET_PARAMS => { 261 let virtio_video_set_params { params } = r.read_obj()?; 262 SetParams { 263 stream_id: hdr.stream_id.into(), 264 queue_type: params.queue_type.try_into()?, 265 params: params.try_into()?, 266 } 267 } 268 VIRTIO_VIDEO_CMD_QUERY_CONTROL => { 269 let body = r.read_obj::<virtio_video_query_control>()?; 270 let query_ctrl_type = match body.control.into() { 271 VIRTIO_VIDEO_CONTROL_PROFILE => QueryCtrlType::Profile( 272 r.read_obj::<virtio_video_query_control_profile>()? 273 .format 274 .try_into()?, 275 ), 276 VIRTIO_VIDEO_CONTROL_LEVEL => QueryCtrlType::Level( 277 r.read_obj::<virtio_video_query_control_level>()? 278 .format 279 .try_into()?, 280 ), 281 t => { 282 return Err(ReadCmdError::UnsupportedCtrlType(t)); 283 } 284 }; 285 QueryControl { query_ctrl_type } 286 } 287 VIRTIO_VIDEO_CMD_GET_CONTROL => { 288 let virtio_video_get_control { control, .. } = r.read_obj()?; 289 let ctrl_type = match control.into() { 290 VIRTIO_VIDEO_CONTROL_BITRATE => CtrlType::Bitrate, 291 VIRTIO_VIDEO_CONTROL_PROFILE => CtrlType::Profile, 292 VIRTIO_VIDEO_CONTROL_LEVEL => CtrlType::Level, 293 VIRTIO_VIDEO_CONTROL_FORCE_KEYFRAME => CtrlType::ForceKeyframe, 294 t => { 295 return Err(ReadCmdError::UnsupportedCtrlType(t)); 296 } 297 }; 298 GetControl { 299 stream_id: hdr.stream_id.into(), 300 ctrl_type, 301 } 302 } 303 VIRTIO_VIDEO_CMD_SET_CONTROL => { 304 let virtio_video_set_control { control, .. } = r.read_obj()?; 305 let ctrl_val = match control.into() { 306 VIRTIO_VIDEO_CONTROL_BITRATE => CtrlVal::Bitrate( 307 r.read_obj::<virtio_video_control_val_bitrate>()? 308 .bitrate 309 .into(), 310 ), 311 VIRTIO_VIDEO_CONTROL_PROFILE => CtrlVal::Profile( 312 r.read_obj::<virtio_video_control_val_profile>()? 313 .profile 314 .try_into()?, 315 ), 316 VIRTIO_VIDEO_CONTROL_LEVEL => CtrlVal::Level( 317 r.read_obj::<virtio_video_control_val_level>()? 318 .level 319 .try_into()?, 320 ), 321 VIRTIO_VIDEO_CONTROL_FORCE_KEYFRAME => CtrlVal::ForceKeyframe(), 322 t => { 323 return Err(ReadCmdError::UnsupportedCtrlType(t)); 324 } 325 }; 326 SetControl { 327 stream_id: hdr.stream_id.into(), 328 ctrl_val, 329 } 330 } 331 _ => return Err(ReadCmdError::InvalidCmdType(hdr.type_.into())), 332 }) 333 } 334 } 335