diff options
author | Marcin Radomski <dextero@google.com> | 2024-03-13 15:49:21 +0000 |
---|---|---|
committer | Marcin Radomski <dextero@google.com> | 2024-03-13 16:10:37 +0000 |
commit | 3d24f3e9ec2c0f5e3e47867910fe79dc02959ac8 (patch) | |
tree | ca5755223472eae63e67730eb7737cbdda877e24 | |
parent | 10173ea7a8b6c900688a5891639a3b7b655eebb1 (diff) | |
download | drm-ffi-upstream.tar.gz |
Import 'drm-ffi' crateupstream
Request Document: go/android-rust-importing-crates
For CL Reviewers: go/android3p#cl-review
For Build Team: go/ab-third-party-imports
Bug: http://b/328179939
Test: m libdrm_ffi (with aosp/2997556 merged locally)
Change-Id: Id6ca48282ad7bd31d281e9e6652d3bcb8b511ba8
-rw-r--r-- | .cargo_vcs_info.json | 6 | ||||
-rw-r--r-- | Android.bp | 23 | ||||
-rw-r--r-- | Cargo.toml | 29 | ||||
-rw-r--r-- | Cargo.toml.orig | 16 | ||||
-rw-r--r-- | LICENSE | 23 | ||||
-rw-r--r-- | METADATA | 20 | ||||
-rw-r--r-- | MODULE_LICENSE_MIT | 0 | ||||
-rw-r--r-- | OWNERS | 7 | ||||
-rw-r--r-- | cargo_embargo.json | 3 | ||||
-rw-r--r-- | src/gem.rs | 68 | ||||
-rw-r--r-- | src/ioctl.rs | 281 | ||||
-rw-r--r-- | src/lib.rs | 215 | ||||
-rw-r--r-- | src/mode.rs | 938 | ||||
-rw-r--r-- | src/syncobj.rs | 264 | ||||
-rw-r--r-- | src/utils.rs | 38 |
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"] @@ -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 @@ -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) }, + _ => (), + } + }; +} |