ioctl: add mmap() ioctl
Add the ability to map MMAP buffers into the user process. This is done
safely by returning a slice structure which we know is valid and will
unmap itself when destroyed.
diff --git a/src/ioctl.rs b/src/ioctl.rs
index 7bdbed5..f1ec147 100644
--- a/src/ioctl.rs
+++ b/src/ioctl.rs
@@ -10,6 +10,7 @@
mod dqbuf;
mod enum_fmt;
mod g_fmt;
+mod mmap;
mod qbuf;
mod querybuf;
mod querycap;
@@ -19,6 +20,7 @@
pub use dqbuf::*;
pub use enum_fmt::*;
pub use g_fmt::*;
+pub use mmap::*;
pub use qbuf::*;
pub use querybuf::*;
pub use querycap::*;
diff --git a/src/ioctl/mmap.rs b/src/ioctl/mmap.rs
new file mode 100644
index 0000000..a3d5429
--- /dev/null
+++ b/src/ioctl/mmap.rs
@@ -0,0 +1,74 @@
+use crate::Result;
+use std::os::unix::io::AsRawFd;
+use std::{
+ ops::{Index, IndexMut},
+ slice,
+};
+
+use nix::{
+ libc::{c_void, off_t, size_t},
+ sys::mman,
+};
+
+pub struct PlaneMapping<'a> {
+ pub data: &'a mut [u8],
+}
+
+impl<'a> PlaneMapping<'a> {
+ pub fn len(&self) -> usize {
+ self.data.len()
+ }
+
+ pub fn is_empty(&self) -> bool {
+ self.len() == 0
+ }
+
+ pub fn as_slice(&'a self) -> &'a [u8] {
+ self.data
+ }
+}
+
+impl<'a> Drop for PlaneMapping<'a> {
+ fn drop(&mut self) {
+ // Safe because the pointer and length were constructed in mmap() and
+ // are always valid.
+ unsafe { mman::munmap(self.data.as_mut_ptr() as *mut c_void, self.data.len()) }
+ .unwrap_or_else(|e| {
+ eprintln!("Error while unmapping plane: {}", e);
+ });
+ }
+}
+
+impl<'a> Index<usize> for PlaneMapping<'a> {
+ type Output = u8;
+ fn index(&self, index: usize) -> &Self::Output {
+ &self.data[index]
+ }
+}
+
+impl<'a> IndexMut<usize> for PlaneMapping<'a> {
+ fn index_mut(&mut self, index: usize) -> &mut Self::Output {
+ &mut self.data[index]
+ }
+}
+
+// TODO should be unsafe because the mapping can be used after a buffer is queued?
+// Or not, since this cannot cause a crash...
+pub fn mmap<'a, F: AsRawFd>(fd: &F, mem_offset: u32, length: u32) -> Result<PlaneMapping<'a>> {
+ let data = unsafe {
+ mman::mmap(
+ std::ptr::null_mut::<c_void>(),
+ length as size_t,
+ mman::ProtFlags::PROT_READ | mman::ProtFlags::PROT_WRITE,
+ mman::MapFlags::MAP_SHARED,
+ fd.as_raw_fd(),
+ mem_offset as off_t,
+ )
+ }?;
+
+ Ok(PlaneMapping {
+ // Safe because we know the pointer is valid and has enough data mapped
+ // to cover the length.
+ data: unsafe { slice::from_raw_parts_mut(data as *mut u8, length as usize) },
+ })
+}