linux_drm/modeset/
props.rs

1use alloc::boxed::Box;
2use alloc::sync::Weak;
3use alloc::vec::Vec;
4
5use crate::ioctl::DrmCardDevice;
6
7use super::{ConnectorId, CrtcId, EncoderId, FramebufferId, PlaneId};
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq)]
10#[repr(transparent)]
11pub struct PropertyId(pub u32);
12
13#[derive(Debug)]
14pub struct ModeProp {
15    pub prop_id: PropertyId,
16    pub value: u64,
17}
18
19#[derive(Debug)]
20#[non_exhaustive]
21#[repr(u32)]
22pub enum PropertyType {
23    Unknown = 0,
24    Range = crate::ioctl::DRM_MODE_PROP_RANGE,
25    Enum = crate::ioctl::DRM_MODE_PROP_ENUM,
26    Blob = crate::ioctl::DRM_MODE_PROP_BLOB,
27    Bitmask = crate::ioctl::DRM_MODE_PROP_BITMASK,
28    Object = crate::ioctl::DRM_MODE_PROP_OBJECT,
29    SignedRange = crate::ioctl::DRM_MODE_PROP_SIGNED_RANGE,
30}
31
32impl PropertyType {
33    pub fn from_raw_flags(flags: u32) -> (Self, bool) {
34        let immutable = (flags & crate::ioctl::DRM_MODE_PROP_IMMUTABLE) != 0;
35        let type_raw = flags
36            & (crate::ioctl::DRM_MODE_PROP_LEGACY_TYPE | crate::ioctl::DRM_MODE_PROP_EXTENDED_TYPE);
37        let typ = match type_raw {
38            crate::ioctl::DRM_MODE_PROP_RANGE => Self::Range,
39            crate::ioctl::DRM_MODE_PROP_ENUM => Self::Enum,
40            crate::ioctl::DRM_MODE_PROP_BLOB => Self::Blob,
41            crate::ioctl::DRM_MODE_PROP_BITMASK => Self::Bitmask,
42            crate::ioctl::DRM_MODE_PROP_OBJECT => Self::Object,
43            crate::ioctl::DRM_MODE_PROP_SIGNED_RANGE => Self::SignedRange,
44            _ => Self::Unknown,
45        };
46        (typ, immutable)
47    }
48}
49
50#[derive(Debug)]
51pub struct ObjectPropMeta<'card> {
52    pub(crate) raw: crate::ioctl::DrmModeGetProperty,
53    pub(crate) card: &'card crate::Card,
54}
55
56impl<'card> ObjectPropMeta<'card> {
57    #[inline(always)]
58    pub(crate) fn new(raw: crate::ioctl::DrmModeGetProperty, card: &'card crate::Card) -> Self {
59        Self { raw, card }
60    }
61
62    #[inline]
63    pub fn property_id(&self) -> PropertyId {
64        PropertyId(self.raw.prop_id)
65    }
66
67    pub fn name(&self) -> &str {
68        let raw = &self.raw.name[..];
69        let raw = raw.split(|c| *c == 0).next().unwrap();
70        // Safety: We assume that raw.name is always ASCII; that should
71        // have been guaranteed by any codepath that instantiates
72        // an ObjectPropMeta object.
73        let raw = unsafe { raw.as_ascii_unchecked() };
74        raw.as_str()
75    }
76
77    #[inline]
78    pub fn property_type(&self) -> PropertyType {
79        let (ret, _) = PropertyType::from_raw_flags(self.raw.flags);
80        ret
81    }
82
83    #[inline]
84    pub fn is_immutable(&self) -> bool {
85        let (_, immut) = PropertyType::from_raw_flags(self.raw.flags);
86        immut
87    }
88
89    #[inline]
90    pub fn is_mutable(&self) -> bool {
91        !self.is_immutable()
92    }
93
94    /// Get the minimum and maximum value for a range-typed property.
95    ///
96    /// This is essentially the same as [`Self::values`], except that because it's
97    /// guaranteed that a range property always has exactly two values this function
98    /// can avoid making a dynamic memory allocation and can instead retrieve the
99    /// values directly into a stack object and then return those values.
100    pub fn range(&self) -> Result<(u64, u64), crate::Error> {
101        const RANGE_TYPES: u32 =
102            crate::ioctl::DRM_MODE_PROP_RANGE | crate::ioctl::DRM_MODE_PROP_SIGNED_RANGE;
103        let is_range = (self.raw.flags & RANGE_TYPES) != 0;
104        if !is_range {
105            return Err(crate::Error::NotSupported);
106        }
107        // Range types should always have exactly two values.
108        if self.raw.count_values() != 2 {
109            return Err(crate::Error::RemoteFailure);
110        }
111
112        let mut pair = [0_u64; 2];
113
114        let mut tmp = crate::ioctl::DrmModeGetProperty::zeroed();
115        tmp.prop_id = self.raw.prop_id;
116        unsafe { tmp.set_values_ptr(&mut pair as *mut u64, 2) };
117        self.card
118            .ioctl(crate::ioctl::DRM_IOCTL_MODE_GETPROPERTY, &mut tmp)
119            .unwrap();
120
121        if tmp.count_values() != 2 {
122            // Something has gone horribly wrong.
123            return Err(crate::Error::RemoteFailure);
124        }
125
126        Ok((pair[0], pair[1]))
127    }
128
129    /// Get a vector of describing the values that are acceptable for this property.
130    ///
131    /// The meaning of the result depends on the property type:
132    /// - For a range or signed range, the result always has length 2 and describes
133    ///   the minimum and maximum values respectively.
134    ///
135    ///     You can avoid a dynamic memory allocation in this case by using
136    ///     [`Self::range`] instead.
137    /// - For an enum or bitmask, the result describes the values of the
138    ///   valid enumeration members.
139    ///
140    ///     For these it's typically better to use [`Self::enum_members`] since
141    ///     that can also return the name associated with each value.
142    pub fn values(&self) -> Result<Vec<u64>, crate::Error> {
143        let mut count = self.raw.count_values() as usize;
144        if count == 0 {
145            return Ok(Vec::new());
146        }
147        loop {
148            let mut values = crate::vec_with_capacity::<u64>(count)?;
149
150            let mut tmp = crate::ioctl::DrmModeGetProperty::zeroed();
151            tmp.prop_id = self.raw.prop_id;
152            unsafe { tmp.set_values_ptr(values.as_mut_ptr(), count as u32) };
153
154            self.card
155                .ioctl(crate::ioctl::DRM_IOCTL_MODE_GETPROPERTY, &mut tmp)
156                .unwrap();
157
158            let new_count = tmp.count_values() as usize;
159            if new_count != count {
160                count = new_count;
161                continue;
162            }
163
164            // Safety: We confirmed above that the kernel generated the number
165            // of values we were expecting.
166            unsafe {
167                values.set_len(count);
168            }
169            return Ok(values);
170        }
171    }
172
173    /// Get a vector describing the valid values for an enum, or the bitfield values
174    /// for a bitmask.
175    pub fn enum_members(&self) -> Result<Vec<ObjectPropEnumMember>, crate::Error> {
176        const ENUM_TYPES: u32 =
177            crate::ioctl::DRM_MODE_PROP_ENUM | crate::ioctl::DRM_MODE_PROP_BITMASK;
178        let is_enum = (self.raw.flags & ENUM_TYPES) != 0;
179        if !is_enum {
180            return Ok(Vec::new());
181        }
182
183        let mut count = self.raw.count_enum_blobs() as usize;
184        loop {
185            // Safety: The following relies on ObjectPropEnumMember having identical
186            // layout to ioctl::DrmModePropertyEnum, which we ensure by marking
187            // it as repr(transparent).
188            let mut members = crate::vec_with_capacity::<ObjectPropEnumMember>(count)?;
189
190            let mut tmp = crate::ioctl::DrmModeGetProperty::zeroed();
191            tmp.prop_id = self.raw.prop_id;
192            unsafe {
193                tmp.set_enum_blob_ptr(
194                    members.as_mut_ptr() as *mut crate::ioctl::DrmModePropertyEnum,
195                    count as u32,
196                )
197            };
198
199            self.card
200                .ioctl(crate::ioctl::DRM_IOCTL_MODE_GETPROPERTY, &mut tmp)
201                .unwrap();
202
203            let new_count = tmp.count_enum_blobs() as usize;
204            if new_count != count {
205                count = new_count;
206                continue;
207            }
208
209            // Safety: We confirmed above that the kernel generated the number
210            // of values we were expecting.
211            unsafe {
212                members.set_len(count);
213            }
214            return Ok(members);
215        }
216    }
217}
218
219#[derive(Debug)]
220#[repr(transparent)]
221pub struct ObjectPropEnumMember {
222    raw: crate::ioctl::DrmModePropertyEnum,
223}
224
225impl ObjectPropEnumMember {
226    #[inline]
227    pub fn value(&self) -> u64 {
228        self.raw.value
229    }
230
231    pub fn name(&self) -> &str {
232        let raw = &self.raw.name[..];
233        let raw = raw.split(|c| *c == 0).next().unwrap();
234        // The following assumes that the kernel will only use ASCII
235        // characters in enum member names, which has been true so
236        // far.
237        let raw = raw.as_ascii().unwrap();
238        raw.as_str()
239    }
240}
241
242/// Trait implemented by types that can be borrowed as raw property values.
243///
244/// For types that represent references to other objects already known by
245/// the kernel, such as property blobs, the caller must keep the original
246/// object live for as long as the result is being used in requests to the
247/// kernel.
248pub trait AsRawPropertyValue {
249    fn as_raw_property_value(&self) -> u64;
250}
251
252/// Trait implemented by types that can be converted into raw property values
253/// while transferring ownership.
254///
255/// This is an extension of [`AsRawPropertyValue`] for situations where the
256/// recipient is taking ownership of the implementing object, so that the
257/// object can be kept live long enough to use its raw representation.
258pub trait IntoRawPropertyValue: AsRawPropertyValue {
259    /// Return the raw `u64` representation to send to the kernel along with
260    /// an optional object that needs to be kept live in order for that
261    /// raw result to remain valid.
262    ///
263    /// The first result should typically be the same as would be returned
264    /// from [`AsRawPropertyValue::as_raw_property_value`].
265    fn into_raw_property_value(self) -> (u64, Option<Box<dyn core::any::Any>>);
266}
267
268macro_rules! trivial_as_property_value {
269    ($t:ty) => {
270        impl AsRawPropertyValue for $t {
271            #[inline(always)]
272            fn as_raw_property_value(&self) -> u64 {
273                *self as u64
274            }
275        }
276        impl IntoRawPropertyValue for $t {
277            #[inline(always)]
278            fn into_raw_property_value(self) -> (u64, Option<Box<dyn core::any::Any>>) {
279                (self as u64, None)
280            }
281        }
282    };
283}
284
285trivial_as_property_value!(u64);
286trivial_as_property_value!(u32);
287trivial_as_property_value!(u16);
288trivial_as_property_value!(u8);
289trivial_as_property_value!(usize);
290trivial_as_property_value!(i64);
291trivial_as_property_value!(i32);
292trivial_as_property_value!(i16);
293trivial_as_property_value!(i8);
294trivial_as_property_value!(isize);
295trivial_as_property_value!(bool);
296
297macro_rules! id_as_property_value {
298    ($t:ty) => {
299        impl AsRawPropertyValue for $t {
300            #[inline(always)]
301            fn as_raw_property_value(&self) -> u64 {
302                self.0 as u64
303            }
304        }
305        impl IntoRawPropertyValue for $t {
306            #[inline(always)]
307            fn into_raw_property_value(self) -> (u64, Option<Box<dyn core::any::Any>>) {
308                (self.0 as u64, None)
309            }
310        }
311    };
312}
313
314id_as_property_value!(CrtcId);
315id_as_property_value!(ConnectorId);
316id_as_property_value!(FramebufferId);
317id_as_property_value!(BlobId);
318id_as_property_value!(EncoderId);
319id_as_property_value!(PlaneId);
320
321#[derive(Debug, Clone, Copy, PartialEq, Eq)]
322#[repr(transparent)]
323pub struct BlobId(pub u32);
324
325/// A handle for a live property blob.
326///
327/// The [`Drop`] implementation for this type destroys the
328#[derive(Debug)]
329pub struct BlobHandle {
330    pub(crate) id: Option<u32>,
331    pub(crate) f: Weak<linux_io::File<DrmCardDevice>>,
332}
333
334impl<'card> BlobHandle {
335    #[inline(always)]
336    pub const fn id(&self) -> u32 {
337        let Some(ret) = self.id else {
338            unreachable!();
339        };
340        ret
341    }
342
343    /// Consume the handle and destroy the underlying blob in the kernel.
344    #[inline(always)]
345    pub fn destroy(mut self) -> Result<(), crate::result::Error> {
346        self.destroy_internal()
347    }
348
349    #[inline]
350    fn destroy_internal(&mut self) -> Result<(), crate::result::Error> {
351        if let Some(f) = self.f.upgrade() {
352            if let Some(blob_id) = self.id.take() {
353                let mut tmp = crate::ioctl::DrmModeDestroyBlob { blob_id };
354                crate::drm_ioctl(&f, crate::ioctl::DRM_IOCTL_MODE_DESTROYPROPBLOB, &mut tmp)?;
355            }
356        }
357        Ok(())
358    }
359}
360
361impl Drop for BlobHandle {
362    #[inline(always)]
363    fn drop(&mut self) {
364        let _ = self.destroy_internal();
365    }
366}
367
368impl AsRawPropertyValue for BlobHandle {
369    fn as_raw_property_value(&self) -> u64 {
370        self.id() as u64
371    }
372}
373
374impl IntoRawPropertyValue for BlobHandle {
375    fn into_raw_property_value(self) -> (u64, Option<Box<dyn core::any::Any>>) {
376        (self.id() as u64, Some(Box::new(self)))
377    }
378}