drm-ffi-0.7.1/.cargo_vcs_info.json0000644000000001450000000000100123650ustar { "git": { "sha1": "328742fddc675b3370057b382eb54acbc9b48c79" }, "path_in_vcs": "drm-ffi" }drm-ffi-0.7.1/Cargo.toml0000644000000015630000000000100103700ustar # 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 "] 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"] drm-ffi-0.7.1/Cargo.toml.orig000064400000000000000000000006561046102023000140530ustar 00000000000000[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 "] 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"] drm-ffi-0.7.1/LICENSE000064400000000000000000000017771046102023000121760ustar 00000000000000Permission 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. drm-ffi-0.7.1/src/gem.rs000064400000000000000000000026631046102023000130710ustar 00000000000000//! //! 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 { 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 { 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 { 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 { 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) } drm-ffi-0.7.1/src/ioctl.rs000064400000000000000000000214711046102023000134310ustar 00000000000000use 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::::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::::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::::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::::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 ); } drm-ffi-0.7.1/src/lib.rs000064400000000000000000000120301046102023000130540ustar 00000000000000//! //! 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 { unsafe { ioctl::get_token(fd) } } /// Authorize another process' 'Magic Authentication Token'. pub fn auth_magic_token(fd: BorrowedFd<'_>, auth: u32) -> io::Result { 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>) -> io::Result { 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 { 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 { 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 { 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 { 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>, mut date_buf: Option<&mut Vec>, mut desc_buf: Option<&mut Vec>, ) -> io::Result { 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 { // 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 }) } drm-ffi-0.7.1/src/mode.rs000064400000000000000000000506701046102023000132460ustar 00000000000000//! //! 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>, mut crtcs: Option<&mut Vec>, mut connectors: Option<&mut Vec>, mut encoders: Option<&mut Vec>, ) -> io::Result { 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>, ) -> io::Result { 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 { 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 { 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 { 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 { 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 { 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 { 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, ) -> io::Result { 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 { 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 { 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 { 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 { 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 { 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>, mut prop_values: Option<&mut Vec>, mut modes: Option<&mut Vec>, mut encoders: Option<&mut Vec>, force_probe: bool, ) -> io::Result { 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 { 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>, ) -> io::Result { 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 { 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>, mut enums: Option<&mut Vec>, ) -> io::Result { 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 { 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>, ) -> io::Result { 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 { 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 { 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>, mut values: Option<&mut Vec>, ) -> io::Result { 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 { 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>, ) -> io::Result { 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>, ) -> io::Result { 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 { 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 { 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 { let mut map = drm_mode_map_dumb { handle, pad, offset, }; unsafe { ioctl::mode::map_dumb(fd, &mut map)?; } Ok(map) } } drm-ffi-0.7.1/src/syncobj.rs000064400000000000000000000135521046102023000137670ustar 00000000000000//! //! 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 { 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 { 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 { 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 { 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 { 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 { 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 { 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 { 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 { 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 { 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 { 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) } drm-ffi-0.7.1/src/utils.rs000064400000000000000000000017051046102023000134550ustar 00000000000000/// Takes an `Option<&mut Vec>` 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>` 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>` 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>` style buffer and shrinks it. macro_rules! map_set { ($buffer:expr, $min:expr) => { match $buffer { Some(ref mut b) => unsafe { b.set_len($min) }, _ => (), } }; }