summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock8
-rw-r--r--Cargo.toml1
-rw-r--r--gpu_buffer/Cargo.toml7
-rw-r--r--gpu_buffer/src/lib.rs553
-rw-r--r--gpu_buffer/src/raw.rs209
5 files changed, 778 insertions, 0 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 2b8a4fa..33f2d56 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -52,6 +52,7 @@ dependencies = [
  "data_model 0.1.0",
  "device_manager 0.1.0",
  "devices 0.1.0",
+ "gpu_buffer 0.1.0",
  "io_jail 0.1.0",
  "kernel_cmdline 0.1.0",
  "kernel_loader 0.1.0",
@@ -134,6 +135,13 @@ version = "0.3.54"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
+name = "gpu_buffer"
+version = "0.1.0"
+dependencies = [
+ "data_model 0.1.0",
+]
+
+[[package]]
 name = "io_jail"
 version = "0.1.0"
 dependencies = [
diff --git a/Cargo.toml b/Cargo.toml
index 1771719..b6db1eb 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -16,6 +16,7 @@ plugin = ["plugin_proto", "crosvm_plugin", "protobuf"]
 arch = { path = "arch" }
 devices = { path = "devices" }
 device_manager = { path = "device_manager" }
+gpu_buffer = { path = "gpu_buffer", optional = true }
 io_jail = { path = "io_jail" }
 kvm = { path = "kvm" }
 kvm_sys = { path = "kvm_sys" }
diff --git a/gpu_buffer/Cargo.toml b/gpu_buffer/Cargo.toml
new file mode 100644
index 0000000..11b47c4
--- /dev/null
+++ b/gpu_buffer/Cargo.toml
@@ -0,0 +1,7 @@
+[package]
+name = "gpu_buffer"
+version = "0.1.0"
+authors = ["The Chromium OS Authors"]
+
+[dependencies]
+data_model = { path = "../data_model" }
diff --git a/gpu_buffer/src/lib.rs b/gpu_buffer/src/lib.rs
new file mode 100644
index 0000000..6d0cfa9
--- /dev/null
+++ b/gpu_buffer/src/lib.rs
@@ -0,0 +1,553 @@
+// Copyright 2018 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+//! A crate for creating [DRM](https://en.wikipedia.org/wiki/Direct_Rendering_Manager) managed
+//! buffer objects. Such objects are useful for exporting as DMABUFs/prime FDs, texturing, render
+//! targets, memory mapping, and scanout.
+//!
+//! # Examples
+//!
+//! ```rust
+//! # use std::error::Error;
+//! # use std::fs::File;
+//! # use std::result::Result;
+//! # use gpu_buffer::*;
+//! # fn test() -> Result<(), Box<Error>> {
+//! let drm_card = File::open("/dev/dri/card0")?;
+//! let device = Device::new(drm_card).map_err(|_| "failed to create device")?;
+//! let bo = device
+//!     .create_buffer(1024,
+//!                    512,
+//!                    Format::new(b'X', b'R', b'2', b'4'),
+//!                    Flags::empty().use_scanout(true))
+//!     .map_err(|_| "failed to create buffer")?;
+//! assert_eq!(bo.width(), 1024);
+//! assert_eq!(bo.height(), 512);
+//! assert_eq!(bo.format(), Format::new(b'X', b'R', b'2', b'4'));
+//! assert_eq!(bo.num_planes(), 1);
+//! # Ok(())
+//! # }
+//! ```
+
+extern crate data_model;
+
+mod raw;
+
+use std::os::raw::c_void;
+use std::fmt;
+use std::cmp::min;
+use std::fs::File;
+use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
+use std::ptr::{copy_nonoverlapping, null_mut};
+use std::rc::Rc;
+use std::result::Result;
+
+use data_model::VolatileSlice;
+
+use raw::*;
+
+const MAP_FAILED: *mut c_void = (-1isize as *mut _);
+
+/// A [fourcc](https://en.wikipedia.org/wiki/FourCC) format identifier.
+#[derive(Copy, Clone, Eq, PartialEq)]
+pub struct Format(u32);
+
+impl Format {
+    /// Constructs a format identifer using a fourcc byte sequence.
+    ///
+    /// # Examples
+    ///
+    /// ```rust
+    /// use gpu_buffer::Format;
+    ///
+    /// let format = Format::new(b'X', b'R', b'2', b'4');
+    /// println!("format: {:?}", format);
+    /// ```
+    #[inline(always)]
+    pub fn new(a: u8, b: u8, c: u8, d: u8) -> Format {
+        Format(a as u32 | (b as u32) << 8 | (c as u32) << 16 | (d as u32) << 24)
+    }
+
+    /// Returns the fourcc code as a sequence of bytes.
+    #[inline(always)]
+    pub fn to_bytes(&self) -> [u8; 4] {
+        let f = self.0;
+        [f as u8, (f >> 8) as u8, (f >> 16) as u8, (f >> 24) as u8]
+    }
+}
+
+impl From<u32> for Format {
+    fn from(u: u32) -> Format {
+        Format(u)
+    }
+}
+
+impl From<Format> for u32 {
+    fn from(f: Format) -> u32 {
+        f.0
+    }
+}
+
+impl fmt::Debug for Format {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        let b = self.to_bytes();
+        if b.iter().all(u8::is_ascii_graphic) {
+            write!(f,
+                   "fourcc({}{}{}{})",
+                   b[0] as char,
+                   b[1] as char,
+                   b[2] as char,
+                   b[3] as char)
+        } else {
+            write!(f,
+                   "fourcc(0x{:02x}{:02x}{:02x}{:02x})",
+                   b[0],
+                   b[1],
+                   b[2],
+                   b[3])
+        }
+    }
+}
+
+/// Usage flags for constructing a buffer object.
+#[derive(Copy, Clone, Eq, PartialEq)]
+pub struct Flags(u32);
+
+impl Flags {
+    /// Returns empty set of flags.
+    #[inline(always)]
+    pub fn empty() -> Flags {
+        Flags(0)
+    }
+
+    /// Returns the given set of raw `GBM_BO` flags wrapped in a `Flags` struct.
+    #[inline(always)]
+    pub fn new(raw: u32) -> Flags {
+        Flags(raw)
+    }
+
+    /// Sets the scanout flag's presence
+    #[inline(always)]
+    pub fn use_scanout(self, e: bool) -> Flags {
+        if e {
+            Flags(self.0 | GBM_BO_USE_SCANOUT)
+        } else {
+            Flags(self.0 & !GBM_BO_USE_SCANOUT)
+        }
+    }
+
+    /// Sets the cursor flag's presence
+    #[inline(always)]
+    pub fn use_cursor(self, e: bool) -> Flags {
+        if e {
+            Flags(self.0 | GBM_BO_USE_CURSOR)
+        } else {
+            Flags(self.0 & !GBM_BO_USE_CURSOR)
+        }
+    }
+
+    /// Sets the cursor 64x64 flag's presence
+    #[inline(always)]
+    pub fn use_cursor64(self, e: bool) -> Flags {
+        if e {
+            Flags(self.0 | GBM_BO_USE_CURSOR_64X64)
+        } else {
+            Flags(self.0 & !GBM_BO_USE_CURSOR_64X64)
+        }
+    }
+
+    /// Sets the rendering flag's presence
+    #[inline(always)]
+    pub fn use_rendering(self, e: bool) -> Flags {
+        if e {
+            Flags(self.0 | GBM_BO_USE_RENDERING)
+        } else {
+            Flags(self.0 & !GBM_BO_USE_RENDERING)
+        }
+    }
+
+    /// Sets the linear flag's presence
+    #[inline(always)]
+    pub fn use_linear(self, e: bool) -> Flags {
+        if e {
+            Flags(self.0 | GBM_BO_USE_LINEAR)
+        } else {
+            Flags(self.0 & !GBM_BO_USE_LINEAR)
+        }
+    }
+
+    /// Sets the texturing flag's presence
+    #[inline(always)]
+    pub fn use_texturing(self, e: bool) -> Flags {
+        if e {
+            Flags(self.0 | GBM_BO_USE_TEXTURING)
+        } else {
+            Flags(self.0 & !GBM_BO_USE_TEXTURING)
+        }
+    }
+}
+
+
+struct DeviceInner {
+    _fd: File,
+    gbm: *mut gbm_device,
+}
+
+impl Drop for DeviceInner {
+    fn drop(self: &mut DeviceInner) {
+        // Safe because DeviceInner is only constructed with a valid gbm_device.
+        unsafe {
+            gbm_device_destroy(self.gbm);
+        }
+    }
+}
+
+/// A device capable of allocating `Buffer`.
+#[derive(Clone)]
+pub struct Device(Rc<DeviceInner>);
+
+impl Device {
+    /// Returns a new `Device` using the given `fd` opened from a device in `/dev/dri/`.
+    pub fn new(fd: File) -> Result<Device, ()> {
+        // gbm_create_device is safe to call with a valid fd, and we check that a valid one is
+        // returned. The FD is not of the appropriate kind (i.e. not a DRM device),
+        // gbm_create_device should reject it.
+        let gbm = unsafe { gbm_create_device(fd.as_raw_fd()) };
+        if gbm.is_null() {
+            Err(())
+        } else {
+            Ok(Device(Rc::new(DeviceInner { _fd: fd, gbm })))
+        }
+    }
+
+    /// Creates a new buffer with the given metadata.
+    pub fn create_buffer(&self,
+                         width: u32,
+                         height: u32,
+                         format: Format,
+                         usage: Flags)
+                         -> Result<Buffer, ()> {
+        // This is safe because only a valid gbm_device is used and the return value is checked.
+        let bo = unsafe { gbm_bo_create(self.0.gbm, width, height, format.0, usage.0) };
+        if bo.is_null() {
+            Err(())
+        } else {
+            Ok(Buffer(bo, self.clone()))
+        }
+    }
+}
+
+/// An allocation from a `Device`.
+pub struct Buffer(*mut gbm_bo, Device);
+
+impl Buffer {
+    /// The device
+    pub fn device(&self) -> &Device {
+        &self.1
+    }
+
+    /// Width in pixels.
+    pub fn width(&self) -> u32 {
+        // This is always safe to call with a valid gbm_bo pointer.
+        unsafe { gbm_bo_get_width(self.0) }
+    }
+
+    /// Height in pixels.
+    pub fn height(&self) -> u32 {
+        // This is always safe to call with a valid gbm_bo pointer.
+        unsafe { gbm_bo_get_height(self.0) }
+    }
+
+    /// Length in bytes of one row of the buffer.
+    pub fn stride(&self) -> u32 {
+        // This is always safe to call with a valid gbm_bo pointer.
+        unsafe { gbm_bo_get_stride(self.0) }
+    }
+
+    /// Length in bytes of the stride or tiling.
+    pub fn stride_or_tiling(&self) -> u32 {
+        // This is always safe to call with a valid gbm_bo pointer.
+        unsafe { gbm_bo_get_stride_or_tiling(self.0) }
+    }
+
+    /// `Format` of the buffer.
+    pub fn format(&self) -> Format {
+        // This is always safe to call with a valid gbm_bo pointer.
+        unsafe { Format(gbm_bo_get_format(self.0)) }
+    }
+
+    /// Format modifier flags for the buffer.
+    pub fn format_modifier(&self) -> u64 {
+        // This is always safe to call with a valid gbm_bo pointer.
+        unsafe { gbm_bo_get_format_modifier(self.0) }
+    }
+
+    /// Number of planes present in this buffer.
+    pub fn num_planes(&self) -> usize {
+        // This is always safe to call with a valid gbm_bo pointer.
+        unsafe { gbm_bo_get_num_planes(self.0) }
+    }
+
+    /// Exports a new dmabuf/prime file descriptor for the given plane.
+    pub fn export_plane_fd(&self, plane: usize) -> Result<File, i32> {
+        // This is always safe to call with a valid gbm_bo pointer.
+        match unsafe { gbm_bo_get_plane_fd(self.0, plane) } {
+            fd if fd >= 0 => Ok(unsafe { File::from_raw_fd(fd) }),
+            ret => Err(ret),
+        }
+    }
+
+    /// Reads the given subsection of the buffer to `dst`.
+    pub fn read_to_volatile(&self,
+                            x: u32,
+                            y: u32,
+                            width: u32,
+                            height: u32,
+                            plane: usize,
+                            dst: VolatileSlice)
+                            -> Result<(), ()> {
+        let mut stride = 0;
+        let mut map_data = null_mut();
+        // Safe because only a valid gbm_bo object is used and the return value is checked. Only
+        // pointers coerced from stack references are used for returned values, and we trust gbm to
+        // only write as many bytes as the size of the pointed to values.
+        let mapping = unsafe {
+            gbm_bo_map(self.0,
+                       x,
+                       y,
+                       width,
+                       height,
+                       GBM_BO_TRANSFER_READ,
+                       &mut stride,
+                       &mut map_data,
+                       plane)
+        };
+        if mapping == MAP_FAILED {
+            return Err(());
+        }
+
+        let copy_size = (y as u64) * (stride as u64);
+
+        let res = if copy_size <= dst.size() {
+            // The two buffers can not be overlapping because we just made a new mapping in this
+            // scope.
+            unsafe {
+                copy_nonoverlapping(mapping as *mut u8, dst.as_ptr(), copy_size as usize);
+            }
+            Ok(())
+        } else {
+            Err(())
+        };
+
+        // safe because the gbm_bo is assumed to be valid and the map_data is the same one given by
+        // gbm_bo_map.
+        unsafe {
+            gbm_bo_unmap(self.0, map_data);
+        }
+
+        res
+    }
+
+    /// Writes to the given subsection of the buffer from `src`.
+    pub fn write_from_slice(&self,
+                            x: u32,
+                            y: u32,
+                            width: u32,
+                            height: u32,
+                            plane: usize,
+                            src: &[u8])
+                            -> Result<(), ()> {
+        let mut stride = 0;
+        let mut map_data = null_mut();
+        // Safe because only a valid gbm_bo object is used and the return value is checked. Only
+        // pointers coerced from stack references are used for returned values, and we trust gbm to
+        // only write as many bytes as the size of the pointed to values.
+        let mapping = unsafe {
+            gbm_bo_map(self.0,
+                       x,
+                       y,
+                       width,
+                       height,
+                       GBM_BO_TRANSFER_WRITE,
+                       &mut stride,
+                       &mut map_data,
+                       plane)
+        };
+        if mapping == MAP_FAILED {
+            return Err(());
+        }
+
+        let copy_size = (height as u64) * (stride as u64);
+        let copy_sg_size = min(src.len() as u64, copy_size);
+
+        // The two buffers can not be overlapping because we just made a new mapping in this scope.
+        unsafe {
+            copy_nonoverlapping(src.as_ptr(), mapping as *mut u8, copy_sg_size as usize);
+        }
+
+        // safe because the gbm_bo is assumed to be valid and the map_data is the same one given by
+        // gbm_bo_map.
+        unsafe {
+            gbm_bo_unmap(self.0, map_data);
+        }
+
+        Ok(())
+    }
+
+    /// Writes to the given subsection of the buffer from `sgs`.
+    pub fn write_from_sg<'a, S: Iterator<Item = VolatileSlice<'a>>>(&self,
+                                                                    x: u32,
+                                                                    y: u32,
+                                                                    width: u32,
+                                                                    height: u32,
+                                                                    plane: usize,
+                                                                    sgs: S)
+                                                                    -> Result<(), ()> {
+        let mut stride = 0;
+        let mut map_data = null_mut();
+        // Safe because only a valid gbm_bo object is used and the return value is checked. Only
+        // pointers coerced from stack references are used for returned values, and we trust gbm to
+        // only write as many bytes as the size of the pointed to values.
+        let mut mapping = unsafe {
+            gbm_bo_map(self.0,
+                       x,
+                       y,
+                       width,
+                       height,
+                       GBM_BO_TRANSFER_WRITE,
+                       &mut stride,
+                       &mut map_data,
+                       plane)
+        };
+        if mapping == MAP_FAILED {
+            return Err(());
+        }
+
+        let mut copy_size = (height as u64) * (stride as u64);
+
+        for sg in sgs {
+            let copy_sg_size = min(sg.size(), copy_size);
+            // The two buffers can not be overlapping because we just made a new mapping in this
+            // scope.
+            unsafe {
+                copy_nonoverlapping(sg.as_ptr(), mapping as *mut u8, copy_sg_size as usize);
+            }
+
+            mapping = mapping.wrapping_offset(copy_sg_size as isize);
+            copy_size -= copy_sg_size;
+            if copy_size == 0 {
+                break;
+            }
+        }
+
+        // safe because the gbm_bo is assumed to be valid and the map_data is the same one given by
+        // gbm_bo_map.
+        unsafe {
+            gbm_bo_unmap(self.0, map_data);
+        }
+
+        Ok(())
+    }
+}
+
+impl Drop for Buffer {
+    fn drop(&mut self) {
+        // This is always safe to call with a valid gbm_bo pointer.
+        unsafe { gbm_bo_destroy(self.0) }
+    }
+}
+
+impl AsRawFd for Buffer {
+    fn as_raw_fd(&self) -> RawFd {
+        // This is always safe to call with a valid gbm_bo pointer.
+        unsafe { gbm_bo_get_fd(self.0) }
+    }
+}
+
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use std::fmt::Write;
+    use data_model::VolatileMemory;
+
+    #[test]
+    fn format_debug() {
+        let f = Format::new(b'X', b'R', b'2', b'4');
+        let mut buf = String::new();
+        write!(&mut buf, "{:?}", f).unwrap();
+        assert_eq!(buf, "fourcc(XR24)");
+
+        let f = Format::new(0, 1, 2, 16);
+        let mut buf = String::new();
+        write!(&mut buf, "{:?}", f).unwrap();
+        assert_eq!(buf, "fourcc(0x00010210)");
+    }
+
+    #[test]
+    #[ignore] // no access to /dev/dri
+    fn open_device() {
+        let drm_card = File::open("/dev/dri/card0").expect("failed to open card");
+        Device::new(drm_card).expect("failed to create device with card");
+    }
+
+    #[test]
+    #[ignore] // no access to /dev/dri
+    fn create_buffer() {
+        let drm_card = File::open("/dev/dri/card0").expect("failed to open card");
+        let device = Device::new(drm_card).expect("failed to create device with card");
+        let bo = device
+            .create_buffer(1024,
+                           512,
+                           Format::new(b'X', b'R', b'2', b'4'),
+                           Flags::empty().use_scanout(true))
+            .expect("failed to create buffer");
+
+        assert_eq!(bo.width(), 1024);
+        assert_eq!(bo.height(), 512);
+        assert_eq!(bo.format(), Format::new(b'X', b'R', b'2', b'4'));
+        assert_eq!(bo.num_planes(), 1);
+    }
+
+    #[test]
+    #[ignore] // no access to /dev/dri
+    fn export_buffer() {
+        let drm_card = File::open("/dev/dri/card0").expect("failed to open card");
+        let device = Device::new(drm_card).expect("failed to create device with card");
+        let bo = device
+            .create_buffer(1024,
+                           1024,
+                           Format::new(b'X', b'R', b'2', b'4'),
+                           Flags::empty().use_scanout(true))
+            .expect("failed to create buffer");
+        bo.export_plane_fd(0).expect("failed to export plane");
+    }
+
+
+    #[test]
+    #[ignore] // no access to /dev/dri
+    fn buffer_transfer() {
+        let drm_card = File::open("/dev/dri/card0").expect("failed to open card");
+        let device = Device::new(drm_card).expect("failed to create device with card");
+        let bo = device
+            .create_buffer(1024,
+                           1024,
+                           Format::new(b'X', b'R', b'2', b'4'),
+                           Flags::empty().use_scanout(true).use_linear(true))
+            .expect("failed to create buffer");
+        let mut dst: Vec<u8> = Vec::new();
+        dst.resize((bo.stride() * bo.height()) as usize, 0x4A);
+        let dst_len = dst.len() as u64;
+        bo.write_from_slice(0, 0, 1024, 1024, 0, dst.as_slice())
+            .expect("failed to read bo");
+        bo.read_to_volatile(0,
+                              0,
+                              1024,
+                              1024,
+                              0,
+                              dst.as_mut_slice().get_slice(0, dst_len).unwrap())
+            .expect("failed to read bo");
+        assert!(dst.iter().all(|&x| x == 0x4A));
+    }
+}
diff --git a/gpu_buffer/src/raw.rs b/gpu_buffer/src/raw.rs
new file mode 100644
index 0000000..e5e258a
--- /dev/null
+++ b/gpu_buffer/src/raw.rs
@@ -0,0 +1,209 @@
+// Copyright 2018 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Generated with bindgen --whitelist-function='gbm_.*' --whitelist-type='gbm_.*' minigbm/gbm.h
+// Hand-modified by zachr
+
+#![allow(dead_code)]
+
+use std::os::raw::{c_int, c_char, c_void};
+
+/// \file gbm.h
+/// \brief Generic Buffer Manager
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct gbm_device {
+    _unused: [u8; 0],
+}
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct gbm_bo {
+    _unused: [u8; 0],
+}
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct gbm_surface {
+    _unused: [u8; 0],
+}
+/// Abstraction representing the handle to a buffer allocated by the
+/// manager
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub union gbm_bo_handle {
+    pub ptr: *mut c_void,
+    pub s32: i32,
+    pub u32: u32,
+    pub s64: i64,
+    pub u64: u64,
+    _bindgen_union_align: u64,
+}
+
+/// Buffer is going to be presented to the screen using an API such as KMS
+pub const GBM_BO_USE_SCANOUT: gbm_bo_flags = 1;
+/// Buffer is going to be used as cursor
+pub const GBM_BO_USE_CURSOR: gbm_bo_flags = 2;
+/// Deprecated
+pub const GBM_BO_USE_CURSOR_64X64: gbm_bo_flags = 2;
+/// Buffer is to be used for rendering - for example it is going to be used
+/// as the storage for a color buffer
+pub const GBM_BO_USE_RENDERING: gbm_bo_flags = 4;
+/// Deprecated
+pub const GBM_BO_USE_WRITE: gbm_bo_flags = 8;
+/// Buffer is guaranteed to be laid out linearly in memory. That is, the
+/// buffer is laid out as an array with 'height' blocks, each block with
+/// length 'stride'. Each stride is in the same order as the rows of the
+/// buffer. This is intended to be used with buffers that will be accessed
+/// via dma-buf mmap().
+pub const GBM_BO_USE_LINEAR: gbm_bo_flags = 16;
+/// The buffer will be used as a texture that will be sampled from.
+pub const GBM_BO_USE_TEXTURING: gbm_bo_flags = 32;
+/// The buffer will be written to by a camera subsystem.
+pub const GBM_BO_USE_CAMERA_WRITE: gbm_bo_flags = 64;
+/// The buffer will be read from by a camera subsystem.
+pub const GBM_BO_USE_CAMERA_READ: gbm_bo_flags = 128;
+/// Buffer inaccessible to unprivileged users.
+pub const GBM_BO_USE_PROTECTED: gbm_bo_flags = 256;
+/// These flags specify the frequency of software access. These flags do not
+/// guarantee the buffer is linear, but do guarantee gbm_bo_map(..) will
+/// present a linear view.
+pub const GBM_BO_USE_SW_READ_OFTEN: gbm_bo_flags = 512;
+/// These flags specify the frequency of software access. These flags do not
+/// guarantee the buffer is linear, but do guarantee gbm_bo_map(..) will
+/// present a linear view.
+pub const GBM_BO_USE_SW_READ_RARELY: gbm_bo_flags = 1024;
+/// These flags specify the frequency of software access. These flags do not
+/// guarantee the buffer is linear, but do guarantee gbm_bo_map(..) will
+/// present a linear view.
+pub const GBM_BO_USE_SW_WRITE_OFTEN: gbm_bo_flags = 2048;
+/// These flags specify the frequency of software access. These flags do not
+/// guarantee the buffer is linear, but do guarantee gbm_bo_map(..) will
+/// present a linear view.
+pub const GBM_BO_USE_SW_WRITE_RARELY: gbm_bo_flags = 4096;
+/// Flags to indicate the intended use for the buffer - these are passed into
+/// gbm_bo_create(). The caller must set the union of all the flags that are
+/// appropriate
+///
+/// \sa Use gbm_device_is_format_supported() to check if the combination of format
+/// and use flags are supported
+#[allow(non_camel_case_types)]
+pub type gbm_bo_flags = u32;
+
+#[link(name = "gbm")]
+extern "C" {
+    pub fn gbm_device_get_fd(gbm: *mut gbm_device) -> c_int;
+    pub fn gbm_device_get_backend_name(gbm: *mut gbm_device) -> *const c_char;
+    pub fn gbm_device_is_format_supported(gbm: *mut gbm_device, format: u32, usage: u32) -> c_int;
+    pub fn gbm_device_destroy(gbm: *mut gbm_device);
+    pub fn gbm_create_device(fd: c_int) -> *mut gbm_device;
+    pub fn gbm_bo_create(gbm: *mut gbm_device,
+                         width: u32,
+                         height: u32,
+                         format: u32,
+                         flags: u32)
+                         -> *mut gbm_bo;
+    pub fn gbm_bo_create_with_modifiers(gbm: *mut gbm_device,
+                                        width: u32,
+                                        height: u32,
+                                        format: u32,
+                                        modifiers: *const u64,
+                                        count: u32)
+                                        -> *mut gbm_bo;
+}
+
+#[repr(C)]
+#[derive(Debug, Copy, Clone )]
+pub struct gbm_import_fd_data {
+    pub fd: c_int,
+    pub width: u32,
+    pub height: u32,
+    pub stride: u32,
+    pub format: u32,
+}
+
+#[repr(C)]
+#[derive(Debug, Copy, Clone )]
+pub struct gbm_import_fd_planar_data {
+    pub fds: [c_int; 4usize],
+    pub width: u32,
+    pub height: u32,
+    pub format: u32,
+    pub strides: [u32; 4usize],
+    pub offsets: [u32; 4usize],
+    pub format_modifiers: [u64; 4usize],
+}
+
+extern "C" {
+    pub fn gbm_bo_import(gbm: *mut gbm_device,
+                         type_: u32,
+                         buffer: *mut c_void,
+                         usage: u32)
+                         -> *mut gbm_bo;
+}
+
+/// Buffer contents read back (or accessed directly) at transfer
+/// create time.
+pub const GBM_BO_TRANSFER_READ: gbm_bo_transfer_flags = 1;
+/// Buffer contents will be written back at unmap time
+/// (or modified as a result of being accessed directly).
+pub const GBM_BO_TRANSFER_WRITE: gbm_bo_transfer_flags = 2;
+/// Read/modify/write
+pub const GBM_BO_TRANSFER_READ_WRITE: gbm_bo_transfer_flags = 3;
+
+/// Flags to indicate the type of mapping for the buffer - these are
+/// passed into gbm_bo_map(). The caller must set the union of all the
+/// flags that are appropriate.
+///
+/// These flags are independent of the GBM_BO_USE_* creation flags. However,
+/// mapping the buffer may require copying to/from a staging buffer.
+///
+/// See also: pipe_transfer_usage
+#[allow(non_camel_case_types)]
+pub type gbm_bo_transfer_flags = u32;
+
+extern "C" {
+    pub fn gbm_bo_map(bo: *mut gbm_bo,
+                      x: u32,
+                      y: u32,
+                      width: u32,
+                      height: u32,
+                      flags: u32,
+                      stride: *mut u32,
+                      map_data: *mut *mut c_void,
+                      plane: usize)
+                      -> *mut c_void;
+    pub fn gbm_bo_unmap(bo: *mut gbm_bo, map_data: *mut c_void);
+    pub fn gbm_bo_get_width(bo: *mut gbm_bo) -> u32;
+    pub fn gbm_bo_get_height(bo: *mut gbm_bo) -> u32;
+    pub fn gbm_bo_get_stride(bo: *mut gbm_bo) -> u32;
+    pub fn gbm_bo_get_stride_or_tiling(bo: *mut gbm_bo) -> u32;
+    pub fn gbm_bo_get_format(bo: *mut gbm_bo) -> u32;
+    pub fn gbm_bo_get_format_modifier(bo: *mut gbm_bo) -> u64;
+    pub fn gbm_bo_get_device(bo: *mut gbm_bo) -> *mut gbm_device;
+    pub fn gbm_bo_get_handle(bo: *mut gbm_bo) -> gbm_bo_handle;
+    pub fn gbm_bo_get_fd(bo: *mut gbm_bo) -> c_int;
+    pub fn gbm_bo_get_num_planes(bo: *mut gbm_bo) -> usize;
+    pub fn gbm_bo_get_plane_handle(bo: *mut gbm_bo, plane: usize) -> gbm_bo_handle;
+    pub fn gbm_bo_get_plane_fd(bo: *mut gbm_bo, plane: usize) -> c_int;
+    pub fn gbm_bo_get_plane_offset(bo: *mut gbm_bo, plane: usize) -> u32;
+    pub fn gbm_bo_get_plane_size(bo: *mut gbm_bo, plane: usize) -> u32;
+    pub fn gbm_bo_get_plane_stride(bo: *mut gbm_bo, plane: usize) -> u32;
+    pub fn gbm_bo_get_plane_format_modifier(bo: *mut gbm_bo, plane: usize) -> u64;
+    // Did not generate cleanly by bindgen. Redone manually by zachr.
+    pub fn gbm_bo_set_user_data(bo: *mut gbm_bo,
+                                data: *mut c_void,
+                                destroy_user_data: extern "C" fn(bo: *mut gbm_bo,
+                                                                 data: *mut c_void));
+    pub fn gbm_bo_get_user_data(bo: *mut gbm_bo) -> *mut c_void;
+    pub fn gbm_bo_destroy(bo: *mut gbm_bo);
+    pub fn gbm_surface_create(gbm: *mut gbm_device,
+                              width: u32,
+                              height: u32,
+                              format: u32,
+                              flags: u32)
+                              -> *mut gbm_surface;
+    pub fn gbm_surface_lock_front_buffer(surface: *mut gbm_surface) -> *mut gbm_bo;
+    pub fn gbm_surface_release_buffer(surface: *mut gbm_surface, bo: *mut gbm_bo);
+    pub fn gbm_surface_has_free_buffers(surface: *mut gbm_surface) -> c_int;
+    pub fn gbm_surface_destroy(surface: *mut gbm_surface);
+}