summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorInna Palant <ipalant@google.com>2024-03-28 06:46:35 +0000
committerInna Palant <ipalant@google.com>2024-03-28 06:46:35 +0000
commit10918a561516b014834e3501177e4c9fb7bc0610 (patch)
treeca5755223472eae63e67730eb7737cbdda877e24
parent10173ea7a8b6c900688a5891639a3b7b655eebb1 (diff)
parent3d24f3e9ec2c0f5e3e47867910fe79dc02959ac8 (diff)
downloaddrm-ffi-sdk-release.tar.gz
Merge remote-tracking branch 'origin/upstream'platform-tools-35.0.1sdk-releasebusytown-mac-infra-release
Import b/328179939
-rw-r--r--.cargo_vcs_info.json6
-rw-r--r--Android.bp23
-rw-r--r--Cargo.toml29
-rw-r--r--Cargo.toml.orig16
-rw-r--r--LICENSE23
-rw-r--r--METADATA20
-rw-r--r--MODULE_LICENSE_MIT0
-rw-r--r--OWNERS7
-rw-r--r--cargo_embargo.json3
-rw-r--r--src/gem.rs68
-rw-r--r--src/ioctl.rs281
-rw-r--r--src/lib.rs215
-rw-r--r--src/mode.rs938
-rw-r--r--src/syncobj.rs264
-rw-r--r--src/utils.rs38
15 files changed, 1931 insertions, 0 deletions
diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json
new file mode 100644
index 0000000..c628ad1
--- /dev/null
+++ b/.cargo_vcs_info.json
@@ -0,0 +1,6 @@
+{
+ "git": {
+ "sha1": "328742fddc675b3370057b382eb54acbc9b48c79"
+ },
+ "path_in_vcs": "drm-ffi"
+} \ No newline at end of file
diff --git a/Android.bp b/Android.bp
new file mode 100644
index 0000000..610101a
--- /dev/null
+++ b/Android.bp
@@ -0,0 +1,23 @@
+// 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: "libdrm_ffi",
+ host_supported: true,
+ crate_name: "drm_ffi",
+ cargo_env_compat: true,
+ cargo_pkg_version: "0.7.1",
+ srcs: ["src/lib.rs"],
+ edition: "2021",
+ rustlibs: [
+ "libdrm_sys",
+ "librustix",
+ ],
+ apex_available: [
+ "//apex_available:platform",
+ "//apex_available:anyapex",
+ ],
+ product_available: true,
+ vendor_available: true,
+}
diff --git a/Cargo.toml b/Cargo.toml
new file mode 100644
index 0000000..f0c9b04
--- /dev/null
+++ b/Cargo.toml
@@ -0,0 +1,29 @@
+# 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"
+rust-version = "1.65"
+name = "drm-ffi"
+version = "0.7.1"
+authors = ["Tyler Slabinski <tslabinski@slabity.net>"]
+description = "Safe, low-level bindings to the Direct Rendering Manager API"
+license = "MIT"
+repository = "https://github.com/Smithay/drm-rs"
+
+[dependencies.drm-sys]
+version = "0.6.1"
+
+[dependencies.rustix]
+version = "0.38.22"
+
+[features]
+use_bindgen = ["drm-sys/use_bindgen"]
diff --git a/Cargo.toml.orig b/Cargo.toml.orig
new file mode 100644
index 0000000..298170f
--- /dev/null
+++ b/Cargo.toml.orig
@@ -0,0 +1,16 @@
+[package]
+name = "drm-ffi"
+description = "Safe, low-level bindings to the Direct Rendering Manager API"
+repository = "https://github.com/Smithay/drm-rs"
+version = "0.7.1"
+license = "MIT"
+authors = ["Tyler Slabinski <tslabinski@slabity.net>"]
+rust-version = "1.65"
+edition = "2021"
+
+[dependencies]
+drm-sys = { path = "drm-sys", version = "0.6.1" }
+rustix = { version = "0.38.22" }
+
+[features]
+use_bindgen = ["drm-sys/use_bindgen"]
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..31aa793
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,23 @@
+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..12c8c9b
--- /dev/null
+++ b/METADATA
@@ -0,0 +1,20 @@
+name: "drm-ffi"
+description: "Safe, low-level bindings to the Direct Rendering Manager API"
+third_party {
+ identifier {
+ type: "crates.io"
+ value: "drm-ffi"
+ }
+ identifier {
+ type: "Archive"
+ value: "https://static.crates.io/crates/drm-ffi/drm-ffi-0.7.1.crate"
+ primary_source: true
+ }
+ version: "0.7.1"
+ 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
+
+dextero@google.com
+vill@google.com
+nputikhin@google.com
+istvannador@google.com
diff --git a/cargo_embargo.json b/cargo_embargo.json
new file mode 100644
index 0000000..cb908d7
--- /dev/null
+++ b/cargo_embargo.json
@@ -0,0 +1,3 @@
+{
+ "run_cargo": false
+}
diff --git a/src/gem.rs b/src/gem.rs
new file mode 100644
index 0000000..01b7f0b
--- /dev/null
+++ b/src/gem.rs
@@ -0,0 +1,68 @@
+//!
+//! Bindings to the Graphics Execution Manager
+//!
+
+use crate::ioctl;
+use drm_sys::*;
+
+use std::{
+ io,
+ os::unix::io::{AsRawFd, BorrowedFd},
+};
+
+/// Open a GEM object given it's 32-bit name, returning the handle.
+pub fn open(fd: BorrowedFd<'_>, name: u32) -> io::Result<drm_gem_open> {
+ let mut gem = drm_gem_open {
+ name,
+ ..Default::default()
+ };
+
+ unsafe {
+ ioctl::gem::open(fd, &mut gem)?;
+ }
+
+ Ok(gem)
+}
+
+/// Closes a GEM object given it's handle.
+pub fn close(fd: BorrowedFd<'_>, handle: u32) -> io::Result<drm_gem_close> {
+ let gem = drm_gem_close {
+ handle,
+ ..Default::default()
+ };
+
+ unsafe {
+ ioctl::gem::close(fd, &gem)?;
+ }
+
+ Ok(gem)
+}
+
+/// Converts a GEM object's handle to a PRIME file descriptor.
+pub fn handle_to_fd(fd: BorrowedFd<'_>, handle: u32, flags: u32) -> io::Result<drm_prime_handle> {
+ let mut prime = drm_prime_handle {
+ handle,
+ flags,
+ ..Default::default()
+ };
+
+ unsafe {
+ ioctl::gem::prime_handle_to_fd(fd, &mut prime)?;
+ }
+
+ Ok(prime)
+}
+
+/// Converts a PRIME file descriptor to a GEM object's handle.
+pub fn fd_to_handle(fd: BorrowedFd<'_>, primefd: BorrowedFd<'_>) -> io::Result<drm_prime_handle> {
+ let mut prime = drm_prime_handle {
+ fd: primefd.as_raw_fd(),
+ ..Default::default()
+ };
+
+ unsafe {
+ ioctl::gem::prime_fd_to_handle(fd, &mut prime)?;
+ }
+
+ Ok(prime)
+}
diff --git a/src/ioctl.rs b/src/ioctl.rs
new file mode 100644
index 0000000..0f46801
--- /dev/null
+++ b/src/ioctl.rs
@@ -0,0 +1,281 @@
+use std::{ffi::c_uint, io, os::unix::io::BorrowedFd};
+
+use drm_sys::*;
+use rustix::ioctl::{
+ ioctl, Getter, NoArg, NoneOpcode, ReadOpcode, ReadWriteOpcode, Setter, Updater, WriteOpcode,
+};
+
+macro_rules! ioctl_readwrite {
+ ($name:ident, $ioty:expr, $nr:expr, $ty:ty) => {
+ pub unsafe fn $name(fd: BorrowedFd, data: &mut $ty) -> io::Result<()> {
+ type Opcode = ReadWriteOpcode<$ioty, $nr, $ty>;
+ Ok(ioctl(fd, Updater::<Opcode, $ty>::new(data))?)
+ }
+ };
+}
+
+macro_rules! ioctl_read {
+ ($name:ident, $ioty:expr, $nr:expr, $ty:ty) => {
+ pub unsafe fn $name(fd: BorrowedFd) -> io::Result<$ty> {
+ type Opcode = ReadOpcode<$ioty, $nr, $ty>;
+ Ok(ioctl(fd, Getter::<Opcode, $ty>::new())?)
+ }
+ };
+}
+
+macro_rules! ioctl_write_ptr {
+ ($name:ident, $ioty:expr, $nr:expr, $ty:ty) => {
+ pub unsafe fn $name(fd: BorrowedFd, data: &$ty) -> io::Result<()> {
+ type Opcode = WriteOpcode<$ioty, $nr, $ty>;
+ Ok(ioctl(fd, Setter::<Opcode, $ty>::new(*data))?)
+ }
+ };
+}
+
+macro_rules! ioctl_none {
+ ($name:ident, $ioty:expr, $nr:expr) => {
+ pub unsafe fn $name(fd: BorrowedFd) -> io::Result<()> {
+ type Opcode = NoneOpcode<$ioty, $nr, ()>;
+ Ok(ioctl(fd, NoArg::<Opcode>::new())?)
+ }
+ };
+}
+
+/// Gets the bus ID of the device
+///
+/// # Locks DRM mutex: Yes
+/// # Permissions: None
+/// # Nodes: Primary
+ioctl_readwrite!(get_bus_id, DRM_IOCTL_BASE, 0x01, drm_unique);
+
+/// Get information about the client
+///
+/// # Locks DRM mutex: No
+/// # Permissions: None
+/// # Nodes: Primary
+ioctl_readwrite!(get_client, DRM_IOCTL_BASE, 0x05, drm_client);
+
+/// Get capabilities of the device.
+///
+/// # Locks DRM mutex: No
+/// # Permissions: None
+/// # Nodes: Primary, Render
+ioctl_readwrite!(get_cap, DRM_IOCTL_BASE, 0x0c, drm_get_cap);
+
+/// Tells the device we understand a capability
+///
+/// # Locks DRM mutex: Yes
+/// # Permissions: None
+/// # Nodes: Primary
+ioctl_write_ptr!(set_cap, DRM_IOCTL_BASE, 0x0d, drm_set_client_cap);
+
+/// Sets the requested interface version
+///
+/// # Locks DRM mutex: Yes
+/// # Permissions: Master
+/// # Nodes: Primary, control
+ioctl_readwrite!(set_version, DRM_IOCTL_BASE, 0x07, drm_set_version);
+
+/// Gets the current interface version
+///
+/// # Locks DRM mutex: No
+/// # Permissions: None
+/// # Nodes: All
+ioctl_readwrite!(get_version, DRM_IOCTL_BASE, 0x00, drm_version);
+
+/// Generates the client's authentication token
+///
+/// # Locks DRM mutex: No
+/// # Permissions: None
+/// # Nodes: Primary
+ioctl_read!(get_token, DRM_IOCTL_BASE, 0x02, drm_auth);
+
+/// Authenticates a client via their authentication token
+///
+/// # Locks DRM mutex: No
+/// # Permissions: Auth, Master
+/// # Nodes: Primary
+ioctl_write_ptr!(auth_token, DRM_IOCTL_BASE, 0x11, drm_auth);
+
+/// Acquires the DRM Master lock
+///
+/// # Locks DRM mutex: No
+/// # Permissions: Root
+/// # Nodes: Primary
+ioctl_none!(acquire_master, DRM_IOCTL_BASE, 0x1e);
+
+/// Drops the DRM Master lock
+///
+/// # Locks DRM mutex: No
+/// # Permissions: Root
+/// # Nodes: Primary
+ioctl_none!(release_master, DRM_IOCTL_BASE, 0x1f);
+
+/// Gets the IRQ number
+///
+/// # Locks DRM mutex: No
+/// # Permissions: None
+/// # Nodes: Primary
+ioctl_readwrite!(get_irq_from_bus_id, DRM_IOCTL_BASE, 0x03, drm_irq_busid);
+
+/// Enable the vblank interrupt and sleep until the requested sequence occurs
+///
+/// # Locks DRM mutex: No
+/// # Permissions: None
+/// # Nodes: Primary
+ioctl_readwrite!(wait_vblank, DRM_IOCTL_BASE, 0x3a, drm_wait_vblank);
+
+pub(crate) mod mode {
+ use super::*;
+
+ /// Modesetting resources
+ ioctl_readwrite!(get_resources, DRM_IOCTL_BASE, 0xA0, drm_mode_card_res);
+
+ ioctl_readwrite!(
+ get_plane_resources,
+ DRM_IOCTL_BASE,
+ 0xB5,
+ drm_mode_get_plane_res
+ );
+
+ /// Connector related functions
+ ioctl_readwrite!(get_connector, DRM_IOCTL_BASE, 0xA7, drm_mode_get_connector);
+
+ /// Encoder related functions
+ ioctl_readwrite!(get_encoder, DRM_IOCTL_BASE, 0xA6, drm_mode_get_encoder);
+
+ /// CRTC related functions
+ ioctl_readwrite!(get_crtc, DRM_IOCTL_BASE, 0xA1, drm_mode_crtc);
+ ioctl_readwrite!(set_crtc, DRM_IOCTL_BASE, 0xA2, drm_mode_crtc);
+
+ /// Gamma related functions
+ ioctl_readwrite!(get_gamma, DRM_IOCTL_BASE, 0xA4, drm_mode_crtc_lut);
+ ioctl_readwrite!(set_gamma, DRM_IOCTL_BASE, 0xA5, drm_mode_crtc_lut);
+
+ // TODO: Figure out GAMMA LUT arrays
+
+ /// FB related functions
+ ioctl_readwrite!(get_fb, DRM_IOCTL_BASE, 0xAD, drm_mode_fb_cmd);
+ ioctl_readwrite!(get_fb2, DRM_IOCTL_BASE, 0xCE, drm_mode_fb_cmd2);
+ ioctl_readwrite!(add_fb, DRM_IOCTL_BASE, 0xAE, drm_mode_fb_cmd);
+ ioctl_readwrite!(add_fb2, DRM_IOCTL_BASE, 0xB8, drm_mode_fb_cmd2);
+ ioctl_readwrite!(rm_fb, DRM_IOCTL_BASE, 0xAF, c_uint);
+
+ /// Plane related functions
+ ioctl_readwrite!(get_plane, DRM_IOCTL_BASE, 0xB6, drm_mode_get_plane);
+
+ ioctl_readwrite!(set_plane, DRM_IOCTL_BASE, 0xB7, drm_mode_set_plane);
+
+ /// Dumbbuffer related functions
+ ioctl_readwrite!(create_dumb, DRM_IOCTL_BASE, 0xB2, drm_mode_create_dumb);
+
+ ioctl_readwrite!(map_dumb, DRM_IOCTL_BASE, 0xB3, drm_mode_map_dumb);
+
+ ioctl_readwrite!(destroy_dumb, DRM_IOCTL_BASE, 0xB4, drm_mode_destroy_dumb);
+
+ /// Cursor related functions
+ ioctl_readwrite!(cursor, DRM_IOCTL_BASE, 0xA3, drm_mode_cursor);
+ ioctl_readwrite!(cursor2, DRM_IOCTL_BASE, 0xBB, drm_mode_cursor2);
+
+ /// Property related functions
+ ioctl_readwrite!(get_property, DRM_IOCTL_BASE, 0xAA, drm_mode_get_property);
+
+ ioctl_readwrite!(
+ connector_set_property,
+ DRM_IOCTL_BASE,
+ 0xAB,
+ drm_mode_connector_set_property
+ );
+
+ ioctl_readwrite!(
+ obj_get_properties,
+ DRM_IOCTL_BASE,
+ 0xB9,
+ drm_mode_obj_get_properties
+ );
+
+ ioctl_readwrite!(
+ obj_set_property,
+ DRM_IOCTL_BASE,
+ 0xBA,
+ drm_mode_obj_set_property
+ );
+
+ /// Property blobs
+ ioctl_readwrite!(get_blob, DRM_IOCTL_BASE, 0xAC, drm_mode_get_blob);
+
+ // TODO: Property blobs probably require a large buffer
+
+ ioctl_readwrite!(create_blob, DRM_IOCTL_BASE, 0xBD, drm_mode_create_blob);
+
+ ioctl_readwrite!(destroy_blob, DRM_IOCTL_BASE, 0xBE, drm_mode_destroy_blob);
+
+ /// Atomic modesetting related functions
+ ioctl_readwrite!(
+ crtc_page_flip,
+ DRM_IOCTL_BASE,
+ 0xB0,
+ drm_mode_crtc_page_flip
+ );
+
+ ioctl_readwrite!(dirty_fb, DRM_IOCTL_BASE, 0xB1, drm_mode_fb_dirty_cmd);
+
+ ioctl_readwrite!(atomic, DRM_IOCTL_BASE, 0xBC, drm_mode_atomic);
+
+ ioctl_readwrite!(create_lease, DRM_IOCTL_BASE, 0xC6, drm_mode_create_lease);
+ ioctl_readwrite!(list_lessees, DRM_IOCTL_BASE, 0xC7, drm_mode_list_lessees);
+ ioctl_readwrite!(get_lease, DRM_IOCTL_BASE, 0xC8, drm_mode_get_lease);
+ ioctl_readwrite!(revoke_lease, DRM_IOCTL_BASE, 0xC9, drm_mode_revoke_lease);
+}
+
+pub(crate) mod gem {
+ use super::*;
+
+ /// GEM related functions
+ ioctl_readwrite!(open, DRM_IOCTL_BASE, 0x0b, drm_gem_open);
+ ioctl_write_ptr!(close, DRM_IOCTL_BASE, 0x09, drm_gem_close);
+
+ /// Converts a buffer handle into a dma-buf file descriptor.
+ ioctl_readwrite!(prime_handle_to_fd, DRM_IOCTL_BASE, 0x2d, drm_prime_handle);
+
+ /// Converts a dma-buf file descriptor into a buffer handle.
+ ioctl_readwrite!(prime_fd_to_handle, DRM_IOCTL_BASE, 0x2e, drm_prime_handle);
+}
+
+pub(crate) mod syncobj {
+ use super::*;
+
+ /// Creates a syncobj.
+ ioctl_readwrite!(create, DRM_IOCTL_BASE, 0xBF, drm_syncobj_create);
+ /// Destroys a syncobj.
+ ioctl_readwrite!(destroy, DRM_IOCTL_BASE, 0xC0, drm_syncobj_destroy);
+ /// Exports a syncobj as an inter-process file descriptor or as a poll()-able sync file.
+ ioctl_readwrite!(handle_to_fd, DRM_IOCTL_BASE, 0xC1, drm_syncobj_handle);
+ /// Imports a file descriptor exported by [`handle_to_fd`] back into a process-local handle.
+ ioctl_readwrite!(fd_to_handle, DRM_IOCTL_BASE, 0xC2, drm_syncobj_handle);
+ /// Waits for one or more syncobjs to become signalled.
+ ioctl_readwrite!(wait, DRM_IOCTL_BASE, 0xC3, drm_syncobj_wait);
+ /// Resets (un-signals) one or more syncobjs.
+ ioctl_readwrite!(reset, DRM_IOCTL_BASE, 0xC4, drm_syncobj_array);
+ /// Signals one or more syncobjs.
+ ioctl_readwrite!(signal, DRM_IOCTL_BASE, 0xC5, drm_syncobj_array);
+
+ /// Waits for one or more specific timeline syncobj points.
+ ioctl_readwrite!(
+ timeline_wait,
+ DRM_IOCTL_BASE,
+ 0xCA,
+ drm_syncobj_timeline_wait
+ );
+ /// Queries for state of one or more timeline syncobjs.
+ ioctl_readwrite!(query, DRM_IOCTL_BASE, 0xCB, drm_syncobj_timeline_array);
+ /// Transfers one timeline syncobj point to another.
+ ioctl_readwrite!(transfer, DRM_IOCTL_BASE, 0xCC, drm_syncobj_transfer);
+ /// Signals one or more specific timeline syncobj points.
+ ioctl_readwrite!(
+ timeline_signal,
+ DRM_IOCTL_BASE,
+ 0xCD,
+ drm_syncobj_timeline_array
+ );
+}
diff --git a/src/lib.rs b/src/lib.rs
new file mode 100644
index 0000000..4be9614
--- /dev/null
+++ b/src/lib.rs
@@ -0,0 +1,215 @@
+//!
+//! Foreign function interface
+//!
+
+#![warn(missing_docs)]
+#![allow(unused_doc_comments)]
+
+pub use drm_sys::{self, *};
+
+#[macro_use]
+pub(crate) mod utils;
+
+pub mod gem;
+mod ioctl;
+pub mod mode;
+pub mod syncobj;
+
+use std::{
+ ffi::{c_int, c_ulong},
+ io,
+ os::unix::io::BorrowedFd,
+};
+
+///
+/// Bindings to the methods of authentication the DRM provides.
+///
+pub mod auth {
+ use crate::ioctl;
+ use drm_sys::*;
+
+ use std::{io, os::unix::io::BorrowedFd};
+
+ /// Get the 'Magic Authentication Token' for this file descriptor.
+ pub fn get_magic_token(fd: BorrowedFd<'_>) -> io::Result<drm_auth> {
+ unsafe { ioctl::get_token(fd) }
+ }
+
+ /// Authorize another process' 'Magic Authentication Token'.
+ pub fn auth_magic_token(fd: BorrowedFd<'_>, auth: u32) -> io::Result<drm_auth> {
+ let token = drm_auth { magic: auth };
+
+ unsafe {
+ ioctl::auth_token(fd, &token)?;
+ }
+
+ Ok(token)
+ }
+
+ /// Acquire the 'Master DRM Lock' for this file descriptor.
+ pub fn acquire_master(fd: BorrowedFd<'_>) -> io::Result<()> {
+ unsafe { ioctl::acquire_master(fd) }
+ }
+
+ /// Release the 'Master DRM Lock' for this file descriptor.
+ pub fn release_master(fd: BorrowedFd<'_>) -> io::Result<()> {
+ unsafe { ioctl::release_master(fd) }
+ }
+}
+
+/// Load this device's Bus ID into a buffer.
+pub fn get_bus_id(fd: BorrowedFd<'_>, mut buf: Option<&mut Vec<u8>>) -> io::Result<drm_unique> {
+ let mut sizes = drm_unique::default();
+ unsafe {
+ ioctl::get_bus_id(fd, &mut sizes)?;
+ }
+
+ if buf.is_none() {
+ return Ok(sizes);
+ }
+
+ map_reserve!(buf, sizes.unique_len as usize);
+
+ let mut busid = drm_unique {
+ unique_len: sizes.unique_len,
+ unique: map_ptr!(&buf),
+ };
+
+ unsafe {
+ ioctl::get_bus_id(fd, &mut busid)?;
+ }
+
+ map_set!(buf, busid.unique_len as usize);
+
+ Ok(busid)
+}
+
+/// Get a device's IRQ.
+pub fn get_interrupt_from_bus_id(
+ fd: BorrowedFd<'_>,
+ bus: c_int,
+ dev: c_int,
+ func: c_int,
+) -> io::Result<drm_irq_busid> {
+ let mut irq = drm_irq_busid {
+ busnum: bus,
+ devnum: dev,
+ funcnum: func,
+ ..Default::default()
+ };
+
+ unsafe {
+ ioctl::get_irq_from_bus_id(fd, &mut irq)?;
+ }
+
+ Ok(irq)
+}
+
+/// Get client information given a client's ID.
+pub fn get_client(fd: BorrowedFd<'_>, idx: c_int) -> io::Result<drm_client> {
+ let mut client = drm_client {
+ idx,
+ ..Default::default()
+ };
+
+ unsafe {
+ ioctl::get_client(fd, &mut client)?;
+ }
+
+ Ok(client)
+}
+
+/// Check if a capability is set.
+pub fn get_capability(fd: BorrowedFd<'_>, cty: u64) -> io::Result<drm_get_cap> {
+ let mut cap = drm_get_cap {
+ capability: cty,
+ ..Default::default()
+ };
+
+ unsafe {
+ ioctl::get_cap(fd, &mut cap)?;
+ }
+
+ Ok(cap)
+}
+
+/// Attempt to enable/disable a client's capability.
+pub fn set_capability(fd: BorrowedFd<'_>, cty: u64, val: bool) -> io::Result<drm_set_client_cap> {
+ let cap = drm_set_client_cap {
+ capability: cty,
+ value: val as u64,
+ };
+
+ unsafe {
+ ioctl::set_cap(fd, &cap)?;
+ }
+
+ Ok(cap)
+}
+
+/// Sets the requested interface version
+pub fn set_version(fd: BorrowedFd<'_>, version: &mut drm_set_version) -> io::Result<()> {
+ unsafe { ioctl::set_version(fd, version) }
+}
+
+/// Gets the driver version for this device.
+pub fn get_version(
+ fd: BorrowedFd<'_>,
+ mut name_buf: Option<&mut Vec<i8>>,
+ mut date_buf: Option<&mut Vec<i8>>,
+ mut desc_buf: Option<&mut Vec<i8>>,
+) -> io::Result<drm_version> {
+ let mut sizes = drm_version::default();
+ unsafe {
+ ioctl::get_version(fd, &mut sizes)?;
+ }
+
+ map_reserve!(name_buf, sizes.name_len as usize);
+ map_reserve!(date_buf, sizes.date_len as usize);
+ map_reserve!(desc_buf, sizes.desc_len as usize);
+
+ let mut version = drm_version {
+ name_len: map_len!(&name_buf),
+ name: map_ptr!(&name_buf),
+ date_len: map_len!(&date_buf),
+ date: map_ptr!(&date_buf),
+ desc_len: map_len!(&desc_buf),
+ desc: map_ptr!(&desc_buf),
+ ..Default::default()
+ };
+
+ unsafe {
+ ioctl::get_version(fd, &mut version)?;
+ }
+
+ map_set!(name_buf, version.name_len as usize);
+ map_set!(date_buf, version.date_len as usize);
+ map_set!(desc_buf, version.desc_len as usize);
+
+ Ok(version)
+}
+
+/// Waits for a vblank.
+pub fn wait_vblank(
+ fd: BorrowedFd<'_>,
+ type_: u32,
+ sequence: u32,
+ signal: usize,
+) -> io::Result<drm_wait_vblank_reply> {
+ // We can't assume the kernel will completely fill the reply in the union
+ // with valid data (it won't populate the timestamp if the event flag is
+ // set, for example), so use `default` to ensure the structure is completely
+ // initialized with zeros
+ let mut wait_vblank = drm_wait_vblank::default();
+ wait_vblank.request = drm_wait_vblank_request {
+ type_,
+ sequence,
+ signal: signal as c_ulong,
+ };
+
+ unsafe {
+ ioctl::wait_vblank(fd, &mut wait_vblank)?;
+ };
+
+ Ok(unsafe { wait_vblank.reply })
+}
diff --git a/src/mode.rs b/src/mode.rs
new file mode 100644
index 0000000..1b30b8f
--- /dev/null
+++ b/src/mode.rs
@@ -0,0 +1,938 @@
+//!
+//! Bindings to the DRM's modesetting capabilities.
+//!
+
+#![allow(clippy::too_many_arguments)]
+
+use crate::ioctl;
+use drm_sys::*;
+
+use std::{io, os::unix::io::BorrowedFd};
+
+/// Enumerate most card resources.
+pub fn get_resources(
+ fd: BorrowedFd<'_>,
+ mut fbs: Option<&mut Vec<u32>>,
+ mut crtcs: Option<&mut Vec<u32>>,
+ mut connectors: Option<&mut Vec<u32>>,
+ mut encoders: Option<&mut Vec<u32>>,
+) -> io::Result<drm_mode_card_res> {
+ let mut sizes = drm_mode_card_res::default();
+ unsafe {
+ ioctl::mode::get_resources(fd, &mut sizes)?;
+ }
+
+ map_reserve!(fbs, sizes.count_fbs as usize);
+ map_reserve!(crtcs, sizes.count_crtcs as usize);
+ map_reserve!(connectors, sizes.count_connectors as usize);
+ map_reserve!(encoders, sizes.count_encoders as usize);
+
+ let mut res = drm_mode_card_res {
+ fb_id_ptr: map_ptr!(&fbs),
+ crtc_id_ptr: map_ptr!(&crtcs),
+ connector_id_ptr: map_ptr!(&connectors),
+ encoder_id_ptr: map_ptr!(&encoders),
+ count_fbs: map_len!(&fbs),
+ count_crtcs: map_len!(&crtcs),
+ count_connectors: map_len!(&connectors),
+ count_encoders: map_len!(&encoders),
+ ..Default::default()
+ };
+
+ unsafe {
+ ioctl::mode::get_resources(fd, &mut res)?;
+ }
+
+ map_set!(fbs, res.count_fbs as usize);
+ map_set!(crtcs, res.count_crtcs as usize);
+ map_set!(connectors, res.count_connectors as usize);
+ map_set!(encoders, res.count_encoders as usize);
+
+ Ok(res)
+}
+
+/// Enumerate plane resources.
+pub fn get_plane_resources(
+ fd: BorrowedFd<'_>,
+ mut planes: Option<&mut Vec<u32>>,
+) -> io::Result<drm_mode_get_plane_res> {
+ let mut sizes = drm_mode_get_plane_res::default();
+ unsafe {
+ ioctl::mode::get_plane_resources(fd, &mut sizes)?;
+ }
+
+ if planes.is_none() {
+ return Ok(sizes);
+ }
+
+ map_reserve!(planes, sizes.count_planes as usize);
+
+ let mut res = drm_mode_get_plane_res {
+ plane_id_ptr: map_ptr!(&planes),
+ count_planes: sizes.count_planes,
+ };
+
+ unsafe {
+ ioctl::mode::get_plane_resources(fd, &mut res)?;
+ }
+
+ map_set!(planes, res.count_planes as usize);
+
+ Ok(res)
+}
+
+/// Get info about a framebuffer.
+pub fn get_framebuffer(fd: BorrowedFd<'_>, fb_id: u32) -> io::Result<drm_mode_fb_cmd> {
+ let mut info = drm_mode_fb_cmd {
+ fb_id,
+ ..Default::default()
+ };
+
+ unsafe {
+ ioctl::mode::get_fb(fd, &mut info)?;
+ }
+
+ Ok(info)
+}
+
+/// Add a new framebuffer.
+pub fn add_fb(
+ fd: BorrowedFd<'_>,
+ width: u32,
+ height: u32,
+ pitch: u32,
+ bpp: u32,
+ depth: u32,
+ handle: u32,
+) -> io::Result<drm_mode_fb_cmd> {
+ let mut fb = drm_mode_fb_cmd {
+ width,
+ height,
+ pitch,
+ bpp,
+ depth,
+ handle,
+ ..Default::default()
+ };
+
+ unsafe {
+ ioctl::mode::add_fb(fd, &mut fb)?;
+ }
+
+ Ok(fb)
+}
+
+/// Get info about a framebuffer (with modifiers).
+pub fn get_framebuffer2(fd: BorrowedFd<'_>, fb_id: u32) -> io::Result<drm_mode_fb_cmd2> {
+ let mut info = drm_mode_fb_cmd2 {
+ fb_id,
+ ..Default::default()
+ };
+
+ unsafe {
+ ioctl::mode::get_fb2(fd, &mut info)?;
+ }
+
+ Ok(info)
+}
+
+/// Add a new framebuffer (with modifiers)
+pub fn add_fb2(
+ fd: BorrowedFd<'_>,
+ width: u32,
+ height: u32,
+ fmt: u32,
+ handles: &[u32; 4],
+ pitches: &[u32; 4],
+ offsets: &[u32; 4],
+ modifier: &[u64; 4],
+ flags: u32,
+) -> io::Result<drm_mode_fb_cmd2> {
+ let mut fb = drm_mode_fb_cmd2 {
+ width,
+ height,
+ pixel_format: fmt,
+ flags,
+ handles: *handles,
+ pitches: *pitches,
+ offsets: *offsets,
+ modifier: *modifier,
+ ..Default::default()
+ };
+
+ unsafe {
+ ioctl::mode::add_fb2(fd, &mut fb)?;
+ }
+
+ Ok(fb)
+}
+
+/// Remove a framebuffer.
+pub fn rm_fb(fd: BorrowedFd<'_>, mut id: u32) -> io::Result<()> {
+ unsafe {
+ ioctl::mode::rm_fb(fd, &mut id)?;
+ }
+
+ Ok(())
+}
+
+/// Mark a framebuffer as dirty.
+pub fn dirty_fb(
+ fd: BorrowedFd<'_>,
+ fb_id: u32,
+ clips: &[drm_clip_rect],
+) -> io::Result<drm_mode_fb_dirty_cmd> {
+ let mut dirty = drm_mode_fb_dirty_cmd {
+ fb_id,
+ num_clips: clips.len() as _,
+ clips_ptr: clips.as_ptr() as _,
+ ..Default::default()
+ };
+
+ unsafe {
+ ioctl::mode::dirty_fb(fd, &mut dirty)?;
+ }
+
+ Ok(dirty)
+}
+
+/// Get info about a CRTC
+pub fn get_crtc(fd: BorrowedFd<'_>, crtc_id: u32) -> io::Result<drm_mode_crtc> {
+ let mut info = drm_mode_crtc {
+ crtc_id,
+ ..Default::default()
+ };
+
+ unsafe {
+ ioctl::mode::get_crtc(fd, &mut info)?;
+ }
+
+ Ok(info)
+}
+
+/// Set CRTC state
+pub fn set_crtc(
+ fd: BorrowedFd<'_>,
+ crtc_id: u32,
+ fb_id: u32,
+ x: u32,
+ y: u32,
+ conns: &[u32],
+ mode: Option<drm_mode_modeinfo>,
+) -> io::Result<drm_mode_crtc> {
+ let mut crtc = drm_mode_crtc {
+ set_connectors_ptr: conns.as_ptr() as _,
+ count_connectors: conns.len() as _,
+ crtc_id,
+ fb_id,
+ x,
+ y,
+ mode_valid: match mode {
+ Some(_) => 1,
+ None => 0,
+ },
+ mode: mode.unwrap_or_default(),
+ ..Default::default()
+ };
+
+ unsafe {
+ ioctl::mode::set_crtc(fd, &mut crtc)?;
+ }
+
+ Ok(crtc)
+}
+
+/// Get CRTC gamma ramp
+pub fn get_gamma(
+ fd: BorrowedFd<'_>,
+ crtc_id: u32,
+ size: usize,
+ red: &mut [u16],
+ green: &mut [u16],
+ blue: &mut [u16],
+) -> io::Result<drm_mode_crtc_lut> {
+ let mut lut = drm_mode_crtc_lut {
+ crtc_id,
+ gamma_size: size as _,
+ red: red.as_mut_ptr() as _,
+ green: green.as_mut_ptr() as _,
+ blue: blue.as_mut_ptr() as _,
+ };
+
+ unsafe {
+ ioctl::mode::get_gamma(fd, &mut lut)?;
+ }
+
+ Ok(lut)
+}
+
+/// Set CRTC gamma ramp
+pub fn set_gamma(
+ fd: BorrowedFd<'_>,
+ crtc_id: u32,
+ size: usize,
+ red: &[u16],
+ green: &[u16],
+ blue: &[u16],
+) -> io::Result<drm_mode_crtc_lut> {
+ let mut lut = drm_mode_crtc_lut {
+ crtc_id,
+ gamma_size: size as _,
+ red: red.as_ptr() as _,
+ green: green.as_ptr() as _,
+ blue: blue.as_ptr() as _,
+ };
+
+ unsafe {
+ ioctl::mode::set_gamma(fd, &mut lut)?;
+ }
+
+ Ok(lut)
+}
+
+/// Set cursor state
+///
+/// The buffer must be allocated using the buffer manager of the driver (GEM or TTM). It is not
+/// allowed to be a dumb buffer.
+#[deprecated = "use a cursor plane instead"]
+pub fn set_cursor(
+ fd: BorrowedFd<'_>,
+ crtc_id: u32,
+ buf_id: u32,
+ width: u32,
+ height: u32,
+) -> io::Result<drm_mode_cursor> {
+ let mut cursor = drm_mode_cursor {
+ flags: DRM_MODE_CURSOR_BO,
+ crtc_id,
+ width,
+ height,
+ handle: buf_id,
+ ..Default::default()
+ };
+
+ unsafe {
+ ioctl::mode::cursor(fd, &mut cursor)?;
+ }
+
+ Ok(cursor)
+}
+
+/// Set cursor state (with hotspot position)
+///
+/// The buffer must be allocated using the buffer manager of the driver (GEM or TTM). It is not
+/// allowed to be a dumb buffer.
+///
+/// The hotspot position is used to coordinate the guest and host cursor location in case of
+/// virtualization.
+#[deprecated = "use a cursor plane instead"]
+pub fn set_cursor2(
+ fd: BorrowedFd<'_>,
+ crtc_id: u32,
+ buf_id: u32,
+ width: u32,
+ height: u32,
+ hot_x: i32,
+ hot_y: i32,
+) -> io::Result<drm_mode_cursor2> {
+ let mut cursor = drm_mode_cursor2 {
+ flags: DRM_MODE_CURSOR_BO,
+ crtc_id,
+ width,
+ height,
+ handle: buf_id,
+ hot_x,
+ hot_y,
+ ..Default::default()
+ };
+
+ unsafe {
+ ioctl::mode::cursor2(fd, &mut cursor)?;
+ }
+
+ Ok(cursor)
+}
+
+/// Move cursor
+#[deprecated = "use a cursor plane instead"]
+pub fn move_cursor(
+ fd: BorrowedFd<'_>,
+ crtc_id: u32,
+ x: i32,
+ y: i32,
+) -> io::Result<drm_mode_cursor> {
+ let mut cursor = drm_mode_cursor {
+ flags: DRM_MODE_CURSOR_MOVE,
+ crtc_id,
+ x,
+ y,
+ ..Default::default()
+ };
+
+ unsafe {
+ ioctl::mode::cursor(fd, &mut cursor)?;
+ }
+
+ Ok(cursor)
+}
+
+/// Get info about a connector
+pub fn get_connector(
+ fd: BorrowedFd<'_>,
+ connector_id: u32,
+ mut props: Option<&mut Vec<u32>>,
+ mut prop_values: Option<&mut Vec<u64>>,
+ mut modes: Option<&mut Vec<drm_mode_modeinfo>>,
+ mut encoders: Option<&mut Vec<u32>>,
+ force_probe: bool,
+) -> io::Result<drm_mode_get_connector> {
+ assert_eq!(props.is_some(), prop_values.is_some());
+
+ let tmp_mode = drm_mode_modeinfo::default();
+ let mut sizes = drm_mode_get_connector {
+ connector_id,
+ modes_ptr: if force_probe {
+ 0
+ } else {
+ &tmp_mode as *const _ as _
+ },
+ count_modes: if force_probe { 0 } else { 1 },
+ ..Default::default()
+ };
+
+ unsafe {
+ ioctl::mode::get_connector(fd, &mut sizes)?;
+ }
+
+ let info = loop {
+ map_reserve!(props, sizes.count_props as usize);
+ map_reserve!(prop_values, sizes.count_props as usize);
+ map_reserve!(modes, sizes.count_modes as usize);
+ map_reserve!(encoders, sizes.count_encoders as usize);
+
+ let mut info = drm_mode_get_connector {
+ connector_id,
+ encoders_ptr: map_ptr!(&encoders),
+ modes_ptr: match &mut modes {
+ Some(b) => b.as_mut_ptr() as _,
+ None => {
+ if force_probe {
+ 0 as _
+ } else {
+ &tmp_mode as *const _ as _
+ }
+ }
+ },
+ props_ptr: map_ptr!(&props),
+ prop_values_ptr: map_ptr!(&prop_values),
+ count_modes: match &modes {
+ Some(b) => b.capacity() as _,
+ None => {
+ if force_probe {
+ 0
+ } else {
+ 1
+ }
+ }
+ },
+ count_props: map_len!(&props),
+ count_encoders: map_len!(&encoders),
+ ..Default::default()
+ };
+
+ unsafe {
+ ioctl::mode::get_connector(fd, &mut info)?;
+ }
+
+ if info.count_modes == sizes.count_modes
+ && info.count_encoders == sizes.count_encoders
+ && info.count_props == sizes.count_props
+ {
+ break info;
+ } else {
+ sizes = info;
+ }
+ };
+
+ map_set!(modes, info.count_modes as usize);
+ map_set!(props, info.count_props as usize);
+ map_set!(prop_values, info.count_props as usize);
+ map_set!(encoders, info.count_encoders as usize);
+
+ Ok(info)
+}
+
+/// Get info about an encoder
+pub fn get_encoder(fd: BorrowedFd<'_>, encoder_id: u32) -> io::Result<drm_mode_get_encoder> {
+ let mut info = drm_mode_get_encoder {
+ encoder_id,
+ ..Default::default()
+ };
+
+ unsafe {
+ ioctl::mode::get_encoder(fd, &mut info)?;
+ }
+
+ Ok(info)
+}
+
+/// Get info about a plane.
+pub fn get_plane(
+ fd: BorrowedFd<'_>,
+ plane_id: u32,
+ mut formats: Option<&mut Vec<u32>>,
+) -> io::Result<drm_mode_get_plane> {
+ let mut sizes = drm_mode_get_plane {
+ plane_id,
+ ..Default::default()
+ };
+
+ unsafe {
+ ioctl::mode::get_plane(fd, &mut sizes)?;
+ }
+
+ if formats.is_none() {
+ return Ok(sizes);
+ }
+
+ map_reserve!(formats, sizes.count_format_types as usize);
+
+ let mut info = drm_mode_get_plane {
+ plane_id,
+ count_format_types: sizes.count_format_types,
+ format_type_ptr: map_ptr!(&formats),
+ ..Default::default()
+ };
+
+ unsafe {
+ ioctl::mode::get_plane(fd, &mut info)?;
+ }
+
+ map_set!(formats, info.count_format_types as usize);
+
+ Ok(info)
+}
+
+/// Set plane state.
+pub fn set_plane(
+ fd: BorrowedFd<'_>,
+ plane_id: u32,
+ crtc_id: u32,
+ fb_id: u32,
+ flags: u32,
+ crtc_x: i32,
+ crtc_y: i32,
+ crtc_w: u32,
+ crtc_h: u32,
+ src_x: u32,
+ src_y: u32,
+ src_w: u32,
+ src_h: u32,
+) -> io::Result<drm_mode_set_plane> {
+ let mut plane = drm_mode_set_plane {
+ plane_id,
+ crtc_id,
+ fb_id,
+ flags,
+ crtc_x,
+ crtc_y,
+ crtc_w,
+ crtc_h,
+ src_x,
+ src_y,
+ src_h,
+ src_w,
+ };
+
+ unsafe {
+ ioctl::mode::set_plane(fd, &mut plane)?;
+ }
+
+ Ok(plane)
+}
+
+/// Get property
+pub fn get_property(
+ fd: BorrowedFd<'_>,
+ prop_id: u32,
+ mut values: Option<&mut Vec<u64>>,
+ mut enums: Option<&mut Vec<drm_mode_property_enum>>,
+) -> io::Result<drm_mode_get_property> {
+ let mut prop = drm_mode_get_property {
+ prop_id,
+ ..Default::default()
+ };
+
+ unsafe {
+ ioctl::mode::get_property(fd, &mut prop)?;
+ }
+
+ // There is no need to call get_property() twice if there is nothing else to retrieve.
+ if prop.count_values == 0 && prop.count_enum_blobs == 0 {
+ return Ok(prop);
+ }
+
+ map_reserve!(values, prop.count_values as usize);
+ map_reserve!(enums, prop.count_enum_blobs as usize);
+
+ prop.values_ptr = map_ptr!(&values);
+ prop.enum_blob_ptr = map_ptr!(&enums);
+
+ unsafe {
+ ioctl::mode::get_property(fd, &mut prop)?;
+ }
+
+ map_set!(values, prop.count_values as usize);
+ map_set!(enums, prop.count_enum_blobs as usize);
+
+ Ok(prop)
+}
+
+/// Set property
+pub fn set_connector_property(
+ fd: BorrowedFd<'_>,
+ connector_id: u32,
+ prop_id: u32,
+ value: u64,
+) -> io::Result<drm_mode_connector_set_property> {
+ let mut prop = drm_mode_connector_set_property {
+ value,
+ prop_id,
+ connector_id,
+ };
+
+ unsafe {
+ ioctl::mode::connector_set_property(fd, &mut prop)?;
+ }
+
+ Ok(prop)
+}
+
+/// Get the value of a property blob
+pub fn get_property_blob(
+ fd: BorrowedFd<'_>,
+ blob_id: u32,
+ mut data: Option<&mut Vec<u8>>,
+) -> io::Result<drm_mode_get_blob> {
+ let mut sizes = drm_mode_get_blob {
+ blob_id,
+ ..Default::default()
+ };
+
+ unsafe {
+ ioctl::mode::get_blob(fd, &mut sizes)?;
+ }
+
+ if data.is_none() {
+ return Ok(sizes);
+ }
+
+ map_reserve!(data, sizes.length as usize);
+
+ let mut blob = drm_mode_get_blob {
+ blob_id,
+ length: sizes.length,
+ data: map_ptr!(&data),
+ };
+
+ unsafe {
+ ioctl::mode::get_blob(fd, &mut blob)?;
+ }
+
+ map_set!(data, blob.length as usize);
+
+ Ok(blob)
+}
+
+/// Create a property blob
+pub fn create_property_blob(
+ fd: BorrowedFd<'_>,
+ data: &mut [u8],
+) -> io::Result<drm_mode_create_blob> {
+ let mut blob = drm_mode_create_blob {
+ data: data.as_mut_ptr() as _,
+ length: data.len() as _,
+ ..Default::default()
+ };
+
+ unsafe {
+ ioctl::mode::create_blob(fd, &mut blob)?;
+ }
+
+ Ok(blob)
+}
+
+/// Destroy a property blob
+pub fn destroy_property_blob(fd: BorrowedFd<'_>, id: u32) -> io::Result<drm_mode_destroy_blob> {
+ let mut blob = drm_mode_destroy_blob { blob_id: id };
+
+ unsafe {
+ ioctl::mode::destroy_blob(fd, &mut blob)?;
+ }
+
+ Ok(blob)
+}
+
+/// Get properties from an object
+pub fn get_properties(
+ fd: BorrowedFd<'_>,
+ obj_id: u32,
+ obj_type: u32,
+ mut props: Option<&mut Vec<u32>>,
+ mut values: Option<&mut Vec<u64>>,
+) -> io::Result<drm_mode_obj_get_properties> {
+ assert_eq!(props.is_some(), values.is_some());
+
+ let mut sizes = drm_mode_obj_get_properties {
+ obj_id,
+ obj_type,
+ ..Default::default()
+ };
+
+ unsafe {
+ ioctl::mode::obj_get_properties(fd, &mut sizes)?;
+ }
+
+ map_reserve!(props, sizes.count_props as usize);
+ map_reserve!(values, sizes.count_props as usize);
+
+ let mut info = drm_mode_obj_get_properties {
+ props_ptr: map_ptr!(&props),
+ prop_values_ptr: map_ptr!(&values),
+ count_props: map_len!(&props),
+ obj_id,
+ obj_type,
+ };
+
+ unsafe {
+ ioctl::mode::obj_get_properties(fd, &mut info)?;
+ }
+
+ map_set!(props, info.count_props as usize);
+ map_set!(values, info.count_props as usize);
+
+ Ok(info)
+}
+
+/// Set the properties of an object
+pub fn set_property(
+ fd: BorrowedFd<'_>,
+ prop_id: u32,
+ obj_id: u32,
+ obj_type: u32,
+ value: u64,
+) -> io::Result<()> {
+ let mut prop = drm_mode_obj_set_property {
+ value,
+ prop_id,
+ obj_id,
+ obj_type,
+ };
+
+ unsafe {
+ ioctl::mode::obj_set_property(fd, &mut prop)?;
+ }
+
+ Ok(())
+}
+
+/// Schedule a page flip
+pub fn page_flip(
+ fd: BorrowedFd<'_>,
+ crtc_id: u32,
+ fb_id: u32,
+ flags: u32,
+ sequence: u32,
+) -> io::Result<()> {
+ let mut flip = drm_mode_crtc_page_flip {
+ crtc_id,
+ fb_id,
+ flags,
+ // Same struct as drm_mode_crtc_page_flip_target
+ reserved: sequence,
+ user_data: crtc_id as _,
+ };
+
+ unsafe {
+ ioctl::mode::crtc_page_flip(fd, &mut flip)?;
+ }
+
+ Ok(())
+}
+
+/// Atomically set properties
+pub fn atomic_commit(
+ fd: BorrowedFd<'_>,
+ flags: u32,
+ objs: &mut [u32],
+ prop_counts: &mut [u32],
+ props: &mut [u32],
+ values: &mut [u64],
+) -> io::Result<()> {
+ let mut atomic = drm_mode_atomic {
+ flags,
+ count_objs: objs.len() as _,
+ objs_ptr: objs.as_mut_ptr() as _,
+ count_props_ptr: prop_counts.as_mut_ptr() as _,
+ props_ptr: props.as_mut_ptr() as _,
+ prop_values_ptr: values.as_mut_ptr() as _,
+ ..Default::default()
+ };
+
+ unsafe {
+ ioctl::mode::atomic(fd, &mut atomic)?;
+ }
+
+ Ok(())
+}
+
+/// Create a drm lease
+pub fn create_lease(
+ fd: BorrowedFd<'_>,
+ objects: &[u32],
+ flags: u32,
+) -> io::Result<drm_mode_create_lease> {
+ let mut data = drm_mode_create_lease {
+ object_ids: objects.as_ptr() as _,
+ object_count: objects.len() as u32,
+ flags,
+ ..Default::default()
+ };
+
+ unsafe {
+ ioctl::mode::create_lease(fd, &mut data)?;
+ }
+
+ Ok(data)
+}
+
+/// List all active drm leases
+pub fn list_lessees(
+ fd: BorrowedFd<'_>,
+ mut lessees: Option<&mut Vec<u32>>,
+) -> io::Result<drm_mode_list_lessees> {
+ let mut sizes = drm_mode_list_lessees::default();
+
+ unsafe {
+ ioctl::mode::list_lessees(fd, &mut sizes)?;
+ };
+
+ map_reserve!(lessees, sizes.count_lessees as usize);
+
+ let mut data = drm_mode_list_lessees {
+ lessees_ptr: map_ptr!(&lessees),
+ count_lessees: map_len!(&lessees),
+ ..Default::default()
+ };
+
+ unsafe {
+ ioctl::mode::list_lessees(fd, &mut data)?;
+ };
+
+ map_set!(lessees, data.count_lessees as usize);
+
+ Ok(data)
+}
+
+/// Get leased objects for a lease file descriptor
+pub fn get_lease(
+ fd: BorrowedFd<'_>,
+ mut objects: Option<&mut Vec<u32>>,
+) -> io::Result<drm_mode_get_lease> {
+ let mut sizes = drm_mode_get_lease::default();
+
+ unsafe {
+ ioctl::mode::get_lease(fd, &mut sizes)?;
+ }
+
+ map_reserve!(objects, sizes.count_objects as usize);
+
+ let mut data = drm_mode_get_lease {
+ count_objects: map_len!(&objects),
+ objects_ptr: map_ptr!(&objects),
+ ..Default::default()
+ };
+
+ unsafe {
+ ioctl::mode::get_lease(fd, &mut data)?;
+ }
+
+ map_set!(objects, data.count_objects as usize);
+
+ Ok(data)
+}
+
+/// Revoke previously issued lease
+pub fn revoke_lease(fd: BorrowedFd<'_>, lessee_id: u32) -> io::Result<()> {
+ let mut data = drm_mode_revoke_lease { lessee_id };
+
+ unsafe {
+ ioctl::mode::revoke_lease(fd, &mut data)?;
+ }
+
+ Ok(())
+}
+
+///
+/// Dumbbuffers are basic buffers that can be used for scanout.
+///
+pub mod dumbbuffer {
+ use crate::ioctl;
+ use drm_sys::*;
+
+ use std::{io, os::unix::io::BorrowedFd};
+
+ /// Create a dumb buffer
+ pub fn create(
+ fd: BorrowedFd<'_>,
+ width: u32,
+ height: u32,
+ bpp: u32,
+ flags: u32,
+ ) -> io::Result<drm_mode_create_dumb> {
+ let mut db = drm_mode_create_dumb {
+ height,
+ width,
+ bpp,
+ flags,
+ ..Default::default()
+ };
+
+ unsafe {
+ ioctl::mode::create_dumb(fd, &mut db)?;
+ }
+
+ Ok(db)
+ }
+
+ /// Destroy a dumb buffer
+ pub fn destroy(fd: BorrowedFd<'_>, handle: u32) -> io::Result<drm_mode_destroy_dumb> {
+ let mut db = drm_mode_destroy_dumb { handle };
+
+ unsafe {
+ ioctl::mode::destroy_dumb(fd, &mut db)?;
+ }
+
+ Ok(db)
+ }
+
+ /// Map a dump buffer and prep it for an mmap
+ pub fn map(
+ fd: BorrowedFd<'_>,
+ handle: u32,
+ pad: u32,
+ offset: u64,
+ ) -> io::Result<drm_mode_map_dumb> {
+ let mut map = drm_mode_map_dumb {
+ handle,
+ pad,
+ offset,
+ };
+
+ unsafe {
+ ioctl::mode::map_dumb(fd, &mut map)?;
+ }
+
+ Ok(map)
+ }
+}
diff --git a/src/syncobj.rs b/src/syncobj.rs
new file mode 100644
index 0000000..6cb45a9
--- /dev/null
+++ b/src/syncobj.rs
@@ -0,0 +1,264 @@
+//!
+//! Bindings for DRM sync objects
+//!
+
+use crate::ioctl;
+use drm_sys::*;
+
+use std::{
+ io,
+ os::unix::io::{AsRawFd, BorrowedFd},
+};
+
+/// Creates a syncobj.
+pub fn create(fd: BorrowedFd<'_>, signaled: bool) -> io::Result<drm_syncobj_create> {
+ let mut args = drm_syncobj_create {
+ handle: 0,
+ flags: if signaled {
+ DRM_SYNCOBJ_CREATE_SIGNALED
+ } else {
+ 0
+ },
+ };
+
+ unsafe {
+ ioctl::syncobj::create(fd, &mut args)?;
+ }
+
+ Ok(args)
+}
+
+/// Destroys a syncobj.
+pub fn destroy(fd: BorrowedFd<'_>, handle: u32) -> io::Result<drm_syncobj_destroy> {
+ let mut args = drm_syncobj_destroy { handle, pad: 0 };
+
+ unsafe {
+ ioctl::syncobj::destroy(fd, &mut args)?;
+ }
+
+ Ok(args)
+}
+
+/// Exports a syncobj as an inter-process file descriptor or as a poll()-able sync file.
+pub fn handle_to_fd(
+ fd: BorrowedFd<'_>,
+ handle: u32,
+ export_sync_file: bool,
+) -> io::Result<drm_syncobj_handle> {
+ let mut args = drm_syncobj_handle {
+ handle,
+ flags: if export_sync_file {
+ DRM_SYNCOBJ_HANDLE_TO_FD_FLAGS_EXPORT_SYNC_FILE
+ } else {
+ 0
+ },
+ fd: 0,
+ pad: 0,
+ };
+
+ unsafe {
+ ioctl::syncobj::handle_to_fd(fd, &mut args)?;
+ }
+
+ Ok(args)
+}
+
+/// Imports a file descriptor exported by [`handle_to_fd`] back into a process-local handle.
+pub fn fd_to_handle(
+ fd: BorrowedFd<'_>,
+ syncobj_fd: BorrowedFd<'_>,
+ import_sync_file: bool,
+) -> io::Result<drm_syncobj_handle> {
+ let mut args = drm_syncobj_handle {
+ handle: 0,
+ flags: if import_sync_file {
+ DRM_SYNCOBJ_FD_TO_HANDLE_FLAGS_IMPORT_SYNC_FILE
+ } else {
+ 0
+ },
+ fd: syncobj_fd.as_raw_fd(),
+ pad: 0,
+ };
+
+ unsafe {
+ ioctl::syncobj::fd_to_handle(fd, &mut args)?;
+ }
+
+ Ok(args)
+}
+
+/// Waits for one or more syncobjs to become signalled.
+pub fn wait(
+ fd: BorrowedFd<'_>,
+ handles: &[u32],
+ timeout_nsec: i64,
+ wait_all: bool,
+ wait_for_submit: bool,
+) -> io::Result<drm_syncobj_wait> {
+ let mut args = drm_syncobj_wait {
+ handles: handles.as_ptr() as _,
+ timeout_nsec,
+ count_handles: handles.len() as _,
+ flags: if wait_all {
+ DRM_SYNCOBJ_WAIT_FLAGS_WAIT_ALL
+ } else {
+ 0
+ } | if wait_for_submit {
+ DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT
+ } else {
+ 0
+ },
+ first_signaled: 0,
+ pad: 0,
+ };
+
+ unsafe {
+ ioctl::syncobj::wait(fd, &mut args)?;
+ }
+
+ Ok(args)
+}
+
+/// Resets (un-signals) one or more syncobjs.
+pub fn reset(fd: BorrowedFd<'_>, handles: &[u32]) -> io::Result<drm_syncobj_array> {
+ let mut args = drm_syncobj_array {
+ handles: handles.as_ptr() as _,
+ count_handles: handles.len() as _,
+ pad: 0,
+ };
+
+ unsafe {
+ ioctl::syncobj::reset(fd, &mut args)?;
+ }
+
+ Ok(args)
+}
+
+/// Signals one or more syncobjs.
+pub fn signal(fd: BorrowedFd<'_>, handles: &[u32]) -> io::Result<drm_syncobj_array> {
+ let mut args = drm_syncobj_array {
+ handles: handles.as_ptr() as _,
+ count_handles: handles.len() as _,
+ pad: 0,
+ };
+
+ unsafe {
+ ioctl::syncobj::signal(fd, &mut args)?;
+ }
+
+ Ok(args)
+}
+
+/// Waits for one or more specific timeline syncobj points.
+pub fn timeline_wait(
+ fd: BorrowedFd<'_>,
+ handles: &[u32],
+ points: &[u64],
+ timeout_nsec: i64,
+ wait_all: bool,
+ wait_for_submit: bool,
+ wait_available: bool,
+) -> io::Result<drm_syncobj_timeline_wait> {
+ debug_assert_eq!(handles.len(), points.len());
+
+ let mut args = drm_syncobj_timeline_wait {
+ handles: handles.as_ptr() as _,
+ points: points.as_ptr() as _,
+ timeout_nsec,
+ count_handles: handles.len() as _,
+ flags: if wait_all {
+ DRM_SYNCOBJ_WAIT_FLAGS_WAIT_ALL
+ } else {
+ 0
+ } | if wait_for_submit {
+ DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT
+ } else {
+ 0
+ } | if wait_available {
+ DRM_SYNCOBJ_WAIT_FLAGS_WAIT_AVAILABLE
+ } else {
+ 0
+ },
+ first_signaled: 0,
+ pad: 0,
+ };
+
+ unsafe {
+ ioctl::syncobj::timeline_wait(fd, &mut args)?;
+ }
+
+ Ok(args)
+}
+
+/// Queries for state of one or more timeline syncobjs.
+pub fn query(
+ fd: BorrowedFd<'_>,
+ handles: &[u32],
+ points: &mut [u64],
+ last_submitted: bool,
+) -> io::Result<drm_syncobj_timeline_array> {
+ debug_assert_eq!(handles.len(), points.len());
+
+ let mut args = drm_syncobj_timeline_array {
+ handles: handles.as_ptr() as _,
+ points: points.as_mut_ptr() as _,
+ count_handles: handles.len() as _,
+ flags: if last_submitted {
+ DRM_SYNCOBJ_QUERY_FLAGS_LAST_SUBMITTED
+ } else {
+ 0
+ },
+ };
+
+ unsafe {
+ ioctl::syncobj::query(fd, &mut args)?;
+ }
+
+ Ok(args)
+}
+
+/// Transfers one timeline syncobj point to another.
+pub fn transfer(
+ fd: BorrowedFd<'_>,
+ src_handle: u32,
+ dst_handle: u32,
+ src_point: u64,
+ dst_point: u64,
+) -> io::Result<drm_syncobj_transfer> {
+ let mut args = drm_syncobj_transfer {
+ src_handle,
+ dst_handle,
+ src_point,
+ dst_point,
+ flags: 0,
+ pad: 0,
+ };
+
+ unsafe {
+ ioctl::syncobj::transfer(fd, &mut args)?;
+ }
+
+ Ok(args)
+}
+
+/// Signals one or more specific timeline syncobj points.
+pub fn timeline_signal(
+ fd: BorrowedFd<'_>,
+ handles: &[u32],
+ points: &[u64],
+) -> io::Result<drm_syncobj_timeline_array> {
+ debug_assert_eq!(handles.len(), points.len());
+
+ let mut args = drm_syncobj_timeline_array {
+ handles: handles.as_ptr() as _,
+ points: points.as_ptr() as _,
+ count_handles: handles.len() as _,
+ flags: 0,
+ };
+
+ unsafe {
+ ioctl::syncobj::timeline_signal(fd, &mut args)?;
+ }
+
+ Ok(args)
+}
diff --git a/src/utils.rs b/src/utils.rs
new file mode 100644
index 0000000..4055365
--- /dev/null
+++ b/src/utils.rs
@@ -0,0 +1,38 @@
+/// Takes an `Option<&mut Vec<T>>` style buffer and gets its pointer.
+macro_rules! map_ptr {
+ ($buffer:expr) => {
+ match $buffer {
+ Some(b) => b.as_ptr() as _,
+ None => 0 as _,
+ }
+ };
+}
+
+/// Takes an `Option<&mut Vec<T>>` style buffer and gets its allocated length.
+macro_rules! map_len {
+ ($buffer:expr) => {
+ match $buffer {
+ Some(b) => b.capacity() as _,
+ None => 0,
+ }
+ };
+}
+
+/// Takes an `Option<&mut Vec<T>>` style buffer and shrinks it.
+macro_rules! map_reserve {
+ ($buffer:expr, $size:expr) => {
+ match $buffer {
+ Some(ref mut b) => b.reserve_exact($size - b.len()),
+ _ => (),
+ }
+ };
+}
+/// Takes an `Option<&mut Vec<T>>` style buffer and shrinks it.
+macro_rules! map_set {
+ ($buffer:expr, $min:expr) => {
+ match $buffer {
+ Some(ref mut b) => unsafe { b.set_len($min) },
+ _ => (),
+ }
+ };
+}