linux_drm/modeset/
props.rs

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