use dma_buf::DmaBuf;
use parking_lot::{ArcRwLockWriteGuard, RwLock, RawRwLock};
use std::fmt;
use std::{io, mem};
use std::ops;
use std::os::fd::{FromRawFd, OwnedFd};
use std::sync::Arc;
use tracing::error;
use crate::buffer;
use crate::device::Handle;
use crate::memory::Memory;
use crate::v4l2;
use crate::v4l_sys::{v4l2_format, v4l2_requestbuffers, v4l2_exportbuffer};
pub trait EntryBuffer {
fn from_owned_fd(fd: OwnedFd) -> Self;
}
impl EntryBuffer for DmaBuf {
fn from_owned_fd(fd: OwnedFd) -> Self {
fd.into()
}
}
pub trait DmabufRelease {
fn release(&mut self) -> io::Result<()>;
}
pub struct Arena<T: EntryBuffer> where Self: DmabufRelease {
handle: Arc<Handle>,
bufs: Vec<T>,
buf_type: buffer::Type,
}
impl<T: EntryBuffer> Arena<T> where Self: DmabufRelease {
pub fn new(handle: Arc<Handle>, buf_type: buffer::Type) -> Self {
Arena {
handle,
bufs: Vec::new(),
buf_type,
}
}
fn requestbuffers_desc(&self) -> v4l2_requestbuffers {
v4l2_requestbuffers {
type_: self.buf_type as u32,
..unsafe { mem::zeroed() }
}
}
pub fn allocate(&mut self, count: u32) -> io::Result<u32> {
let mut v4l2_fmt = v4l2_format {
type_: self.buf_type as u32,
..unsafe { mem::zeroed() }
};
unsafe {
v4l2::ioctl(
self.handle.fd(),
v4l2::vidioc::VIDIOC_G_FMT,
&mut v4l2_fmt as *mut _ as *mut std::os::raw::c_void,
)?;
}
let mut v4l2_reqbufs = v4l2_requestbuffers {
count,
memory: Memory::Mmap as u32,
..self.requestbuffers_desc()
};
unsafe {
v4l2::ioctl(
self.handle.fd(),
v4l2::vidioc::VIDIOC_REQBUFS,
&mut v4l2_reqbufs as *mut _ as *mut std::os::raw::c_void,
)?;
}
for index in 0..v4l2_reqbufs.count {
let mut v4l2_exportbuf = v4l2_exportbuffer {
index,
type_: self.buf_type as u32,
flags: libc::O_RDWR as _,
..unsafe { mem::zeroed() }
};
let fd = unsafe {
v4l2::ioctl(
self.handle.fd(),
v4l2::vidioc::VIDIOC_EXPBUF,
&mut v4l2_exportbuf as *mut _ as *mut std::os::raw::c_void,
)?;
OwnedFd::from_raw_fd(v4l2_exportbuf.fd)
};
self.bufs.push(T::from_owned_fd(fd));
}
let mut v4l2_reqbufs = v4l2_requestbuffers {
count,
memory: Memory::DmaBuf as u32,
..self.requestbuffers_desc()
};
unsafe {
v4l2::ioctl(
self.handle.fd(),
v4l2::vidioc::VIDIOC_REQBUFS,
&mut v4l2_reqbufs as *mut _ as *mut std::os::raw::c_void,
)?;
}
Ok(v4l2_reqbufs.count)
}
pub fn len(&self) -> usize {
self.bufs.len()
}
}
impl Arena<DmaBuf> {
pub fn get_dmabuf(&mut self, index: usize) -> Result<&DmaBuf, &'static str> {
self.bufs.get(index)
.ok_or("Index higher than available buffers")
}
pub fn get_dmabuf_mut(&mut self, index: usize) -> Result<&mut DmaBuf, &'static str> {
self.bufs.get_mut(index)
.ok_or("Index higher than available buffers")
}
}
impl DmabufRelease for Arena<DmaBuf> {
fn release(&mut self) -> io::Result<()> {
let mut v4l2_reqbufs = v4l2_requestbuffers {
count: 0,
memory: Memory::DmaBuf as u32,
..self.requestbuffers_desc()
};
unsafe {
v4l2::ioctl(
self.handle.fd(),
v4l2::vidioc::VIDIOC_REQBUFS,
&mut v4l2_reqbufs as *mut _ as *mut std::os::raw::c_void,
)
}
}
}
#[derive(Clone)]
pub struct DmaBufProtected(Arc<RwLock<DmaBuf>>);
impl ops::Deref for DmaBufProtected {
type Target = Arc<RwLock<DmaBuf>>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl fmt::Debug for DmaBufProtected {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("DmaBufProtected")
.field(&self.0.data_ptr())
.finish()
}
}
pub struct EntryDmaBufProtected {
id: *mut DmaBuf,
buf: Option<ArcRwLockWriteGuard<RawRwLock, DmaBuf>>,
}
impl EntryBuffer for EntryDmaBufProtected {
fn from_owned_fd(fd: OwnedFd) -> Self {
let buf = Arc::new(RwLock::new(DmaBuf::from(fd)));
Self {
id: buf.data_ptr(),
buf: Some(buf.write_arc()),
}
}
}
impl fmt::Debug for EntryDmaBufProtected {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("EntryDmaBufProtected")
.field(&self.id)
.field(&self.buf.as_ref().map(|_| ()))
.finish()
}
}
pub type ManuallyManaged = Arena<EntryDmaBufProtected>;
impl ManuallyManaged {
pub fn replace_buffer(&mut self, buf: DmaBufProtected) -> Result<usize, (DmaBufProtected, &'static str)> {
let index = self.bufs.iter().position(|entry| entry.id == buf.0.data_ptr());
match index {
None => Err((buf, "Buffer not from this stream")),
Some(index) => {
self.bufs[index] = EntryDmaBufProtected {
id: buf.0.data_ptr(),
buf: Some(buf.0.write_arc()),
};
Ok(index)
}
}
}
pub fn take_buffer(&mut self, index: usize) -> Result<DmaBufProtected, &'static str> {
let rw_buf = self.bufs.get_mut(index)
.ok_or("Index higher than available buffers")?
.buf.take()
.ok_or("No buffer was stored at this index")?;
Ok(DmaBufProtected(ArcRwLockWriteGuard::rwlock(&rw_buf).clone()))
}
pub fn get_dmabuf_mut(&mut self, index: usize) -> Result<&mut DmaBuf, &'static str> {
Ok(
self.bufs.get_mut(index)
.ok_or("Index higher than available buffers")?
.buf.as_mut()
.ok_or("No buffer was stored at this index")?
)
}
pub unsafe fn force_release(&mut self) -> io::Result<()> {
let mut v4l2_reqbufs = v4l2_requestbuffers {
count: 0,
memory: Memory::DmaBuf as u32,
..self.requestbuffers_desc()
};
unsafe {
v4l2::ioctl(
self.handle.fd(),
v4l2::vidioc::VIDIOC_REQBUFS,
&mut v4l2_reqbufs as *mut _ as *mut std::os::raw::c_void,
)
}
}
}
impl DmabufRelease for ManuallyManaged {
fn release(&mut self) -> io::Result<()> {
let free_buffer = self.bufs.iter()
.find(|EntryDmaBufProtected { buf, .. }| buf.is_none());
if let Some(_) = free_buffer {
return Err(io::Error::other("Busy: not all buffers were returned"));
}
unsafe { self.force_release() }
}
}
impl<T: EntryBuffer> Drop for Arena<T> where Self: DmabufRelease {
fn drop(&mut self) {
if self.bufs.is_empty() {
return;
}
if let Err(e) = self.release() {
if let Some(code) = e.raw_os_error() {
if code == 19 {
return;
}
}
error!("DMABUF leak: {:?}", e);
}
}
}