1 // Copyright 2020 The ChromiumOS Authors 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; 8 use std::convert::TryInto; 9 use std::io; 10 11 use base::error; 12 use data_model::Le32; 13 use enumn::N; 14 use remain::sorted; 15 use thiserror::Error as ThisError; 16 17 use crate::virtio::video::control::*; 18 use crate::virtio::video::format::*; 19 use crate::virtio::video::params::Params; 20 use crate::virtio::video::protocol::*; 21 use crate::virtio::video::resource::ResourceType; 22 use crate::virtio::video::resource::UnresolvedResourceEntry; 23 use crate::virtio::Reader; 24 25 /// An error indicating a failure while reading a request from the guest. 26 #[sorted] 27 #[derive(Debug, ThisError)] 28 pub enum ReadCmdError { 29 /// Invalid argument is passed. 30 #[error("invalid argument passed to command")] 31 InvalidArgument, 32 /// The type of the command was invalid. 33 #[error("invalid command type: {0}")] 34 InvalidCmdType(u32), 35 /// Failed to read an object. 36 #[error("failed to read object: {0}")] 37 IoError(#[from] io::Error), 38 /// The type of the requested control was unsupported. 39 #[error("unsupported control type: {0}")] 40 UnsupportedCtrlType(u32), 41 } 42 43 #[derive(PartialEq, Eq, PartialOrd, Ord, N, Clone, Copy, Debug)] 44 #[repr(u32)] 45 pub enum QueueType { 46 Input = VIRTIO_VIDEO_QUEUE_TYPE_INPUT, 47 Output = VIRTIO_VIDEO_QUEUE_TYPE_OUTPUT, 48 } 49 impl_try_from_le32_for_enumn!(QueueType, "queue_type"); 50 51 #[derive(Debug)] 52 pub enum VideoCmd { 53 QueryCapability { 54 queue_type: QueueType, 55 }, 56 StreamCreate { 57 stream_id: u32, 58 coded_format: Format, 59 input_resource_type: ResourceType, 60 output_resource_type: ResourceType, 61 }, 62 StreamDestroy { 63 stream_id: u32, 64 }, 65 StreamDrain { 66 stream_id: u32, 67 }, 68 ResourceCreate { 69 stream_id: u32, 70 queue_type: QueueType, 71 resource_id: u32, 72 plane_offsets: Vec<u32>, 73 /// The outer vector contains one entry per memory plane, whereas the inner vector contains 74 /// all the memory entries that make a single plane (i.e. one for virtio objects, one or 75 /// more for guest pages). 76 plane_entries: Vec<Vec<UnresolvedResourceEntry>>, 77 }, 78 ResourceQueue { 79 stream_id: u32, 80 queue_type: QueueType, 81 resource_id: u32, 82 timestamp: u64, 83 data_sizes: Vec<u32>, 84 }, 85 ResourceDestroyAll { 86 stream_id: u32, 87 queue_type: QueueType, 88 }, 89 QueueClear { 90 stream_id: u32, 91 queue_type: QueueType, 92 }, 93 GetParams { 94 stream_id: u32, 95 queue_type: QueueType, 96 /// `true` if this command has been created from the GET_PARAMS_EXT guest command. 97 is_ext: bool, 98 }, 99 SetParams { 100 stream_id: u32, 101 queue_type: QueueType, 102 params: Params, 103 /// `true` if this command has been created from the SET_PARAMS_EXT guest command. 104 is_ext: bool, 105 }, 106 QueryControl { 107 query_ctrl_type: QueryCtrlType, 108 }, 109 GetControl { 110 stream_id: u32, 111 ctrl_type: CtrlType, 112 }, 113 SetControl { 114 stream_id: u32, 115 ctrl_val: CtrlVal, 116 }, 117 } 118 119 impl<'a> VideoCmd { 120 /// Reads a request on virtqueue and construct a VideoCmd value. from_reader(r: &'a mut Reader) -> Result<Self, ReadCmdError>121 pub fn from_reader(r: &'a mut Reader) -> Result<Self, ReadCmdError> { 122 use self::ReadCmdError::*; 123 use self::VideoCmd::*; 124 125 // Unlike structs in virtio_video.h in the kernel, our command structs in protocol.rs don't 126 // have a field of `struct virtio_video_cmd_hdr`. So, we first read the header here and 127 // a body below. 128 let hdr = r.read_obj::<virtio_video_cmd_hdr>()?; 129 130 Ok(match hdr.type_.into() { 131 VIRTIO_VIDEO_CMD_QUERY_CAPABILITY => { 132 let virtio_video_query_capability { queue_type, .. } = r.read_obj()?; 133 QueryCapability { 134 queue_type: queue_type.try_into()?, 135 } 136 } 137 VIRTIO_VIDEO_CMD_STREAM_CREATE => { 138 let virtio_video_stream_create { 139 in_mem_type, 140 out_mem_type, 141 coded_format, 142 .. 143 } = r.read_obj()?; 144 145 let input_resource_type = match in_mem_type.into() { 146 VIRTIO_VIDEO_MEM_TYPE_VIRTIO_OBJECT => ResourceType::VirtioObject, 147 VIRTIO_VIDEO_MEM_TYPE_GUEST_PAGES => ResourceType::GuestPages, 148 m => { 149 error!("Unsupported input resource memory type 0x{:x}!", m); 150 return Err(InvalidArgument); 151 } 152 }; 153 154 let output_resource_type = match out_mem_type.into() { 155 VIRTIO_VIDEO_MEM_TYPE_VIRTIO_OBJECT => ResourceType::VirtioObject, 156 VIRTIO_VIDEO_MEM_TYPE_GUEST_PAGES => ResourceType::GuestPages, 157 m => { 158 error!("Unsupported output resource memory type 0x{:x}!", m); 159 return Err(InvalidArgument); 160 } 161 }; 162 163 StreamCreate { 164 stream_id: hdr.stream_id.into(), 165 coded_format: coded_format.try_into()?, 166 input_resource_type, 167 output_resource_type, 168 } 169 } 170 VIRTIO_VIDEO_CMD_STREAM_DESTROY => { 171 let virtio_video_stream_destroy { .. } = r.read_obj()?; 172 StreamDestroy { 173 stream_id: hdr.stream_id.into(), 174 } 175 } 176 VIRTIO_VIDEO_CMD_STREAM_DRAIN => { 177 let virtio_video_stream_drain { .. } = r.read_obj()?; 178 StreamDrain { 179 stream_id: hdr.stream_id.into(), 180 } 181 } 182 VIRTIO_VIDEO_CMD_RESOURCE_CREATE => { 183 let virtio_video_resource_create { 184 queue_type, 185 resource_id, 186 planes_layout, 187 num_planes, 188 plane_offsets, 189 num_entries, 190 } = r.read_obj()?; 191 192 // Assume ChromeOS-specific requirements. 193 let planes_layout = Into::<u32>::into(planes_layout); 194 if planes_layout != VIRTIO_VIDEO_PLANES_LAYOUT_SINGLE_BUFFER { 195 error!("Only single-planar formats are supported for now"); 196 return Err(InvalidArgument); 197 } 198 199 let num_planes = Into::<u32>::into(num_planes) as usize; 200 if num_planes > plane_offsets.len() { 201 error!( 202 "num_planes is {} but shall not exceed {}", 203 num_planes, 204 plane_offsets.len(), 205 ); 206 return Err(InvalidArgument); 207 } 208 if planes_layout == VIRTIO_VIDEO_PLANES_LAYOUT_SINGLE_BUFFER && num_planes != 1 { 209 error!( 210 "Single-planar format specified but num_planes is {}", 211 num_planes 212 ); 213 return Err(InvalidArgument); 214 } 215 216 let plane_offsets = plane_offsets[0..num_planes] 217 .iter() 218 .map(|x| Into::<u32>::into(*x)) 219 .collect::<Vec<u32>>(); 220 221 // Read all the entries for all the planes. 222 let plane_entries = (0..num_planes as usize) 223 .into_iter() 224 .map(|i| { 225 let num_entries: u32 = num_entries[i].into(); 226 (0..num_entries) 227 .into_iter() 228 .map(|_| r.read_obj::<UnresolvedResourceEntry>()) 229 .collect::<Result<Vec<_>, _>>() 230 }) 231 .collect::<Result<Vec<_>, _>>()?; 232 233 ResourceCreate { 234 stream_id: hdr.stream_id.into(), 235 queue_type: queue_type.try_into()?, 236 resource_id: resource_id.into(), 237 plane_offsets, 238 plane_entries, 239 } 240 } 241 VIRTIO_VIDEO_CMD_RESOURCE_QUEUE => { 242 let virtio_video_resource_queue { 243 queue_type, 244 resource_id, 245 timestamp, 246 num_data_sizes, 247 data_sizes, 248 .. 249 } = r.read_obj()?; 250 251 let num_data_sizes: u32 = num_data_sizes.into(); 252 if num_data_sizes as usize > data_sizes.len() { 253 return Err(InvalidArgument); 254 } 255 let data_sizes = data_sizes[0..num_data_sizes as usize] 256 .iter() 257 .map(|x| Into::<u32>::into(*x)) 258 .collect::<Vec<u32>>(); 259 ResourceQueue { 260 stream_id: hdr.stream_id.into(), 261 queue_type: queue_type.try_into()?, 262 resource_id: resource_id.into(), 263 timestamp: timestamp.into(), 264 data_sizes, 265 } 266 } 267 VIRTIO_VIDEO_CMD_RESOURCE_DESTROY_ALL => { 268 let virtio_video_resource_destroy_all { queue_type, .. } = r.read_obj()?; 269 ResourceDestroyAll { 270 stream_id: hdr.stream_id.into(), 271 queue_type: queue_type.try_into()?, 272 } 273 } 274 VIRTIO_VIDEO_CMD_QUEUE_CLEAR => { 275 let virtio_video_queue_clear { queue_type, .. } = r.read_obj()?; 276 QueueClear { 277 stream_id: hdr.stream_id.into(), 278 queue_type: queue_type.try_into()?, 279 } 280 } 281 VIRTIO_VIDEO_CMD_GET_PARAMS => { 282 let virtio_video_get_params { queue_type, .. } = r.read_obj()?; 283 GetParams { 284 stream_id: hdr.stream_id.into(), 285 queue_type: queue_type.try_into()?, 286 is_ext: false, 287 } 288 } 289 VIRTIO_VIDEO_CMD_SET_PARAMS => { 290 let virtio_video_set_params { params } = r.read_obj()?; 291 SetParams { 292 stream_id: hdr.stream_id.into(), 293 queue_type: params.queue_type.try_into()?, 294 params: params.try_into()?, 295 is_ext: false, 296 } 297 } 298 VIRTIO_VIDEO_CMD_QUERY_CONTROL => { 299 let body = r.read_obj::<virtio_video_query_control>()?; 300 let query_ctrl_type = match body.control.into() { 301 VIRTIO_VIDEO_CONTROL_PROFILE => QueryCtrlType::Profile( 302 r.read_obj::<virtio_video_query_control_profile>()? 303 .format 304 .try_into()?, 305 ), 306 VIRTIO_VIDEO_CONTROL_LEVEL => QueryCtrlType::Level( 307 r.read_obj::<virtio_video_query_control_level>()? 308 .format 309 .try_into()?, 310 ), 311 t => { 312 return Err(ReadCmdError::UnsupportedCtrlType(t)); 313 } 314 }; 315 QueryControl { query_ctrl_type } 316 } 317 VIRTIO_VIDEO_CMD_GET_CONTROL => { 318 let virtio_video_get_control { control, .. } = r.read_obj()?; 319 let ctrl_type = match control.into() { 320 VIRTIO_VIDEO_CONTROL_BITRATE => CtrlType::Bitrate, 321 VIRTIO_VIDEO_CONTROL_BITRATE_PEAK => CtrlType::BitratePeak, 322 VIRTIO_VIDEO_CONTROL_BITRATE_MODE => CtrlType::BitrateMode, 323 VIRTIO_VIDEO_CONTROL_PROFILE => CtrlType::Profile, 324 VIRTIO_VIDEO_CONTROL_LEVEL => CtrlType::Level, 325 VIRTIO_VIDEO_CONTROL_FORCE_KEYFRAME => CtrlType::ForceKeyframe, 326 VIRTIO_VIDEO_CONTROL_PREPEND_SPSPPS_TO_IDR => CtrlType::PrependSpsPpsToIdr, 327 t => { 328 return Err(ReadCmdError::UnsupportedCtrlType(t)); 329 } 330 }; 331 GetControl { 332 stream_id: hdr.stream_id.into(), 333 ctrl_type, 334 } 335 } 336 VIRTIO_VIDEO_CMD_SET_CONTROL => { 337 let virtio_video_set_control { control, .. } = r.read_obj()?; 338 let ctrl_val = match control.into() { 339 VIRTIO_VIDEO_CONTROL_BITRATE => CtrlVal::Bitrate( 340 r.read_obj::<virtio_video_control_val_bitrate>()? 341 .bitrate 342 .into(), 343 ), 344 VIRTIO_VIDEO_CONTROL_BITRATE_PEAK => CtrlVal::BitratePeak( 345 r.read_obj::<virtio_video_control_val_bitrate_peak>()? 346 .bitrate_peak 347 .into(), 348 ), 349 VIRTIO_VIDEO_CONTROL_BITRATE_MODE => CtrlVal::BitrateMode( 350 r.read_obj::<virtio_video_control_val_bitrate_mode>()? 351 .bitrate_mode 352 .try_into()?, 353 ), 354 VIRTIO_VIDEO_CONTROL_PROFILE => CtrlVal::Profile( 355 r.read_obj::<virtio_video_control_val_profile>()? 356 .profile 357 .try_into()?, 358 ), 359 VIRTIO_VIDEO_CONTROL_LEVEL => CtrlVal::Level( 360 r.read_obj::<virtio_video_control_val_level>()? 361 .level 362 .try_into()?, 363 ), 364 VIRTIO_VIDEO_CONTROL_FORCE_KEYFRAME => CtrlVal::ForceKeyframe, 365 VIRTIO_VIDEO_CONTROL_PREPEND_SPSPPS_TO_IDR => CtrlVal::PrependSpsPpsToIdr( 366 r.read_obj::<virtio_video_control_val_prepend_spspps_to_idr>()? 367 .prepend_spspps_to_idr 368 != 0, 369 ), 370 t => { 371 return Err(ReadCmdError::UnsupportedCtrlType(t)); 372 } 373 }; 374 SetControl { 375 stream_id: hdr.stream_id.into(), 376 ctrl_val, 377 } 378 } 379 VIRTIO_VIDEO_CMD_GET_PARAMS_EXT => { 380 let virtio_video_get_params_ext { queue_type, .. } = r.read_obj()?; 381 GetParams { 382 stream_id: hdr.stream_id.into(), 383 queue_type: queue_type.try_into()?, 384 is_ext: true, 385 } 386 } 387 VIRTIO_VIDEO_CMD_SET_PARAMS_EXT => { 388 let virtio_video_set_params_ext { params } = r.read_obj()?; 389 SetParams { 390 stream_id: hdr.stream_id.into(), 391 queue_type: params.base.queue_type.try_into()?, 392 params: params.try_into()?, 393 is_ext: true, 394 } 395 } 396 _ => return Err(ReadCmdError::InvalidCmdType(hdr.type_.into())), 397 }) 398 } 399 } 400