linux_drm/
lib.rs

1//! This library provides relatively-low-level bindings to the Linux Direct
2//! Rendering Manager and Kernel Modesetting APIs.
3//!
4//! This library currently focuses mainly on the modesetting APIs. Rendering
5//! requires driver-specific userspace code that is beyond the scope of
6//! this library.
7//!
8//! # Features
9//!
10//! The default implementation of this library currently relies on unstable
11//! features and so requires nightly Rust. The feature `stable_polyfill`
12//! opts in to replacing those unstable features with external libraries to
13//! allow this to work on stable Rust. This feature will be maintained on a
14//! best-effort basis but will be removed in future versions if it causes
15//! maintainence challenges. It will also be removed once the relevant features
16//! have been stablized.
17
18#![no_std]
19#![cfg_attr(not(feature = "stable_polyfill"), feature(ptr_metadata))]
20#![cfg_attr(not(feature = "stable_polyfill"), feature(ascii_char))]
21
22extern crate alloc;
23
24/// Types and other symbols used for event handling.
25pub mod event;
26/// Low-level `ioctl`-based access to DRM devices.
27pub mod ioctl;
28/// Types and other symbols used for modesetting.
29pub mod modeset;
30pub mod result;
31
32mod util;
33
34use core::iter::{self, zip};
35use core::ptr::null_mut;
36
37use alloc::sync::Arc;
38use alloc::vec::Vec;
39use linux_io::fd::ioctl::IoctlReq;
40use modeset::{
41    BlobId, BufferObjectId, ConnectorId, CrtcId, EncoderId, EncoderState, FramebufferId, ModeInfo,
42    ModeProp, PlaneId,
43};
44use result::{Error, InitError};
45
46#[repr(transparent)]
47#[derive(Debug)]
48pub struct Card {
49    f: Arc<linux_io::File<ioctl::DrmCardDevice>>,
50}
51
52impl Card {
53    /// Open the file at the given path and attempt to use it as a
54    /// DRM card file descriptor.
55    ///
56    /// Returns [`result::InitError::NotDrmCard`] if the opened file
57    /// does not support the `DRM_IOCTL_VERSION` ioctl request.
58    pub fn open(path: impl AsRef<core::ffi::CStr>) -> Result<Self, InitError> {
59        let f = linux_io::File::open(path.as_ref(), linux_io::OpenOptions::read_write())?;
60        Self::from_file(f)
61    }
62
63    /// Attempt to use the given file as a DRM card device.
64    ///
65    /// Returns [`result::InitError::NotDrmCard`] if the opened file
66    /// does not support the `DRM_IOCTL_VERSION` ioctl request.
67    pub fn from_file<D>(f: linux_io::File<D>) -> Result<Self, InitError> {
68        // We'll use the VERSION ioctl to decide whether this file
69        // seems to be a DRM card device. To do that we need to
70        // first optimistically convert it to a DrmCardDevice,
71        // so that our ioctl constant will be compatible.
72        // Safety: We'll return this new f only if our ioctl
73        // probe is successful, which therefore suggests that
74        // this ought to be a DRM card device.
75        let f: linux_io::File<ioctl::DrmCardDevice> = unsafe { f.to_device(ioctl::DrmCardDevice) };
76        let ret = Self { f: Arc::new(f) };
77        let mut v = ioctl::DrmVersion::zeroed();
78        ret.ioctl(ioctl::DRM_IOCTL_VERSION, &mut v)?;
79        Ok(ret)
80    }
81
82    /// Wraps the given file in [`Card`] without checking whether
83    /// it supports any DRM card ioctl requests.
84    pub unsafe fn from_file_unchecked<D>(f: linux_io::File<D>) -> Self {
85        let f: linux_io::File<ioctl::DrmCardDevice> = unsafe { f.to_device(ioctl::DrmCardDevice) };
86        Self { f: Arc::new(f) }
87    }
88
89    /// Get the open file descriptor for the card.
90    ///
91    /// Interacting with this file descriptor outside of the [`Card`] abstraction
92    /// may cause the abstraction to malfunction. It's exposed primarily so
93    /// it can be used with system functions like `poll` to wait for events
94    /// on multiple file descriptors at once.
95    pub fn fd(&self) -> linux_unsafe::int {
96        self.f.fd()
97    }
98
99    /// Determine the DRM API version supported by this device.
100    pub fn api_version(&self) -> Result<ApiVersion, Error> {
101        let mut v = ioctl::DrmVersion::zeroed();
102        self.ioctl(ioctl::DRM_IOCTL_VERSION, &mut v)?;
103        Ok(ApiVersion {
104            major: v.version_major as i64,
105            minor: v.version_minor as i64,
106            patch: v.version_patchlevel as i64,
107        })
108    }
109
110    /// Read the driver name into the given slice.
111    pub fn read_driver_name<'a>(&self, into: &'a mut [u8]) -> Result<&'a mut [u8], Error> {
112        let mut v = ioctl::DrmVersion::zeroed();
113        let ptr = into.as_mut_ptr();
114        unsafe { v.set_name_ptr(ptr as *mut _, into.len()) };
115        self.ioctl(ioctl::DRM_IOCTL_VERSION, &mut v)?;
116        Ok(&mut into[..v.name_len()])
117    }
118
119    /// Read the driver name into a vector.
120    pub fn driver_name(&self) -> Result<Vec<u8>, Error> {
121        let mut v = ioctl::DrmVersion::zeroed();
122        self.ioctl(ioctl::DRM_IOCTL_VERSION, &mut v)?;
123        let len = v.name_len();
124        let mut ret = vec_with_capacity::<u8>(len)?;
125        v = ioctl::DrmVersion::zeroed();
126        unsafe { v.set_name_ptr(ret.as_mut_ptr() as *mut _, len) };
127        self.ioctl(ioctl::DRM_IOCTL_VERSION, &mut v)?;
128        unsafe { ret.set_len(v.name_len()) };
129        Ok(ret)
130    }
131
132    /// Read a device capability value.
133    #[inline(always)]
134    pub fn get_device_cap(&self, capability: DeviceCap) -> Result<u64, Error> {
135        self.get_device_cap_raw(capability.into())
136    }
137
138    /// Read a device capability value using a raw capability number.
139    #[inline]
140    pub fn get_device_cap_raw(&self, capability: ioctl::DrmCap) -> Result<u64, Error> {
141        let mut s = ioctl::DrmGetCap {
142            capability,
143            value: 0,
144        };
145        self.ioctl(ioctl::DRM_IOCTL_GET_CAP, &mut s)?;
146        Ok(s.value)
147    }
148
149    /// Read a device capability value using a raw capability number.
150    #[inline(always)]
151    pub fn set_client_cap(&mut self, capability: ClientCap, value: u64) -> Result<(), Error> {
152        self.set_client_cap_raw(capability.into(), value)
153    }
154
155    /// Attempt to set a client capability, which might then change the behavior
156    /// of other device functions.
157    #[inline]
158    pub fn set_client_cap_raw(
159        &mut self,
160        capability: ioctl::DrmClientCap,
161        value: u64,
162    ) -> Result<(), Error> {
163        let s = ioctl::DrmSetClientCap { capability, value };
164        self.ioctl(ioctl::DRM_IOCTL_SET_CLIENT_CAP, &s)?;
165        Ok(())
166    }
167
168    /// Attempt to become the "master" of this device, which is required for
169    /// modesetting.
170    #[inline]
171    pub fn become_master(&mut self) -> Result<(), Error> {
172        self.ioctl(ioctl::DRM_IOCTL_SET_MASTER, ())?;
173        Ok(())
174    }
175
176    /// Release the "master" status of this device, thus allowing other
177    /// processes to claim it.
178    #[inline]
179    pub fn drop_master(&mut self) -> Result<(), Error> {
180        self.ioctl(ioctl::DRM_IOCTL_DROP_MASTER, ())?;
181        Ok(())
182    }
183
184    /// Get metadata about a DRM property using its id.
185    ///
186    /// Property ids are assigned dynamically and so must be detected at runtime.
187    pub fn property_meta(
188        &self,
189        prop_id: modeset::PropertyId,
190    ) -> Result<modeset::ObjectPropMeta, Error> {
191        let mut tmp = ioctl::DrmModeGetProperty::zeroed();
192        tmp.prop_id = prop_id.0;
193        self.ioctl(ioctl::DRM_IOCTL_MODE_GETPROPERTY, &mut tmp)?;
194        if !tmp.name.is_ascii() {
195            // ObjectPropMeta assumes that the name is always ASCII so
196            // we can cheaply treat it as a str, which has been true
197            // so far but we'll make sure things stay sound by
198            // rejecting any property with a non-ASCII name.
199            return Err(Error::NotSupported);
200        }
201        Ok(modeset::ObjectPropMeta::new(tmp, &self))
202    }
203
204    /// Get the properties of the specified object in their raw form.
205    ///
206    /// Use either [`Self::property_meta`] or [`Self::each_object_property_meta`]
207    /// to discover the name and type information for each property id.
208    pub fn object_properties(
209        &self,
210        obj_id: impl Into<modeset::ObjectId>,
211    ) -> Result<Vec<modeset::ModeProp>, Error> {
212        fn real_object_properties(
213            card: &Card,
214            obj_id: modeset::ObjectId,
215        ) -> Result<Vec<modeset::ModeProp>, Error> {
216            let (type_id, raw_id) = obj_id.as_raw_type_and_id();
217            let mut tmp = ioctl::DrmModeObjGetProperties::zeroed();
218            tmp.obj_type = type_id;
219            tmp.obj_id = raw_id;
220            card.ioctl(ioctl::DRM_IOCTL_MODE_OBJ_GETPROPERTIES, &mut tmp)?;
221
222            // The sets of properties can potentially change due to hotplug events
223            // while we're producing this result, and so we need to keep retrying
224            // until we get a consistent result.
225            loop {
226                let prop_count = tmp.count_props() as usize;
227
228                let mut prop_ids = vec_with_capacity::<u32>(prop_count)?;
229                let mut prop_values = vec_with_capacity::<u64>(prop_count)?;
230
231                unsafe {
232                    tmp.set_prop_ptrs(
233                        prop_ids.as_mut_ptr(),
234                        prop_values.as_mut_ptr(),
235                        prop_count as u32,
236                    )
237                };
238
239                card.ioctl(ioctl::DRM_IOCTL_MODE_OBJ_GETPROPERTIES, &mut tmp)?;
240
241                let new_prop_count = tmp.count_props() as usize;
242                if new_prop_count != prop_count {
243                    // The number of properties has changed since the previous
244                    // request, so we'll retry.
245                    continue;
246                }
247
248                // Safety: We ensured the slices capacities above, and ensured
249                // that the kernel has populated the number of ids we expected
250                // in each case.
251                unsafe {
252                    prop_ids.set_len(prop_count);
253                    prop_values.set_len(prop_count);
254                };
255                return Ok(iter::zip(prop_ids.into_iter(), prop_values.into_iter())
256                    .map(|(id, val)| modeset::ModeProp {
257                        prop_id: modeset::PropertyId(id),
258                        value: val,
259                    })
260                    .collect());
261            }
262        }
263        real_object_properties(self, obj_id.into())
264    }
265
266    /// Call `f` with the metadata for each property of the object with the given id.
267    ///
268    /// This is intended for use by callers that want to build a lookup
269    /// table of property ids for later use in efficiently setting those
270    /// properties. Pass a closure that mutates the lookup table only
271    /// for the subset of properties that are interesting.
272    pub fn each_object_property_meta(
273        &self,
274        obj_id: impl Into<modeset::ObjectId>,
275        mut f: impl FnMut(modeset::ObjectPropMeta, u64),
276    ) -> Result<(), Error> {
277        let obj_id = obj_id.into();
278        let (type_id, raw_id) = obj_id.as_raw_type_and_id();
279        let mut tmp = ioctl::DrmModeObjGetProperties::zeroed();
280        tmp.obj_type = type_id;
281        tmp.obj_id = raw_id;
282        self.ioctl(ioctl::DRM_IOCTL_MODE_OBJ_GETPROPERTIES, &mut tmp)?;
283        if tmp.count_props() == 0 {
284            return Ok(());
285        }
286
287        let (prop_ids, prop_values) = loop {
288            let prop_count = tmp.count_props() as usize;
289            let mut prop_ids = vec_with_capacity::<u32>(prop_count)?;
290            let mut prop_values = vec_with_capacity::<u64>(prop_count)?;
291            unsafe {
292                tmp.set_prop_ptrs(
293                    prop_ids.as_mut_ptr(),
294                    prop_values.as_mut_ptr(),
295                    prop_count as u32,
296                )
297            };
298            self.ioctl(ioctl::DRM_IOCTL_MODE_OBJ_GETPROPERTIES, &mut tmp)?;
299
300            let new_prop_count = tmp.count_props() as usize;
301            if new_prop_count != prop_count {
302                // The number of properties has changed since the previous
303                // request, so we'll retry.
304                continue;
305            }
306
307            // Safety: We ensured the slice capacities above, and ensured
308            // that the kernel has populated the number of ids we expected.
309            unsafe {
310                prop_ids.set_len(prop_count);
311                prop_values.set_len(prop_count);
312            };
313            break (prop_ids, prop_values);
314        };
315
316        for (prop_id, value) in zip(prop_ids, prop_values) {
317            let mut raw = ioctl::DrmModeGetProperty::zeroed();
318            raw.prop_id = prop_id;
319            self.ioctl(ioctl::DRM_IOCTL_MODE_GETPROPERTY, &mut raw)?;
320            // We can only produce a str from a property name that is all ASCII
321            // characters, which is true for all property names used in the kernel
322            // so far. We'll just ignore any properties that have non-ASCII names
323            // for now, and then adjust this to do something else if an important
324            // non-ASCII name shows up in a later kernel release.
325            if !raw.name.is_ascii() {
326                continue;
327            }
328            f(modeset::ObjectPropMeta::new(raw, &self), value);
329        }
330
331        Ok(())
332    }
333
334    /// Read information about the modesetting resources available for this device.
335    ///
336    /// The result includes ids for the available connectors, encoders, CRTCs,
337    /// planes, and framebuffers.
338    pub fn resources(&self) -> Result<modeset::CardResources, Error> {
339        // The sets of resources can potentially change due to hotplug events
340        // while we're producing this result, and so we need to keep retrying
341        // until we get a consistent result.
342        let mut ret = loop {
343            let mut r = ioctl::DrmModeCardRes::zeroed();
344            self.ioctl(ioctl::DRM_IOCTL_MODE_GETRESOURCES, &mut r)?;
345            let fb_count = r.count_fbs() as usize;
346            let connector_count = r.count_connectors() as usize;
347            let crtc_count = r.count_crtcs() as usize;
348            let encoder_count = r.count_encoders() as usize;
349
350            let mut fb_ids = vec_with_capacity::<FramebufferId>(fb_count)?;
351            let mut connector_ids = vec_with_capacity::<ConnectorId>(connector_count)?;
352            let mut crtc_ids = vec_with_capacity::<CrtcId>(crtc_count)?;
353            let mut encoder_ids = vec_with_capacity::<EncoderId>(encoder_count)?;
354
355            r = ioctl::DrmModeCardRes::zeroed();
356            unsafe {
357                r.set_fb_id_ptr(fb_ids.as_mut_ptr() as *mut u32, fb_count as u32);
358                r.set_connector_id_ptr(
359                    connector_ids.as_mut_ptr() as *mut u32,
360                    connector_count as u32,
361                );
362                r.set_crtc_id_ptr(crtc_ids.as_mut_ptr() as *mut u32, crtc_count as u32);
363                r.set_encoder_id_ptr(encoder_ids.as_mut_ptr() as *mut u32, encoder_count as u32);
364            };
365
366            self.ioctl(ioctl::DRM_IOCTL_MODE_GETRESOURCES, &mut r)?;
367            // If any of the counts changed since our original call then the kernel
368            // would not have populated the arrays and we'll need to retry and
369            // hope that we don't collide with hotplugging next time.
370            if r.count_fbs() as usize != fb_count {
371                continue;
372            }
373            if r.count_connectors() as usize != connector_count {
374                continue;
375            }
376            if r.count_crtcs() as usize != crtc_count {
377                continue;
378            }
379            if r.count_encoders() as usize != encoder_count {
380                continue;
381            }
382
383            // Safety: We ensured the slices capacities above, and ensured
384            // that the kernel has populated the number of ids we expected
385            // in each case.
386            unsafe {
387                fb_ids.set_len(fb_count);
388                connector_ids.set_len(connector_count);
389                crtc_ids.set_len(crtc_count);
390                encoder_ids.set_len(encoder_count);
391            };
392            break modeset::CardResources {
393                fb_ids,
394                connector_ids,
395                crtc_ids,
396                encoder_ids,
397                plane_ids: Vec::new(),
398                min_width: r.min_width,
399                max_width: r.max_width,
400                min_height: r.min_height,
401                max_height: r.max_height,
402            };
403        };
404
405        // The planes come from a different ioctl request so we'll deal
406        // with those now too. Similar requirement to retry.
407        loop {
408            let mut tmp = ioctl::DrmModeGetPlaneRes::zeroed();
409            self.ioctl(ioctl::DRM_IOCTL_MODE_GETPLANERESOURCES, &mut tmp)?;
410
411            let plane_count = tmp.count_planes() as usize;
412            let mut plane_ids = vec_with_capacity::<modeset::PlaneId>(plane_count)?;
413            unsafe { tmp.set_plane_id_ptr(plane_ids.as_mut_ptr() as *mut u32, plane_count as u32) };
414
415            self.ioctl(ioctl::DRM_IOCTL_MODE_GETPLANERESOURCES, &mut tmp)?;
416            if tmp.count_planes() as usize != plane_count {
417                // Need to try again, then.
418                continue;
419            }
420
421            // Safety: We ensured the slices capacity above, and ensured
422            // that the kernel has populated the number of ids we expected.
423            unsafe {
424                plane_ids.set_len(plane_count);
425            };
426            ret.plane_ids = plane_ids;
427            return Ok(ret);
428        }
429    }
430
431    /// Read current state information for the connector with the given id.
432    pub fn connector_state(
433        &self,
434        connector_id: ConnectorId,
435    ) -> Result<modeset::ConnectorState, Error> {
436        // Hotplug events can cause the state to change between our calls, so
437        // we'll keep retrying until we get a consistent result.
438        loop {
439            let mut tmp = ioctl::DrmModeGetConnector::zeroed();
440            tmp.connector_id = connector_id.0;
441            self.ioctl(ioctl::DRM_IOCTL_MODE_GETCONNECTOR, &mut tmp)?;
442
443            let mode_count = tmp.count_modes();
444            let encoder_count = tmp.count_encoders();
445            let prop_count = tmp.count_props();
446
447            let mut modes = vec_with_capacity::<ioctl::DrmModeInfo>(mode_count as usize)?;
448            let mut ret_modes = vec_with_capacity::<ModeInfo>(mode_count as usize)?;
449            let mut encoder_ids = vec_with_capacity::<u32>(encoder_count as usize)?;
450            let mut prop_ids = vec_with_capacity::<u32>(prop_count as usize)?;
451            let mut prop_values = vec_with_capacity::<u64>(prop_count as usize)?;
452            let mut ret_props = vec_with_capacity::<ModeProp>(prop_count as usize)?;
453
454            tmp = ioctl::DrmModeGetConnector::zeroed();
455            tmp.connector_id = connector_id.0;
456            unsafe {
457                tmp.set_modes_ptr(modes.as_mut_ptr(), mode_count);
458                tmp.set_encoders_ptr(encoder_ids.as_mut_ptr(), encoder_count);
459                tmp.set_props_ptrs(prop_ids.as_mut_ptr(), prop_values.as_mut_ptr(), prop_count);
460            };
461            self.ioctl(ioctl::DRM_IOCTL_MODE_GETCONNECTOR, &mut tmp)?;
462
463            if tmp.count_modes() != mode_count
464                || tmp.count_props() != prop_count
465                || tmp.count_encoders() != encoder_count
466            {
467                // Seems like things have changed since our first call, so we need to start over.
468                continue;
469            }
470
471            // We can now safely set the lengths of the various vectors,
472            // because we confirmed above that the kernel gave us the
473            // lengths we asked for.
474            unsafe {
475                modes.set_len(mode_count as usize);
476                encoder_ids.set_len(encoder_count as usize);
477                prop_ids.set_len(prop_count as usize);
478                prop_values.set_len(prop_count as usize);
479            }
480
481            ret_modes.extend(modes.into_iter().map(|raw| {
482                let r: modeset::ModeInfo = raw.into();
483                r
484            }));
485            ret_props.extend(
486                core::iter::zip(prop_ids.iter().copied(), prop_values.iter().copied()).map(
487                    |(prop_id, value)| ModeProp {
488                        prop_id: modeset::PropertyId(prop_id),
489                        value,
490                    },
491                ),
492            );
493            return Ok(modeset::ConnectorState {
494                id: ConnectorId(tmp.connector_id),
495                current_encoder_id: EncoderId(tmp.encoder_id),
496                connector_type: tmp.connector_type.into(),
497                connector_type_id: tmp.connector_type_id,
498                connection_state: tmp.connection.into(),
499                width_mm: tmp.mm_width,
500                height_mm: tmp.mm_height,
501                subpixel_type: tmp.subpixel.into(),
502                modes: ret_modes,
503                props: ret_props,
504                available_encoder_ids: encoder_ids,
505            });
506        }
507    }
508
509    /// Read current state information for the encoder with the given id.
510    pub fn encoder_state(&self, encoder_id: EncoderId) -> Result<modeset::EncoderState, Error> {
511        let mut tmp = ioctl::DrmModeGetEncoder::zeroed();
512        tmp.encoder_id = encoder_id.0;
513        self.ioctl(ioctl::DRM_IOCTL_MODE_GETENCODER, &mut tmp)?;
514        Ok(EncoderState {
515            encoder_id: EncoderId(tmp.encoder_id),
516            encoder_type: tmp.encoder_type,
517            current_crtc_id: CrtcId(tmp.crtc_id),
518            possible_crtcs: tmp.possible_crtcs,
519            possible_clones: tmp.possible_clones,
520        })
521    }
522
523    /// Read current state information for the CRTC with the given id.
524    pub fn crtc_state(&self, crtc_id: CrtcId) -> Result<modeset::CrtcState, Error> {
525        let mut tmp = ioctl::DrmModeCrtc::zeroed();
526        tmp.crtc_id = crtc_id.0;
527        self.ioctl(ioctl::DRM_IOCTL_MODE_GETCRTC, &mut tmp)?;
528        Ok(tmp.into())
529    }
530
531    /// Read current state information for the plane with the given id.
532    pub fn plane_state(&self, plane_id: PlaneId) -> Result<modeset::PlaneState, Error> {
533        let mut tmp = ioctl::DrmModeGetPlane::zeroed();
534        tmp.plane_id = plane_id.0;
535        self.ioctl(ioctl::DRM_IOCTL_MODE_GETPLANE, &mut tmp)?;
536        Ok(modeset::PlaneState {
537            id: PlaneId(tmp.plane_id),
538            crtc_id: CrtcId(tmp.crtc_id),
539            fb_id: FramebufferId(tmp.fb_id),
540            possible_crtcs: tmp.possible_crtcs,
541            gamma_size: tmp.gamma_size,
542        })
543    }
544
545    /// Attempt to commit an atomic modesetting request.
546    ///
547    /// Callers which intend to perform frequent modesetting, such as modesetting on
548    /// every frame for double buffering, are encouraged to retain their
549    /// [`modeset::AtomicRequest`] object and reset it to use again on a subsequent
550    /// request if that request will involve a similar set of objects and properties,
551    /// to minimize the need for reallocating the backing storage for the request
552    /// on every frame.
553    pub fn atomic_commit(
554        &mut self,
555        req: &modeset::AtomicRequest,
556        flags: modeset::AtomicCommitFlags,
557        user_data: u64,
558    ) -> Result<(), Error> {
559        let mut tmp = ioctl::DrmModeAtomic::zeroed();
560        let mut raw_parts = req.for_ioctl_req();
561        unsafe {
562            tmp.set_ptrs(ioctl::DrmModeAtomicPtrs {
563                count_objs: raw_parts.obj_ids.len() as u32,
564                objs_ptr: raw_parts.obj_ids.as_mut_ptr(),
565                count_props_ptr: raw_parts.obj_prop_counts.as_mut_ptr(),
566                props_ptr: raw_parts.prop_ids.as_mut_ptr(),
567                prop_values_ptr: raw_parts.prop_values.as_mut_ptr(),
568            })
569        };
570        tmp.flags = flags.0;
571        tmp.user_data = user_data;
572
573        self.ioctl(ioctl::DRM_IOCTL_MODE_ATOMIC, &mut tmp)?;
574        Ok(())
575    }
576
577    /// Send the given content to the kernel as a property blob, ready to use
578    /// for assignment to a blob-typed object property.
579    ///
580    /// The returned [`modeset::BlobHandle`] must remain live long enough to
581    /// be assigned to the target property. The blob object in the kernel
582    /// will be destroyed when the blob handle is dropped.
583    pub fn new_property_blob<'card, 'content>(
584        &'card self,
585        content: &'content [u8],
586    ) -> Result<modeset::BlobHandle, Error> {
587        let mut tmp = ioctl::DrmModeCreateBlob::zeroed();
588        if content.len() > (u32::MAX as usize) {
589            return Err(Error::Invalid);
590        }
591        unsafe { tmp.set_data(content.as_ptr(), content.len() as u32) };
592        self.ioctl(ioctl::DRM_IOCTL_MODE_CREATEPROPBLOB, &mut tmp)?;
593        Ok(modeset::BlobHandle {
594            id: Some(BlobId(tmp.blob_id)),
595            f: Arc::downgrade(&self.f),
596        })
597    }
598
599    /// Reset the given CRTC to its default (zeroed) settings.
600    pub fn reset_crtc(&mut self, crtc_id: u32) -> Result<modeset::CrtcState, Error> {
601        let mut tmp = ioctl::DrmModeCrtc::zeroed();
602        tmp.crtc_id = crtc_id;
603        self.ioctl(ioctl::DRM_IOCTL_MODE_SETCRTC, &mut tmp)?;
604        Ok(tmp.into())
605    }
606
607    /// Set the given CRTC to display the image from the given "dumb buffer",
608    /// used for software rendering.
609    pub fn set_crtc_dumb_buffer(
610        &mut self,
611        crtc_id: CrtcId,
612        buf: &modeset::DumbBuffer,
613        mode: &ModeInfo,
614        conn_ids: &[ConnectorId],
615    ) -> Result<modeset::CrtcState, Error> {
616        let mut tmp = ioctl::DrmModeCrtc::zeroed();
617        tmp.crtc_id = crtc_id.0;
618        if conn_ids.len() > (u32::MAX as usize) {
619            return Err(Error::Invalid);
620        }
621        unsafe {
622            tmp.set_set_connectors_ptr(conn_ids.as_ptr() as *const u32, conn_ids.len() as u32)
623        };
624        tmp.fb_id = buf.fb_id.0;
625        tmp.mode = mode.into();
626        tmp.mode_valid = 1;
627
628        self.ioctl(ioctl::DRM_IOCTL_MODE_SETCRTC, &mut tmp)?;
629        Ok(tmp.into())
630    }
631
632    /// Use a page-flipping request to change the given CRTC to display the image
633    /// from the given "dumb buffer".
634    pub fn crtc_page_flip_dumb_buffer(
635        &mut self,
636        crtd_id: CrtcId,
637        buf: &modeset::DumbBuffer,
638        flags: modeset::PageFlipFlags,
639    ) -> Result<(), Error> {
640        let mut tmp = ioctl::DrmModeCrtcPageFlip::zeroed();
641        tmp.crtc_id = crtd_id.0;
642        tmp.fb_id = buf.fb_id.0;
643        tmp.flags = flags.into();
644        self.ioctl(ioctl::DRM_IOCTL_MODE_PAGE_FLIP, &mut tmp)?;
645        Ok(())
646    }
647
648    /// Create a new "dumb buffer" that can be used for portable (hardware-agnostic)
649    /// software rendering.
650    pub fn create_dumb_buffer(
651        &self,
652        req: modeset::DumbBufferRequest,
653    ) -> Result<modeset::DumbBuffer, Error> {
654        let mut buf_req = ioctl::DrmModeCreateDumb::zeroed();
655        buf_req.width = req.width;
656        buf_req.height = req.height;
657        buf_req.bpp = req.bpp;
658        self.ioctl(ioctl::DRM_IOCTL_MODE_CREATE_DUMB, &mut buf_req)?;
659        let buffer_handle = buf_req.handle;
660        let mut cleanup_db = util::Cleanup::new(|| {
661            let mut msg = crate::ioctl::DrmModeDestroyDumb::zeroed();
662            msg.handle = buffer_handle;
663            let _ = self.ioctl(crate::ioctl::DRM_IOCTL_MODE_DESTROY_DUMB, &mut msg);
664        });
665
666        let mut fb_req = ioctl::DrmModeFbCmd::zeroed();
667        fb_req.width = buf_req.width;
668        fb_req.height = buf_req.height;
669        fb_req.bpp = buf_req.bpp;
670        fb_req.depth = req.depth;
671        fb_req.pitch = buf_req.pitch;
672        fb_req.handle = buf_req.handle;
673        self.ioctl(ioctl::DRM_IOCTL_MODE_ADDFB, &mut fb_req)?;
674        let mut fb_id = fb_req.fb_id;
675        let mut cleanup_fb = util::Cleanup::new(|| {
676            let _ = self.ioctl(crate::ioctl::DRM_IOCTL_MODE_RMFB, &mut fb_id);
677        });
678
679        let mut map_req = ioctl::DrmModeMapDumb::zeroed();
680        map_req.handle = buf_req.handle;
681        self.ioctl(ioctl::DRM_IOCTL_MODE_MAP_DUMB, &mut map_req)?;
682
683        let buf_ptr = unsafe {
684            self.f.mmap_raw(
685                map_req.offset as _,
686                buf_req.size as usize,
687                null_mut(),
688                0b11, // PROT_READ | PROT_WRITE,
689                0x01, // MAP_SHARED,
690            )?
691        };
692
693        // The DumbBuffer object's Drop is responsible for freeing
694        // the mmap, framebuffer object, and dumb buffer.
695        cleanup_fb.cancel();
696        cleanup_db.cancel();
697        Ok(modeset::DumbBuffer {
698            width: buf_req.width,
699            height: buf_req.height,
700            bpp: buf_req.bpp,
701            pitch: buf_req.pitch,
702            ptr: buf_ptr as *mut u8,
703            len: buf_req.size as usize,
704            fb_id: FramebufferId(fb_req.fb_id),
705            buffer_handle: BufferObjectId(buf_req.handle),
706            file: Arc::downgrade(&self.f),
707        })
708    }
709
710    /// Read raw events from the card's file descriptor.
711    ///
712    /// DRM deals with events by having clients read from the card file descriptor,
713    /// at which point the driver writes as many whole pending events as will fit
714    /// into the given buffer. To give callers control over the buffer size, this
715    /// function takes a preallocated mutable buffer to use for the temporary
716    /// storage and then interprets the data one event at a time as the resulting
717    /// iterator is used. The buffer should be at least large enough to contain
718    /// one instance of the largest event type the kernel might return.
719    ///
720    /// If this function returns successfully then the caller *must* read the
721    /// resulting iterator until it produces `None`, or otherwise any unread events
722    /// will be lost.
723    ///
724    /// All objects returned from the iterator are views into portions of the
725    /// provided buffer.
726    pub fn read_events_raw<'a>(
727        &self,
728        buf: &'a mut [u8],
729    ) -> Result<impl Iterator<Item = &'a event::raw::DrmEvent> + 'a, Error> {
730        let len = self.f.read(buf)?;
731        let buf = &buf[0..len];
732        Ok(event::raw::events_from_bytes(buf))
733    }
734
735    /// Read events from the card's file descriptor.
736    ///
737    /// If this function returns successfully then the caller *must* read the
738    /// resulting iterator until it produces `None`, or otherwise any unread
739    /// events will be lost.
740    ///
741    /// This uses `buf` in the same way as [`Self::read_events_raw`], but
742    /// instead of returning direct references to parts of the buffer it
743    /// copies the event data into owned objects that can therefore outlive
744    /// the buffer. This is really just a convenience wrapper around
745    /// passing the [`Self::read_events_raw`] results through
746    /// [`event::DrmEvent::from_raw`].
747    ///
748    /// Unlike [`Self::read_events_raw`], this function's iterator will
749    /// sometimes perform dynamic allocations to capture the bodies of
750    /// events with unrecognized types.
751    pub fn read_events<'a>(
752        &self,
753        buf: &'a mut [u8],
754    ) -> Result<impl Iterator<Item = event::DrmEvent> + 'a, Error> {
755        let raws = self.read_events_raw(buf)?;
756        Ok(raws.map(|raw| event::DrmEvent::from_raw(raw)))
757    }
758
759    /// Close the filehandle underlying the card object.
760    #[inline]
761    pub fn close(self) -> linux_io::result::Result<()> {
762        let f = self.take_file()?;
763        f.close()
764    }
765
766    /// Take the file from underneath this card object without closing it.
767    pub fn take_file(self) -> linux_io::result::Result<linux_io::File<ioctl::DrmCardDevice>> {
768        Arc::into_inner(self.f).ok_or(linux_io::result::EBUSY)
769    }
770
771    /// Borrow the file object that this card object wraps.
772    #[inline(always)]
773    pub fn borrow_file(&self) -> &linux_io::File<ioctl::DrmCardDevice> {
774        self.f.as_ref()
775    }
776
777    /// Perform a direct ioctl request to the underlying card device filehandle.
778    #[inline(always)]
779    pub fn ioctl<'a, Req: IoctlReq<'a, ioctl::DrmCardDevice> + Copy>(
780        &'a self,
781        request: Req,
782        arg: Req::ExtArg,
783    ) -> linux_io::result::Result<Req::Result> {
784        drm_ioctl(&self.f, request, arg)
785    }
786}
787
788pub(crate) fn drm_ioctl<'a, Req: IoctlReq<'a, ioctl::DrmCardDevice> + Copy>(
789    f: &'a linux_io::File<ioctl::DrmCardDevice>,
790    request: Req,
791    arg: Req::ExtArg,
792) -> linux_io::result::Result<Req::Result> {
793    // All DRM ioctls can potentially be interrupted if our process
794    // receives a signal while we're waiting, so we'll keep retrying
795    // until we get a non-interrupted result.
796    //
797    // This requires some unsafe trickery because the borrow checker
798    // doesn't understand that only the final non-interrupted call
799    // will actually make use of "arg".
800    let arg_ptr = &arg as *const _;
801    loop {
802        let arg = unsafe { core::ptr::read(arg_ptr) };
803        let ret = f.ioctl(request, arg);
804        if !matches!(ret, Err(linux_io::result::EINTR)) {
805            return ret;
806        }
807    }
808}
809
810impl<D> TryFrom<linux_io::File<D>> for Card {
811    type Error = InitError;
812
813    #[inline(always)]
814    fn try_from(value: linux_io::File<D>) -> Result<Self, InitError> {
815        Card::from_file(value)
816    }
817}
818
819/// DRM API version information.
820#[derive(Debug)]
821pub struct ApiVersion {
822    pub major: i64,
823    pub minor: i64,
824    pub patch: i64,
825}
826
827impl core::fmt::Display for ApiVersion {
828    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
829        f.write_fmt(format_args!("{}.{}.{}", self.major, self.minor, self.patch))
830    }
831}
832
833/// Enumeration of DRM device capabilities.
834#[repr(u64)]
835#[non_exhaustive]
836pub enum DeviceCap {
837    DumbBuffer = ioctl::DRM_CAP_DUMB_BUFFER.0,
838    VBlankHighCrtc = ioctl::DRM_CAP_VBLANK_HIGH_CRTC.0,
839    DumbPreferredDepth = ioctl::DRM_CAP_DUMB_PREFERRED_DEPTH.0,
840    DumbPreferShadow = ioctl::DRM_CAP_DUMB_PREFER_SHADOW.0,
841    Prime = ioctl::DRM_CAP_PRIME.0,
842    TimestampMonotonic = ioctl::DRM_CAP_TIMESTAMP_MONOTONIC.0,
843    AsyncPageFlip = ioctl::DRM_CAP_ASYNC_PAGE_FLIP.0,
844    CursorWidth = ioctl::DRM_CAP_CURSOR_WIDTH.0,
845    CursorHeight = ioctl::DRM_CAP_CURSOR_HEIGHT.0,
846    Addfb2Modifiers = ioctl::DRM_CAP_ADDFB2_MODIFIERS.0,
847    PageFlipTarget = ioctl::DRM_CAP_PAGE_FLIP_TARGET.0,
848    CrtcInVblankEvent = ioctl::DRM_CAP_CRTC_IN_VBLANK_EVENT.0,
849    Syncobj = ioctl::DRM_CAP_SYNCOBJ.0,
850    SyncobjTimeline = ioctl::DRM_CAP_SYNCOBJ_TIMELINE.0,
851}
852
853impl From<DeviceCap> for ioctl::DrmCap {
854    #[inline(always)]
855    fn from(value: DeviceCap) -> Self {
856        // We always use the raw value as the enum representation,
857        // so this conversion is free.
858        ioctl::DrmCap(value as u64)
859    }
860}
861
862/// Enumeration of DRM client capabilities.
863#[repr(u64)]
864#[non_exhaustive]
865pub enum ClientCap {
866    Stereo3d = ioctl::DRM_CLIENT_CAP_STEREO_3D.0,
867    UniversalPlanes = ioctl::DRM_CLIENT_CAP_UNIVERSAL_PLANES.0,
868    Atomic = ioctl::DRM_CLIENT_CAP_ATOMIC.0,
869    AspectRatio = ioctl::DRM_CLIENT_CAP_ASPECT_RATIO.0,
870    WritebackConnectors = ioctl::DRM_CLIENT_CAP_WRITEBACK_CONNECTORS.0,
871    CursorPlaneHotspot = ioctl::DRM_CLIENT_CAP_CURSOR_PLANE_HOTSPOT.0,
872}
873
874impl From<ClientCap> for ioctl::DrmClientCap {
875    #[inline(always)]
876    fn from(value: ClientCap) -> Self {
877        // We always use the raw value as the enum representation,
878        // so this conversion is free.
879        ioctl::DrmClientCap(value as u64)
880    }
881}
882
883// Returns a vector that is guaranteed to have the given capacity exactly, or
884// an error if there isn't enough memory to reserve that capacity.
885//
886// This is intended for situations where the kernel will then populate the
887// reserved buffer and then the caller will set the length to something no
888// greater than the capacity before returning.
889pub(crate) fn vec_with_capacity<T>(
890    capacity: usize,
891) -> Result<Vec<T>, alloc::collections::TryReserveError> {
892    let mut ret = Vec::<T>::new();
893    ret.try_reserve_exact(capacity)?;
894    Ok(ret)
895}