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) },
+    })
+}