#[allow(non_camel_case_types)]
#[allow(non_snake_case)]
pub mod sys;
use bitflags::bitflags;
use const_enum::const_enum;
use std::ffi::CStr;
use std::fmt;
use std::mem;
use std::str;
use sys::{media_v2_entity, media_v2_link};
pub trait Zeroed where Self: Sized {
fn zeroed() -> Self {
unsafe { mem::zeroed() }
}
}
#[repr(C)]
#[repr(packed)]
pub struct MediaDeviceInfo {
driver_name: [u8; 16],
device_name: [u8; 32],
serial_number: [u8; 40],
bus_info: [u8; 32],
media_version: u32,
hardware_device_version: u32,
driver_version: u32,
reserved: [u32; 31],
}
impl Zeroed for MediaDeviceInfo {}
impl fmt::Debug for MediaDeviceInfo {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("MediaDeviceInfo")
.field("driver", &self.get_driver())
.field("device", &self.get_device())
.finish()
}
}
fn str_from_slice(s: &[u8]) -> &str {
let full = str::from_utf8(s).unwrap();
let end_byte = full.char_indices()
.find(|(_i, c)| *c == '\0')
.map(|(i, _c)| i)
.unwrap_or(full.len());
full.split_at(end_byte).0
}
impl MediaDeviceInfo {
pub fn get_driver(&self) -> &str {
str_from_slice(&self.driver_name)
}
pub fn get_device(&self) -> &str {
str_from_slice(&self.device_name)
}
}
nix::ioctl_readwrite!(media_ioc_device_info, b'|', 0x00, MediaDeviceInfo);
#[repr(C)]
#[repr(packed)]
#[derive(Debug)]
pub struct media_v2_topology {
topology_version: u64,
pub num_entities: u32,
reserved1: u32,
ptr_entities: u64,
pub num_interfaces: u32,
reserved2: u32,
ptr_interfaces: u64,
num_pads: u32,
reserved3: u32,
ptr_pads: u64,
pub num_links: u32,
reserved4: u32,
pub ptr_links: u64,
}
impl Zeroed for media_v2_topology {}
impl media_v2_topology {
pub fn set_entities(&mut self, entities: &mut Vec<media_v2_entity>) {
self.num_entities = entities.len() as u32;
self.ptr_entities = entities.as_mut_ptr() as u64;
}
pub fn set_interfaces(&mut self, arr: &mut Vec<media_v2_interface>) {
self.num_interfaces = arr.len() as u32;
self.ptr_interfaces = arr.as_mut_ptr() as u64;
}
}
const_enum! {
enum MediaEntF, sys, MEDIA_ENT_F_ {
UNKNOWN,
V4L2_SUBDEV_UNKNOWN,
DTV_DEMOD,
TS_DEMUX,
DTV_CA,
DTV_NET_DECAP,
IO_V4L,
IO_DTV,
IO_VBI,
IO_SWRADIO,
CAM_SENSOR,
FLASH,
LENS,
TUNER,
IF_VID_DECODER,
IF_AUD_DECODER,
AUDIO_CAPTURE,
AUDIO_PLAYBACK,
AUDIO_MIXER,
PROC_VIDEO_COMPOSER,
PROC_VIDEO_PIXEL_FORMATTER,
PROC_VIDEO_PIXEL_ENC_CONV,
PROC_VIDEO_LUT,
PROC_VIDEO_SCALER,
PROC_VIDEO_STATISTICS,
PROC_VIDEO_ENCODER,
PROC_VIDEO_DECODER,
PROC_VIDEO_ISP,
VID_MUX,
VID_IF_BRIDGE,
ATV_DECODER,
DV_DECODER,
DV_ENCODER,
}
}
bitflags! {
#[derive(Debug)]
pub struct MediaEntFl: u32 {
const DEFAULT = sys::MEDIA_ENT_FL_DEFAULT;
const CONNECTOR = sys::MEDIA_ENT_FL_CONNECTOR;
}
}
#[derive(Debug)]
pub struct MediaV2Entity {
pub id: u32,
pub name: Result<String, [::std::os::raw::c_char; 64usize]>,
pub function: MediaEntF,
pub flags: MediaEntFl,
}
impl From<media_v2_entity> for MediaV2Entity {
fn from(v: media_v2_entity) -> Self {
let name: &[u8; 64] = unsafe { mem::transmute(&v.name) };
let name = CStr::from_bytes_until_nul(name).ok()
.and_then(|name| name.to_str().ok())
.map(String::from)
.ok_or_else(|| v.name.clone());
Self {
id: v.id,
name,
function: v.function.into(),
flags: MediaEntFl::from_bits_retain(v.flags),
}
}
}
impl Zeroed for media_v2_entity {}
#[repr(C)]
#[repr(packed)]
union MediaV2Interface_devnode {
devnode: MediaV2IntfDevnode,
raw: [u32; 16],
}
#[repr(C)]
#[repr(packed)]
pub struct media_v2_interface {
id: u32,
intf_type: u32,
flags: u32,
reserved: [u32; 9],
devnode: MediaV2Interface_devnode,
}
const_enum! {
enum MediaIntfT, sys, MEDIA_INTF_T_ {
DVB_FE,
DVB_DEMUX,
DVB_DVR,
DVB_CA,
DVB_NET,
V4L_VIDEO,
V4L_VBI,
V4L_RADIO,
V4L_SUBDEV,
V4L_SWRADIO,
V4L_TOUCH,
ALSA_PCM_CAPTURE,
ALSA_PCM_PLAYBACK,
ALSA_CONTROL,
}
}
#[derive(Debug)]
pub struct MediaV2Interface {
pub id: u32,
pub intf_type: MediaIntfT,
pub flags: u32,
pub devnode: MediaV2IntfDevnode,
}
impl From<&media_v2_interface> for MediaV2Interface {
fn from(v: &media_v2_interface) -> Self {
MediaV2Interface {
id: v.id,
intf_type: v.intf_type.into(),
flags: v.flags,
devnode: unsafe { v.devnode.devnode },
}
}
}
impl Zeroed for media_v2_interface {}
#[repr(C)]
#[repr(packed)]
#[derive(Clone, Copy, Debug)]
pub struct MediaV2IntfDevnode {
pub major: u32,
pub minor: u32,
}
#[derive(Debug)]
pub struct MediaV2Link {
id: u32,
source_id: u32,
sink_id: u32,
flags: MediaLnkFl,
}
bitflags! {
#[derive(Debug)]
pub struct MediaLnkFl: u32 {
const ENABLED = sys::MEDIA_LNK_FL_ENABLED;
const IMMUTABLE = sys::MEDIA_LNK_FL_IMMUTABLE;
const DYNAMIC = sys::MEDIA_LNK_FL_DYNAMIC;
const LINK_TYPE = sys::MEDIA_LNK_FL_LINK_TYPE;
const DATA_LINK = sys::MEDIA_LNK_FL_DATA_LINK;
const INTERFACE_LINK = sys::MEDIA_LNK_FL_INTERFACE_LINK;
const ANCILLARY_LINK = sys::MEDIA_LNK_FL_ANCILLARY_LINK;
}
}
impl From<media_v2_link> for MediaV2Link {
fn from(v: media_v2_link) ->Self {
Self {
id: v.id,
source_id: v.source_id,
sink_id: v.sink_id,
flags: MediaLnkFl::from_bits_retain(v.flags),
}
}
}
impl Zeroed for media_v2_link {}
nix::ioctl_readwrite!(media_ioc_g_topology, b'|', 0x04, media_v2_topology);
#[cfg(test)]
mod test {
use super::*;
#[test]
fn entity_bad_string() {
let v = [0xfeu8; mem::size_of::<media_v2_entity>()];
dbg!(v.len());
let entity: media_v2_entity = unsafe { mem::transmute_copy(&v) };
let entity = MediaV2Entity::from(entity);
match entity.name {
Err(_) => {},
Ok(_) => panic!("bad string should not get accepted"),
}
}
}