Merge remote-tracking branch 'origin/upstream' am: 4d2bf6d693

Original change: undetermined

Change-Id: I3176af8a19a866216fd13b8b91f41370a8068cbc
Signed-off-by: Automerger Merge Worker <[email protected]>
diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json
new file mode 100644
index 0000000..c8aa4db
--- /dev/null
+++ b/.cargo_vcs_info.json
@@ -0,0 +1,6 @@
+{
+  "git": {
+    "sha1": "606c4260e0147256fb5c2901bbe837c0dc7d9f2d"
+  },
+  "path_in_vcs": ""
+}
\ No newline at end of file
diff --git a/Android.bp b/Android.bp
new file mode 100644
index 0000000..6b22e07
--- /dev/null
+++ b/Android.bp
@@ -0,0 +1,30 @@
+// This file is generated by cargo_embargo.
+// Do not modify this file as changes will be overridden on upgrade.
+
+// TODO: Add license.
+rust_library {
+    name: "libgbm_rust",
+    host_supported: true,
+    crate_name: "gbm",
+    cargo_env_compat: true,
+    cargo_pkg_version: "0.14.2",
+    srcs: ["src/lib.rs"],
+    edition: "2021",
+    features: [
+        "drm",
+        "drm-support",
+    ],
+    rustlibs: [
+        "libbitflags",
+        "libdrm_fourcc",
+        "libdrm_rust",
+        "libgbm_sys",
+        "liblibc",
+    ],
+    apex_available: [
+        "//apex_available:platform",
+        "//apex_available:anyapex",
+    ],
+    product_available: true,
+    vendor_available: true,
+}
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..402ff68
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,81 @@
+## 0.14.2
+
+- buffer objects: Add a method to receive the corresponding devices file descriptor
+
+## 0.14.1
+
+- Bugfix: Don't limit modifier lists to `GBM_MAX_PLANES`
+
+## 0.14.0
+
+- Update to drm-rs 0.11
+- Use `BorrowedFd` instead of `RawFd` in API
+- Don't require generated bindings for specific OS/architecture to build
+- Fix build without default features
+
+## 0.13.0
+
+- Update to drm-rs 0.10
+- Update wayland-server to 0.31
+
+## 0.12.0
+
+- Update to drm-rs 0.9
+
+## 0.11.0
+
+- Test for `-1` in fd and fd_for_plane
+
+## 0.10.0
+
+- Update `wayland-rs` to 0.30
+- Use io-safe types over `RawFd`
+- Update to drm-rs 0.8
+- YANKED: No errors for fd-methods, use 0.11.0
+
+## 0.9.0
+
+- Update to drm-rs 0.7
+
+## 0.8.0
+
+- Update to drm-rs 0.6
+
+## 0.7.0
+
+- Update to drm-rs 0.5
+
+## 0.6.0
+
+- Update to drm-rs 0.4
+- Update bindings, add new functionality
+- Make Device clonable
+- Use drm-fourcc for Formats
+- Implement Send where applicable
+- Switch to new std-Error trait
+
+## 0.5.0
+
+- Make `Surface::lock_front_buffer` unsafe as it may cause segfaults
+
+## 0.4.0
+
+- API overhaul to use ref-counting internally to:
+  - Enable out-of-order destruction without causing leaks, crashes or double-frees
+  - Remove lifetimes, which made this api a pain to work with and almost required hacks like the `rental` crate
+- Remove `FromRaw` as it is not possible to create most structs anymore without a reference to the underlying `Device`
+- Remove `Device` initializers other then `new_from_fd`. Lifetimes do not exist anymore and it is part of the contract to drop the `Device` before closing the file descriptor.
+- Add `Device` initializer `new` that wraps any open drm device.
+- Implement the [`drm-rs`](https://github.com/Smithay/drm-rs) `Device` traits for `Device` where applicable.
+
+## 0.3.0
+
+- Upgrade to bitflags 1.0 with associated consts
+
+## 0.2.0
+
+- drm-rs support
+
+## 0.1.0
+
+- Initial release
diff --git a/Cargo.toml b/Cargo.toml
new file mode 100644
index 0000000..693d325
--- /dev/null
+++ b/Cargo.toml
@@ -0,0 +1,76 @@
+# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
+#
+# When uploading crates to the registry Cargo will automatically
+# "normalize" Cargo.toml files for maximal compatibility
+# with all versions of Cargo and also rewrite `path` dependencies
+# to registry (e.g., crates.io) dependencies.
+#
+# If you are reading this file be aware that the original Cargo.toml
+# will likely look very different (and much more reasonable).
+# See Cargo.toml.orig for the original contents.
+
+[package]
+edition = "2021"
+name = "gbm"
+version = "0.14.2"
+authors = ["Victoria Brekenfeld <[email protected]>"]
+exclude = [
+    ".gitignore",
+    ".travis.yml",
+    ".rustfmt.toml",
+    ".github",
+]
+description = "libgbm bindings for rust"
+documentation = "https://docs.rs/gbm"
+readme = "README.md"
+keywords = [
+    "wayland",
+    "gbm",
+    "drm",
+    "bindings",
+]
+categories = ["external-ffi-bindings"]
+license = "MIT"
+repository = "https://github.com/Smithay/gbm.rs"
+
+[dependencies.bitflags]
+version = "1.2"
+
+[dependencies.drm]
+version = "0.11.0"
+optional = true
+
+[dependencies.drm-fourcc]
+version = "2.2"
+
+[dependencies.gbm-sys]
+version = "0.3.0"
+
+[dependencies.libc]
+version = "0.2"
+
+[dependencies.wayland-backend]
+version = "0.3"
+features = ["server_system"]
+optional = true
+
+[dependencies.wayland-server]
+version = "0.31"
+optional = true
+
+[dev-dependencies.drm]
+version = "0.11.0"
+
+[features]
+default = [
+    "import-wayland",
+    "import-egl",
+    "drm-support",
+]
+drm-support = ["drm"]
+import-egl = []
+import-wayland = [
+    "wayland-server",
+    "wayland-backend",
+]
+use_bindgen = ["gbm-sys/use_bindgen"]
diff --git a/Cargo.toml.orig b/Cargo.toml.orig
new file mode 100644
index 0000000..ecc5649
--- /dev/null
+++ b/Cargo.toml.orig
@@ -0,0 +1,49 @@
+[package]
+name = "gbm"
+description = "libgbm bindings for rust"
+license = "MIT"
+documentation = "https://docs.rs/gbm"
+repository = "https://github.com/Smithay/gbm.rs"
+version = "0.14.2"
+keywords = ["wayland", "gbm", "drm", "bindings"]
+categories = ["external-ffi-bindings"]
+authors = ["Victoria Brekenfeld <[email protected]>"]
+exclude = [".gitignore", ".travis.yml", ".rustfmt.toml", ".github"]
+edition = "2021"
+
+[dependencies]
+libc = "0.2"
+bitflags = "1.2"
+drm-fourcc = "2.2"
+
+[dependencies.gbm-sys]
+version = "0.3.0"
+path = "./gbm-sys"
+
+[dependencies.drm]
+version = "0.11.0"
+optional = true
+
+[dependencies.wayland-server]
+version = "0.31"
+optional = true
+
+[dependencies.wayland-backend]
+version = "0.3"
+features = ["server_system"]
+optional = true
+
+[dev-dependencies.drm]
+version = "0.11.0"
+
+[features]
+default = ["import-wayland", "import-egl", "drm-support"]
+import-wayland = ["wayland-server", "wayland-backend"]
+import-egl = []
+drm-support = ["drm"]
+use_bindgen = ["gbm-sys/use_bindgen"]
+
+[workspace]
+members = [
+  "gbm-sys"
+]
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..286a2a3
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2017 Victor Brekenfeld
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/METADATA b/METADATA
new file mode 100644
index 0000000..53181fb
--- /dev/null
+++ b/METADATA
@@ -0,0 +1,20 @@
+name: "gbm"
+description: "libgbm bindings for rust"
+third_party {
+  identifier {
+    type: "crates.io"
+    value: "gbm"
+  }
+  identifier {
+    type: "Archive"
+    value: "https://static.crates.io/crates/gbm/gbm-0.14.2.crate"
+    primary_source: true
+  }
+  version: "0.14.2"
+  license_type: NOTICE
+  last_upgrade_date {
+    year: 2024
+    month: 3
+    day: 13
+  }
+}
diff --git a/MODULE_LICENSE_MIT b/MODULE_LICENSE_MIT
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/MODULE_LICENSE_MIT
diff --git a/OWNERS b/OWNERS
new file mode 100644
index 0000000..697f117
--- /dev/null
+++ b/OWNERS
@@ -0,0 +1,7 @@
+# Bug component: 688011
+include platform/prebuilts/rust:main:/OWNERS
+
[email protected]
[email protected]
[email protected]
[email protected]
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..116f775
--- /dev/null
+++ b/README.md
@@ -0,0 +1,65 @@
+## Safe `libgbm` bindings for [rust](https://www.rust-lang.org)
+
+The Generic Buffer Manager
+
+This module provides an abstraction that the caller can use to request a
+buffer from the underlying memory management system for the platform.
+
+This allows the creation of portable code whilst still allowing access to
+the underlying memory manager.
+
+This library is best used in combination with [`drm-rs`](https://github.com/Smithay/drm-rs),
+provided through the `drm-support` feature.
+
+## Usage
+
+Add to your Cargo.toml
+
+```toml
+gbm = "0.14.2"
+```
+
+## Example
+
+```rust
+extern crate drm;
+extern crate gbm;
+
+use drm::control::{self, crtc, framebuffer};
+use gbm::{BufferObjectFlags, Device, Format};
+
+// ... init your drm device ...
+let drm = init_drm_device();
+
+// init a GBM device
+let gbm = Device::new(drm).unwrap();
+
+// create a buffer
+let mut bo = gbm
+    .create_buffer_object::<()>(
+        1280,
+        720,
+        Format::Argb8888,
+        BufferObjectFlags::SCANOUT | BufferObjectFlags::WRITE,
+    )
+    .unwrap();
+
+// write something to it (usually use import or egl rendering instead)
+let buffer = {
+    let mut buffer = Vec::new();
+    for i in 0..1280 {
+        for _ in 0..720 {
+            buffer.push(if i % 2 == 0 { 0 } else { 255 });
+        }
+    }
+    buffer
+};
+bo.write(&buffer).unwrap();
+
+// create a framebuffer from our buffer
+let fb = gbm.add_framebuffer(&bo, 32, 32).unwrap();
+
+// display it (and get a crtc, mode and connector before)
+gbm.set_crtc(crtc_handle, Some(fb), (0, 0), &[con], Some(mode))
+    .unwrap();
+```
diff --git a/cargo_embargo.json b/cargo_embargo.json
new file mode 100644
index 0000000..f275bc5
--- /dev/null
+++ b/cargo_embargo.json
@@ -0,0 +1,10 @@
+{
+  "run_cargo": false,
+  "module_name_overrides": {
+    "libdrm": "libdrm_rust",
+    "libgbm": "libgbm_rust"
+  },
+  "features": [
+    "drm-support"
+  ]
+}
diff --git a/src/buffer_object.rs b/src/buffer_object.rs
new file mode 100644
index 0000000..514ee8e
--- /dev/null
+++ b/src/buffer_object.rs
@@ -0,0 +1,711 @@
+use crate::{AsRaw, Device, DeviceDestroyedError, Format, Modifier, Ptr, WeakPtr};
+
+#[cfg(feature = "drm-support")]
+use drm::buffer::{Buffer as DrmBuffer, Handle, PlanarBuffer as DrmPlanarBuffer};
+use std::os::unix::io::{AsFd, BorrowedFd, FromRawFd, OwnedFd};
+
+use std::error;
+use std::fmt;
+use std::io::{Error as IoError, Result as IoResult};
+use std::marker::PhantomData;
+use std::ops::{Deref, DerefMut};
+use std::ptr;
+use std::slice;
+
+/// A GBM buffer object
+pub struct BufferObject<T: 'static> {
+    pub(crate) ffi: Ptr<ffi::gbm_bo>,
+    pub(crate) _device: WeakPtr<ffi::gbm_device>,
+    pub(crate) _userdata: PhantomData<T>,
+}
+
+impl<T> fmt::Debug for BufferObject<T> {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        f.debug_struct("BufferObject")
+            .field("ptr", &format_args!("{:p}", self.ffi))
+            .field("device", &format_args!("{:p}", &self._device))
+            .field("width", &self.width().unwrap_or(0))
+            .field("height", &self.height().unwrap_or(0))
+            .field("offsets", &self.offsets())
+            .field("stride", &self.stride().unwrap_or(0))
+            .field("format", &self.format().ok())
+            .field("modifier", &self.modifier().ok())
+            .finish()
+    }
+}
+
+bitflags! {
+    /// Flags to indicate the intended use for the buffer - these are passed into
+    /// [`Device::create_buffer_object()`].
+    ///
+    /// Use [`Device::is_format_supported()`] to check if the combination of format
+    /// and use flags are supported
+    pub struct BufferObjectFlags: u32 {
+        /// Buffer is going to be presented to the screen using an API such as KMS
+        const SCANOUT      = ffi::gbm_bo_flags::GBM_BO_USE_SCANOUT as u32;
+        /// Buffer is going to be used as cursor
+        const CURSOR       = ffi::gbm_bo_flags::GBM_BO_USE_CURSOR as u32;
+        /// Buffer is going to be used as cursor (deprecated)
+        #[deprecated = "Use CURSOR instead"]
+        const CURSOR_64X64 = ffi::gbm_bo_flags::GBM_BO_USE_CURSOR_64X64 as u32;
+        /// Buffer is to be used for rendering - for example it is going to be used
+        /// as the storage for a color buffer
+        const RENDERING    = ffi::gbm_bo_flags::GBM_BO_USE_RENDERING as u32;
+        /// Buffer can be used for [`BufferObject::write()`].  This is guaranteed to work
+        /// with [`Self::CURSOR`], but may not work for other combinations.
+        const WRITE        = ffi::gbm_bo_flags::GBM_BO_USE_WRITE as u32;
+        /// Buffer is linear, i.e. not tiled.
+        const LINEAR       = ffi::gbm_bo_flags::GBM_BO_USE_LINEAR as u32;
+        /// Buffer is protected
+        const PROTECTED    = ffi::gbm_bo_flags::GBM_BO_USE_PROTECTED as u32;
+    }
+}
+
+/// Abstraction representing the handle to a buffer allocated by the manager
+pub type BufferObjectHandle = ffi::gbm_bo_handle;
+
+enum BORef<'a, T: 'static> {
+    Ref(&'a BufferObject<T>),
+    Mut(&'a mut BufferObject<T>),
+}
+
+/// A mapped buffer object
+pub struct MappedBufferObject<'a, T: 'static> {
+    bo: BORef<'a, T>,
+    buffer: &'a mut [u8],
+    data: *mut ::libc::c_void,
+    stride: u32,
+    height: u32,
+    width: u32,
+    x: u32,
+    y: u32,
+}
+
+impl<'a, T> fmt::Debug for MappedBufferObject<'a, T> {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        f.debug_struct("MappedBufferObject")
+            .field(
+                "mode",
+                &match self.bo {
+                    BORef::Ref(_) => format_args!("read"),
+                    BORef::Mut(_) => format_args!("write"),
+                },
+            )
+            .field(
+                "buffer",
+                match &self.bo {
+                    BORef::Ref(bo) => *bo,
+                    BORef::Mut(bo) => *bo,
+                },
+            )
+            .finish()
+    }
+}
+
+impl<'a, T: 'static> MappedBufferObject<'a, T> {
+    /// Get the stride of the buffer object
+    ///
+    /// This is calculated by the backend when it does the allocation of the buffer.
+    pub fn stride(&self) -> u32 {
+        self.stride
+    }
+
+    /// The height of the mapped region for the buffer
+    pub fn height(&self) -> u32 {
+        self.height
+    }
+
+    /// The width of the mapped region for the buffer
+    pub fn width(&self) -> u32 {
+        self.width
+    }
+
+    /// The X (top left origin) starting position of the mapped region for the buffer
+    pub fn x(&self) -> u32 {
+        self.x
+    }
+
+    /// The Y (top left origin) starting position of the mapped region for the buffer
+    pub fn y(&self) -> u32 {
+        self.y
+    }
+
+    /// Access to the underlying image buffer
+    pub fn buffer(&self) -> &[u8] {
+        self.buffer
+    }
+
+    /// Mutable access to the underlying image buffer
+    pub fn buffer_mut(&mut self) -> &mut [u8] {
+        self.buffer
+    }
+}
+
+impl<'a, T: 'static> Deref for MappedBufferObject<'a, T> {
+    type Target = BufferObject<T>;
+    fn deref(&self) -> &BufferObject<T> {
+        match &self.bo {
+            BORef::Ref(bo) => bo,
+            BORef::Mut(bo) => bo,
+        }
+    }
+}
+
+impl<'a, T: 'static> DerefMut for MappedBufferObject<'a, T> {
+    fn deref_mut(&mut self) -> &mut BufferObject<T> {
+        match &mut self.bo {
+            BORef::Ref(_) => unreachable!(),
+            BORef::Mut(bo) => bo,
+        }
+    }
+}
+
+impl<'a, T: 'static> Drop for MappedBufferObject<'a, T> {
+    fn drop(&mut self) {
+        let ffi = match &self.bo {
+            BORef::Ref(bo) => &bo.ffi,
+            BORef::Mut(bo) => &bo.ffi,
+        };
+        unsafe { ffi::gbm_bo_unmap(**ffi, self.data) }
+    }
+}
+
+unsafe extern "C" fn destroy<T: 'static>(_: *mut ffi::gbm_bo, ptr: *mut ::libc::c_void) {
+    let ptr = ptr as *mut T;
+    if !ptr.is_null() {
+        let _ = Box::from_raw(ptr);
+    }
+}
+
+impl<T: 'static> BufferObject<T> {
+    /// Get the width of the buffer object
+    pub fn width(&self) -> Result<u32, DeviceDestroyedError> {
+        self._device.upgrade().ok_or(DeviceDestroyedError)?;
+        Ok(unsafe { ffi::gbm_bo_get_width(*self.ffi) })
+    }
+
+    /// Get the height of the buffer object
+    pub fn height(&self) -> Result<u32, DeviceDestroyedError> {
+        self._device.upgrade().ok_or(DeviceDestroyedError)?;
+        Ok(unsafe { ffi::gbm_bo_get_height(*self.ffi) })
+    }
+
+    /// Get the stride of the buffer object
+    pub fn stride(&self) -> Result<u32, DeviceDestroyedError> {
+        self._device.upgrade().ok_or(DeviceDestroyedError)?;
+        Ok(unsafe { ffi::gbm_bo_get_stride(*self.ffi) })
+    }
+
+    /// Get the stride of the buffer object
+    pub fn stride_for_plane(&self, plane: i32) -> Result<u32, DeviceDestroyedError> {
+        self._device.upgrade().ok_or(DeviceDestroyedError)?;
+        Ok(unsafe { ffi::gbm_bo_get_stride_for_plane(*self.ffi, plane) })
+    }
+
+    /// Get the format of the buffer object
+    pub fn format(&self) -> Result<Format, DeviceDestroyedError> {
+        self._device.upgrade().ok_or(DeviceDestroyedError)?;
+        Ok(
+            Format::try_from(unsafe { ffi::gbm_bo_get_format(*self.ffi) })
+                .expect("libgbm returned invalid buffer format"),
+        )
+    }
+
+    /// Get the bits per pixel of the buffer object
+    pub fn bpp(&self) -> Result<u32, DeviceDestroyedError> {
+        self._device.upgrade().ok_or(DeviceDestroyedError)?;
+        Ok(unsafe { ffi::gbm_bo_get_bpp(*self.ffi) })
+    }
+
+    /// Get the offset for a plane of the buffer object
+    pub fn offset(&self, plane: i32) -> Result<u32, DeviceDestroyedError> {
+        self._device.upgrade().ok_or(DeviceDestroyedError)?;
+        Ok(unsafe { ffi::gbm_bo_get_offset(*self.ffi, plane) })
+    }
+
+    /// Get the plane count of the buffer object
+    pub fn plane_count(&self) -> Result<u32, DeviceDestroyedError> {
+        self._device.upgrade().ok_or(DeviceDestroyedError)?;
+        Ok(unsafe { ffi::gbm_bo_get_plane_count(*self.ffi) as u32 })
+    }
+
+    /// Get the modifier of the buffer object
+    pub fn modifier(&self) -> Result<Modifier, DeviceDestroyedError> {
+        self._device.upgrade().ok_or(DeviceDestroyedError)?;
+        Ok(Modifier::from(unsafe {
+            ffi::gbm_bo_get_modifier(*self.ffi)
+        }))
+    }
+
+    /// Get a DMA-BUF file descriptor for the buffer object
+    ///
+    /// This function creates a DMA-BUF (also known as PRIME) file descriptor
+    /// handle for the buffer object.  Each call to [`Self::fd()`] returns a new
+    /// file descriptor and the caller is responsible for closing the file
+    /// descriptor.
+    pub fn fd(&self) -> Result<OwnedFd, FdError> {
+        self._device.upgrade().ok_or(DeviceDestroyedError)?;
+        unsafe {
+            let fd = ffi::gbm_bo_get_fd(*self.ffi);
+
+            if fd == -1 {
+                return Err(InvalidFdError.into());
+            }
+
+            Ok(OwnedFd::from_raw_fd(fd))
+        }
+    }
+
+    /// Get the file descriptor of the gbm device of this buffer object
+    pub fn device_fd(&self) -> Result<BorrowedFd, DeviceDestroyedError> {
+        let device_ptr = self._device.upgrade().ok_or(DeviceDestroyedError)?;
+        Ok(unsafe { BorrowedFd::borrow_raw(ffi::gbm_device_get_fd(*device_ptr)) })
+    }
+
+    /// Get the handle of the buffer object
+    ///
+    /// This is stored in the platform generic union [`BufferObjectHandle`] type.  However
+    /// the format of this handle is platform specific.
+    pub fn handle(&self) -> Result<BufferObjectHandle, DeviceDestroyedError> {
+        self._device.upgrade().ok_or(DeviceDestroyedError)?;
+        Ok(unsafe { ffi::gbm_bo_get_handle(*self.ffi) })
+    }
+
+    /// Get a DMA-BUF file descriptor for a plane of the buffer object
+    ///
+    /// This function creates a DMA-BUF (also known as PRIME) file descriptor
+    /// handle for a plane of the buffer object. Each call to [`Self::fd_for_plane()`]
+    /// returns a new file descriptor and the caller is responsible for closing
+    /// the file descriptor.
+    pub fn fd_for_plane(&self, plane: i32) -> Result<OwnedFd, FdError> {
+        self._device.upgrade().ok_or(DeviceDestroyedError)?;
+        unsafe {
+            let fd = ffi::gbm_bo_get_fd_for_plane(*self.ffi, plane);
+
+            if fd == -1 {
+                return Err(InvalidFdError.into());
+            }
+
+            Ok(OwnedFd::from_raw_fd(fd))
+        }
+    }
+
+    /// Get the handle of a plane of the buffer object
+    ///
+    /// This is stored in the platform generic union [`BufferObjectHandle`] type.  However
+    /// the format of this handle is platform specific.
+    pub fn handle_for_plane(&self, plane: i32) -> Result<BufferObjectHandle, DeviceDestroyedError> {
+        self._device.upgrade().ok_or(DeviceDestroyedError)?;
+        Ok(unsafe { ffi::gbm_bo_get_handle_for_plane(*self.ffi, plane) })
+    }
+
+    /// Map a region of a GBM buffer object for cpu access
+    ///
+    /// This function maps a region of a GBM bo for cpu read access.
+    pub fn map<'a, D, F, S>(
+        &'a self,
+        device: &Device<D>,
+        x: u32,
+        y: u32,
+        width: u32,
+        height: u32,
+        f: F,
+    ) -> Result<IoResult<S>, WrongDeviceError>
+    where
+        D: AsFd + 'static,
+        F: FnOnce(&MappedBufferObject<'a, T>) -> S,
+    {
+        let device_ref = self._device.upgrade().ok_or(WrongDeviceError)?;
+        if *device_ref != device.as_raw_mut() {
+            // not matching
+            return Err(WrongDeviceError);
+        }
+
+        unsafe {
+            let mut data: *mut ::libc::c_void = ptr::null_mut();
+            let mut stride = 0;
+            let ptr = ffi::gbm_bo_map(
+                *self.ffi,
+                x,
+                y,
+                width,
+                height,
+                ffi::gbm_bo_transfer_flags::GBM_BO_TRANSFER_READ as u32,
+                &mut stride as *mut _,
+                &mut data as *mut _,
+            );
+
+            if ptr.is_null() {
+                Ok(Err(IoError::last_os_error()))
+            } else {
+                Ok(Ok(f(&MappedBufferObject {
+                    bo: BORef::Ref(self),
+                    buffer: slice::from_raw_parts_mut(ptr as *mut _, (height * stride) as usize),
+                    data,
+                    stride,
+                    height,
+                    width,
+                    x,
+                    y,
+                })))
+            }
+        }
+    }
+
+    /// Map a region of a GBM buffer object for cpu access
+    ///
+    /// This function maps a region of a GBM bo for cpu read/write access.
+    pub fn map_mut<'a, D, F, S>(
+        &'a mut self,
+        device: &Device<D>,
+        x: u32,
+        y: u32,
+        width: u32,
+        height: u32,
+        f: F,
+    ) -> Result<IoResult<S>, WrongDeviceError>
+    where
+        D: AsFd + 'static,
+        F: FnOnce(&mut MappedBufferObject<'a, T>) -> S,
+    {
+        let device_ref = self._device.upgrade().ok_or(WrongDeviceError)?;
+        if *device_ref != device.as_raw_mut() {
+            // not matching
+            return Err(WrongDeviceError);
+        }
+
+        unsafe {
+            let mut data: *mut ::libc::c_void = ptr::null_mut();
+            let mut stride = 0;
+            let ptr = ffi::gbm_bo_map(
+                *self.ffi,
+                x,
+                y,
+                width,
+                height,
+                ffi::gbm_bo_transfer_flags::GBM_BO_TRANSFER_READ_WRITE as u32,
+                &mut stride as *mut _,
+                &mut data as *mut _,
+            );
+
+            if ptr.is_null() {
+                Ok(Err(IoError::last_os_error()))
+            } else {
+                Ok(Ok(f(&mut MappedBufferObject {
+                    bo: BORef::Mut(self),
+                    buffer: slice::from_raw_parts_mut(ptr as *mut _, (height * stride) as usize),
+                    data,
+                    stride,
+                    height,
+                    width,
+                    x,
+                    y,
+                })))
+            }
+        }
+    }
+
+    ///  Write data into the buffer object
+    ///
+    /// If the buffer object was created with the [`BufferObjectFlags::WRITE`] flag,
+    /// this function can be used to write data into the buffer object.  The
+    /// data is copied directly into the object and it's the responsibility
+    /// of the caller to make sure the data represents valid pixel data,
+    /// according to the width, height, stride and format of the buffer object.
+    pub fn write(&mut self, buffer: &[u8]) -> Result<IoResult<()>, DeviceDestroyedError> {
+        self._device.upgrade().ok_or(DeviceDestroyedError)?;
+        let result =
+            unsafe { ffi::gbm_bo_write(*self.ffi, buffer.as_ptr() as *const _, buffer.len() as _) };
+        if result != 0 {
+            Ok(Err(IoError::last_os_error()))
+        } else {
+            Ok(Ok(()))
+        }
+    }
+
+    /// Sets the userdata of the buffer object.
+    ///
+    /// If previously userdata was set, it is returned.
+    pub fn set_userdata(&mut self, userdata: T) -> Result<Option<T>, DeviceDestroyedError> {
+        self._device.upgrade().ok_or(DeviceDestroyedError)?;
+        let old = self.take_userdata();
+
+        let boxed = Box::new(userdata);
+        unsafe {
+            ffi::gbm_bo_set_user_data(
+                *self.ffi,
+                Box::into_raw(boxed) as *mut _,
+                Some(destroy::<T>),
+            );
+        }
+
+        old
+    }
+
+    /// Clears the set userdata of the buffer object.
+    pub fn clear_userdata(&mut self) -> Result<(), DeviceDestroyedError> {
+        self._device.upgrade().ok_or(DeviceDestroyedError)?;
+        let _ = self.take_userdata();
+        Ok(())
+    }
+
+    /// Returns a reference to set userdata, if any.
+    pub fn userdata(&self) -> Result<Option<&T>, DeviceDestroyedError> {
+        self._device.upgrade().ok_or(DeviceDestroyedError)?;
+        let raw = unsafe { ffi::gbm_bo_get_user_data(*self.ffi) };
+
+        if raw.is_null() {
+            Ok(None)
+        } else {
+            unsafe { Ok(Some(&*(raw as *mut T))) }
+        }
+    }
+
+    /// Returns a mutable reference to set userdata, if any.
+    pub fn userdata_mut(&mut self) -> Result<Option<&mut T>, DeviceDestroyedError> {
+        self._device.upgrade().ok_or(DeviceDestroyedError)?;
+        let raw = unsafe { ffi::gbm_bo_get_user_data(*self.ffi) };
+
+        if raw.is_null() {
+            Ok(None)
+        } else {
+            unsafe { Ok(Some(&mut *(raw as *mut T))) }
+        }
+    }
+
+    /// Takes ownership of previously set userdata, if any.
+    ///
+    /// This removes the userdata from the buffer object.
+    pub fn take_userdata(&mut self) -> Result<Option<T>, DeviceDestroyedError> {
+        self._device.upgrade().ok_or(DeviceDestroyedError)?;
+        let raw = unsafe { ffi::gbm_bo_get_user_data(*self.ffi) };
+
+        if raw.is_null() {
+            Ok(None)
+        } else {
+            unsafe {
+                let boxed = Box::from_raw(raw as *mut T);
+                ffi::gbm_bo_set_user_data(*self.ffi, ptr::null_mut(), None);
+                Ok(Some(*boxed))
+            }
+        }
+    }
+
+    pub(crate) unsafe fn new(
+        ffi: *mut ffi::gbm_bo,
+        device: WeakPtr<ffi::gbm_device>,
+    ) -> BufferObject<T> {
+        BufferObject {
+            ffi: Ptr::<ffi::gbm_bo>::new(ffi, |ptr| ffi::gbm_bo_destroy(ptr)),
+            _device: device,
+            _userdata: PhantomData,
+        }
+    }
+
+    fn offsets(&self) -> [u32; 4] {
+        let num = self
+            .plane_count()
+            .expect("GbmDevice does not exist anymore");
+        [
+            BufferObject::<T>::offset(self, 0).unwrap(),
+            if num > 1 {
+                BufferObject::<T>::offset(self, 1).unwrap()
+            } else {
+                0
+            },
+            if num > 2 {
+                BufferObject::<T>::offset(self, 2).unwrap()
+            } else {
+                0
+            },
+            if num > 3 {
+                BufferObject::<T>::offset(self, 3).unwrap()
+            } else {
+                0
+            },
+        ]
+    }
+}
+
+impl<T: 'static> AsRaw<ffi::gbm_bo> for BufferObject<T> {
+    fn as_raw(&self) -> *const ffi::gbm_bo {
+        *self.ffi
+    }
+}
+
+#[cfg(feature = "drm-support")]
+impl<T: 'static> DrmBuffer for BufferObject<T> {
+    fn size(&self) -> (u32, u32) {
+        (
+            self.width().expect("GbmDevice does not exist anymore"),
+            self.height().expect("GbmDevice does not exist anymore"),
+        )
+    }
+
+    fn format(&self) -> Format {
+        BufferObject::<T>::format(self).expect("GbmDevice does not exist anymore")
+    }
+
+    fn pitch(&self) -> u32 {
+        self.stride().expect("GbmDevice does not exist anymore")
+    }
+
+    fn handle(&self) -> Handle {
+        use std::num::NonZeroU32;
+        unsafe {
+            Handle::from(NonZeroU32::new_unchecked(
+                self.handle()
+                    .expect("GbmDevice does not exist anymore")
+                    .u32_,
+            ))
+        }
+    }
+}
+
+#[cfg(feature = "drm-support")]
+impl<T: 'static> DrmPlanarBuffer for BufferObject<T> {
+    fn size(&self) -> (u32, u32) {
+        (
+            self.width().expect("GbmDevice does not exist anymore"),
+            self.height().expect("GbmDevice does not exist anymore"),
+        )
+    }
+    fn format(&self) -> Format {
+        BufferObject::<T>::format(self).expect("GbmDevice does not exist anymore")
+    }
+    fn modifier(&self) -> Option<Modifier> {
+        Some(BufferObject::<T>::modifier(self).expect("GbmDevice does not exist anymore"))
+    }
+    fn pitches(&self) -> [u32; 4] {
+        let num = self
+            .plane_count()
+            .expect("GbmDevice does not exist anymore");
+        [
+            BufferObject::<T>::stride_for_plane(self, 0).unwrap(),
+            if num > 1 {
+                BufferObject::<T>::stride_for_plane(self, 1).unwrap()
+            } else {
+                0
+            },
+            if num > 2 {
+                BufferObject::<T>::stride_for_plane(self, 2).unwrap()
+            } else {
+                0
+            },
+            if num > 3 {
+                BufferObject::<T>::stride_for_plane(self, 3).unwrap()
+            } else {
+                0
+            },
+        ]
+    }
+    fn handles(&self) -> [Option<Handle>; 4] {
+        use std::num::NonZeroU32;
+        let num = self
+            .plane_count()
+            .expect("GbmDevice does not exist anymore");
+        [
+            Some(unsafe {
+                Handle::from(NonZeroU32::new_unchecked(
+                    BufferObject::<T>::handle_for_plane(self, 0).unwrap().u32_,
+                ))
+            }),
+            if num > 1 {
+                Some(unsafe {
+                    Handle::from(NonZeroU32::new_unchecked(
+                        BufferObject::<T>::handle_for_plane(self, 1).unwrap().u32_,
+                    ))
+                })
+            } else {
+                None
+            },
+            if num > 2 {
+                Some(unsafe {
+                    Handle::from(NonZeroU32::new_unchecked(
+                        BufferObject::<T>::handle_for_plane(self, 2).unwrap().u32_,
+                    ))
+                })
+            } else {
+                None
+            },
+            if num > 3 {
+                Some(unsafe {
+                    Handle::from(NonZeroU32::new_unchecked(
+                        BufferObject::<T>::handle_for_plane(self, 3).unwrap().u32_,
+                    ))
+                })
+            } else {
+                None
+            },
+        ]
+    }
+    fn offsets(&self) -> [u32; 4] {
+        self.offsets()
+    }
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+/// Thrown when the GBM device does not belong to the buffer object
+pub struct WrongDeviceError;
+
+impl fmt::Display for WrongDeviceError {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        write!(
+            f,
+            "The gbm device specified is not the one this buffer object belongs to"
+        )
+    }
+}
+
+impl error::Error for WrongDeviceError {}
+
+/// Thrown when the fd is invalid
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub struct InvalidFdError;
+
+impl fmt::Display for InvalidFdError {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        write!(f, "The returned fd is invalid")
+    }
+}
+
+impl error::Error for InvalidFdError {}
+
+/// Thrown when an error occurs during getting a bo fd
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub enum FdError {
+    /// The underlying device has been destroyed
+    DeviceDestroyed(DeviceDestroyedError),
+    /// The operation returned an invalid fd
+    InvalidFd(InvalidFdError),
+}
+
+impl From<DeviceDestroyedError> for FdError {
+    fn from(err: DeviceDestroyedError) -> Self {
+        FdError::DeviceDestroyed(err)
+    }
+}
+
+impl From<InvalidFdError> for FdError {
+    fn from(err: InvalidFdError) -> Self {
+        FdError::InvalidFd(err)
+    }
+}
+
+impl fmt::Display for FdError {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        match self {
+            FdError::DeviceDestroyed(err) => err.fmt(f),
+            FdError::InvalidFd(err) => err.fmt(f),
+        }
+    }
+}
+
+impl error::Error for FdError {
+    fn source(&self) -> Option<&(dyn error::Error + 'static)> {
+        match self {
+            FdError::DeviceDestroyed(err) => Some(err),
+            FdError::InvalidFd(err) => Some(err),
+        }
+    }
+}
diff --git a/src/device.rs b/src/device.rs
new file mode 100644
index 0000000..b5aa670
--- /dev/null
+++ b/src/device.rs
@@ -0,0 +1,421 @@
+use crate::{AsRaw, BufferObject, BufferObjectFlags, Format, Modifier, Ptr, Surface};
+
+use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd};
+
+use std::error;
+use std::ffi::CStr;
+use std::fmt;
+use std::io::{Error as IoError, Result as IoResult};
+use std::ops::{Deref, DerefMut};
+
+#[cfg(feature = "import-wayland")]
+use wayland_server::protocol::wl_buffer::WlBuffer;
+
+#[cfg(feature = "import-egl")]
+/// An EGLImage handle
+pub type EGLImage = *mut libc::c_void;
+
+#[cfg(feature = "drm-support")]
+use drm::control::Device as DrmControlDevice;
+#[cfg(feature = "drm-support")]
+use drm::Device as DrmDevice;
+
+/// An open GBM device
+pub struct Device<T: AsFd> {
+    // Declare `ffi` first so it is dropped before `fd`
+    ffi: Ptr<ffi::gbm_device>,
+    fd: T,
+}
+
+impl<T: AsFd> fmt::Debug for Device<T> {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        f.debug_struct("Device")
+            .field("ptr", &format_args!("{:p}", &self.ffi))
+            .finish()
+    }
+}
+
+impl<T: AsFd + Clone> Clone for Device<T> {
+    fn clone(&self) -> Device<T> {
+        Device {
+            fd: self.fd.clone(),
+            ffi: self.ffi.clone(),
+        }
+    }
+}
+
+impl<T: AsFd> AsFd for Device<T> {
+    fn as_fd(&self) -> BorrowedFd {
+        unsafe { BorrowedFd::borrow_raw(ffi::gbm_device_get_fd(*self.ffi)) }
+    }
+}
+
+impl<T: AsFd> AsRaw<ffi::gbm_device> for Device<T> {
+    fn as_raw(&self) -> *const ffi::gbm_device {
+        *self.ffi
+    }
+}
+
+impl<T: AsFd> Deref for Device<T> {
+    type Target = T;
+    fn deref(&self) -> &T {
+        &self.fd
+    }
+}
+
+impl<T: AsFd> DerefMut for Device<T> {
+    fn deref_mut(&mut self) -> &mut T {
+        &mut self.fd
+    }
+}
+
+impl<T: AsFd> Device<T> {
+    /// Open a GBM device from a given open DRM device.
+    ///
+    /// The underlying file descriptor passed in is used by the backend to communicate with
+    /// platform for allocating the memory.  For allocations using DRI this would be
+    /// the file descriptor returned when opening a device such as `/dev/dri/card0`.
+    pub fn new(fd: T) -> IoResult<Device<T>> {
+        let ptr = unsafe { ffi::gbm_create_device(fd.as_fd().as_raw_fd()) };
+        if ptr.is_null() {
+            Err(IoError::last_os_error())
+        } else {
+            Ok(Device {
+                fd,
+                ffi: Ptr::<ffi::gbm_device>::new(ptr, |ptr| unsafe {
+                    ffi::gbm_device_destroy(ptr)
+                }),
+            })
+        }
+    }
+
+    /// Get the backend name
+    pub fn backend_name(&self) -> &str {
+        unsafe {
+            CStr::from_ptr(ffi::gbm_device_get_backend_name(*self.ffi))
+                .to_str()
+                .expect("GBM passed invalid utf8 string")
+        }
+    }
+
+    /// Test if a format is supported for a given set of usage flags
+    pub fn is_format_supported(&self, format: Format, usage: BufferObjectFlags) -> bool {
+        unsafe { ffi::gbm_device_is_format_supported(*self.ffi, format as u32, usage.bits()) != 0 }
+    }
+
+    /// Allocate a new surface object
+    pub fn create_surface<U: 'static>(
+        &self,
+        width: u32,
+        height: u32,
+        format: Format,
+        usage: BufferObjectFlags,
+    ) -> IoResult<Surface<U>> {
+        let ptr = unsafe {
+            ffi::gbm_surface_create(*self.ffi, width, height, format as u32, usage.bits())
+        };
+        if ptr.is_null() {
+            Err(IoError::last_os_error())
+        } else {
+            Ok(unsafe { Surface::new(ptr, self.ffi.downgrade()) })
+        }
+    }
+
+    /// Allocate a new surface object with explicit modifiers
+    pub fn create_surface_with_modifiers<U: 'static>(
+        &self,
+        width: u32,
+        height: u32,
+        format: Format,
+        modifiers: impl Iterator<Item = Modifier>,
+    ) -> IoResult<Surface<U>> {
+        let mods = modifiers.map(|m| m.into()).collect::<Vec<u64>>();
+        let ptr = unsafe {
+            ffi::gbm_surface_create_with_modifiers(
+                *self.ffi,
+                width,
+                height,
+                format as u32,
+                mods.as_ptr(),
+                mods.len() as u32,
+            )
+        };
+        if ptr.is_null() {
+            Err(IoError::last_os_error())
+        } else {
+            Ok(unsafe { Surface::new(ptr, self.ffi.downgrade()) })
+        }
+    }
+
+    /// Allocate a new surface object with explicit modifiers and flags
+    pub fn create_surface_with_modifiers2<U: 'static>(
+        &self,
+        width: u32,
+        height: u32,
+        format: Format,
+        modifiers: impl Iterator<Item = Modifier>,
+        usage: BufferObjectFlags,
+    ) -> IoResult<Surface<U>> {
+        let mods = modifiers.map(|m| m.into()).collect::<Vec<u64>>();
+        let ptr = unsafe {
+            ffi::gbm_surface_create_with_modifiers2(
+                *self.ffi,
+                width,
+                height,
+                format as u32,
+                mods.as_ptr(),
+                mods.len() as u32,
+                usage.bits(),
+            )
+        };
+        if ptr.is_null() {
+            Err(IoError::last_os_error())
+        } else {
+            Ok(unsafe { Surface::new(ptr, self.ffi.downgrade()) })
+        }
+    }
+
+    ///  Allocate a buffer object for the given dimensions
+    pub fn create_buffer_object<U: 'static>(
+        &self,
+        width: u32,
+        height: u32,
+        format: Format,
+        usage: BufferObjectFlags,
+    ) -> IoResult<BufferObject<U>> {
+        let ptr =
+            unsafe { ffi::gbm_bo_create(*self.ffi, width, height, format as u32, usage.bits()) };
+        if ptr.is_null() {
+            Err(IoError::last_os_error())
+        } else {
+            Ok(unsafe { BufferObject::new(ptr, self.ffi.downgrade()) })
+        }
+    }
+
+    ///  Allocate a buffer object for the given dimensions with explicit modifiers
+    pub fn create_buffer_object_with_modifiers<U: 'static>(
+        &self,
+        width: u32,
+        height: u32,
+        format: Format,
+        modifiers: impl Iterator<Item = Modifier>,
+    ) -> IoResult<BufferObject<U>> {
+        let mods = modifiers.map(|m| m.into()).collect::<Vec<u64>>();
+        let ptr = unsafe {
+            ffi::gbm_bo_create_with_modifiers(
+                *self.ffi,
+                width,
+                height,
+                format as u32,
+                mods.as_ptr(),
+                mods.len() as u32,
+            )
+        };
+        if ptr.is_null() {
+            Err(IoError::last_os_error())
+        } else {
+            Ok(unsafe { BufferObject::new(ptr, self.ffi.downgrade()) })
+        }
+    }
+
+    ///  Allocate a buffer object for the given dimensions with explicit modifiers and flags
+    pub fn create_buffer_object_with_modifiers2<U: 'static>(
+        &self,
+        width: u32,
+        height: u32,
+        format: Format,
+        modifiers: impl Iterator<Item = Modifier>,
+        usage: BufferObjectFlags,
+    ) -> IoResult<BufferObject<U>> {
+        let mods = modifiers.map(|m| m.into()).collect::<Vec<u64>>();
+        let ptr = unsafe {
+            ffi::gbm_bo_create_with_modifiers2(
+                *self.ffi,
+                width,
+                height,
+                format as u32,
+                mods.as_ptr(),
+                mods.len() as u32,
+                usage.bits(),
+            )
+        };
+        if ptr.is_null() {
+            Err(IoError::last_os_error())
+        } else {
+            Ok(unsafe { BufferObject::new(ptr, self.ffi.downgrade()) })
+        }
+    }
+
+    /// Create a GBM buffer object from a wayland buffer
+    ///
+    /// This function imports a foreign [`WlBuffer`] object and creates a new GBM
+    /// buffer object for it.
+    /// This enables using the foreign object with a display API such as KMS.
+    ///
+    /// The GBM bo shares the underlying pixels but its life-time is
+    /// independent of the foreign object.
+    #[cfg(feature = "import-wayland")]
+    pub fn import_buffer_object_from_wayland<U: 'static>(
+        &self,
+        buffer: &WlBuffer,
+        usage: BufferObjectFlags,
+    ) -> IoResult<BufferObject<U>> {
+        use wayland_server::Resource;
+
+        let ptr = unsafe {
+            ffi::gbm_bo_import(
+                *self.ffi,
+                ffi::GBM_BO_IMPORT_WL_BUFFER as u32,
+                buffer.id().as_ptr() as *mut _,
+                usage.bits(),
+            )
+        };
+        if ptr.is_null() {
+            Err(IoError::last_os_error())
+        } else {
+            Ok(unsafe { BufferObject::new(ptr, self.ffi.downgrade()) })
+        }
+    }
+
+    /// Create a GBM buffer object from an egl buffer
+    ///
+    /// This function imports a foreign [`EGLImage`] object and creates a new GBM
+    /// buffer object for it.
+    /// This enables using the foreign object with a display API such as KMS.
+    ///
+    /// The GBM bo shares the underlying pixels but its life-time is
+    /// independent of the foreign object.
+    ///
+    /// # Safety
+    ///
+    /// The given [`EGLImage`] is a raw pointer.  Passing null or an invalid [`EGLImage`] will
+    /// cause undefined behavior.
+    #[cfg(feature = "import-egl")]
+    pub unsafe fn import_buffer_object_from_egl<U: 'static>(
+        &self,
+        buffer: EGLImage,
+        usage: BufferObjectFlags,
+    ) -> IoResult<BufferObject<U>> {
+        let ptr = ffi::gbm_bo_import(
+            *self.ffi,
+            ffi::GBM_BO_IMPORT_EGL_IMAGE as u32,
+            buffer,
+            usage.bits(),
+        );
+        if ptr.is_null() {
+            Err(IoError::last_os_error())
+        } else {
+            Ok(BufferObject::new(ptr, self.ffi.downgrade()))
+        }
+    }
+
+    /// Create a GBM buffer object from a dma buffer
+    ///
+    /// This function imports a foreign dma buffer from an open file descriptor
+    /// and creates a new GBM buffer object for it.
+    /// This enables using the foreign object with a display API such as KMS.
+    ///
+    /// The GBM bo shares the underlying pixels but its life-time is
+    /// independent of the foreign object.
+    pub fn import_buffer_object_from_dma_buf<U: 'static>(
+        &self,
+        buffer: BorrowedFd<'_>,
+        width: u32,
+        height: u32,
+        stride: u32,
+        format: Format,
+        usage: BufferObjectFlags,
+    ) -> IoResult<BufferObject<U>> {
+        let mut fd_data = ffi::gbm_import_fd_data {
+            fd: buffer.as_raw_fd(),
+            width,
+            height,
+            stride,
+            format: format as u32,
+        };
+
+        let ptr = unsafe {
+            ffi::gbm_bo_import(
+                *self.ffi,
+                ffi::GBM_BO_IMPORT_FD as u32,
+                &mut fd_data as *mut ffi::gbm_import_fd_data as *mut _,
+                usage.bits(),
+            )
+        };
+        if ptr.is_null() {
+            Err(IoError::last_os_error())
+        } else {
+            Ok(unsafe { BufferObject::new(ptr, self.ffi.downgrade()) })
+        }
+    }
+
+    /// Create a GBM buffer object from a dma buffer with explicit modifiers
+    ///
+    /// This function imports a foreign dma buffer from an open file descriptor
+    /// and creates a new GBM buffer object for it.
+    /// This enables using the foreign object with a display API such as KMS.
+    ///
+    /// The GBM bo shares the underlying pixels but its life-time is
+    /// independent of the foreign object.
+    #[allow(clippy::too_many_arguments)]
+    pub fn import_buffer_object_from_dma_buf_with_modifiers<U: 'static>(
+        &self,
+        len: u32,
+        buffers: [Option<BorrowedFd<'_>>; 4],
+        width: u32,
+        height: u32,
+        format: Format,
+        usage: BufferObjectFlags,
+        strides: [i32; 4],
+        offsets: [i32; 4],
+        modifier: Modifier,
+    ) -> IoResult<BufferObject<U>> {
+        let fds = buffers.map(|fd| fd.map_or(-1, |x| x.as_raw_fd()));
+        let mut fd_data = ffi::gbm_import_fd_modifier_data {
+            fds,
+            width,
+            height,
+            format: format as u32,
+            strides,
+            offsets,
+            modifier: modifier.into(),
+            num_fds: len,
+        };
+
+        let ptr = unsafe {
+            ffi::gbm_bo_import(
+                *self.ffi,
+                ffi::GBM_BO_IMPORT_FD_MODIFIER as u32,
+                &mut fd_data as *mut ffi::gbm_import_fd_modifier_data as *mut _,
+                usage.bits(),
+            )
+        };
+        if ptr.is_null() {
+            Err(IoError::last_os_error())
+        } else {
+            Ok(unsafe { BufferObject::new(ptr, self.ffi.downgrade()) })
+        }
+    }
+}
+
+#[cfg(feature = "drm-support")]
+impl<T: DrmDevice + AsFd> DrmDevice for Device<T> {}
+
+#[cfg(feature = "drm-support")]
+impl<T: DrmControlDevice + AsFd> DrmControlDevice for Device<T> {}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+/// Thrown when the underlying GBM device was already destroyed
+pub struct DeviceDestroyedError;
+
+impl fmt::Display for DeviceDestroyedError {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        write!(f, "The underlying GBM device was already destroyed")
+    }
+}
+
+impl error::Error for DeviceDestroyedError {
+    fn cause(&self) -> Option<&dyn error::Error> {
+        None
+    }
+}
diff --git a/src/lib.rs b/src/lib.rs
new file mode 100644
index 0000000..7d0c8cf
--- /dev/null
+++ b/src/lib.rs
@@ -0,0 +1,209 @@
+//! # Safe `libgbm` bindings for [rust](https://www.rust-lang.org)
+//!
+//! The Generic Buffer Manager
+//!
+//! This module provides an abstraction that the caller can use to request a
+//! buffer from the underlying memory management system for the platform.
+//!
+//! This allows the creation of portable code whilst still allowing access to
+//! the underlying memory manager.
+//!
+//! This library is best used in combination with [`drm-rs`](https://github.com/Smithay/drm-rs),
+//! provided through the `drm-support` feature.
+//!
+//! ## Example
+//!
+//! ```rust,no_run
+//! # extern crate drm;
+//! # extern crate gbm;
+//! # use drm::control::connector::Info as ConnectorInfo;
+//! # use drm::control::Mode;
+//! use drm::control::{self, crtc, framebuffer};
+//! use gbm::{BufferObjectFlags, Device, Format};
+//!
+//! # use std::fs::{File, OpenOptions};
+//! # use std::os::unix::io::{AsFd, BorrowedFd};
+//! #
+//! # use drm::control::Device as ControlDevice;
+//! # use drm::Device as BasicDevice;
+//! # struct Card(File);
+//! #
+//! # impl AsFd for Card {
+//! #     fn as_fd(&self) -> BorrowedFd {
+//! #         self.0.as_fd()
+//! #     }
+//! # }
+//! #
+//! # impl BasicDevice for Card {}
+//! # impl ControlDevice for Card {}
+//! #
+//! # fn init_drm_device() -> Card {
+//! #     let mut options = OpenOptions::new();
+//! #     options.read(true);
+//! #     options.write(true);
+//! #     let file = options.open("/dev/dri/card0").unwrap();
+//! #     Card(file)
+//! # }
+//! # fn main() {
+//! // ... init your drm device ...
+//! let drm = init_drm_device();
+//!
+//! // init a GBM device
+//! let gbm = Device::new(drm).unwrap();
+//!
+//! // create a 4x4 buffer
+//! let mut bo = gbm
+//!     .create_buffer_object::<()>(
+//!         1280,
+//!         720,
+//!         Format::Argb8888,
+//!         BufferObjectFlags::SCANOUT | BufferObjectFlags::WRITE,
+//!     )
+//!     .unwrap();
+//! // write something to it (usually use import or egl rendering instead)
+//! let buffer = {
+//!     let mut buffer = Vec::new();
+//!     for i in 0..1280 {
+//!         for _ in 0..720 {
+//!             buffer.push(if i % 2 == 0 { 0 } else { 255 });
+//!         }
+//!     }
+//!     buffer
+//! };
+//! bo.write(&buffer).unwrap();
+//!
+//! // create a framebuffer from our buffer
+//! let fb = gbm.add_framebuffer(&bo, 32, 32).unwrap();
+//!
+//! # let res_handles = gbm.resource_handles().unwrap();
+//! # let con = *res_handles.connectors().iter().next().unwrap();
+//! # let crtc_handle = *res_handles.crtcs().iter().next().unwrap();
+//! # let connector_info: ConnectorInfo = gbm.get_connector(con, false).unwrap();
+//! # let mode: Mode = connector_info.modes()[0];
+//! #
+//! // display it (and get a crtc, mode and connector before)
+//! gbm.set_crtc(crtc_handle, Some(fb), (0, 0), &[con], Some(mode))
+//!     .unwrap();
+//! # }
+//! ```
+#![warn(missing_debug_implementations)]
+#![deny(missing_docs)]
+
+extern crate gbm_sys as ffi;
+extern crate libc;
+
+#[cfg(feature = "import-wayland")]
+extern crate wayland_server;
+
+#[cfg(feature = "drm-support")]
+extern crate drm;
+
+extern crate drm_fourcc;
+
+#[macro_use]
+extern crate bitflags;
+
+mod buffer_object;
+mod device;
+mod surface;
+
+pub use self::buffer_object::*;
+pub use self::device::*;
+pub use self::surface::*;
+pub use drm_fourcc::{DrmFourcc as Format, DrmModifier as Modifier};
+
+use std::fmt;
+use std::sync::{Arc, Weak};
+
+/// Trait for types that allow to obtain the underlying raw libinput pointer.
+pub trait AsRaw<T> {
+    /// Receive a raw pointer representing this type.
+    fn as_raw(&self) -> *const T;
+
+    #[doc(hidden)]
+    fn as_raw_mut(&self) -> *mut T {
+        self.as_raw() as *mut _
+    }
+}
+
+struct PtrDrop<T>(*mut T, Option<Box<dyn FnOnce(*mut T) + Send + 'static>>);
+
+impl<T> Drop for PtrDrop<T> {
+    fn drop(&mut self) {
+        (self.1.take().unwrap())(self.0);
+    }
+}
+
+#[derive(Clone)]
+pub(crate) struct Ptr<T>(Arc<PtrDrop<T>>);
+// SAFETY: The types used with Ptr in this crate are all Send (namely gbm_device, gbm_surface and gbm_bo).
+// The type is private and can thus not be used unsoundly by other crates.
+unsafe impl<T> Send for Ptr<T> {}
+
+impl<T> Ptr<T> {
+    fn new<F: FnOnce(*mut T) + Send + 'static>(ptr: *mut T, destructor: F) -> Ptr<T> {
+        Ptr(Arc::new(PtrDrop(ptr, Some(Box::new(destructor)))))
+    }
+
+    fn downgrade(&self) -> WeakPtr<T> {
+        WeakPtr(Arc::downgrade(&self.0))
+    }
+}
+
+impl<T> std::ops::Deref for Ptr<T> {
+    type Target = *mut T;
+
+    fn deref(&self) -> &Self::Target {
+        &(self.0).0
+    }
+}
+
+impl<T> fmt::Pointer for Ptr<T> {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
+        fmt::Pointer::fmt(&self.0 .0, f)
+    }
+}
+
+#[derive(Clone)]
+pub(crate) struct WeakPtr<T>(Weak<PtrDrop<T>>);
+
+impl<T> WeakPtr<T> {
+    fn upgrade(&self) -> Option<Ptr<T>> {
+        self.0.upgrade().map(Ptr)
+    }
+}
+
+impl<T> fmt::Pointer for WeakPtr<T> {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
+        match self.upgrade() {
+            Some(x) => fmt::Pointer::fmt(&x, f),
+            None => fmt::Pointer::fmt(&std::ptr::null::<T>(), f),
+        }
+    }
+}
+
+unsafe impl<T> Send for WeakPtr<T> where Ptr<T>: Send {}
+
+#[cfg(test)]
+mod test {
+    use std::os::unix::io::OwnedFd;
+
+    fn is_send<T: Send>() {}
+
+    #[test]
+    fn device_is_send() {
+        is_send::<super::Device<std::fs::File>>();
+        is_send::<super::Device<OwnedFd>>();
+    }
+
+    #[test]
+    fn surface_is_send() {
+        is_send::<super::Surface<std::fs::File>>();
+        is_send::<super::Surface<OwnedFd>>();
+    }
+
+    #[test]
+    fn unmapped_bo_is_send() {
+        is_send::<super::BufferObject<()>>();
+    }
+}
diff --git a/src/surface.rs b/src/surface.rs
new file mode 100644
index 0000000..f8040d2
--- /dev/null
+++ b/src/surface.rs
@@ -0,0 +1,125 @@
+use crate::{AsRaw, BufferObject, DeviceDestroyedError, Ptr, WeakPtr};
+use std::error;
+use std::fmt;
+use std::marker::PhantomData;
+
+/// A GBM rendering surface
+pub struct Surface<T: 'static> {
+    ffi: Ptr<ffi::gbm_surface>,
+    _device: WeakPtr<ffi::gbm_device>,
+    _bo_userdata: PhantomData<T>,
+}
+
+impl<T: 'static> fmt::Debug for Surface<T> {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        f.debug_struct("Surface")
+            .field("ptr", &format_args!("{:p}", &self.ffi))
+            .field("device", &format_args!("{:p}", &self._device))
+            .finish()
+    }
+}
+
+/// Errors that may happen when locking the front buffer
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub enum FrontBufferError {
+    /// No free buffers are currently available
+    NoFreeBuffers,
+    /// An unknown error happened
+    Unknown,
+    /// Device was already released
+    Destroyed(DeviceDestroyedError),
+}
+
+impl fmt::Display for FrontBufferError {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        match *self {
+            FrontBufferError::NoFreeBuffers => write!(f, "No free buffers remaining"),
+            FrontBufferError::Unknown => write!(f, "Unknown error"),
+            FrontBufferError::Destroyed(ref err) => write!(f, "Buffer was destroyed: {}", err),
+        }
+    }
+}
+
+impl error::Error for FrontBufferError {
+    fn cause(&self) -> Option<&dyn error::Error> {
+        match *self {
+            FrontBufferError::Destroyed(ref err) => Some(err),
+            _ => None,
+        }
+    }
+}
+
+impl<T: 'static> Surface<T> {
+    ///  Return whether or not a surface has free (non-locked) buffers
+    ///
+    /// Before starting a new frame, the surface must have a buffer
+    /// available for rendering.  Initially, a GBM surface will have a free
+    /// buffer, but after one or more buffers
+    /// [have been locked](Self::lock_front_buffer()),
+    /// the application must check for a free buffer before rendering.
+    pub fn has_free_buffers(&self) -> bool {
+        let device = self._device.upgrade();
+        if device.is_some() {
+            unsafe { ffi::gbm_surface_has_free_buffers(*self.ffi) != 0 }
+        } else {
+            false
+        }
+    }
+
+    /// Lock the surface's current front buffer
+    ///
+    /// Locks rendering to the surface's current front buffer and returns
+    /// a handle to the underlying [`BufferObject`].
+    ///
+    /// If an error occurs a [`FrontBufferError`] is returned.
+    ///
+    /// # Safety
+    /// This function must be called exactly once after calling
+    /// `eglSwapBuffers`.  Calling it before any `eglSwapBuffers` has happened
+    /// on the surface or two or more times after `eglSwapBuffers` is an
+    /// error and may cause undefined behavior.
+    pub unsafe fn lock_front_buffer(&self) -> Result<BufferObject<T>, FrontBufferError> {
+        let device = self._device.upgrade();
+        if device.is_some() {
+            if ffi::gbm_surface_has_free_buffers(*self.ffi) != 0 {
+                let buffer_ptr = ffi::gbm_surface_lock_front_buffer(*self.ffi);
+                if !buffer_ptr.is_null() {
+                    let surface_ptr = self.ffi.downgrade();
+                    let buffer = BufferObject {
+                        ffi: Ptr::new(buffer_ptr, move |ptr| {
+                            if let Some(surface) = surface_ptr.upgrade() {
+                                ffi::gbm_surface_release_buffer(*surface, ptr);
+                            }
+                        }),
+                        _device: self._device.clone(),
+                        _userdata: std::marker::PhantomData,
+                    };
+                    Ok(buffer)
+                } else {
+                    Err(FrontBufferError::Unknown)
+                }
+            } else {
+                Err(FrontBufferError::NoFreeBuffers)
+            }
+        } else {
+            Err(FrontBufferError::Destroyed(DeviceDestroyedError))
+        }
+    }
+
+    pub(crate) unsafe fn new(
+        ffi: *mut ffi::gbm_surface,
+        device: WeakPtr<ffi::gbm_device>,
+    ) -> Surface<T> {
+        Surface {
+            ffi: Ptr::new(ffi, |ptr| ffi::gbm_surface_destroy(ptr)),
+            _device: device,
+            _bo_userdata: PhantomData,
+        }
+    }
+}
+
+impl<T: 'static> AsRaw<ffi::gbm_surface> for Surface<T> {
+    fn as_raw(&self) -> *const ffi::gbm_surface {
+        *self.ffi
+    }
+}