1 // Copyright 2018 The Chromium OS Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #![cfg(feature = "minigbm")]
6 
7 use std::ffi::CString;
8 use std::fs::{File, OpenOptions};
9 
10 #[cfg(target_pointer_width = "64")]
11 use std::os::raw::c_ulong;
12 use std::os::raw::{c_char, c_int, c_uint};
13 use std::path::Path;
14 use std::ptr::null_mut;
15 
16 use base::{ioctl_iowr_nr, ioctl_with_mut_ref};
17 
18 use crate::rutabaga_utils::{RutabagaError, RutabagaResult};
19 
20 // Consistent with __kernel_size_t in include/uapi/asm-generic/posix_types.h.
21 #[cfg(not(target_pointer_width = "64"))]
22 #[allow(non_camel_case_types)]
23 type __kernel_size_t = c_uint;
24 #[cfg(target_pointer_width = "64")]
25 #[allow(non_camel_case_types)]
26 type __kernel_size_t = c_ulong;
27 
28 const DRM_IOCTL_BASE: c_uint = 0x64;
29 
30 #[repr(C)]
31 #[derive(Copy, Clone)]
32 struct drm_version {
33     version_major: c_int,
34     version_minor: c_int,
35     version_patchlevel: c_int,
36     name_len: __kernel_size_t,
37     name: *mut c_char,
38     date_len: __kernel_size_t,
39     date: *mut c_char,
40     desc_len: __kernel_size_t,
41     desc: *mut c_char,
42 }
43 
44 ioctl_iowr_nr!(DRM_IOCTL_VERSION, DRM_IOCTL_BASE, 0x0, drm_version);
45 
get_drm_device_name(fd: &File) -> Result<String, ()>46 fn get_drm_device_name(fd: &File) -> Result<String, ()> {
47     let mut version = drm_version {
48         version_major: 0,
49         version_minor: 0,
50         version_patchlevel: 0,
51         name_len: 0,
52         name: null_mut(),
53         date_len: 0,
54         date: null_mut(),
55         desc_len: 0,
56         desc: null_mut(),
57     };
58 
59     // Get the length of the device name.
60     if unsafe { ioctl_with_mut_ref(fd, DRM_IOCTL_VERSION(), &mut version) } < 0 {
61         return Err(());
62     }
63 
64     // Enough bytes to hold the device name and terminating null character.
65     let mut name_bytes: Vec<u8> = vec![0; (version.name_len + 1) as usize];
66     let mut version = drm_version {
67         version_major: 0,
68         version_minor: 0,
69         version_patchlevel: 0,
70         name_len: name_bytes.len() as __kernel_size_t,
71         name: name_bytes.as_mut_ptr() as *mut c_char,
72         date_len: 0,
73         date: null_mut(),
74         desc_len: 0,
75         desc: null_mut(),
76     };
77 
78     // Safe as no more than name_len + 1 bytes will be written to name.
79     if unsafe { ioctl_with_mut_ref(fd, DRM_IOCTL_VERSION(), &mut version) } < 0 {
80         return Err(());
81     }
82 
83     Ok(CString::new(&name_bytes[..(version.name_len as usize)])
84         .map_err(|_| ())?
85         .into_string()
86         .map_err(|_| ())?)
87 }
88 
89 /// Returns a `fd` for an opened rendernode device, while filtering out specified
90 /// undesired drivers.
open_device(undesired: &[&str]) -> RutabagaResult<File>91 pub fn open_device(undesired: &[&str]) -> RutabagaResult<File> {
92     const DRM_DIR_NAME: &str = "/dev/dri";
93     const DRM_MAX_MINOR: u32 = 15;
94     const RENDER_NODE_START: u32 = 128;
95 
96     for n in RENDER_NODE_START..=RENDER_NODE_START + DRM_MAX_MINOR {
97         let path = Path::new(DRM_DIR_NAME).join(format!("renderD{}", n));
98 
99         if let Ok(fd) = OpenOptions::new().read(true).write(true).open(path) {
100             if let Ok(name) = get_drm_device_name(&fd) {
101                 if !undesired.iter().any(|item| *item == name) {
102                     return Ok(fd);
103                 }
104             }
105         }
106     }
107 
108     Err(RutabagaError::Unsupported)
109 }
110