1 //! Safe definitions around V4L2 extended controls.
2 //!
3 //! Extended controls are represented using the `[SafeExtControl]` type, which is a transparent
4 //! wrapper for `v4l2_ext_control`. It takes a generic parameter defining the actual control to
5 //! use, which limits its API to the methods safe to use for that control type.
6 //!
7 //! A mutable reference to a single `[SafeExtControl]` can be passed to ioctl methods such as
8 //! [`crate::ioctl::g_ext_ctrls`] to get or set the value of that control only. Setting more than
9 //! one control at the same time requires to pass a type implementing [`AsV4l2ControlSlice`], that
10 //! returns a slice if the `v4l2_ext_control`s to manipulate.
11 //!
12 //! Since [`SafeExtControl`] is a transparent wrapper around `v4l2_ext_control`, an array of it can
13 //! safely implement `AsV4l2ControlSlice`. Or, more conveniently, a `#[repr(C)]` type containing
14 //! only [`SafeExtControl`]s:
15 //!
16 //! ```no_run
17 //! # use std::os::fd::OwnedFd;
18 //! # use std::path::Path;
19 //! #
20 //! # use v4l2r::bindings::v4l2_ext_control;
21 //! # use v4l2r::controls::AsV4l2ControlSlice;
22 //! # use v4l2r::controls::SafeExtControl;
23 //! # use v4l2r::controls::user::Brightness;
24 //! # use v4l2r::controls::user::Contrast;
25 //! # use v4l2r::device::Device;
26 //! # use v4l2r::ioctl::s_ext_ctrls;
27 //! # use v4l2r::ioctl::CtrlWhich;
28 //! #
29 //! # let device = Device::open(Path::new("/dev/video0"), Default::default()).unwrap();
30 //! #
31 //! #[repr(C)]
32 //! struct Controls {
33 //!     brightness: SafeExtControl<Brightness>,
34 //!     contrast: SafeExtControl<Contrast>,
35 //! }
36 //!
37 //! impl AsV4l2ControlSlice for &mut Controls {
38 //!     fn as_v4l2_control_slice(&mut self) -> &mut [v4l2_ext_control] {
39 //!         let ptr = (*self) as *mut Controls as *mut v4l2_ext_control;
40 //!         unsafe { std::slice::from_raw_parts_mut(ptr, 2) }
41 //!     }
42 //! }
43 //!
44 //! let mut controls = Controls {
45 //!     brightness: SafeExtControl::<Brightness>::from_value(128),
46 //!     contrast: SafeExtControl::<Contrast>::from_value(128),
47 //! };
48 //!
49 //! s_ext_ctrls(&device, CtrlWhich::Current, &mut controls).unwrap();
50 //! assert_eq!(controls.brightness.value(), 128);
51 //! assert_eq!(controls.contrast.value(), 128);
52 //! ```
53 //!
54 //! Due to the use of `repr(C)`, the `Controls` type has the same layout as an array of
55 //! `v4l2_ext_control`s and thus can be passed to `s_ext_ctrls` safely.
56 //!
57 //! Sub-modules contain the type definitions for each control, organized by control class. Due to
58 //! the large number of controls they are not all defined, so please add those you need if they are
59 //! missing.
60 
61 pub mod codec;
62 pub mod user;
63 
64 use std::marker::PhantomData;
65 
66 use crate::bindings;
67 use crate::bindings::v4l2_ctrl_fwht_params;
68 use crate::bindings::v4l2_ctrl_h264_decode_params;
69 use crate::bindings::v4l2_ctrl_h264_pps;
70 use crate::bindings::v4l2_ctrl_h264_pred_weights;
71 use crate::bindings::v4l2_ctrl_h264_scaling_matrix;
72 use crate::bindings::v4l2_ctrl_h264_slice_params;
73 use crate::bindings::v4l2_ctrl_h264_sps;
74 use crate::bindings::v4l2_ctrl_vp8_frame;
75 use crate::bindings::v4l2_ext_control;
76 use crate::bindings::v4l2_ext_control__bindgen_ty_1;
77 use crate::controls::codec::FwhtFlags;
78 
79 /// Trait implemented by types that can be passed to the
80 /// [`g/s/try_ext_ctrls`](crate::ioctl::g_ext_ctrls) family of functions.
81 pub trait AsV4l2ControlSlice {
as_v4l2_control_slice(&mut self) -> &mut [v4l2_ext_control]82     fn as_v4l2_control_slice(&mut self) -> &mut [v4l2_ext_control];
83 }
84 
85 impl AsV4l2ControlSlice for &mut [v4l2_ext_control] {
as_v4l2_control_slice(&mut self) -> &mut [v4l2_ext_control]86     fn as_v4l2_control_slice(&mut self) -> &mut [v4l2_ext_control] {
87         self
88     }
89 }
90 
91 /// Trait implemented by types representing a given control in order to define its properties and
92 /// set of available methods.
93 pub trait ExtControlTrait {
94     /// One of `V4L2_CID_*`
95     const ID: u32;
96     /// Type of the value of this control.
97     type PAYLOAD;
98 }
99 
100 /// Memory-safe `v4l2_ext_control`.
101 ///
102 /// This type is a `v4l2_ext_control` with the following invariants:
103 ///
104 /// * `id` is always a valid control ID,
105 /// * `size` is always correct (0 for non-pointer controls or size of payload for pointer
106 ///   controls),
107 /// * For pointer types, the payload is always allocated to match `size` bytes.
108 ///
109 /// In addition, the value of the control can only be accessed through methods that return the
110 /// correct type.
111 #[repr(transparent)]
112 pub struct SafeExtControl<T: ExtControlTrait>(v4l2_ext_control, PhantomData<T>);
113 
114 impl<T: ExtControlTrait> SafeExtControl<T> {
id(&self) -> u32115     pub fn id(&self) -> u32 {
116         self.0.id
117     }
118 }
119 
120 /// Allows us to pass a `&mut` of a single `SafeExtControl` to `g/s/try_ext_ctrls`.
121 impl<T: ExtControlTrait> AsV4l2ControlSlice for &mut SafeExtControl<T> {
as_v4l2_control_slice(&mut self) -> &mut [v4l2_ext_control]122     fn as_v4l2_control_slice(&mut self) -> &mut [v4l2_ext_control] {
123         unsafe { std::slice::from_raw_parts_mut(&mut self.0, 1) }
124     }
125 }
126 
127 impl<T> SafeExtControl<T>
128 where
129     T: ExtControlTrait<PAYLOAD = i32>,
130 {
131     /// Create a new control from its value.
from_value(value: i32) -> Self132     pub fn from_value(value: i32) -> Self {
133         Self(
134             v4l2_ext_control {
135                 id: T::ID,
136                 __bindgen_anon_1: v4l2_ext_control__bindgen_ty_1 { value },
137                 ..Default::default()
138             },
139             PhantomData,
140         )
141     }
142 
143     /// Returns the value of the control.
value(&self) -> i32144     pub fn value(&self) -> i32 {
145         unsafe { self.0.__bindgen_anon_1.value }
146     }
147 
148     /// Updates the value of the control.
set_value(&mut self, value: i32)149     pub fn set_value(&mut self, value: i32) {
150         self.0.__bindgen_anon_1.value = value;
151     }
152 }
153 
154 impl<T> SafeExtControl<T>
155 where
156     T: ExtControlTrait<PAYLOAD = i64>,
157 {
158     /// Create a new control from its value.
from_value64(value64: i64) -> Self159     pub fn from_value64(value64: i64) -> Self {
160         Self(
161             v4l2_ext_control {
162                 id: T::ID,
163                 __bindgen_anon_1: v4l2_ext_control__bindgen_ty_1 { value64 },
164                 ..Default::default()
165             },
166             PhantomData,
167         )
168     }
169 
170     /// Returns the value of the control.
value64(&self) -> i64171     pub fn value64(&self) -> i64 {
172         unsafe { self.0.__bindgen_anon_1.value64 }
173     }
174 
175     /// Updates the value of the control.
set_value64(&mut self, value: i64)176     pub fn set_value64(&mut self, value: i64) {
177         self.0.__bindgen_anon_1.value64 = value;
178     }
179 }
180 
181 impl<T> From<v4l2_ctrl_fwht_params> for SafeExtControl<T>
182 where
183     T: ExtControlTrait<PAYLOAD = v4l2_ctrl_fwht_params>,
184 {
from(params: v4l2_ctrl_fwht_params) -> Self185     fn from(params: v4l2_ctrl_fwht_params) -> Self {
186         let payload = Box::new(params);
187 
188         Self(
189             v4l2_ext_control {
190                 id: T::ID,
191                 size: std::mem::size_of::<T::PAYLOAD>() as u32,
192                 __bindgen_anon_1: v4l2_ext_control__bindgen_ty_1 {
193                     p_fwht_params: Box::into_raw(payload),
194                 },
195                 ..Default::default()
196             },
197             PhantomData,
198         )
199     }
200 }
201 
202 impl<T> SafeExtControl<T>
203 where
204     T: ExtControlTrait<PAYLOAD = v4l2_ctrl_fwht_params>,
205 {
fwht_params(&self) -> &v4l2_ctrl_fwht_params206     pub fn fwht_params(&self) -> &v4l2_ctrl_fwht_params {
207         unsafe { self.0.__bindgen_anon_1.p_fwht_params.as_ref().unwrap() }
208     }
209 
fwht_params_mut(&mut self) -> &mut v4l2_ctrl_fwht_params210     pub fn fwht_params_mut(&mut self) -> &mut v4l2_ctrl_fwht_params {
211         unsafe { self.0.__bindgen_anon_1.p_fwht_params.as_mut().unwrap() }
212     }
213 
flags(&self) -> Option<FwhtFlags>214     pub fn flags(&self) -> Option<FwhtFlags> {
215         FwhtFlags::from_bits(self.fwht_params().flags)
216     }
217 }
218 
219 impl<T> From<v4l2_ctrl_h264_sps> for SafeExtControl<T>
220 where
221     T: ExtControlTrait<PAYLOAD = v4l2_ctrl_h264_sps>,
222 {
from(params: v4l2_ctrl_h264_sps) -> Self223     fn from(params: v4l2_ctrl_h264_sps) -> Self {
224         let payload = Box::new(params);
225 
226         Self(
227             v4l2_ext_control {
228                 id: T::ID,
229                 size: std::mem::size_of::<T::PAYLOAD>() as u32,
230                 __bindgen_anon_1: v4l2_ext_control__bindgen_ty_1 {
231                     p_h264_sps: Box::into_raw(payload),
232                 },
233                 ..unsafe { std::mem::zeroed() }
234             },
235             PhantomData,
236         )
237     }
238 }
239 
240 impl<T> SafeExtControl<T>
241 where
242     T: ExtControlTrait<PAYLOAD = v4l2_ctrl_h264_sps>,
243 {
h264_sps(&self) -> &v4l2_ctrl_h264_sps244     pub fn h264_sps(&self) -> &v4l2_ctrl_h264_sps {
245         unsafe { self.0.__bindgen_anon_1.p_h264_sps.as_ref().unwrap() }
246     }
247 
h264_sps_mut(&mut self) -> &mut v4l2_ctrl_h264_sps248     pub fn h264_sps_mut(&mut self) -> &mut v4l2_ctrl_h264_sps {
249         unsafe { self.0.__bindgen_anon_1.p_h264_sps.as_mut().unwrap() }
250     }
251 }
252 
253 impl<T> From<v4l2_ctrl_h264_pps> for SafeExtControl<T>
254 where
255     T: ExtControlTrait<PAYLOAD = v4l2_ctrl_h264_pps>,
256 {
from(params: v4l2_ctrl_h264_pps) -> Self257     fn from(params: v4l2_ctrl_h264_pps) -> Self {
258         let payload = Box::new(params);
259 
260         Self(
261             v4l2_ext_control {
262                 id: T::ID,
263                 size: std::mem::size_of::<T::PAYLOAD>() as u32,
264                 __bindgen_anon_1: v4l2_ext_control__bindgen_ty_1 {
265                     p_h264_pps: Box::into_raw(payload),
266                 },
267                 ..unsafe { std::mem::zeroed() }
268             },
269             PhantomData,
270         )
271     }
272 }
273 
274 impl<T> SafeExtControl<T>
275 where
276     T: ExtControlTrait<PAYLOAD = v4l2_ctrl_h264_pps>,
277 {
h264_pps(&self) -> &v4l2_ctrl_h264_pps278     pub fn h264_pps(&self) -> &v4l2_ctrl_h264_pps {
279         unsafe { self.0.__bindgen_anon_1.p_h264_pps.as_ref().unwrap() }
280     }
281 
h264_pps_mut(&mut self) -> &mut v4l2_ctrl_h264_pps282     pub fn h264_pps_mut(&mut self) -> &mut v4l2_ctrl_h264_pps {
283         unsafe { self.0.__bindgen_anon_1.p_h264_pps.as_mut().unwrap() }
284     }
285 }
286 
287 impl<T> From<v4l2_ctrl_h264_scaling_matrix> for SafeExtControl<T>
288 where
289     T: ExtControlTrait<PAYLOAD = v4l2_ctrl_h264_scaling_matrix>,
290 {
from(params: v4l2_ctrl_h264_scaling_matrix) -> Self291     fn from(params: v4l2_ctrl_h264_scaling_matrix) -> Self {
292         let payload = Box::new(params);
293 
294         Self(
295             v4l2_ext_control {
296                 id: T::ID,
297                 size: std::mem::size_of::<T::PAYLOAD>() as u32,
298                 __bindgen_anon_1: v4l2_ext_control__bindgen_ty_1 {
299                     p_h264_scaling_matrix: Box::into_raw(payload),
300                 },
301                 ..unsafe { std::mem::zeroed() }
302             },
303             PhantomData,
304         )
305     }
306 }
307 
308 impl<T> SafeExtControl<T>
309 where
310     T: ExtControlTrait<PAYLOAD = v4l2_ctrl_h264_scaling_matrix>,
311 {
h264_scaling_matrix(&self) -> &v4l2_ctrl_h264_scaling_matrix312     pub fn h264_scaling_matrix(&self) -> &v4l2_ctrl_h264_scaling_matrix {
313         unsafe {
314             self.0
315                 .__bindgen_anon_1
316                 .p_h264_scaling_matrix
317                 .as_ref()
318                 .unwrap()
319         }
320     }
321 
h264_scaling_matrix_mut(&mut self) -> &mut v4l2_ctrl_h264_scaling_matrix322     pub fn h264_scaling_matrix_mut(&mut self) -> &mut v4l2_ctrl_h264_scaling_matrix {
323         unsafe {
324             self.0
325                 .__bindgen_anon_1
326                 .p_h264_scaling_matrix
327                 .as_mut()
328                 .unwrap()
329         }
330     }
331 }
332 
333 impl<T> From<v4l2_ctrl_h264_pred_weights> for SafeExtControl<T>
334 where
335     T: ExtControlTrait<PAYLOAD = v4l2_ctrl_h264_pred_weights>,
336 {
from(params: v4l2_ctrl_h264_pred_weights) -> Self337     fn from(params: v4l2_ctrl_h264_pred_weights) -> Self {
338         let payload = Box::new(params);
339 
340         Self(
341             v4l2_ext_control {
342                 id: T::ID,
343                 size: std::mem::size_of::<T::PAYLOAD>() as u32,
344                 __bindgen_anon_1: v4l2_ext_control__bindgen_ty_1 {
345                     p_h264_pred_weights: Box::into_raw(payload),
346                 },
347                 ..unsafe { std::mem::zeroed() }
348             },
349             PhantomData,
350         )
351     }
352 }
353 
354 impl<T> SafeExtControl<T>
355 where
356     T: ExtControlTrait<PAYLOAD = v4l2_ctrl_h264_pred_weights>,
357 {
h264_pred_weights(&self) -> &v4l2_ctrl_h264_pred_weights358     pub fn h264_pred_weights(&self) -> &v4l2_ctrl_h264_pred_weights {
359         unsafe {
360             self.0
361                 .__bindgen_anon_1
362                 .p_h264_pred_weights
363                 .as_ref()
364                 .unwrap()
365         }
366     }
367 
h264_pred_weights_mut(&mut self) -> &mut v4l2_ctrl_h264_pred_weights368     pub fn h264_pred_weights_mut(&mut self) -> &mut v4l2_ctrl_h264_pred_weights {
369         unsafe {
370             self.0
371                 .__bindgen_anon_1
372                 .p_h264_pred_weights
373                 .as_mut()
374                 .unwrap()
375         }
376     }
377 }
378 
379 impl<T> From<v4l2_ctrl_h264_slice_params> for SafeExtControl<T>
380 where
381     T: ExtControlTrait<PAYLOAD = v4l2_ctrl_h264_slice_params>,
382 {
from(params: v4l2_ctrl_h264_slice_params) -> Self383     fn from(params: v4l2_ctrl_h264_slice_params) -> Self {
384         let payload = Box::new(params);
385 
386         Self(
387             v4l2_ext_control {
388                 id: T::ID,
389                 size: std::mem::size_of::<T::PAYLOAD>() as u32,
390                 __bindgen_anon_1: v4l2_ext_control__bindgen_ty_1 {
391                     p_h264_slice_params: Box::into_raw(payload),
392                 },
393                 ..unsafe { std::mem::zeroed() }
394             },
395             PhantomData,
396         )
397     }
398 }
399 
400 impl<T> SafeExtControl<T>
401 where
402     T: ExtControlTrait<PAYLOAD = v4l2_ctrl_h264_slice_params>,
403 {
h264_slice_params(&self) -> &v4l2_ctrl_h264_slice_params404     pub fn h264_slice_params(&self) -> &v4l2_ctrl_h264_slice_params {
405         unsafe {
406             self.0
407                 .__bindgen_anon_1
408                 .p_h264_slice_params
409                 .as_ref()
410                 .unwrap()
411         }
412     }
413 
h264_slice_params_mut(&mut self) -> &mut v4l2_ctrl_h264_slice_params414     pub fn h264_slice_params_mut(&mut self) -> &mut v4l2_ctrl_h264_slice_params {
415         unsafe {
416             self.0
417                 .__bindgen_anon_1
418                 .p_h264_slice_params
419                 .as_mut()
420                 .unwrap()
421         }
422     }
423 }
424 
425 impl<T> From<v4l2_ctrl_h264_decode_params> for SafeExtControl<T>
426 where
427     T: ExtControlTrait<PAYLOAD = v4l2_ctrl_h264_decode_params>,
428 {
from(params: v4l2_ctrl_h264_decode_params) -> Self429     fn from(params: v4l2_ctrl_h264_decode_params) -> Self {
430         let payload = Box::new(params);
431 
432         Self(
433             v4l2_ext_control {
434                 id: T::ID,
435                 size: std::mem::size_of::<T::PAYLOAD>() as u32,
436                 __bindgen_anon_1: v4l2_ext_control__bindgen_ty_1 {
437                     p_h264_decode_params: Box::into_raw(payload),
438                 },
439                 ..unsafe { std::mem::zeroed() }
440             },
441             PhantomData,
442         )
443     }
444 }
445 
446 impl<T> SafeExtControl<T>
447 where
448     T: ExtControlTrait<PAYLOAD = v4l2_ctrl_h264_decode_params>,
449 {
h264_decode_params(&self) -> &v4l2_ctrl_h264_decode_params450     pub fn h264_decode_params(&self) -> &v4l2_ctrl_h264_decode_params {
451         unsafe {
452             self.0
453                 .__bindgen_anon_1
454                 .p_h264_decode_params
455                 .as_ref()
456                 .unwrap()
457         }
458     }
459 
h264_decode_params_mut(&mut self) -> &mut v4l2_ctrl_h264_decode_params460     pub fn h264_decode_params_mut(&mut self) -> &mut v4l2_ctrl_h264_decode_params {
461         unsafe {
462             self.0
463                 .__bindgen_anon_1
464                 .p_h264_decode_params
465                 .as_mut()
466                 .unwrap()
467         }
468     }
469 }
470 
471 impl<T> From<v4l2_ctrl_vp8_frame> for SafeExtControl<T>
472 where
473     T: ExtControlTrait<PAYLOAD = v4l2_ctrl_vp8_frame>,
474 {
from(params: v4l2_ctrl_vp8_frame) -> Self475     fn from(params: v4l2_ctrl_vp8_frame) -> Self {
476         let payload = Box::new(params);
477 
478         Self(
479             v4l2_ext_control {
480                 id: T::ID,
481                 size: std::mem::size_of::<T::PAYLOAD>() as u32,
482                 __bindgen_anon_1: v4l2_ext_control__bindgen_ty_1 {
483                     p_vp8_frame: Box::into_raw(payload),
484                 },
485                 ..Default::default()
486             },
487             PhantomData,
488         )
489     }
490 }
491 
492 impl<T> SafeExtControl<T>
493 where
494     T: ExtControlTrait<PAYLOAD = v4l2_ctrl_vp8_frame>,
495 {
vp8_frame(&self) -> &v4l2_ctrl_vp8_frame496     pub fn vp8_frame(&self) -> &v4l2_ctrl_vp8_frame {
497         unsafe { self.0.__bindgen_anon_1.p_vp8_frame.as_ref().unwrap() }
498     }
499 
vp8_frame_mut(&mut self) -> &mut v4l2_ctrl_vp8_frame500     pub fn vp8_frame_mut(&mut self) -> &mut v4l2_ctrl_vp8_frame {
501         unsafe { self.0.__bindgen_anon_1.p_vp8_frame.as_mut().unwrap() }
502     }
503 }
504 
505 // Due to a limitation of the type system we cannot conditionally implement the `Drop` trait on
506 // e.g. `where T: ControlTrait<PAYLOAD = v4l2_ctrl_fwht_params>`, so we need this global implementation.
507 impl<T: ExtControlTrait> Drop for SafeExtControl<T> {
drop(&mut self)508     fn drop(&mut self) {
509         // If we have allocated some payload for this control, re-wrap it into its original
510         // container that we immediately drop to free it.
511         if self.0.size > 0 {
512             unsafe {
513                 match self.0.id {
514                     bindings::V4L2_CID_STATELESS_FWHT_PARAMS => {
515                         let _ = Box::from_raw(self.0.__bindgen_anon_1.p_fwht_params);
516                     }
517                     bindings::V4L2_CID_STATELESS_VP8_FRAME => {
518                         let _ = Box::from_raw(self.0.__bindgen_anon_1.p_vp8_frame);
519                     }
520                     _ => (),
521                 }
522             }
523         }
524     }
525 }
526