onix 0.1.0

Decode image files using V4L2
Documentation
use crate::ioctl;
use crate::util;
use crate::Media;
use core::ffi::CStr;
use core::fmt;
use nix::errno::Errno;
use nix::libc::c_char;
use std::os::fd::AsRawFd;

/// Description of the media device.
#[repr(C)]
pub struct DeviceInfo {
    driver: [c_char; 16],
    model: [c_char; 32],
    serial: [c_char; 40],
    bus_info: [c_char; 32],
    media_version: u32,
    hw_revision: u32,
    driver_version: u32,
    reserved: [u32; 31],
}

impl fmt::Debug for DeviceInfo {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        fmt.debug_struct("DeviceInfo")
            .field("driver", &self.driver())
            .field("model", &self.model())
            .field("serial", &self.serial())
            .field("bus_info", &self.bus_info())
            .field("media_version", &self.media_version())
            .field("hw_revision", &self.hw_revision)
            .field("driver_version", &self.driver_version())
            .finish()
    }
}

impl Default for DeviceInfo {
    fn default() -> DeviceInfo {
        DeviceInfo {
            driver: [0; 16],
            model: [0; 32],
            serial: [0; 40],
            bus_info: [0; 32],
            media_version: 0,
            hw_revision: 0,
            driver_version: 0,
            reserved: [0; 31],
        }
    }
}

impl DeviceInfo {
    pub(crate) fn from_media(media: &Media) -> Result<DeviceInfo, Errno> {
        let mut data = DeviceInfo::default();
        unsafe { ioctl::device_info(media.as_raw_fd(), &mut data) }?;
        Ok(data)
    }

    // Safety: These from_utf8_unchecked() calls are safe, as the kernel will only ever give us
    // either ASCII or UTF-8.

    /// Name of the driver implementing the media API as an ASCII string. The driver version is
    /// stored in the driver_version field.
    ///
    /// Driver specific applications can use this information to verify the driver identity. It is
    /// also useful to work around known bugs, or to identify drivers in error reports.
    pub fn driver(&self) -> &str {
        unsafe {
            let c_str = CStr::from_ptr(self.driver.as_ptr());
            std::str::from_utf8_unchecked(c_str.to_bytes())
        }
    }

    /// Device model name as an UTF-8 string. The device version is stored in the device_version
    /// field and is not be appended to the model name.
    pub fn model(&self) -> &str {
        unsafe {
            let c_str = CStr::from_ptr(self.model.as_ptr());
            std::str::from_utf8_unchecked(c_str.to_bytes())
        }
    }

    /// Serial number as an ASCII string.
    pub fn serial(&self) -> &str {
        unsafe {
            let c_str = CStr::from_ptr(self.serial.as_ptr());
            std::str::from_utf8_unchecked(c_str.to_bytes())
        }
    }

    /// Location of the device in the system as an ASCII string. This includes the bus type name
    /// (PCI, USB, …) and a bus-specific identifier.
    pub fn bus_info(&self) -> &str {
        unsafe {
            let c_str = CStr::from_ptr(self.bus_info.as_ptr());
            std::str::from_utf8_unchecked(c_str.to_bytes())
        }
    }

    /// Media API version, already formatted as a kernel version.
    pub fn media_version(&self) -> String {
        util::format_kernel_version(self.media_version)
    }

    /// Hardware device revision in a driver-specific format.
    pub fn hw_revision(&self) -> u32 {
        self.hw_revision
    }

    /// Media device driver version, already formatted as a kernel version. Together with the
    /// driver field this identifies a particular driver.
    pub fn driver_version(&self) -> String {
        util::format_kernel_version(self.driver_version)
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    macro_rules! assert_size (
        ($t:ty, $sz:expr) => (
            assert_eq!(::std::mem::size_of::<$t>(), $sz);
        );
    );

    #[test]
    fn size() {
        assert_size!(DeviceInfo, 256);
    }
}