1
0
Fork 0
forked from wry/wry
wry/src/video/drm/sys.rs
2026-03-14 14:44:57 +01:00

1480 lines
37 KiB
Rust

#![allow(non_camel_case_types)]
#![allow(non_upper_case_globals)]
use {
crate::{
utils::{bitflags::BitflagsExt, compat::IoctlNumber, oserror::OsError},
video::drm::{
DrmBlob, DrmCardResources, DrmConnector, DrmConnectorInfo, DrmCrtc, DrmEncoder,
DrmEncoderInfo, DrmError, DrmFb, DrmModeInfo, DrmPlane, DrmPlaneInfo, DrmProperty,
DrmPropertyDefinition, DrmPropertyEnumValue, DrmPropertyType, DrmPropertyValue,
DrmVersion, NodeType,
},
},
ahash::AHashMap,
bstr::ByteSlice,
std::{
ffi::CString,
io::{BufRead, BufReader},
},
uapi::{
OwnedFd, Pod, Ustring,
c::{self, c_int},
pod_zeroed,
},
};
pub unsafe fn ioctl<T>(fd: c::c_int, request: c::c_ulong, t: &mut T) -> Result<c::c_int, OsError> {
let mut ret;
loop {
ret = unsafe { c::ioctl(fd, request as IoctlNumber, &mut *t) };
if ret != -1 {
return Ok(ret);
}
let err = uapi::get_errno();
if not_matches!(err, c::EINTR | c::EAGAIN) {
return Err(OsError(err));
}
}
}
pub const DRM_IOCTL_BASE: u64 = b'd' as u64;
pub const fn drm_io(nr: u64) -> u64 {
uapi::_IO(DRM_IOCTL_BASE, nr)
}
pub const fn drm_iow<T>(nr: u64) -> u64 {
uapi::_IOW::<T>(DRM_IOCTL_BASE, nr)
}
pub const fn drm_iowr<T>(nr: u64) -> u64 {
uapi::_IOWR::<T>(DRM_IOCTL_BASE, nr)
}
const DRM_IOCTL_MODE_CREATE_LEASE: u64 = drm_iowr::<drm_mode_create_lease>(0xc6);
#[repr(C)]
struct drm_mode_create_lease {
object_ids: u64,
object_count: u32,
flags: u32,
lessee_id: u32,
fd: u32,
}
pub fn create_lease(fd: c::c_int, objects: &[u32], flags: u32) -> Result<(OwnedFd, u32), OsError> {
let mut create = drm_mode_create_lease {
object_ids: objects.as_ptr() as usize as _,
object_count: objects.len() as _,
flags,
lessee_id: 0,
fd: 0,
};
unsafe {
ioctl(fd, DRM_IOCTL_MODE_CREATE_LEASE, &mut create)?;
}
Ok((OwnedFd::new(create.fd as _), create.lessee_id))
}
const DRM_IOCTL_MODE_REVOKE_LEASE: u64 = drm_iowr::<drm_mode_revoke_lease>(0xc9);
#[repr(C)]
struct drm_mode_revoke_lease {
lessee_id: u32,
}
pub fn revoke_lease(fd: c::c_int, lessee_id: u32) -> Result<(), OsError> {
let mut revoke = drm_mode_revoke_lease { lessee_id };
unsafe {
ioctl(fd, DRM_IOCTL_MODE_REVOKE_LEASE, &mut revoke)?;
}
Ok(())
}
pub fn get_node_type_from_fd(fd: c::c_int) -> Result<NodeType, OsError> {
let (_, _, min) = drm_stat(fd)?;
get_minor_type(min)
}
pub fn node_is_drm(maj: u64, min: u64) -> bool {
let path = device_dir(maj, min);
uapi::stat(path).is_ok()
}
pub fn get_minor_type(min: u64) -> Result<NodeType, OsError> {
const DRM_NODE_PRIMARY: u64 = 0;
const DRM_NODE_CONTROL: u64 = 1;
const DRM_NODE_RENDER: u64 = 2;
match min >> 6 {
DRM_NODE_PRIMARY => Ok(NodeType::Primary),
DRM_NODE_CONTROL => Ok(NodeType::Control),
DRM_NODE_RENDER => Ok(NodeType::Render),
_ => Err(OsError(c::ENODEV)),
}
}
const DRM_DIR_NAME: &str = "/dev/dri";
fn device_dir(maj: u64, min: u64) -> Ustring {
uapi::format_ustr!("/sys/dev/char/{maj}:{min}/device/drm")
}
pub fn get_minor_name_from_fd(fd: c::c_int, ty: NodeType) -> Result<Ustring, OsError> {
let (_, maj, min) = drm_stat(fd)?;
let dir = device_dir(maj, min);
let mut dir = uapi::opendir(dir)?;
while let Some(entry) = uapi::readdir(&mut dir) {
let entry = entry?;
if entry.name().to_bytes().starts_with_str(ty.name()) {
return Ok(uapi::format_ustr!(
"{}/{}",
DRM_DIR_NAME,
entry.name().to_bytes().as_bstr()
));
}
}
Err(OsError(c::ENOENT))
}
fn drm_stat(fd: c::c_int) -> Result<(c::stat, u64, u64), OsError> {
let stat = uapi::fstat(fd)?;
let maj = uapi::major(stat.st_rdev);
let min = uapi::minor(stat.st_rdev);
if !is_drm(maj, min, &stat) {
return Err(OsError(c::ENODEV));
}
Ok((stat, maj, min))
}
fn is_drm(maj: u64, min: u64, stat: &c::stat) -> bool {
stat.st_mode & c::S_IFMT == c::S_IFCHR && node_is_drm(maj, min)
}
pub fn get_device_name_from_fd2(fd: c::c_int) -> Result<Ustring, OsError> {
let (_, maj, min) = drm_stat(fd)?;
let path = uapi::format_ustr!("/sys/dev/char/{maj}:{min}/uevent");
let mut buf = vec![];
let mut br = BufReader::new(uapi::open(path, c::O_RDONLY, 0)?);
loop {
buf.clear();
if br.read_until(b'\n', &mut buf)? == 0 {
break;
}
if let Some(pf) = buf.strip_prefix(b"DEVNAME=") {
return Ok(uapi::format_ustr!("/dev/{}", pf.trim_ascii_end().as_bstr()));
}
}
Err(OsError(c::ENOENT))
}
pub fn get_nodes(fd: c::c_int) -> Result<AHashMap<NodeType, CString>, OsError> {
let (_, maj, min) = drm_stat(fd)?;
get_drm_nodes_from_dev(maj, min)
}
pub fn get_drm_nodes_from_dev(maj: u64, min: u64) -> Result<AHashMap<NodeType, CString>, OsError> {
let dir = device_dir(maj, min);
let mut dir = uapi::opendir(dir)?;
let mut res = AHashMap::new();
'outer: while let Some(entry) = uapi::readdir(&mut dir) {
let entry = entry?;
let name = entry.name().to_bytes();
let ty = 'ty: {
for ty in [NodeType::Render, NodeType::Control, NodeType::Primary] {
if name.starts_with_str(ty.name()) {
break 'ty ty;
}
}
continue 'outer;
};
res.insert(
ty,
uapi::format_ustr!("{}/{}", DRM_DIR_NAME, name.as_bstr())
.into_c_string()
.unwrap(),
);
}
Ok(res)
}
const DRM_PROP_NAME_LEN: usize = 32;
#[repr(C)]
#[derive(Default)]
struct drm_mode_get_property {
values_ptr: u64,
enum_blob_ptr: u64,
prop_id: u32,
flags: u32,
name: [u8; DRM_PROP_NAME_LEN],
count_values: u32,
count_enum_blobs: u32,
}
const DRM_IOCTL_MODE_GETPROPERTY: u64 = drm_iowr::<drm_mode_get_property>(0xaa);
#[expect(dead_code)]
const DRM_MODE_PROP_PENDING: u32 = 1 << 0;
const DRM_MODE_PROP_RANGE: u32 = 1 << 1;
const DRM_MODE_PROP_IMMUTABLE: u32 = 1 << 2;
const DRM_MODE_PROP_ENUM: u32 = 1 << 3;
const DRM_MODE_PROP_BLOB: u32 = 1 << 4;
const DRM_MODE_PROP_BITMASK: u32 = 1 << 5;
const DRM_MODE_PROP_LEGACY_TYPE: u32 =
DRM_MODE_PROP_RANGE | DRM_MODE_PROP_ENUM | DRM_MODE_PROP_BLOB | DRM_MODE_PROP_BITMASK;
const DRM_MODE_PROP_EXTENDED_TYPE: u32 = 0x0000ffc0;
const fn drm_mode_prop_type(n: u32) -> u32 {
n << 6
}
const DRM_MODE_PROP_OBJECT: u32 = drm_mode_prop_type(1);
const DRM_MODE_PROP_SIGNED_RANGE: u32 = drm_mode_prop_type(2);
const DRM_MODE_PROP_ATOMIC: u32 = 0x80000000;
pub const DRM_CAP_CURSOR_WIDTH: u64 = 0x8;
pub const DRM_CAP_CURSOR_HEIGHT: u64 = 0x9;
pub const DRM_CAP_ATOMIC_ASYNC_PAGE_FLIP: u64 = 0x15;
#[repr(C)]
struct drm_mode_property_enum {
value: u64,
name: [u8; DRM_PROP_NAME_LEN],
}
pub fn mode_getproperty(
fd: c::c_int,
property_id: DrmProperty,
) -> Result<DrmPropertyDefinition, DrmError> {
let mut prop = drm_mode_get_property {
prop_id: property_id.0,
..Default::default()
};
let get = |prop: &mut drm_mode_get_property| {
unsafe {
if let Err(e) = ioctl(fd, DRM_IOCTL_MODE_GETPROPERTY, prop) {
return Err(DrmError::GetProperty(e));
}
}
Ok(())
};
get(&mut prop)?;
let ty = prop.flags & (DRM_MODE_PROP_LEGACY_TYPE | DRM_MODE_PROP_EXTENDED_TYPE);
let ty = match ty {
DRM_MODE_PROP_RANGE | DRM_MODE_PROP_SIGNED_RANGE => {
if prop.count_values != 2 {
return Err(DrmError::RangeValues);
}
prop.count_enum_blobs = 0;
let mut vals = [0u64, 0];
prop.values_ptr = vals.as_mut_ptr() as _;
get(&mut prop)?;
if ty == DRM_MODE_PROP_RANGE {
DrmPropertyType::Range {
_min: vals[0],
max: vals[1],
}
} else {
DrmPropertyType::SignedRange {
_min: vals[0] as _,
max: vals[1] as _,
}
}
}
DRM_MODE_PROP_ENUM | DRM_MODE_PROP_BITMASK => {
prop.count_values = 0;
let mut props =
Vec::<drm_mode_property_enum>::with_capacity(prop.count_enum_blobs as usize);
prop.enum_blob_ptr = props.as_mut_ptr() as _;
get(&mut prop)?;
unsafe {
props.set_len(prop.count_enum_blobs as usize);
}
let mut values = Vec::with_capacity(props.len());
for v in props {
values.push(DrmPropertyEnumValue {
value: v.value,
name: v.name.split(|n| *n == 0).next().unwrap().to_vec().into(),
})
}
DrmPropertyType::Enum {
values,
bitmask: ty == DRM_MODE_PROP_BITMASK,
}
}
DRM_MODE_PROP_BLOB => DrmPropertyType::Blob,
DRM_MODE_PROP_OBJECT => {
if prop.count_values != 1 {
return Err(DrmError::ObjectValues);
}
let mut ty = 0u64;
prop.values_ptr = &mut ty as *mut _ as u64;
get(&mut prop)?;
DrmPropertyType::Object { _ty: ty as _ }
}
_ => return Err(DrmError::UnknownPropertyType(ty)),
};
Ok(DrmPropertyDefinition {
id: property_id,
name: prop.name.split(|n| *n == 0).next().unwrap().to_vec().into(),
_immutable: prop.flags.contains(DRM_MODE_PROP_IMMUTABLE),
_atomic: prop.flags.contains(DRM_MODE_PROP_ATOMIC),
ty,
})
}
#[repr(C)]
#[derive(Debug)]
struct drm_mode_obj_get_properties {
props_ptr: u64,
prop_values_ptr: u64,
count_props: u32,
obj_id: u32,
obj_type: u32,
}
const DRM_IOCTL_MODE_OBJ_GETPROPERTIES: u64 = drm_iowr::<drm_mode_obj_get_properties>(0xb9);
pub fn mode_obj_getproperties(
fd: c::c_int,
obj_id: u32,
obj_type: u32,
) -> Result<Vec<DrmPropertyValue>, DrmError> {
let mut props = drm_mode_obj_get_properties {
props_ptr: 0,
prop_values_ptr: 0,
count_props: 0,
obj_id,
obj_type,
};
let get = |prop: &mut drm_mode_obj_get_properties| {
unsafe {
if let Err(e) = ioctl(fd, DRM_IOCTL_MODE_OBJ_GETPROPERTIES, prop) {
return Err(DrmError::GetProperties(e));
}
}
Ok(())
};
get(&mut props)?;
let mut ids = Vec::<u32>::new();
let mut values = Vec::<u64>::new();
let mut num_props = 0;
while num_props != props.count_props {
num_props = props.count_props;
ids.reserve(num_props as _);
values.reserve(num_props as _);
props.props_ptr = ids.as_mut_ptr() as _;
props.prop_values_ptr = values.as_mut_ptr() as _;
get(&mut props)?;
}
unsafe {
ids.set_len(num_props as _);
values.set_len(num_props as _);
}
let mut props = Vec::with_capacity(num_props as _);
for (id, value) in ids.into_iter().zip(values.into_iter()) {
props.push(DrmPropertyValue {
id: DrmProperty(id),
value,
})
}
Ok(props)
}
pub const DRM_MODE_OBJECT_CRTC: u32 = 0xcccccccc;
pub const DRM_MODE_OBJECT_CONNECTOR: u32 = 0xc0c0c0c0;
pub const DRM_MODE_OBJECT_ENCODER: u32 = 0xe0e0e0e0;
pub const DRM_MODE_OBJECT_PROPERTY: u32 = 0xb0b0b0b0;
pub const DRM_MODE_OBJECT_FB: u32 = 0xfbfbfbfb;
pub const DRM_MODE_OBJECT_BLOB: u32 = 0xbbbbbbbb;
pub const DRM_MODE_OBJECT_PLANE: u32 = 0xeeeeeeee;
#[expect(dead_code)]
pub const DRM_MODE_OBJECT_ANY: u32 = 0;
pub const DRM_MODE_CONNECTOR_Unknown: u32 = 0;
pub const DRM_MODE_CONNECTOR_VGA: u32 = 1;
pub const DRM_MODE_CONNECTOR_DVII: u32 = 2;
pub const DRM_MODE_CONNECTOR_DVID: u32 = 3;
pub const DRM_MODE_CONNECTOR_DVIA: u32 = 4;
pub const DRM_MODE_CONNECTOR_Composite: u32 = 5;
pub const DRM_MODE_CONNECTOR_SVIDEO: u32 = 6;
pub const DRM_MODE_CONNECTOR_LVDS: u32 = 7;
pub const DRM_MODE_CONNECTOR_Component: u32 = 8;
pub const DRM_MODE_CONNECTOR_9PinDIN: u32 = 9;
pub const DRM_MODE_CONNECTOR_DisplayPort: u32 = 10;
pub const DRM_MODE_CONNECTOR_HDMIA: u32 = 11;
pub const DRM_MODE_CONNECTOR_HDMIB: u32 = 12;
pub const DRM_MODE_CONNECTOR_TV: u32 = 13;
pub const DRM_MODE_CONNECTOR_eDP: u32 = 14;
pub const DRM_MODE_CONNECTOR_VIRTUAL: u32 = 15;
pub const DRM_MODE_CONNECTOR_DSI: u32 = 16;
pub const DRM_MODE_CONNECTOR_DPI: u32 = 17;
pub const DRM_MODE_CONNECTOR_WRITEBACK: u32 = 18;
pub const DRM_MODE_CONNECTOR_SPI: u32 = 19;
pub const DRM_MODE_CONNECTOR_USB: u32 = 20;
#[repr(C)]
struct drm_set_client_cap {
capability: u64,
value: u64,
}
const DRM_IOCTL_SET_CLIENT_CAP: u64 = drm_iow::<drm_set_client_cap>(0x0d);
pub const DRM_CLIENT_CAP_ATOMIC: u64 = 3;
pub fn set_client_cap(fd: c::c_int, capability: u64, value: u64) -> Result<(), OsError> {
let mut cap = drm_set_client_cap { capability, value };
unsafe {
ioctl(fd, DRM_IOCTL_SET_CLIENT_CAP, &mut cap)?;
}
Ok(())
}
#[repr(C)]
struct drm_get_cap {
capability: u64,
value: u64,
}
const DRM_IOCTL_GET_CAP: u64 = drm_iowr::<drm_get_cap>(0x0c);
pub fn get_cap(fd: c::c_int, capability: u64) -> Result<u64, OsError> {
let mut cap = drm_get_cap {
capability,
value: 0,
};
unsafe {
ioctl(fd, DRM_IOCTL_GET_CAP, &mut cap)?;
}
Ok(cap.value)
}
#[repr(C)]
#[derive(Default)]
struct drm_mode_card_res {
fb_id_ptr: u64,
crtc_id_ptr: u64,
connector_id_ptr: u64,
encoder_id_ptr: u64,
count_fbs: u32,
count_crtcs: u32,
count_connectors: u32,
count_encoders: u32,
min_width: u32,
max_width: u32,
min_height: u32,
max_height: u32,
}
const DRM_IOCTL_MODE_GETRESOURCES: u64 = drm_iowr::<drm_mode_card_res>(0xa0);
pub fn mode_get_resources(fd: c::c_int) -> Result<DrmCardResources, DrmError> {
let mut res = drm_mode_card_res::default();
let get = |res: &mut drm_mode_card_res| {
unsafe {
if let Err(e) = ioctl(fd, DRM_IOCTL_MODE_GETRESOURCES, res) {
return Err(DrmError::GetResources(e));
}
}
Ok(())
};
get(&mut res)?;
let mut count_fbs = 0;
let mut count_crtcs = 0;
let mut count_connectors = 0;
let mut count_encoders = 0;
let mut fbs = Vec::<DrmFb>::new();
let mut crtcs = Vec::<DrmCrtc>::new();
let mut connectors = Vec::<DrmConnector>::new();
let mut encoders = Vec::<DrmEncoder>::new();
while (count_fbs, count_crtcs, count_connectors, count_encoders)
!= (
res.count_fbs,
res.count_crtcs,
res.count_connectors,
res.count_encoders,
)
{
count_fbs = res.count_fbs;
count_crtcs = res.count_crtcs;
count_connectors = res.count_connectors;
count_encoders = res.count_encoders;
fbs.reserve(count_fbs as _);
crtcs.reserve(count_crtcs as _);
connectors.reserve(count_connectors as _);
encoders.reserve(count_encoders as _);
res.fb_id_ptr = fbs.as_mut_ptr() as _;
res.crtc_id_ptr = crtcs.as_mut_ptr() as _;
res.connector_id_ptr = connectors.as_mut_ptr() as _;
res.encoder_id_ptr = encoders.as_mut_ptr() as _;
get(&mut res)?;
}
unsafe {
fbs.set_len(count_fbs as _);
crtcs.set_len(count_crtcs as _);
connectors.set_len(count_connectors as _);
encoders.set_len(count_encoders as _);
}
Ok(DrmCardResources {
_min_width: res.min_width,
_max_width: res.max_width,
_min_height: res.min_height,
_max_height: res.max_height,
_fbs: fbs,
crtcs,
connectors,
encoders,
})
}
pub fn mode_supports_get_resources(fd: c_int) -> Result<bool, DrmError> {
let mut res = drm_mode_card_res::default();
let res = unsafe { ioctl(fd, DRM_IOCTL_MODE_GETRESOURCES, &mut res) };
match res {
Ok(_) => Ok(true),
Err(e) if e.0 == c::EOPNOTSUPP => Ok(false),
Err(e) => Err(DrmError::GetResources(e)),
}
}
#[repr(C)]
struct drm_mode_get_plane_res {
plane_id_ptr: u64,
count_planes: u32,
}
const DRM_IOCTL_MODE_GETPLANERESOURCES: u64 = drm_iowr::<drm_mode_get_plane_res>(0xb5);
pub fn mode_getplaneresources(fd: c::c_int) -> Result<Vec<DrmPlane>, DrmError> {
let mut res = drm_mode_get_plane_res {
plane_id_ptr: 0,
count_planes: 0,
};
let get = |res: &mut drm_mode_get_plane_res| {
unsafe {
if let Err(e) = ioctl(fd, DRM_IOCTL_MODE_GETPLANERESOURCES, res) {
return Err(DrmError::GetPlaneResources(e));
}
}
Ok(())
};
get(&mut res)?;
let mut count_planes = 0;
let mut planes = Vec::<DrmPlane>::new();
while count_planes != res.count_planes {
count_planes = res.count_planes;
planes.reserve(count_planes as _);
res.plane_id_ptr = planes.as_mut_ptr() as _;
get(&mut res)?;
}
unsafe {
planes.set_len(count_planes as _);
}
Ok(planes)
}
#[repr(C)]
#[derive(Default)]
struct drm_mode_get_plane {
plane_id: u32,
crtc_id: u32,
fb_id: u32,
possible_crtcs: u32,
gamma_size: u32,
count_format_types: u32,
format_type_ptr: u64,
}
const DRM_IOCTL_MODE_GETPLANE: u64 = drm_iowr::<drm_mode_get_plane>(0xb6);
pub fn mode_getplane(fd: c::c_int, plane_id: u32) -> Result<DrmPlaneInfo, DrmError> {
let mut res = drm_mode_get_plane {
plane_id,
..Default::default()
};
let get = |res: &mut drm_mode_get_plane| {
unsafe {
if let Err(e) = ioctl(fd, DRM_IOCTL_MODE_GETPLANE, res) {
return Err(DrmError::GetPlane(e));
}
}
Ok(())
};
get(&mut res)?;
let mut count_formats = 0;
let mut formats = Vec::<u32>::new();
while count_formats != res.count_format_types {
count_formats = res.count_format_types;
formats.reserve(count_formats as _);
res.format_type_ptr = formats.as_mut_ptr() as _;
get(&mut res)?;
}
unsafe {
formats.set_len(count_formats as _);
}
Ok(DrmPlaneInfo {
_plane_id: DrmPlane(plane_id),
_crtc_id: DrmCrtc(res.crtc_id),
_fb_id: DrmFb(res.fb_id),
possible_crtcs: res.possible_crtcs,
_gamma_size: res.gamma_size,
format_types: formats,
})
}
#[repr(C)]
#[derive(Default)]
struct drm_mode_get_encoder {
encoder_id: u32,
encoder_type: u32,
crtc_id: u32,
possible_crtcs: u32,
possible_clones: u32,
}
const DRM_IOCTL_MODE_GETENCODER: u64 = drm_iowr::<drm_mode_get_encoder>(0xa6);
pub fn mode_getencoder(fd: c::c_int, encoder_id: u32) -> Result<DrmEncoderInfo, DrmError> {
let mut res = drm_mode_get_encoder {
encoder_id,
..Default::default()
};
unsafe {
if let Err(e) = ioctl(fd, DRM_IOCTL_MODE_GETENCODER, &mut res) {
return Err(DrmError::GetEncoder(e));
}
}
Ok(DrmEncoderInfo {
_encoder_id: DrmEncoder(encoder_id),
_encoder_type: res.encoder_type,
_crtc_id: DrmCrtc(res.crtc_id),
possible_crtcs: res.possible_crtcs,
_possible_clones: res.possible_clones,
})
}
pub const DRM_DISPLAY_MODE_LEN: usize = 32;
#[repr(C)]
#[derive(Debug)]
pub struct drm_mode_modeinfo {
pub clock: u32,
pub hdisplay: u16,
pub hsync_start: u16,
pub hsync_end: u16,
pub htotal: u16,
pub hskew: u16,
pub vdisplay: u16,
pub vsync_start: u16,
pub vsync_end: u16,
pub vtotal: u16,
pub vscan: u16,
pub vrefresh: u32,
pub flags: u32,
pub ty: u32,
pub name: [u8; DRM_DISPLAY_MODE_LEN],
}
unsafe impl Pod for drm_mode_modeinfo {}
impl Into<DrmModeInfo> for drm_mode_modeinfo {
fn into(self) -> DrmModeInfo {
DrmModeInfo {
clock: self.clock,
hdisplay: self.hdisplay,
hsync_start: self.hsync_start,
hsync_end: self.hsync_end,
htotal: self.htotal,
hskew: self.hskew,
vdisplay: self.vdisplay,
vsync_start: self.vsync_start,
vsync_end: self.vsync_end,
vtotal: self.vtotal,
vscan: self.vscan,
vrefresh: self.vrefresh,
flags: self.flags,
ty: self.ty,
name: self.name.split(|n| *n == 0).next().unwrap().to_vec().into(),
}
}
}
pub const CONNECTOR_STATUS_CONNECTED: u32 = 1;
pub const CONNECTOR_STATUS_DISCONNECTED: u32 = 2;
pub const CONNECTOR_STATUS_UNKNOWN: u32 = 3;
#[derive(Default)]
#[repr(C)]
struct drm_mode_get_connector {
encoders_ptr: u64,
modes_ptr: u64,
props_ptr: u64,
prop_values_ptr: u64,
count_modes: u32,
count_props: u32,
count_encoders: u32,
encoder_id: u32,
connector_id: u32,
connector_type: u32,
connector_type_id: u32,
connection: u32,
mm_width: u32,
mm_height: u32,
subpixel: u32,
pad: u32,
}
const DRM_IOCTL_MODE_GETCONNECTOR: u64 = drm_iowr::<drm_mode_get_connector>(0xa7);
pub fn mode_getconnector(
fd: c::c_int,
connector: u32,
force: bool,
) -> Result<DrmConnectorInfo, DrmError> {
let mut count_modes = if force { 0 } else { 1 };
let mut count_props = 0;
let mut count_encoders = 0;
let mut modes = Vec::<drm_mode_modeinfo>::with_capacity(1);
let mut props = Vec::<u32>::new();
let mut prop_values = Vec::<u64>::new();
let mut encoders = Vec::<DrmEncoder>::new();
let mut res = drm_mode_get_connector {
connector_id: connector,
count_modes,
modes_ptr: modes.as_mut_ptr() as _,
..Default::default()
};
let get = |res: &mut drm_mode_get_connector| {
unsafe {
if let Err(e) = ioctl(fd, DRM_IOCTL_MODE_GETCONNECTOR, res) {
return Err(DrmError::GetConnector(e));
}
}
Ok(())
};
get(&mut res)?;
while (count_modes, count_props, count_encoders)
!= (res.count_modes, res.count_props, res.count_encoders)
{
count_modes = res.count_modes;
count_props = res.count_props;
count_encoders = res.count_encoders;
modes.reserve(count_modes as _);
props.reserve(count_props as _);
prop_values.reserve(count_props as _);
encoders.reserve(count_encoders as _);
res.modes_ptr = modes.as_mut_ptr() as _;
res.props_ptr = props.as_mut_ptr() as _;
res.prop_values_ptr = prop_values.as_mut_ptr() as _;
res.encoders_ptr = encoders.as_mut_ptr() as _;
get(&mut res)?;
}
unsafe {
modes.set_len(count_modes as _);
props.set_len(count_props as _);
prop_values.set_len(count_props as _);
encoders.set_len(count_encoders as _);
}
Ok(DrmConnectorInfo {
encoders,
modes: modes.into_iter().map(|m| m.into()).collect(),
_props: props
.into_iter()
.zip(prop_values)
.map(|(id, value)| DrmPropertyValue {
id: DrmProperty(id),
value,
})
.collect(),
_encoder_id: DrmEncoder(res.encoder_id),
_connector_id: DrmConnector(res.connector_id),
connector_type: res.connector_type,
connector_type_id: res.connector_type_id,
connection: res.connection,
mm_width: res.mm_width,
mm_height: res.mm_height,
subpixel: res.subpixel,
})
}
#[repr(C)]
struct drm_mode_atomic {
flags: u32,
count_objs: u32,
objs_ptr: u64,
count_props_ptr: u64,
props_ptr: u64,
prop_values_ptr: u64,
reserved: u64,
user_data: u64,
}
const DRM_IOCTL_MODE_ATOMIC: u64 = drm_iowr::<drm_mode_atomic>(0xbc);
pub const DRM_MODE_PAGE_FLIP_EVENT: u32 = 0x01;
pub const DRM_MODE_PAGE_FLIP_ASYNC: u32 = 0x02;
pub const DRM_MODE_ATOMIC_TEST_ONLY: u32 = 0x0100;
pub const DRM_MODE_ATOMIC_NONBLOCK: u32 = 0x0200;
pub const DRM_MODE_ATOMIC_ALLOW_MODESET: u32 = 0x0400;
pub fn mode_atomic(
fd: c::c_int,
flags: u32,
objs: &[u32],
count_props: &[u32],
props: &[u32],
prop_values: &[u64],
user_data: u64,
) -> Result<(), DrmError> {
assert_eq!(objs.len(), count_props.len());
assert_eq!(props.len(), prop_values.len());
assert_eq!(
count_props.iter().copied().sum::<u32>() as usize,
props.len()
);
if objs.is_empty() {
return Ok(());
}
let mut req = drm_mode_atomic {
flags,
count_objs: objs.len() as _,
objs_ptr: objs.as_ptr() as _,
count_props_ptr: count_props.as_ptr() as _,
props_ptr: props.as_ptr() as _,
prop_values_ptr: prop_values.as_ptr() as _,
reserved: 0,
user_data,
};
unsafe {
if let Err(e) = ioctl(fd, DRM_IOCTL_MODE_ATOMIC, &mut req) {
return Err(DrmError::Atomic(e));
}
}
Ok(())
}
#[repr(C)]
struct drm_mode_create_blob {
data: u64,
length: u32,
blob_id: u32,
}
const DRM_IOCTL_MODE_CREATEPROPBLOB: u64 = drm_iowr::<drm_mode_create_blob>(0xbd);
pub fn mode_create_blob<T: ?Sized>(fd: c::c_int, t: &T) -> Result<DrmBlob, OsError> {
let mut res = drm_mode_create_blob {
data: t as *const T as *const () as _,
length: size_of_val(t) as _,
blob_id: 0,
};
unsafe {
ioctl(fd, DRM_IOCTL_MODE_CREATEPROPBLOB, &mut res)?;
}
Ok(DrmBlob(res.blob_id))
}
#[repr(C)]
struct drm_mode_destroy_blob {
blob_id: u32,
}
const DRM_IOCTL_MODE_DESTROYPROPBLOB: u64 = drm_iowr::<drm_mode_destroy_blob>(0xbe);
pub fn mode_destroy_blob(fd: c::c_int, id: DrmBlob) -> Result<(), OsError> {
let mut res = drm_mode_destroy_blob { blob_id: id.0 };
unsafe {
ioctl(fd, DRM_IOCTL_MODE_DESTROYPROPBLOB, &mut res)?;
}
Ok(())
}
#[repr(C)]
#[derive(Debug)]
struct drm_mode_fb_cmd2 {
fb_id: u32,
width: u32,
height: u32,
pixel_format: u32,
flags: u32,
handles: [u32; 4],
pitches: [u32; 4],
offsets: [u32; 4],
modifiers: [u64; 4],
}
#[expect(dead_code)]
pub const DRM_MODE_FB_INTERLACED: u32 = 1 << 0;
pub const DRM_MODE_FB_MODIFIERS: u32 = 1 << 1;
const DRM_IOCTL_MODE_ADDFB2: u64 = drm_iowr::<drm_mode_fb_cmd2>(0xb8);
pub fn mode_addfb2(
fd: c::c_int,
width: u32,
height: u32,
pixel_format: u32,
flags: u32,
handles: [u32; 4],
strides: [u32; 4],
offsets: [u32; 4],
modifiers: [u64; 4],
) -> Result<DrmFb, OsError> {
let mut res = drm_mode_fb_cmd2 {
fb_id: 0,
width,
height,
pixel_format,
flags,
handles,
pitches: strides,
offsets,
modifiers,
};
// log::info!("{:#?}", res);
unsafe {
ioctl(fd, DRM_IOCTL_MODE_ADDFB2, &mut res)?;
}
Ok(DrmFb(res.fb_id))
}
const DRM_IOCTL_MODE_RMFB: u64 = drm_iowr::<c::c_uint>(0xaf);
pub fn mode_rmfb(fd: c::c_int, id: DrmFb) -> Result<(), OsError> {
let mut res = id.0 as c::c_uint;
unsafe {
ioctl(fd, DRM_IOCTL_MODE_RMFB, &mut res)?;
}
Ok(())
}
#[repr(C)]
struct drm_prime_handle {
handle: u32,
flags: u32,
fd: i32,
}
const DRM_IOCTL_PRIME_FD_TO_HANDLE: u64 = drm_iowr::<drm_prime_handle>(0x2e);
pub fn prime_fd_to_handle(fd: c::c_int, prime: c::c_int) -> Result<u32, OsError> {
let mut res = drm_prime_handle {
handle: 0,
flags: 0,
fd: prime,
};
unsafe {
ioctl(fd, DRM_IOCTL_PRIME_FD_TO_HANDLE, &mut res)?;
}
Ok(res.handle)
}
#[repr(C)]
struct drm_gem_close {
handle: u32,
pad: u32,
}
const DRM_IOCTL_GEM_CLOSE: u64 = drm_iow::<drm_gem_close>(0x09);
pub fn gem_close(fd: c::c_int, handle: u32) -> Result<(), OsError> {
let mut res = drm_gem_close { handle, pad: 0 };
unsafe {
ioctl(fd, DRM_IOCTL_GEM_CLOSE, &mut res)?;
}
Ok(())
}
#[expect(dead_code)]
pub const DRM_EVENT_VBLANK: u32 = 0x01;
pub const DRM_EVENT_FLIP_COMPLETE: u32 = 0x02;
pub const DRM_EVENT_CRTC_SEQUENCE: u32 = 0x03;
#[repr(C)]
pub struct drm_event {
pub ty: u32,
pub length: u32,
}
unsafe impl Pod for drm_event {}
#[repr(C)]
pub struct drm_event_vblank {
pub base: drm_event,
pub user_data: u64,
pub tv_sec: u32,
pub tv_usec: u32,
pub sequence: u32,
pub crtc_id: u32,
}
unsafe impl Pod for drm_event_vblank {}
#[repr(C)]
pub struct drm_event_crtc_sequence {
pub base: drm_event,
pub user_data: u64,
pub time_ns: i64,
pub sequence: u64,
}
unsafe impl Pod for drm_event_crtc_sequence {}
#[repr(C)]
struct drm_mode_get_blob {
blob_id: u32,
length: u32,
data: u64,
}
const DRM_IOCTL_MODE_GETPROPBLOB: u64 = drm_iowr::<drm_mode_get_blob>(0xac);
pub fn mode_getprobblob<T: Pod + ?Sized>(
fd: c::c_int,
blob_id: u32,
t: &mut T,
) -> Result<usize, OsError> {
let mut res = drm_mode_get_blob {
blob_id,
length: size_of_val(t) as _,
data: t as *const T as *const u8 as _,
};
unsafe {
ioctl(fd, DRM_IOCTL_MODE_GETPROPBLOB, &mut res)?;
}
Ok(res.length as _)
}
#[repr(C)]
struct drm_version {
version_major: c::c_int,
version_minor: c::c_int,
version_patchlevel: c::c_int,
name_len: usize, // actually __kernel_size_t but nobody cares about x32
name: *mut u8,
date_len: usize,
date: *mut u8,
desc_len: usize,
desc: *mut u8,
}
unsafe impl Pod for drm_version {}
const DRM_IOCTL_VERSION: u64 = drm_iowr::<drm_version>(0x00);
pub fn get_version(fd: c::c_int) -> Result<DrmVersion, OsError> {
let mut name = Vec::<u8>::new();
let mut date = Vec::<u8>::new();
let mut desc = Vec::<u8>::new();
let mut res: drm_version = pod_zeroed();
loop {
res.name_len = name.capacity();
res.name = name.as_mut_ptr();
res.date_len = date.capacity();
res.date = date.as_mut_ptr();
res.desc_len = desc.capacity();
res.desc = desc.as_mut_ptr();
unsafe {
ioctl(fd, DRM_IOCTL_VERSION, &mut res)?;
}
if res.name_len <= name.capacity()
&& res.date_len <= date.capacity()
&& res.desc_len <= desc.capacity()
{
break;
}
name.reserve_exact(res.name_len);
date.reserve_exact(res.date_len);
desc.reserve_exact(res.desc_len);
}
unsafe {
name.set_len(res.name_len);
date.set_len(res.date_len);
desc.set_len(res.desc_len);
}
Ok(DrmVersion {
version_major: res.version_major,
version_minor: res.version_minor,
version_patchlevel: res.version_patchlevel,
name: name.into(),
date: date.into(),
desc: desc.into(),
})
}
pub const FORMAT_BLOB_CURRENT: u32 = 1;
#[repr(C)]
pub struct drm_format_modifier_blob {
pub version: u32,
pub flags: u32,
pub count_formats: u32,
pub formats_offset: u32,
pub count_modifiers: u32,
pub modifiers_offset: u32,
}
unsafe impl Pod for drm_format_modifier_blob {}
#[repr(C)]
pub struct drm_format_modifier {
pub formats: u64,
pub offset: u32,
pub pad: u32,
pub modifier: u64,
}
unsafe impl Pod for drm_format_modifier {}
pub const DRM_SYNCOBJ_CREATE_SIGNALED: u32 = 1 << 0;
#[repr(C)]
struct drm_syncobj_create {
handle: u32,
flags: u32,
}
const DRM_IOCTL_SYNCOBJ_CREATE: u64 = drm_iowr::<drm_syncobj_create>(0xBF);
pub fn syncobj_create(drm: c::c_int, flags: u32) -> Result<u32, OsError> {
let mut res = drm_syncobj_create { handle: 0, flags };
unsafe {
ioctl(drm, DRM_IOCTL_SYNCOBJ_CREATE, &mut res)?;
}
Ok(res.handle)
}
#[repr(C)]
struct drm_syncobj_destroy {
handle: u32,
pad: u32,
}
const DRM_IOCTL_SYNCOBJ_DESTROY: u64 = drm_iowr::<drm_syncobj_destroy>(0xC0);
pub fn syncobj_destroy(drm: c::c_int, handle: u32) -> Result<(), OsError> {
let mut res = drm_syncobj_destroy { handle, pad: 0 };
unsafe {
ioctl(drm, DRM_IOCTL_SYNCOBJ_DESTROY, &mut res)?;
}
Ok(())
}
pub const DRM_SYNCOBJ_FD_TO_HANDLE_FLAGS_IMPORT_SYNC_FILE: u32 = 1 << 0;
pub const DRM_SYNCOBJ_FD_TO_HANDLE_FLAGS_TIMELINE: u32 = 1 << 1;
pub const DRM_SYNCOBJ_HANDLE_TO_FD_FLAGS_EXPORT_SYNC_FILE: u32 = 1 << 0;
pub const DRM_SYNCOBJ_HANDLE_TO_FD_FLAGS_TIMELINE: u32 = 1 << 1;
#[repr(C)]
struct drm_syncobj_handle {
handle: u32,
flags: u32,
fd: i32,
pad: u32,
point: u64,
}
const DRM_IOCTL_SYNCOBJ_HANDLE_TO_FD: u64 = drm_iowr::<drm_syncobj_handle>(0xC1);
const DRM_IOCTL_SYNCOBJ_FD_TO_HANDLE: u64 = drm_iowr::<drm_syncobj_handle>(0xC2);
pub fn syncobj_handle_to_fd(
drm: c::c_int,
handle: u32,
flags: u32,
point: u64,
) -> Result<OwnedFd, OsError> {
let mut res = drm_syncobj_handle {
handle,
flags,
fd: 0,
pad: 0,
point,
};
unsafe {
ioctl(drm, DRM_IOCTL_SYNCOBJ_HANDLE_TO_FD, &mut res)?;
}
Ok(OwnedFd::new(res.fd))
}
pub fn syncobj_fd_to_handle(
drm: c::c_int,
fd: c::c_int,
flags: u32,
handle: u32,
point: u64,
) -> Result<u32, OsError> {
let mut res = drm_syncobj_handle {
handle,
flags,
fd,
pad: 0,
point,
};
unsafe {
ioctl(drm, DRM_IOCTL_SYNCOBJ_FD_TO_HANDLE, &mut res)?;
}
Ok(res.handle)
}
// pub const DRM_SYNCOBJ_WAIT_FLAGS_WAIT_ALL: u32 = 1 << 0;
pub const DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT: u32 = 1 << 1;
pub const DRM_SYNCOBJ_WAIT_FLAGS_WAIT_AVAILABLE: u32 = 1 << 2;
#[repr(C)]
struct drm_syncobj_eventfd {
handle: u32,
flags: u32,
point: u64,
fd: i32,
pad: u32,
}
const DRM_IOCTL_SYNCOBJ_EVENTFD: u64 = drm_iowr::<drm_syncobj_eventfd>(0xCF);
pub fn syncobj_eventfd(
drm: c::c_int,
eventfd: c::c_int,
handle: u32,
point: u64,
flags: u32,
) -> Result<(), OsError> {
let mut res = drm_syncobj_eventfd {
handle,
flags,
point,
fd: eventfd,
pad: 0,
};
unsafe {
ioctl(drm, DRM_IOCTL_SYNCOBJ_EVENTFD, &mut res)?;
}
Ok(())
}
#[repr(C)]
struct drm_syncobj_timeline_array {
handles: u64,
points: u64,
count_handles: u32,
flags: u32,
}
const DRM_IOCTL_SYNCOBJ_TIMELINE_SIGNAL: u64 = drm_iowr::<drm_syncobj_timeline_array>(0xCD);
pub fn syncobj_signal(drm: c::c_int, handle: u32, point: u64) -> Result<(), OsError> {
let mut res = drm_syncobj_timeline_array {
handles: &handle as *const u32 as u64,
points: &point as *const u64 as u64,
count_handles: 1,
flags: 0,
};
unsafe {
ioctl(drm, DRM_IOCTL_SYNCOBJ_TIMELINE_SIGNAL, &mut res)?;
}
Ok(())
}
const DRM_IOCTL_SYNCOBJ_QUERY: u64 = drm_iowr::<drm_syncobj_timeline_array>(0xCB);
pub fn syncobj_query(drm: c::c_int, handle: u32) -> Result<u64, OsError> {
let mut point = 0u64;
let mut res = drm_syncobj_timeline_array {
handles: &handle as *const u32 as u64,
points: &mut point as *mut u64 as u64,
count_handles: 1,
flags: 0,
};
unsafe {
ioctl(drm, DRM_IOCTL_SYNCOBJ_QUERY, &mut res)?;
}
Ok(point)
}
#[repr(C)]
struct drm_syncobj_transfer {
src_handle: u32,
dst_handle: u32,
src_point: u64,
dst_point: u64,
flags: u32,
pad: u32,
}
const DRM_IOCTL_SYNCOBJ_TRANSFER: u64 = drm_iowr::<drm_syncobj_transfer>(0xCC);
pub fn syncobj_transfer(
drm: c::c_int,
src_handle: u32,
src_point: u64,
dst_handle: u32,
dst_point: u64,
flags: u32,
) -> Result<(), OsError> {
let mut res = drm_syncobj_transfer {
src_handle,
dst_handle,
src_point,
dst_point,
flags,
pad: 0,
};
unsafe {
ioctl(drm, DRM_IOCTL_SYNCOBJ_TRANSFER, &mut res)?;
}
Ok(())
}
#[repr(C)]
struct sync_merge_data {
name: [u8; 32],
fd2: i32,
fence: i32,
flags: u32,
pad: u32,
}
const SYNC_IOC_MAGIC: u64 = b'>' as _;
const SYNC_IOC_MERGE: u64 = uapi::_IOWR::<sync_merge_data>(SYNC_IOC_MAGIC, 3);
pub fn sync_ioc_merge(left: c::c_int, right: c::c_int) -> Result<OwnedFd, OsError> {
let mut res = sync_merge_data {
name: [0; 32],
fd2: right,
fence: 0,
flags: 0,
pad: 0,
};
unsafe {
ioctl(left, SYNC_IOC_MERGE, &mut res)?;
}
Ok(OwnedFd::new(res.fence))
}
const DRM_IOCTL_DROP_MASTER: u64 = drm_io(0x1f);
pub fn drop_master(fd: c::c_int) -> Result<(), OsError> {
let mut res = 0u8;
unsafe {
ioctl(fd, DRM_IOCTL_DROP_MASTER, &mut res)?;
}
Ok(())
}
const DRM_IOCTL_AUTH_MAGIC: u64 = drm_iow::<drm_auth>(0x11);
#[repr(C)]
struct drm_auth {
magic: c::c_uint,
}
pub fn auth_magic(fd: c::c_int, magic: c::c_uint) -> Result<(), OsError> {
let mut res = drm_auth { magic };
unsafe {
ioctl(fd, DRM_IOCTL_AUTH_MAGIC, &mut res)?;
}
Ok(())
}
const DRM_CRTC_SEQUENCE_RELATIVE: u32 = 0x00000001;
// const DRM_CRTC_SEQUENCE_NEXT_ON_MISS: u32 = 0x00000002;
#[repr(C)]
struct drm_crtc_queue_sequence {
crtc_id: u32,
flags: u32,
sequence: u64,
user_data: u64,
}
const DRM_IOCTL_CRTC_QUEUE_SEQUENCE: u64 = drm_iowr::<drm_crtc_queue_sequence>(0x3c);
pub fn queue_sequence(fd: c::c_int, crtc: DrmCrtc) -> Result<(), OsError> {
let mut res = drm_crtc_queue_sequence {
crtc_id: crtc.0,
flags: DRM_CRTC_SEQUENCE_RELATIVE,
sequence: 1,
user_data: crtc.0 as _,
};
unsafe {
ioctl(fd, DRM_IOCTL_CRTC_QUEUE_SEQUENCE, &mut res)?;
}
Ok(())
}