@@ -30,6 +30,11 @@ use v4l2r::bindings::v4l2_requestbuffers;
3030use v4l2r:: ioctl:: BufferCapabilities ;
3131use v4l2r:: ioctl:: BufferField ;
3232use v4l2r:: ioctl:: BufferFlags ;
33+ use v4l2r:: ioctl:: CtrlId ;
34+ use v4l2r:: ioctl:: CtrlWhich ;
35+ use v4l2r:: ioctl:: EventType as V4l2EventType ;
36+ use v4l2r:: ioctl:: QueryCtrlFlags ;
37+ use v4l2r:: ioctl:: SubscribeEventFlags ;
3338use v4l2r:: ioctl:: V4l2Buffer ;
3439use v4l2r:: ioctl:: V4l2PlanesWithBackingMut ;
3540use v4l2r:: memory:: MemoryType ;
@@ -45,10 +50,33 @@ use virtio_media::ioctl::virtio_media_dispatch_ioctl;
4550use virtio_media:: memfd:: MemFdBuffer ;
4651use virtio_media:: mmap:: MmapMappingManager ;
4752use virtio_media:: protocol:: DequeueBufferEvent ;
53+ use virtio_media:: protocol:: SessionEvent ;
4854use virtio_media:: protocol:: SgEntry ;
4955use virtio_media:: protocol:: V4l2Event ;
5056use virtio_media:: protocol:: V4l2Ioctl ;
5157use virtio_media:: protocol:: VIRTIO_MEDIA_MMAP_FLAG_RW ;
58+ use std:: str:: FromStr ;
59+
60+ /// https://developer.android.com/reference/android/hardware/camera2/CameraMetadata#LENS_FACING_FRONT
61+ #[ derive( Debug , Clone , Copy , PartialEq , Eq ) ]
62+ pub enum LensFacing {
63+ Front = 0 ,
64+ Back = 1 ,
65+ External = 2 ,
66+ }
67+
68+ impl FromStr for LensFacing {
69+ type Err = String ;
70+
71+ fn from_str ( s : & str ) -> Result < Self , Self :: Err > {
72+ match s {
73+ "FRONT" => Ok ( LensFacing :: Front ) ,
74+ "BACK" => Ok ( LensFacing :: Back ) ,
75+ "EXTERNAL" => Ok ( LensFacing :: External ) ,
76+ _ => Err ( format ! ( "Invalid lens facing: {}. Expected FRONT, BACK, or EXTERNAL" , s) ) ,
77+ }
78+ }
79+ }
5280
5381/// Current status of a buffer.
5482#[ derive( Debug , PartialEq , Eq ) ]
@@ -243,18 +271,40 @@ pub struct EmulatedCamera<Q: VirtioMediaEventQueue, HM: VirtioMediaHostMemoryMap
243271 /// same time. It will fails if we allow simultaneous sessions to be active, so we need this
244272 /// artificial limitation to make it pass fully.
245273 active_session : Option < u32 > ,
274+ /// Lens facing configuration.
275+ lens_facing : LensFacing ,
246276}
247277
248278impl < Q , HM > EmulatedCamera < Q , HM >
249279where
250280 Q : VirtioMediaEventQueue ,
251281 HM : VirtioMediaHostMemoryMapper ,
252282{
253- pub fn new ( evt_queue : Q , mapper : HM ) -> Self {
283+ pub fn new ( evt_queue : Q , mapper : HM , lens_facing : LensFacing ) -> Self {
254284 Self {
255285 evt_queue,
256286 mmap_manager : MmapMappingManager :: from ( mapper) ,
257287 active_session : None ,
288+ lens_facing,
289+ }
290+ }
291+
292+ fn lens_facing_query_ext_ctrl ( & self ) -> bindings:: v4l2_query_ext_ctrl {
293+ let name_str = "LENS_FACING" ;
294+ let mut name = [ 0u8 ; 32 ] ;
295+ name[ 0 ..name_str. len ( ) ] . copy_from_slice ( name_str. as_bytes ( ) ) ;
296+ bindings:: v4l2_query_ext_ctrl {
297+ id : CID_LENS_FACING ,
298+ type_ : bindings:: v4l2_ctrl_type_V4L2_CTRL_TYPE_INTEGER,
299+ name : name. map ( |b| b as i8 ) ,
300+ minimum : LensFacing :: Front as i64 ,
301+ maximum : LensFacing :: External as i64 ,
302+ step : 1 ,
303+ default_value : self . lens_facing as i64 ,
304+ flags : bindings:: V4L2_CTRL_FLAG_READ_ONLY ,
305+ elems : 1 ,
306+ elem_size : std:: mem:: size_of :: < u32 > ( ) as u32 ,
307+ ..Default :: default ( )
258308 }
259309 }
260310}
@@ -337,6 +387,10 @@ where
337387 }
338388}
339389
390+ // Use an offset for virtio-media custom camera class control id values.
391+ const CID_OFFSET : u32 = bindings:: V4L2_CID_CAMERA_CLASS_BASE + 0x100 ;
392+ const CID_LENS_FACING : u32 = CID_OFFSET + 1 ;
393+
340394const PIXELFORMAT : u32 = PixelFormat :: from_fourcc ( b"YM12" ) . to_u32 ( ) ;
341395const WIDTH : u32 = 640 ;
342396const HEIGHT : u32 = 480 ;
@@ -776,4 +830,122 @@ where
776830 ..Default :: default ( )
777831 } )
778832 }
833+
834+ /// https://www.kernel.org/doc/html/latest/userspace-api/media/v4l/vidioc-queryctrl.html#control-flags
835+ fn query_ext_ctrl (
836+ & mut self ,
837+ _session : & Self :: Session ,
838+ id : CtrlId ,
839+ flags : QueryCtrlFlags ,
840+ ) -> IoctlResult < bindings:: v4l2_query_ext_ctrl > {
841+ let id: u32 = unsafe { std:: mem:: transmute ( id) } ;
842+ // If V4L2_CTRL_FLAG_NEXT_CTRL present returns the first control with a higher ID.
843+ if flags. contains ( QueryCtrlFlags :: NEXT ) {
844+ if id < CID_LENS_FACING {
845+ return Ok ( self . lens_facing_query_ext_ctrl ( ) ) ;
846+ }
847+ } else if id == CID_LENS_FACING {
848+ return Ok ( self . lens_facing_query_ext_ctrl ( ) ) ;
849+ }
850+ return Err ( libc:: EINVAL ) ;
851+ }
852+
853+ fn g_ext_ctrls (
854+ & mut self ,
855+ _session : & Self :: Session ,
856+ _which : CtrlWhich ,
857+ ctrls : & mut bindings:: v4l2_ext_controls ,
858+ ctrl_array : & mut Vec < bindings:: v4l2_ext_control > ,
859+ _user_regions : Vec < Vec < SgEntry > > ,
860+ ) -> IoctlResult < ( ) > {
861+ for ctrl in ctrl_array {
862+ match ctrl. id {
863+ CID_LENS_FACING => {
864+ ctrl. __bindgen_anon_1 . value = self . lens_facing as i32 ;
865+ }
866+ _ => {
867+ ctrls. error_idx = ctrls. count ;
868+ return Err ( libc:: EINVAL ) ;
869+ }
870+ }
871+ }
872+ Ok ( ( ) )
873+ }
874+
875+ fn try_ext_ctrls (
876+ & mut self ,
877+ _session : & Self :: Session ,
878+ _which : CtrlWhich ,
879+ ctrls : & mut bindings:: v4l2_ext_controls ,
880+ ctrl_array : & mut Vec < bindings:: v4l2_ext_control > ,
881+ _user_regions : Vec < Vec < SgEntry > > ,
882+ ) -> IoctlResult < ( ) > {
883+ for ( idx, ctrl) in ctrl_array. iter_mut ( ) . enumerate ( ) {
884+ ctrls. error_idx = idx as u32 ;
885+ let err_code = match ctrl. id {
886+ CID_LENS_FACING => libc:: EACCES ,
887+ _ => libc:: EINVAL ,
888+ } ;
889+ return Err ( err_code) ;
890+ }
891+ Ok ( ( ) )
892+ }
893+
894+ fn s_ext_ctrls (
895+ & mut self ,
896+ _session : & mut Self :: Session ,
897+ _which : CtrlWhich ,
898+ ctrls : & mut bindings:: v4l2_ext_controls ,
899+ ctrl_array : & mut Vec < bindings:: v4l2_ext_control > ,
900+ _user_regions : Vec < Vec < SgEntry > > ,
901+ ) -> IoctlResult < ( ) > {
902+ for ctrl in ctrl_array {
903+ ctrls. error_idx = ctrls. count ;
904+ let err_code = match ctrl. id {
905+ CID_LENS_FACING => libc:: EACCES ,
906+ _ => libc:: EINVAL ,
907+ } ;
908+ return Err ( err_code) ;
909+ }
910+ Ok ( ( ) )
911+ }
912+
913+ fn subscribe_event (
914+ & mut self ,
915+ session : & mut Self :: Session ,
916+ event : V4l2EventType ,
917+ flags : SubscribeEventFlags ,
918+ ) -> IoctlResult < ( ) > {
919+ if !flags. contains ( SubscribeEventFlags :: SEND_INITIAL ) {
920+ return Err ( libc:: EINVAL ) ;
921+ }
922+ match event {
923+ V4l2EventType :: Ctrl ( id) => match id {
924+ CID_LENS_FACING => {
925+ let ctrl_event = bindings:: v4l2_event {
926+ type_ : bindings:: V4L2_EVENT_CTRL ,
927+ id : CID_LENS_FACING ,
928+ ..Default :: default ( )
929+ } ;
930+ self . evt_queue
931+ . send_event ( V4l2Event :: Event ( SessionEvent :: new ( session. id , ctrl_event) ) ) ;
932+ Ok ( ( ) )
933+ }
934+ _ => Err ( libc:: EINVAL ) ,
935+ } ,
936+ _ => Err ( libc:: EINVAL ) ,
937+ }
938+ }
939+
940+ fn unsubscribe_event (
941+ & mut self ,
942+ _session : & mut Self :: Session ,
943+ event : bindings:: v4l2_event_subscription ,
944+ ) -> IoctlResult < ( ) > {
945+ return if event. type_ == bindings:: V4L2_EVENT_CTRL && event. id == CID_LENS_FACING {
946+ Ok ( ( ) )
947+ } else {
948+ Err ( libc:: EINVAL )
949+ } ;
950+ }
779951}
0 commit comments