• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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