use std::os::unix::io::AsRawFd;
use std::time::Instant;
use crate::error::syscall_ret;
use crate::reactor::Fd;
use crate::CoreError;
const O_RDWR: i32 = libc::O_RDWR;
const O_CLOEXEC: i32 = libc::O_CLOEXEC;
const DRM_IOCTL_BASE: u32 = 0x64; const DRM_IOCTL_WAIT_VBLANK_NR: u32 = 0x3a;
const DRM_VBLANK_RELATIVE: u32 = 0x1;
#[repr(C)]
#[derive(Clone, Copy)]
struct DrmWaitVBlank {
request_type: u32,
request_sequence: u32,
request_signal: u64, reply_sequence: u32,
reply_t_sec: u32,
reply_t_usec: u32,
}
const _: () = assert!(std::mem::size_of::<DrmWaitVBlank>() == 32);
pub struct DrmCard {
fd: Fd,
}
impl DrmCard {
pub fn open(path: &str) -> Result<Self, CoreError> {
let c_path = std::ffi::CString::new(path).map_err(|_| {
CoreError::sys(libc::EINVAL, "drm open path contains NUL")
})?;
let fd = unsafe { libc::open(c_path.as_ptr(), O_RDWR | O_CLOEXEC) };
syscall_ret(fd, "open(drm)")?;
let fd = unsafe { Fd::from_owned_raw_fd(fd, "drm") }?;
Ok(Self { fd })
}
pub fn wait_vblank(&self) -> Result<Instant, CoreError> {
let mut vblank = DrmWaitVBlank {
request_type: DRM_VBLANK_RELATIVE,
request_sequence: 1,
request_signal: 0,
reply_sequence: 0,
reply_t_sec: 0,
reply_t_usec: 0,
};
let nr = DRM_IOCTL_WAIT_VBLANK_NR;
let size = std::mem::size_of::<DrmWaitVBlank>() as u32;
let ioctl_nr: libc::Ioctl = ((3u32 << 30)
| (DRM_IOCTL_BASE << 8)
| (nr << 0)
| (size << 16)) as libc::Ioctl;
let ret = unsafe { libc::ioctl(self.fd.as_raw_fd(), ioctl_nr, &mut vblank) };
syscall_ret(ret, "DRM_IOCTL_WAIT_VBLANK")?;
Ok(Instant::now())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn wait_vblank_ioctl_matches_known_constant() {
let size = std::mem::size_of::<DrmWaitVBlank>() as u32;
let computed: libc::Ioctl =
((3u32 << 30) | (DRM_IOCTL_BASE << 8) | (0x3au32) | (size << 16)) as libc::Ioctl;
assert_eq!(computed, 0xC020643Au32 as libc::Ioctl);
assert_eq!(size, 32);
}
#[test]
fn vblank_request_struct_layout_is_32_bytes() {
assert_eq!(std::mem::size_of::<DrmWaitVBlank>(), 32);
}
}