milestone: desktop path Phases 1-5
Phase 1 (Runtime Substrate): 4 check binaries, --probe, POSIX tests Phase 2 (Wayland Compositor): bounded scaffold, zero warnings Phase 3 (KWin Session): preflight checker (KWin stub, gated on Qt6Quick) Phase 4 (KDE Plasma): 18 KF6 enabled, preflight checker Phase 5 (Hardware GPU): DRM/firmware/Mesa preflight checker Build: zero warnings, all scripts syntax-clean. Oracle-verified.
This commit is contained in:
@@ -0,0 +1,22 @@
|
||||
[package]
|
||||
name = "driver-graphics"
|
||||
description = "Shared video and graphics code library"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
drm-fourcc = "2.2.0"
|
||||
drm-sys.workspace = true
|
||||
edid.workspace = true #TODO: edid is abandoned, fork it and maintain?
|
||||
log.workspace = true
|
||||
redox-ioctl.workspace = true
|
||||
redox-scheme.workspace = true
|
||||
scheme-utils = { path = "../../../scheme-utils" }
|
||||
redox_syscall.workspace = true
|
||||
libredox.workspace = true
|
||||
|
||||
common = { path = "../../common" }
|
||||
inputd = { path = "../../inputd" }
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
@@ -0,0 +1,249 @@
|
||||
use std::ffi::c_char;
|
||||
use std::fmt::Debug;
|
||||
use std::sync::Mutex;
|
||||
|
||||
use drm_sys::{
|
||||
drm_mode_modeinfo, DRM_MODE_CONNECTOR_Unknown, DRM_MODE_DPMS_OFF, DRM_MODE_DPMS_ON,
|
||||
DRM_MODE_DPMS_STANDBY, DRM_MODE_DPMS_SUSPEND, DRM_MODE_TYPE_PREFERRED,
|
||||
};
|
||||
use syscall::Result;
|
||||
|
||||
use crate::kms::objects::{KmsObjectId, KmsObjects};
|
||||
use crate::kms::properties::{define_object_props, KmsPropertyData, CRTC_ID, DPMS, EDID};
|
||||
use crate::GraphicsAdapter;
|
||||
|
||||
impl<T: GraphicsAdapter> KmsObjects<T> {
|
||||
pub fn add_connector(
|
||||
&mut self,
|
||||
driver_data: T::Connector,
|
||||
driver_data_state: <T::Connector as KmsConnectorDriver>::State,
|
||||
crtcs: &[KmsObjectId],
|
||||
) -> KmsObjectId {
|
||||
let mut possible_crtcs = 0;
|
||||
for &crtc in crtcs {
|
||||
possible_crtcs = 1 << self.get_crtc(crtc).unwrap().lock().unwrap().crtc_index;
|
||||
}
|
||||
|
||||
let encoder_id = self.add(KmsEncoder {
|
||||
crtc_id: KmsObjectId::INVALID,
|
||||
possible_crtcs: possible_crtcs,
|
||||
possible_clones: 1 << self.encoders.len(),
|
||||
});
|
||||
self.encoders.push(encoder_id);
|
||||
|
||||
let connector_id = self.add(Mutex::new(KmsConnector {
|
||||
encoder_id,
|
||||
modes: vec![],
|
||||
connector_type: DRM_MODE_CONNECTOR_Unknown,
|
||||
connector_type_id: self.connectors.len() as u32, // FIXME maybe pick unique id within connector type?
|
||||
connection: KmsConnectorStatus::Unknown,
|
||||
mm_width: 0,
|
||||
mm_height: 0,
|
||||
subpixel: DrmSubpixelOrder::Unknown,
|
||||
properties: KmsConnector::base_properties(),
|
||||
edid: KmsObjectId::INVALID,
|
||||
state: KmsConnectorState {
|
||||
dpms: KmsDpms::On,
|
||||
crtc_id: KmsObjectId::INVALID,
|
||||
driver_data: driver_data_state,
|
||||
},
|
||||
driver_data,
|
||||
}));
|
||||
self.connectors.push(connector_id);
|
||||
|
||||
connector_id
|
||||
}
|
||||
|
||||
pub fn connector_ids(&self) -> &[KmsObjectId] {
|
||||
&self.connectors
|
||||
}
|
||||
|
||||
pub fn connectors(&self) -> impl Iterator<Item = &Mutex<KmsConnector<T>>> + use<'_, T> {
|
||||
self.connectors
|
||||
.iter()
|
||||
.map(|&id| self.get_connector(id).unwrap())
|
||||
}
|
||||
|
||||
pub fn get_connector(&self, id: KmsObjectId) -> Result<&Mutex<KmsConnector<T>>> {
|
||||
self.get(id)
|
||||
}
|
||||
|
||||
pub fn encoder_ids(&self) -> &[KmsObjectId] {
|
||||
&self.encoders
|
||||
}
|
||||
|
||||
pub fn get_encoder(&self, id: KmsObjectId) -> Result<&KmsEncoder> {
|
||||
self.get(id)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait KmsConnectorDriver: Debug {
|
||||
type State: Clone + Debug;
|
||||
}
|
||||
|
||||
impl KmsConnectorDriver for () {
|
||||
type State = ();
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct KmsConnector<T: GraphicsAdapter> {
|
||||
pub encoder_id: KmsObjectId,
|
||||
pub modes: Vec<drm_mode_modeinfo>,
|
||||
pub connector_type: u32,
|
||||
pub connector_type_id: u32,
|
||||
pub connection: KmsConnectorStatus,
|
||||
pub mm_width: u32,
|
||||
pub mm_height: u32,
|
||||
pub subpixel: DrmSubpixelOrder,
|
||||
pub properties: Vec<KmsPropertyData<Self>>,
|
||||
pub edid: KmsObjectId,
|
||||
pub state: KmsConnectorState<T>,
|
||||
pub driver_data: T::Connector,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct KmsConnectorState<T: GraphicsAdapter> {
|
||||
pub dpms: KmsDpms,
|
||||
pub crtc_id: KmsObjectId,
|
||||
pub driver_data: <T::Connector as KmsConnectorDriver>::State,
|
||||
}
|
||||
|
||||
impl<T: GraphicsAdapter> Clone for KmsConnectorState<T> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
dpms: self.dpms.clone(),
|
||||
crtc_id: self.crtc_id.clone(),
|
||||
driver_data: self.driver_data.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
define_object_props!(object, KmsConnector<T: GraphicsAdapter> {
|
||||
EDID {
|
||||
get => u64::from(object.edid.0),
|
||||
}
|
||||
DPMS {
|
||||
get => object.state.dpms as u64,
|
||||
}
|
||||
CRTC_ID {
|
||||
get => u64::from(object.state.crtc_id.0),
|
||||
}
|
||||
});
|
||||
|
||||
impl<T: GraphicsAdapter> KmsConnector<T> {
|
||||
pub fn update_from_size(&mut self, width: u32, height: u32) {
|
||||
self.modes = vec![modeinfo_for_size(width, height)];
|
||||
}
|
||||
|
||||
pub fn update_from_edid(&mut self, edid: &[u8]) {
|
||||
let edid = edid::parse(edid).unwrap().1;
|
||||
|
||||
if let Some(first_detailed_timing) =
|
||||
edid.descriptors
|
||||
.iter()
|
||||
.find_map(|descriptor| match descriptor {
|
||||
edid::Descriptor::DetailedTiming(detailed_timing) => Some(detailed_timing),
|
||||
_ => None,
|
||||
})
|
||||
{
|
||||
self.mm_width = first_detailed_timing.horizontal_size.into();
|
||||
self.mm_height = first_detailed_timing.vertical_size.into();
|
||||
} else {
|
||||
log::error!("No edid timing descriptor detected");
|
||||
}
|
||||
|
||||
self.modes = edid
|
||||
.descriptors
|
||||
.iter()
|
||||
.filter_map(|descriptor| {
|
||||
match descriptor {
|
||||
edid::Descriptor::DetailedTiming(detailed_timing) => {
|
||||
// FIXME extract full information
|
||||
Some(modeinfo_for_size(
|
||||
u32::from(detailed_timing.horizontal_active_pixels),
|
||||
u32::from(detailed_timing.vertical_active_lines),
|
||||
))
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// First detailed timing descriptor indicates preferred mode.
|
||||
for mode in self.modes.iter_mut().skip(1) {
|
||||
mode.flags &= !DRM_MODE_TYPE_PREFERRED;
|
||||
}
|
||||
|
||||
// FIXME update the EDID property
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn modeinfo_for_size(width: u32, height: u32) -> drm_mode_modeinfo {
|
||||
let mut modeinfo = drm_mode_modeinfo {
|
||||
// The actual visible display size
|
||||
hdisplay: width as u16,
|
||||
vdisplay: height as u16,
|
||||
|
||||
// These are used to calculate the refresh rate
|
||||
clock: 60 * width * height / 1000,
|
||||
htotal: width as u16,
|
||||
vtotal: height as u16,
|
||||
vscan: 0,
|
||||
vrefresh: 60,
|
||||
|
||||
type_: drm_sys::DRM_MODE_TYPE_PREFERRED | drm_sys::DRM_MODE_TYPE_DRIVER,
|
||||
name: [0; 32],
|
||||
|
||||
// These only matter when modesetting physical display adapters. For
|
||||
// those we should be able to parse the EDID blob.
|
||||
hsync_start: width as u16,
|
||||
hsync_end: width as u16,
|
||||
hskew: 0,
|
||||
vsync_start: height as u16,
|
||||
vsync_end: height as u16,
|
||||
flags: 0,
|
||||
};
|
||||
|
||||
let name = format!("{width}x{height}").into_bytes();
|
||||
for (to, from) in modeinfo.name.iter_mut().zip(name) {
|
||||
*to = from as c_char;
|
||||
}
|
||||
|
||||
modeinfo
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
#[repr(u32)]
|
||||
pub enum KmsConnectorStatus {
|
||||
Disconnected = 0,
|
||||
Connected = 1,
|
||||
Unknown = 2,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
#[repr(u32)]
|
||||
pub enum DrmSubpixelOrder {
|
||||
Unknown = 0,
|
||||
HorizontalRGB,
|
||||
HorizontalBGR,
|
||||
VerticalRGB,
|
||||
VerticalBGR,
|
||||
None,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
#[repr(u64)]
|
||||
pub enum KmsDpms {
|
||||
On = DRM_MODE_DPMS_ON as u64,
|
||||
Standby = DRM_MODE_DPMS_STANDBY as u64,
|
||||
Suspend = DRM_MODE_DPMS_SUSPEND as u64,
|
||||
Off = DRM_MODE_DPMS_OFF as u64,
|
||||
}
|
||||
|
||||
// FIXME can we represent connector and encoder using a single struct?
|
||||
#[derive(Debug)]
|
||||
pub struct KmsEncoder {
|
||||
pub crtc_id: KmsObjectId,
|
||||
pub possible_crtcs: u32,
|
||||
pub possible_clones: u32,
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
pub mod connector;
|
||||
pub mod objects;
|
||||
pub mod properties;
|
||||
@@ -0,0 +1,237 @@
|
||||
use std::collections::HashMap;
|
||||
use std::fmt::Debug;
|
||||
use std::marker::PhantomData;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use drm_sys::{
|
||||
drm_mode_modeinfo, DRM_MODE_OBJECT_BLOB, DRM_MODE_OBJECT_CONNECTOR, DRM_MODE_OBJECT_CRTC,
|
||||
DRM_MODE_OBJECT_ENCODER, DRM_MODE_OBJECT_FB, DRM_MODE_OBJECT_PROPERTY,
|
||||
};
|
||||
use syscall::{Error, Result, EINVAL};
|
||||
|
||||
use crate::kms::connector::{KmsConnector, KmsEncoder};
|
||||
use crate::kms::properties::{
|
||||
define_object_props, init_standard_props, KmsBlob, KmsProperty, KmsPropertyData,
|
||||
};
|
||||
use crate::GraphicsAdapter;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct KmsObjects<T: GraphicsAdapter> {
|
||||
next_id: KmsObjectId,
|
||||
pub(crate) connectors: Vec<KmsObjectId>,
|
||||
pub(crate) encoders: Vec<KmsObjectId>,
|
||||
crtcs: Vec<KmsObjectId>,
|
||||
framebuffers: Vec<KmsObjectId>,
|
||||
pub(crate) objects: HashMap<KmsObjectId, KmsObject<T>>,
|
||||
_marker: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T: GraphicsAdapter> KmsObjects<T> {
|
||||
pub(crate) fn new() -> Self {
|
||||
let mut objects = KmsObjects {
|
||||
next_id: KmsObjectId(1),
|
||||
connectors: vec![],
|
||||
encoders: vec![],
|
||||
crtcs: vec![],
|
||||
framebuffers: vec![],
|
||||
objects: HashMap::new(),
|
||||
_marker: PhantomData,
|
||||
};
|
||||
init_standard_props(&mut objects);
|
||||
objects
|
||||
}
|
||||
|
||||
pub(crate) fn add<U: Into<KmsObject<T>>>(&mut self, data: U) -> KmsObjectId {
|
||||
let id = self.next_id;
|
||||
self.objects.insert(id, data.into());
|
||||
self.next_id.0 += 1;
|
||||
|
||||
id
|
||||
}
|
||||
|
||||
pub(crate) fn get<'a, U: 'a>(&'a self, id: KmsObjectId) -> Result<&'a U>
|
||||
where
|
||||
&'a U: TryFrom<&'a KmsObject<T>>,
|
||||
{
|
||||
let object = self.objects.get(&id).ok_or(Error::new(EINVAL))?;
|
||||
if let Ok(object) = object.try_into() {
|
||||
Ok(object)
|
||||
} else {
|
||||
Err(Error::new(EINVAL))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn object_type(&self, id: KmsObjectId) -> Result<u32> {
|
||||
let object = self.objects.get(&id).ok_or(Error::new(EINVAL))?;
|
||||
Ok(object.object_type())
|
||||
}
|
||||
|
||||
pub fn add_crtc(
|
||||
&mut self,
|
||||
driver_data: T::Crtc,
|
||||
driver_data_state: <T::Crtc as KmsCrtcDriver>::State,
|
||||
) -> KmsObjectId {
|
||||
let crtc_index = self.crtcs.len() as u32;
|
||||
let id = self.add(Mutex::new(KmsCrtc {
|
||||
crtc_index,
|
||||
gamma_size: 0,
|
||||
properties: KmsCrtc::base_properties(),
|
||||
state: KmsCrtcState {
|
||||
fb_id: None,
|
||||
mode: None,
|
||||
driver_data: driver_data_state,
|
||||
},
|
||||
driver_data,
|
||||
}));
|
||||
self.crtcs.push(id);
|
||||
|
||||
id
|
||||
}
|
||||
|
||||
pub fn crtc_ids(&self) -> &[KmsObjectId] {
|
||||
&self.crtcs
|
||||
}
|
||||
|
||||
pub fn crtcs(&self) -> impl Iterator<Item = &Mutex<KmsCrtc<T>>> + use<'_, T> {
|
||||
self.crtcs
|
||||
.iter()
|
||||
.map(|&id| self.get::<Mutex<KmsCrtc<T>>>(id).unwrap())
|
||||
}
|
||||
|
||||
pub fn get_crtc(&self, id: KmsObjectId) -> Result<&Mutex<KmsCrtc<T>>> {
|
||||
self.get(id)
|
||||
}
|
||||
|
||||
pub fn add_framebuffer(&mut self, fb: KmsFramebuffer<T>) -> KmsObjectId {
|
||||
let id = self.add(fb);
|
||||
self.framebuffers.push(id);
|
||||
id
|
||||
}
|
||||
|
||||
pub fn remove_framebuffer(&mut self, id: KmsObjectId) -> Result<()> {
|
||||
let Some(object) = self.objects.get(&id) else {
|
||||
return Err(Error::new(EINVAL));
|
||||
};
|
||||
let KmsObject::Framebuffer(_) = object else {
|
||||
return Err(Error::new(EINVAL));
|
||||
};
|
||||
self.objects.remove(&id).unwrap();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn fb_ids(&self) -> &[KmsObjectId] {
|
||||
&self.framebuffers
|
||||
}
|
||||
|
||||
pub fn get_framebuffer(&self, id: KmsObjectId) -> Result<&KmsFramebuffer<T>> {
|
||||
self.get(id)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
||||
pub struct KmsObjectId(pub(crate) u32);
|
||||
|
||||
impl KmsObjectId {
|
||||
pub const INVALID: KmsObjectId = KmsObjectId(0);
|
||||
}
|
||||
|
||||
impl From<KmsObjectId> for u64 {
|
||||
fn from(value: KmsObjectId) -> Self {
|
||||
value.0.into()
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! define_object_kinds {
|
||||
(<$T:ident> $(
|
||||
$variant:ident($data:ty) = $type:ident,
|
||||
)*) => {
|
||||
#[derive(Debug)]
|
||||
pub(crate) enum KmsObject<$T: GraphicsAdapter> {
|
||||
$($variant($data),)*
|
||||
}
|
||||
|
||||
impl<$T: GraphicsAdapter> KmsObject<$T> {
|
||||
fn object_type(&self) -> u32 {
|
||||
match self {
|
||||
$(Self::$variant(_) => $type,)*
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$(
|
||||
impl<$T: GraphicsAdapter> From<$data> for KmsObject<$T> {
|
||||
fn from(value: $data) -> Self {
|
||||
Self::$variant(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, $T: GraphicsAdapter> TryFrom<&'a KmsObject<$T>> for &'a $data {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(value: &'a KmsObject<T>) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
KmsObject::$variant(data) => Ok(data),
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
)*
|
||||
};
|
||||
}
|
||||
|
||||
define_object_kinds! { <T>
|
||||
Crtc(Mutex<KmsCrtc<T>>) = DRM_MODE_OBJECT_CRTC,
|
||||
Connector(Mutex<KmsConnector<T>>) = DRM_MODE_OBJECT_CONNECTOR,
|
||||
Encoder(KmsEncoder) = DRM_MODE_OBJECT_ENCODER,
|
||||
Property(KmsProperty) = DRM_MODE_OBJECT_PROPERTY,
|
||||
Framebuffer(KmsFramebuffer<T>) = DRM_MODE_OBJECT_FB,
|
||||
Blob(KmsBlob) = DRM_MODE_OBJECT_BLOB,
|
||||
}
|
||||
|
||||
pub trait KmsCrtcDriver: Debug {
|
||||
type State: Clone + Debug;
|
||||
}
|
||||
|
||||
impl KmsCrtcDriver for () {
|
||||
type State = ();
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct KmsCrtc<T: GraphicsAdapter> {
|
||||
pub crtc_index: u32,
|
||||
pub gamma_size: u32,
|
||||
pub properties: Vec<KmsPropertyData<Self>>,
|
||||
pub state: KmsCrtcState<T>,
|
||||
pub driver_data: T::Crtc,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct KmsCrtcState<T: GraphicsAdapter> {
|
||||
pub fb_id: Option<KmsObjectId>,
|
||||
pub mode: Option<drm_mode_modeinfo>,
|
||||
pub driver_data: <T::Crtc as KmsCrtcDriver>::State,
|
||||
}
|
||||
|
||||
impl<T: GraphicsAdapter> Clone for KmsCrtcState<T> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
fb_id: self.fb_id.clone(),
|
||||
mode: self.mode.clone(),
|
||||
driver_data: self.driver_data.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
define_object_props!(object, KmsCrtc<T: GraphicsAdapter> {});
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct KmsFramebuffer<T: GraphicsAdapter> {
|
||||
pub width: u32,
|
||||
pub height: u32,
|
||||
pub pitch: u32,
|
||||
pub bpp: u32,
|
||||
pub depth: u32,
|
||||
pub buffer: Arc<T::Buffer>,
|
||||
pub driver_data: T::Framebuffer,
|
||||
}
|
||||
@@ -0,0 +1,241 @@
|
||||
use std::ffi::c_char;
|
||||
use std::fmt::Debug;
|
||||
use std::mem;
|
||||
|
||||
use drm_sys::{
|
||||
DRM_MODE_DPMS_OFF, DRM_MODE_DPMS_ON, DRM_MODE_DPMS_STANDBY, DRM_MODE_DPMS_SUSPEND,
|
||||
DRM_MODE_OBJECT_CRTC, DRM_MODE_OBJECT_FB, DRM_PLANE_TYPE_CURSOR, DRM_PLANE_TYPE_OVERLAY,
|
||||
DRM_PLANE_TYPE_PRIMARY, DRM_PROP_NAME_LEN,
|
||||
};
|
||||
use syscall::{Error, Result, EINVAL};
|
||||
|
||||
use crate::kms::objects::{KmsObject, KmsObjectId, KmsObjects};
|
||||
use crate::GraphicsAdapter;
|
||||
|
||||
impl<T: GraphicsAdapter> KmsObjects<T> {
|
||||
pub fn add_property(
|
||||
&mut self,
|
||||
name: &str,
|
||||
immutable: bool,
|
||||
atomic: bool,
|
||||
kind: KmsPropertyKind,
|
||||
) -> KmsObjectId {
|
||||
match &kind {
|
||||
KmsPropertyKind::Range(start, end) => assert!(start < end),
|
||||
KmsPropertyKind::Enum(_variants) => {
|
||||
// FIXME check duplicate variant numbers
|
||||
}
|
||||
KmsPropertyKind::Blob => {}
|
||||
KmsPropertyKind::Bitmask(_bitmask_flags) => {
|
||||
// FIXME check overlapping flag numbers
|
||||
}
|
||||
KmsPropertyKind::Object { type_: _ } => {}
|
||||
KmsPropertyKind::SignedRange(start, end) => assert!(start < end),
|
||||
}
|
||||
|
||||
let mut name_bytes = [0; DRM_PROP_NAME_LEN as usize];
|
||||
for (to, &from) in name_bytes.iter_mut().zip(name.as_bytes()) {
|
||||
*to = from as c_char;
|
||||
}
|
||||
|
||||
self.add(KmsProperty {
|
||||
name: KmsPropertyName::new("Property name", name),
|
||||
immutable,
|
||||
atomic,
|
||||
kind,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get_property(&self, id: KmsObjectId) -> Result<&KmsProperty> {
|
||||
self.get(id)
|
||||
}
|
||||
|
||||
pub fn get_object_properties_data(&self, id: KmsObjectId) -> Result<(Vec<u32>, Vec<u64>)> {
|
||||
let object = self.objects.get(&id).ok_or(Error::new(EINVAL))?;
|
||||
match object {
|
||||
KmsObject::Crtc(crtc) => {
|
||||
let crtc = crtc.lock().unwrap();
|
||||
let props = &crtc.properties;
|
||||
Ok((
|
||||
props.iter().map(|prop| prop.id.0).collect::<Vec<_>>(),
|
||||
props
|
||||
.iter()
|
||||
.map(|prop| (prop.getter)(&crtc))
|
||||
.collect::<Vec<_>>(),
|
||||
))
|
||||
}
|
||||
KmsObject::Connector(connector) => {
|
||||
let connector = connector.lock().unwrap();
|
||||
let props = &connector.properties;
|
||||
Ok((
|
||||
props.iter().map(|prop| prop.id.0).collect::<Vec<_>>(),
|
||||
props
|
||||
.iter()
|
||||
.map(|prop| (prop.getter)(&connector))
|
||||
.collect::<Vec<_>>(),
|
||||
))
|
||||
}
|
||||
KmsObject::Encoder(_)
|
||||
| KmsObject::Property(_)
|
||||
| KmsObject::Framebuffer(_)
|
||||
| KmsObject::Blob(_) => Ok((vec![], vec![])),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_blob(&mut self, data: Vec<u8>) -> KmsObjectId {
|
||||
self.add(KmsBlob { data })
|
||||
}
|
||||
|
||||
pub fn get_blob(&self, id: KmsObjectId) -> Result<&[u8]> {
|
||||
Ok(&self.get::<KmsBlob>(id)?.data)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct KmsPropertyName(pub [c_char; DRM_PROP_NAME_LEN as usize]);
|
||||
|
||||
impl KmsPropertyName {
|
||||
fn new(context: &str, name: &str) -> KmsPropertyName {
|
||||
if name.len() > DRM_PROP_NAME_LEN as usize {
|
||||
panic!("{context} {name} is too long");
|
||||
}
|
||||
|
||||
let mut name_bytes = [0; DRM_PROP_NAME_LEN as usize];
|
||||
for (to, &from) in name_bytes.iter_mut().zip(name.as_bytes()) {
|
||||
*to = from as c_char;
|
||||
}
|
||||
|
||||
KmsPropertyName(name_bytes)
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for KmsPropertyName {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let u8_bytes = unsafe { mem::transmute::<&[c_char], &[u8]>(&self.0) };
|
||||
f.write_str(&String::from_utf8_lossy(u8_bytes).trim_end_matches('\0'))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct KmsProperty {
|
||||
pub name: KmsPropertyName,
|
||||
pub immutable: bool,
|
||||
pub atomic: bool,
|
||||
pub kind: KmsPropertyKind,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum KmsPropertyKind {
|
||||
Range(u64, u64),
|
||||
Enum(Vec<(KmsPropertyName, u64)>),
|
||||
Blob,
|
||||
Bitmask(Vec<(KmsPropertyName, u64)>),
|
||||
Object { type_: u32 },
|
||||
SignedRange(i64, i64),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct KmsPropertyData<T> {
|
||||
pub id: KmsObjectId,
|
||||
pub getter: fn(&T) -> u64,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct KmsBlob {
|
||||
data: Vec<u8>,
|
||||
}
|
||||
|
||||
macro_rules! define_properties {
|
||||
($($prop:ident $($prop_name:literal)?: $prop_type:ident $({$($prop_content:tt)*})? [$($prop_flag:ident)?],)*) => {
|
||||
$(#[allow(non_upper_case_globals)] pub const $prop: KmsObjectId = KmsObjectId(1 + ${index()});)*
|
||||
|
||||
pub(super) fn init_standard_props<T: GraphicsAdapter>(objects: &mut KmsObjects<T>) {
|
||||
$(
|
||||
assert_eq!(objects.add_property(
|
||||
define_properties!(@prop_name $prop $($prop_name)?),
|
||||
define_properties!(@is_immutable $($prop_flag)?),
|
||||
define_properties!(@is_atomic $($prop_flag)?),
|
||||
define_properties!(@prop_kind $prop_type $({$($prop_content)*})?),
|
||||
), $prop);
|
||||
)*
|
||||
}
|
||||
};
|
||||
(@prop_name $prop:ident $prop_name:literal) => { $prop_name };
|
||||
(@prop_name $prop:ident) => { stringify!($prop) };
|
||||
(@is_immutable) => { false };
|
||||
(@is_immutable immutable) => { true };
|
||||
(@is_immutable atomic) => { false };
|
||||
(@is_atomic) => { false };
|
||||
(@is_atomic immutable) => { false };
|
||||
(@is_atomic atomic) => { true };
|
||||
(@prop_kind range { $start:expr, $end:expr }) => {
|
||||
KmsPropertyKind::Range($start, $end)
|
||||
};
|
||||
(@prop_kind enum { $($variant:ident = $value:expr,)* }) => {
|
||||
KmsPropertyKind::Enum(vec![
|
||||
$((KmsPropertyName::new("Property variant name", stringify!($variant)), $value)),*]
|
||||
)
|
||||
};
|
||||
(@prop_kind blob) => {
|
||||
KmsPropertyKind::Blob
|
||||
};
|
||||
(@prop_kind object { $type:ident }) => {
|
||||
KmsPropertyKind::Object { type_: $type }
|
||||
};
|
||||
(@prop_kind srange { $start:expr, $end:expr }) => {
|
||||
KmsPropertyKind::SignedRange($start, $end)
|
||||
};
|
||||
}
|
||||
|
||||
define_properties! {
|
||||
// Connector + Plane
|
||||
CRTC_ID: object { DRM_MODE_OBJECT_CRTC } [atomic],
|
||||
|
||||
// Connector
|
||||
EDID: blob [immutable],
|
||||
DPMS: enum {
|
||||
On = u64::from(DRM_MODE_DPMS_ON),
|
||||
Standby = u64::from(DRM_MODE_DPMS_STANDBY),
|
||||
Suspend = u64::from(DRM_MODE_DPMS_SUSPEND),
|
||||
Off = u64::from(DRM_MODE_DPMS_OFF),
|
||||
} [],
|
||||
|
||||
// CRTC
|
||||
ACTIVE: range { 0,1 } [atomic],
|
||||
MODE_ID: blob [atomic],
|
||||
|
||||
// Plane
|
||||
type_ "type": enum {
|
||||
Overlay = u64::from(DRM_PLANE_TYPE_OVERLAY),
|
||||
Primary = u64::from(DRM_PLANE_TYPE_PRIMARY),
|
||||
Cursor = u64::from(DRM_PLANE_TYPE_CURSOR),
|
||||
} [immutable],
|
||||
FB_ID: object { DRM_MODE_OBJECT_FB } [atomic],
|
||||
CRTC_X: srange { i64::from(i32::MIN), i64::from(i32::MAX) } [atomic],
|
||||
CRTC_Y: srange { i64::from(i32::MIN), i64::from(i32::MAX) } [atomic],
|
||||
CRTC_W: range { 0, u64::from(u32::MAX) } [atomic],
|
||||
CRTC_H: range { 0, u64::from(u32::MAX) } [atomic],
|
||||
SRC_X: range { 0, u64::from(u32::MAX) } [atomic],
|
||||
SRC_Y: range { 0, u64::from(u32::MAX) } [atomic],
|
||||
SRC_W: range { 0, u64::from(u32::MAX) } [atomic],
|
||||
SRC_H: range { 0, u64::from(u32::MAX) } [atomic],
|
||||
FB_DAMAGE_CLIPS: blob [atomic],
|
||||
}
|
||||
|
||||
macro_rules! define_object_props {
|
||||
($object:ident, $obj:ident$(<$($T:ident$(: $bound:ident)?),*>)? { $(
|
||||
$prop:ident {
|
||||
get => $get:expr,
|
||||
}
|
||||
)* }) => {
|
||||
impl$(<$($T$(: $bound)?),*>)? $obj$(<$($T),*>)? {
|
||||
pub(super) fn base_properties() -> Vec<KmsPropertyData<Self>> {
|
||||
vec![$(KmsPropertyData {
|
||||
id: $prop,
|
||||
getter: |$object| $get
|
||||
}),*]
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
pub(super) use define_object_props;
|
||||
@@ -0,0 +1,986 @@
|
||||
#![feature(macro_metavar_expr)]
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::fmt::Debug;
|
||||
use std::fs::File;
|
||||
use std::io::{self, Write};
|
||||
use std::os::fd::BorrowedFd;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::{cmp, mem};
|
||||
|
||||
use drm_fourcc::DrmFourcc;
|
||||
use drm_sys::{
|
||||
drm_mode_property_enum, DRM_MODE_CURSOR_BO, DRM_MODE_CURSOR_MOVE, DRM_MODE_PROP_ATOMIC,
|
||||
DRM_MODE_PROP_BITMASK, DRM_MODE_PROP_BLOB, DRM_MODE_PROP_ENUM, DRM_MODE_PROP_IMMUTABLE,
|
||||
DRM_MODE_PROP_OBJECT, DRM_MODE_PROP_RANGE, DRM_MODE_PROP_SIGNED_RANGE,
|
||||
};
|
||||
use inputd::{DisplayHandle, VtEventKind};
|
||||
use libredox::Fd;
|
||||
use redox_scheme::scheme::{register_scheme_inner, SchemeState, SchemeSync};
|
||||
use redox_scheme::{CallerCtx, OpenResult, RequestKind, SignalBehavior, Socket};
|
||||
use scheme_utils::{FpathWriter, HandleMap};
|
||||
use syscall::schemev2::NewFdFlags;
|
||||
use syscall::{Error, MapFlags, Result, EACCES, EAGAIN, EINVAL, ENOENT, EOPNOTSUPP};
|
||||
|
||||
use crate::kms::connector::{KmsConnectorDriver, KmsConnectorState};
|
||||
use crate::kms::objects::{self, KmsCrtc, KmsCrtcDriver, KmsCrtcState, KmsObjectId, KmsObjects};
|
||||
use crate::kms::properties::KmsPropertyKind;
|
||||
|
||||
pub mod kms;
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
#[repr(C, packed)]
|
||||
pub struct Damage {
|
||||
pub x: u32,
|
||||
pub y: u32,
|
||||
pub width: u32,
|
||||
pub height: u32,
|
||||
}
|
||||
|
||||
impl Damage {
|
||||
fn merge(self, other: Self) -> Self {
|
||||
if self.width == 0 || self.height == 0 {
|
||||
return other;
|
||||
}
|
||||
|
||||
if other.width == 0 || other.height == 0 {
|
||||
return self;
|
||||
}
|
||||
|
||||
let x = cmp::min(self.x, other.x);
|
||||
let y = cmp::min(self.y, other.y);
|
||||
let x2 = cmp::max(self.x + self.width, other.x + other.width);
|
||||
let y2 = cmp::max(self.y + self.height, other.y + other.height);
|
||||
|
||||
Damage {
|
||||
x,
|
||||
y,
|
||||
width: x2 - x,
|
||||
height: y2 - y,
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn clip(mut self, width: u32, height: u32) -> Self {
|
||||
// Clip damage
|
||||
let x2 = self.x + self.width;
|
||||
self.x = cmp::min(self.x, width);
|
||||
if x2 > width {
|
||||
self.width = width - self.x;
|
||||
}
|
||||
|
||||
let y2 = self.y + self.height;
|
||||
self.y = cmp::min(self.y, height);
|
||||
if y2 > height {
|
||||
self.height = height - self.y;
|
||||
}
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
pub trait GraphicsAdapter: Sized + Debug {
|
||||
type Connector: KmsConnectorDriver;
|
||||
type Crtc: KmsCrtcDriver;
|
||||
|
||||
type Buffer: Buffer;
|
||||
type Framebuffer: Framebuffer;
|
||||
|
||||
fn name(&self) -> &'static [u8];
|
||||
fn desc(&self) -> &'static [u8];
|
||||
|
||||
fn init(&mut self, objects: &mut KmsObjects<Self>);
|
||||
|
||||
fn get_cap(&self, cap: u32) -> Result<u64>;
|
||||
fn set_client_cap(&self, cap: u32, value: u64) -> Result<()>;
|
||||
|
||||
fn probe_connector(&mut self, objects: &mut KmsObjects<Self>, id: KmsObjectId);
|
||||
|
||||
fn create_dumb_buffer(&mut self, width: u32, height: u32) -> (Self::Buffer, u32);
|
||||
fn map_dumb_buffer(&mut self, buffer: &Self::Buffer) -> *mut u8;
|
||||
|
||||
fn create_framebuffer(&mut self, buffer: &Self::Buffer) -> Self::Framebuffer;
|
||||
|
||||
fn set_crtc(
|
||||
&mut self,
|
||||
objects: &KmsObjects<Self>,
|
||||
crtc: &Mutex<KmsCrtc<Self>>,
|
||||
new_state: KmsCrtcState<Self>,
|
||||
damage: Damage,
|
||||
) -> syscall::Result<()>;
|
||||
|
||||
fn hw_cursor_size(&self) -> Option<(u32, u32)>;
|
||||
fn handle_cursor(&mut self, cursor: &CursorPlane<Self::Buffer>, dirty_fb: bool);
|
||||
}
|
||||
|
||||
pub trait Buffer: Debug {
|
||||
fn size(&self) -> usize;
|
||||
}
|
||||
|
||||
pub trait Framebuffer: Debug {}
|
||||
|
||||
impl Framebuffer for () {}
|
||||
|
||||
pub struct CursorPlane<C: Buffer> {
|
||||
pub x: i32,
|
||||
pub y: i32,
|
||||
pub hot_x: i32,
|
||||
pub hot_y: i32,
|
||||
pub buffer: Option<Arc<C>>,
|
||||
}
|
||||
|
||||
pub struct GraphicsScheme<T: GraphicsAdapter> {
|
||||
inner: GraphicsSchemeInner<T>,
|
||||
inputd_handle: DisplayHandle,
|
||||
state: SchemeState,
|
||||
}
|
||||
|
||||
impl<T: GraphicsAdapter> GraphicsScheme<T> {
|
||||
pub fn new(mut adapter: T, scheme_name: String, early: bool) -> Self {
|
||||
assert!(scheme_name.starts_with("display"));
|
||||
let socket = Socket::nonblock().expect("failed to create graphics scheme");
|
||||
|
||||
let disable_graphical_debug = Some(
|
||||
File::open("/scheme/debug/disable-graphical-debug")
|
||||
.expect("vesad: Failed to open /scheme/debug/disable-graphical-debug"),
|
||||
);
|
||||
|
||||
let mut objects = KmsObjects::new();
|
||||
adapter.init(&mut objects);
|
||||
for connector_id in objects.connector_ids().to_vec() {
|
||||
adapter.probe_connector(&mut objects, connector_id)
|
||||
}
|
||||
|
||||
let mut inner = GraphicsSchemeInner {
|
||||
adapter,
|
||||
scheme_name,
|
||||
disable_graphical_debug,
|
||||
socket,
|
||||
objects,
|
||||
handles: HandleMap::new(),
|
||||
active_vt: 0,
|
||||
vts: HashMap::new(),
|
||||
};
|
||||
|
||||
let cap_id = inner.scheme_root().expect("failed to get this scheme root");
|
||||
register_scheme_inner(&inner.socket, &inner.scheme_name, cap_id)
|
||||
.expect("failed to register graphics scheme root");
|
||||
|
||||
let display_handle = if early {
|
||||
DisplayHandle::new_early(&inner.scheme_name).unwrap()
|
||||
} else {
|
||||
DisplayHandle::new(&inner.scheme_name).unwrap()
|
||||
};
|
||||
|
||||
Self {
|
||||
inner,
|
||||
inputd_handle: display_handle,
|
||||
state: SchemeState::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn event_handle(&self) -> &Fd {
|
||||
self.inner.socket.inner()
|
||||
}
|
||||
|
||||
pub fn inputd_event_handle(&self) -> BorrowedFd<'_> {
|
||||
self.inputd_handle.inner()
|
||||
}
|
||||
|
||||
pub fn adapter(&self) -> &T {
|
||||
&self.inner.adapter
|
||||
}
|
||||
|
||||
pub fn adapter_mut(&mut self) -> &mut T {
|
||||
&mut self.inner.adapter
|
||||
}
|
||||
|
||||
pub fn kms_objects(&self) -> &KmsObjects<T> {
|
||||
&self.inner.objects
|
||||
}
|
||||
|
||||
pub fn kms_objects_mut(&mut self) -> &mut KmsObjects<T> {
|
||||
&mut self.inner.objects
|
||||
}
|
||||
|
||||
pub fn adapter_and_kms_objects_mut(&mut self) -> (&mut T, &mut KmsObjects<T>) {
|
||||
(&mut self.inner.adapter, &mut self.inner.objects)
|
||||
}
|
||||
|
||||
pub fn handle_vt_events(&mut self) {
|
||||
while let Some(vt_event) = self
|
||||
.inputd_handle
|
||||
.read_vt_event()
|
||||
.expect("driver-graphics: failed to read display handle")
|
||||
{
|
||||
match vt_event.kind {
|
||||
VtEventKind::Activate => self.inner.activate_vt(vt_event.vt),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn notify_displays_changed(&mut self) {
|
||||
// FIXME notify clients
|
||||
}
|
||||
|
||||
/// Process new scheme requests.
|
||||
///
|
||||
/// This needs to be called each time there is a new event on the scheme
|
||||
/// file.
|
||||
pub fn tick(&mut self) -> io::Result<()> {
|
||||
loop {
|
||||
let request = match self.inner.socket.next_request(SignalBehavior::Restart) {
|
||||
Ok(Some(request)) => request,
|
||||
Ok(None) => {
|
||||
// Scheme likely got unmounted
|
||||
std::process::exit(0);
|
||||
}
|
||||
Err(err) if err.errno == EAGAIN => break,
|
||||
Err(err) => panic!("driver-graphics: failed to read display scheme: {err}"),
|
||||
};
|
||||
|
||||
match request.kind() {
|
||||
RequestKind::Call(call) => {
|
||||
let response = call.handle_sync(&mut self.inner, &mut self.state);
|
||||
self.inner
|
||||
.socket
|
||||
.write_response(response, SignalBehavior::Restart)
|
||||
.expect("driver-graphics: failed to write response");
|
||||
}
|
||||
RequestKind::OnClose { id } => {
|
||||
self.inner.on_close(id);
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
struct GraphicsSchemeInner<T: GraphicsAdapter> {
|
||||
adapter: T,
|
||||
|
||||
scheme_name: String,
|
||||
disable_graphical_debug: Option<File>,
|
||||
socket: Socket,
|
||||
objects: KmsObjects<T>,
|
||||
handles: HandleMap<Handle<T>>,
|
||||
|
||||
active_vt: usize,
|
||||
vts: HashMap<usize, VtState<T>>,
|
||||
}
|
||||
|
||||
struct VtState<T: GraphicsAdapter> {
|
||||
connector_state: Vec<KmsConnectorState<T>>,
|
||||
crtc_state: Vec<KmsCrtcState<T>>,
|
||||
cursor_plane: CursorPlane<T::Buffer>,
|
||||
}
|
||||
|
||||
enum Handle<T: GraphicsAdapter> {
|
||||
V2 {
|
||||
vt: usize,
|
||||
next_id: u32,
|
||||
buffers: HashMap<u32, Arc<T::Buffer>>,
|
||||
},
|
||||
SchemeRoot,
|
||||
}
|
||||
|
||||
impl<T: GraphicsAdapter> GraphicsSchemeInner<T> {
|
||||
fn get_or_create_vt<'a>(
|
||||
objects: &KmsObjects<T>,
|
||||
vts: &'a mut HashMap<usize, VtState<T>>,
|
||||
vt: usize,
|
||||
) -> &'a mut VtState<T> {
|
||||
vts.entry(vt).or_insert_with(|| VtState {
|
||||
connector_state: objects
|
||||
.connectors()
|
||||
.map(|connector| connector.lock().unwrap().state.clone())
|
||||
.collect(),
|
||||
crtc_state: objects
|
||||
.crtcs()
|
||||
.map(|crtc| crtc.lock().unwrap().state.clone())
|
||||
.collect(),
|
||||
cursor_plane: CursorPlane {
|
||||
x: 0,
|
||||
y: 0,
|
||||
hot_x: 0,
|
||||
hot_y: 0,
|
||||
buffer: None,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
fn activate_vt(&mut self, vt: usize) {
|
||||
log::info!("activate {}", vt);
|
||||
|
||||
// Disable the kernel graphical debug writing once switching vt's for the
|
||||
// first time. This way the kernel graphical debug remains enabled if the
|
||||
// userspace logging infrastructure doesn't start up because for example a
|
||||
// kernel panic happened prior to it starting up or logd crashed.
|
||||
if let Some(mut disable_graphical_debug) = self.disable_graphical_debug.take() {
|
||||
let _ = disable_graphical_debug.write(&[1]);
|
||||
}
|
||||
|
||||
self.active_vt = vt;
|
||||
|
||||
let vt_state = GraphicsSchemeInner::get_or_create_vt(&self.objects, &mut self.vts, vt);
|
||||
|
||||
for (connector_idx, connector_state) in vt_state.connector_state.iter().enumerate() {
|
||||
let connector_id = self.objects.connector_ids()[connector_idx];
|
||||
let mut connector = self
|
||||
.objects
|
||||
.get_connector(connector_id)
|
||||
.unwrap()
|
||||
.lock()
|
||||
.unwrap();
|
||||
connector.state = connector_state.clone();
|
||||
}
|
||||
|
||||
for (crtc_idx, crtc_state) in vt_state.crtc_state.iter().enumerate() {
|
||||
let crtc_id = self.objects.crtc_ids()[crtc_idx];
|
||||
let crtc = self.objects.get_crtc(crtc_id).unwrap();
|
||||
let connector_id = self.objects.connector_ids()[crtc_idx];
|
||||
|
||||
let fb = crtc_state.fb_id.map(|fb_id| {
|
||||
self.objects
|
||||
.get_framebuffer(fb_id)
|
||||
.expect("removed framebuffers should be unset")
|
||||
});
|
||||
|
||||
self.adapter
|
||||
.set_crtc(
|
||||
&self.objects,
|
||||
crtc,
|
||||
crtc_state.clone(),
|
||||
Damage {
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: fb.map_or(0, |fb| fb.width),
|
||||
height: fb.map_or(0, |fb| fb.height),
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
self.objects
|
||||
.get_connector(connector_id)
|
||||
.unwrap()
|
||||
.lock()
|
||||
.unwrap()
|
||||
.state
|
||||
.crtc_id = crtc_id;
|
||||
}
|
||||
|
||||
if self.adapter.hw_cursor_size().is_some() {
|
||||
self.adapter.handle_cursor(&vt_state.cursor_plane, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const MAP_FAKE_OFFSET_MULTIPLIER: usize = 0x10_000_000;
|
||||
|
||||
impl<T: GraphicsAdapter> SchemeSync for GraphicsSchemeInner<T> {
|
||||
fn scheme_root(&mut self) -> Result<usize> {
|
||||
Ok(self.handles.insert(Handle::SchemeRoot))
|
||||
}
|
||||
fn openat(
|
||||
&mut self,
|
||||
dirfd: usize,
|
||||
path: &str,
|
||||
_flags: usize,
|
||||
_fcntl_flags: u32,
|
||||
_ctx: &CallerCtx,
|
||||
) -> Result<OpenResult> {
|
||||
if !matches!(self.handles.get(dirfd)?, Handle::SchemeRoot) {
|
||||
return Err(Error::new(EACCES));
|
||||
}
|
||||
if path.is_empty() {
|
||||
return Err(Error::new(EINVAL));
|
||||
}
|
||||
|
||||
let handle = if path.starts_with("v") {
|
||||
if !path.starts_with("v2/") {
|
||||
return Err(Error::new(ENOENT));
|
||||
}
|
||||
let vt = path["v2/".len()..]
|
||||
.parse::<usize>()
|
||||
.map_err(|_| Error::new(EINVAL))?;
|
||||
|
||||
// Ensure the VT exists such that the rest of the methods can freely access it.
|
||||
Self::get_or_create_vt(&self.objects, &mut self.vts, vt);
|
||||
|
||||
Handle::V2 {
|
||||
vt,
|
||||
next_id: 0,
|
||||
buffers: HashMap::new(),
|
||||
}
|
||||
} else {
|
||||
return Err(Error::new(EINVAL));
|
||||
};
|
||||
let id = self.handles.insert(handle);
|
||||
Ok(OpenResult::ThisScheme {
|
||||
number: id,
|
||||
flags: NewFdFlags::empty(),
|
||||
})
|
||||
}
|
||||
|
||||
fn fpath(&mut self, id: usize, buf: &mut [u8], _ctx: &CallerCtx) -> syscall::Result<usize> {
|
||||
FpathWriter::with(buf, &self.scheme_name, |w| {
|
||||
match self.handles.get(id)? {
|
||||
Handle::V2 {
|
||||
vt,
|
||||
next_id: _,
|
||||
buffers: _,
|
||||
} => write!(w, "v2/{vt}").unwrap(),
|
||||
Handle::SchemeRoot => return Err(Error::new(EOPNOTSUPP)),
|
||||
};
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
fn call(
|
||||
&mut self,
|
||||
id: usize,
|
||||
payload: &mut [u8],
|
||||
metadata: &[u64],
|
||||
_ctx: &CallerCtx,
|
||||
) -> Result<usize> {
|
||||
use redox_ioctl::drm as ipc;
|
||||
|
||||
fn id_index(id: u32) -> u32 {
|
||||
id & 0xFF
|
||||
}
|
||||
|
||||
fn plane_id(i: u32) -> u32 {
|
||||
id_index(i) | (1 << 13)
|
||||
}
|
||||
|
||||
match self.handles.get_mut(id)? {
|
||||
Handle::SchemeRoot => return Err(Error::new(EOPNOTSUPP)),
|
||||
Handle::V2 {
|
||||
vt,
|
||||
next_id,
|
||||
buffers,
|
||||
} => match metadata[0] {
|
||||
ipc::VERSION => ipc::DrmVersion::with(payload, |mut data| {
|
||||
data.set_version_major(1);
|
||||
data.set_version_minor(4);
|
||||
data.set_version_patchlevel(0);
|
||||
|
||||
data.set_name(unsafe { mem::transmute(self.adapter.name()) });
|
||||
data.set_date(unsafe { mem::transmute(&b"0"[..]) });
|
||||
data.set_desc(unsafe { mem::transmute(self.adapter.desc()) });
|
||||
|
||||
Ok(0)
|
||||
}),
|
||||
ipc::GET_CAP => ipc::DrmGetCap::with(payload, |mut data| {
|
||||
data.set_value(
|
||||
self.adapter.get_cap(
|
||||
data.capability()
|
||||
.try_into()
|
||||
.map_err(|_| syscall::Error::new(EINVAL))?,
|
||||
)?,
|
||||
);
|
||||
Ok(0)
|
||||
}),
|
||||
ipc::SET_CLIENT_CAP => ipc::DrmSetClientCap::with(payload, |data| {
|
||||
self.adapter.set_client_cap(
|
||||
data.capability()
|
||||
.try_into()
|
||||
.map_err(|_| syscall::Error::new(EINVAL))?,
|
||||
data.value(),
|
||||
)?;
|
||||
Ok(0)
|
||||
}),
|
||||
ipc::MODE_CARD_RES => ipc::DrmModeCardRes::with(payload, |mut data| {
|
||||
let conn_ids = self
|
||||
.objects
|
||||
.connector_ids()
|
||||
.iter()
|
||||
.map(|id| id.0)
|
||||
.collect::<Vec<_>>();
|
||||
let crtc_ids = self
|
||||
.objects
|
||||
.crtc_ids()
|
||||
.iter()
|
||||
.map(|id| id.0)
|
||||
.collect::<Vec<_>>();
|
||||
let enc_ids = self
|
||||
.objects
|
||||
.encoder_ids()
|
||||
.iter()
|
||||
.map(|id| id.0)
|
||||
.collect::<Vec<_>>();
|
||||
let fb_ids = self
|
||||
.objects
|
||||
.fb_ids()
|
||||
.iter()
|
||||
.map(|id| id.0)
|
||||
.collect::<Vec<_>>();
|
||||
data.set_fb_id_ptr(&fb_ids);
|
||||
data.set_crtc_id_ptr(&crtc_ids);
|
||||
data.set_connector_id_ptr(&conn_ids);
|
||||
data.set_encoder_id_ptr(&enc_ids);
|
||||
data.set_min_width(0);
|
||||
data.set_max_width(16384);
|
||||
data.set_min_height(0);
|
||||
data.set_max_height(16384);
|
||||
Ok(0)
|
||||
}),
|
||||
ipc::MODE_GET_CRTC => ipc::DrmModeCrtc::with(payload, |mut data| {
|
||||
let crtc = self
|
||||
.objects
|
||||
.get_crtc(KmsObjectId(data.crtc_id()))?
|
||||
.lock()
|
||||
.unwrap();
|
||||
// Don't touch set_connectors, that is only used by MODE_SET_CRTC
|
||||
data.set_fb_id(crtc.state.fb_id.unwrap_or(KmsObjectId::INVALID).0);
|
||||
// FIXME fill x and y with the data from the primary plane
|
||||
data.set_x(0);
|
||||
data.set_y(0);
|
||||
data.set_gamma_size(crtc.gamma_size);
|
||||
if let Some(mode) = crtc.state.mode {
|
||||
data.set_mode_valid(1);
|
||||
data.set_mode(mode);
|
||||
} else {
|
||||
data.set_mode_valid(0);
|
||||
data.set_mode(Default::default());
|
||||
}
|
||||
Ok(0)
|
||||
}),
|
||||
ipc::MODE_SET_CRTC => ipc::DrmModeCrtc::with(payload, |data| {
|
||||
let crtc = self.objects.get_crtc(KmsObjectId(data.crtc_id()))?;
|
||||
let connector_ids: Vec<KmsObjectId> = data
|
||||
.set_connectors_ptr()
|
||||
.iter()
|
||||
.take(data.count_connectors() as usize)
|
||||
.map(|&id| KmsObjectId(id))
|
||||
.collect();
|
||||
let fb_id = if data.fb_id() != 0 {
|
||||
Some(KmsObjectId(data.fb_id()))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let mode = if data.mode_valid() != 0 {
|
||||
Some(data.mode())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let mut new_state = crtc.lock().unwrap().state.clone();
|
||||
new_state.fb_id = fb_id;
|
||||
new_state.mode = mode;
|
||||
if *vt == self.active_vt {
|
||||
self.adapter.set_crtc(
|
||||
&self.objects,
|
||||
crtc,
|
||||
new_state.clone(),
|
||||
Damage {
|
||||
x: data.x(),
|
||||
y: data.y(),
|
||||
width: mode.map_or(0, |m| m.hdisplay as u32),
|
||||
height: mode.map_or(0, |m| m.vdisplay as u32),
|
||||
},
|
||||
)?;
|
||||
|
||||
for connector in connector_ids {
|
||||
self.objects
|
||||
.get_connector(connector)?
|
||||
.lock()
|
||||
.unwrap()
|
||||
.state
|
||||
.crtc_id = KmsObjectId(data.crtc_id());
|
||||
}
|
||||
}
|
||||
self.vts.get_mut(vt).unwrap().crtc_state
|
||||
[crtc.lock().unwrap().crtc_index as usize] = new_state;
|
||||
Ok(0)
|
||||
}),
|
||||
ipc::MODE_CURSOR => ipc::DrmModeCursor::with(payload, |data| {
|
||||
let vt_state = self.vts.get_mut(vt).unwrap();
|
||||
|
||||
let cursor_plane = &mut vt_state.cursor_plane;
|
||||
|
||||
let update_buffer = data.flags() & DRM_MODE_CURSOR_BO != 0;
|
||||
if update_buffer {
|
||||
cursor_plane.buffer = if data.handle() == 0 {
|
||||
None
|
||||
} else if let Some(buffer) = buffers.get(&data.handle()) {
|
||||
Some(buffer.clone())
|
||||
} else {
|
||||
return Err(Error::new(EINVAL));
|
||||
};
|
||||
}
|
||||
|
||||
if data.flags() & DRM_MODE_CURSOR_MOVE != 0 {
|
||||
cursor_plane.x = data.x();
|
||||
cursor_plane.y = data.y();
|
||||
}
|
||||
|
||||
self.adapter.handle_cursor(cursor_plane, update_buffer);
|
||||
|
||||
Ok(0)
|
||||
}),
|
||||
ipc::MODE_GET_ENCODER => ipc::DrmModeGetEncoder::with(payload, |mut data| {
|
||||
let encoder = self.objects.get_encoder(KmsObjectId(data.encoder_id()))?;
|
||||
data.set_crtc_id(encoder.crtc_id.0);
|
||||
data.set_possible_crtcs(encoder.possible_crtcs);
|
||||
data.set_possible_clones(encoder.possible_clones);
|
||||
Ok(0)
|
||||
}),
|
||||
ipc::MODE_GET_CONNECTOR => ipc::DrmModeGetConnector::with(payload, |mut data| {
|
||||
if data.count_modes() == 0 {
|
||||
self.adapter
|
||||
.probe_connector(&mut self.objects, KmsObjectId(data.connector_id()));
|
||||
}
|
||||
let connector = self
|
||||
.objects
|
||||
.get_connector(KmsObjectId(data.connector_id()))?
|
||||
.lock()
|
||||
.unwrap();
|
||||
data.set_encoders_ptr(&[connector.encoder_id.0]);
|
||||
data.set_modes_ptr(&connector.modes);
|
||||
data.set_connector_type(data.connector_type());
|
||||
data.set_connector_type_id(data.connector_type_id());
|
||||
data.set_connection(connector.connection as u32);
|
||||
data.set_mm_width(connector.mm_width);
|
||||
data.set_mm_height(connector.mm_width);
|
||||
data.set_subpixel(connector.subpixel as u32);
|
||||
drop(connector);
|
||||
let (props, prop_vals) = self
|
||||
.objects
|
||||
.get_object_properties_data(KmsObjectId(data.connector_id()))?;
|
||||
data.set_props_ptr(&props);
|
||||
data.set_prop_values_ptr(&prop_vals);
|
||||
Ok(0)
|
||||
}),
|
||||
ipc::MODE_GET_PROPERTY => ipc::DrmModeGetProperty::with(payload, |mut data| {
|
||||
let property = self.objects.get_property(KmsObjectId(data.prop_id()))?;
|
||||
data.set_name(property.name.0);
|
||||
let mut flags = 0;
|
||||
if property.immutable {
|
||||
flags |= DRM_MODE_PROP_IMMUTABLE;
|
||||
}
|
||||
if property.atomic {
|
||||
flags |= DRM_MODE_PROP_ATOMIC;
|
||||
}
|
||||
match &property.kind {
|
||||
&KmsPropertyKind::Range(start, end) => {
|
||||
data.set_flags(flags | DRM_MODE_PROP_RANGE);
|
||||
data.set_values_ptr(&[start, end]);
|
||||
data.set_enum_blob_ptr(&[]);
|
||||
}
|
||||
KmsPropertyKind::Enum(variants) => {
|
||||
data.set_flags(flags | DRM_MODE_PROP_ENUM);
|
||||
data.set_values_ptr(
|
||||
&variants.iter().map(|&(_, value)| value).collect::<Vec<_>>(),
|
||||
);
|
||||
data.set_enum_blob_ptr(
|
||||
&variants
|
||||
.iter()
|
||||
.map(|&(name, value)| drm_mode_property_enum {
|
||||
name: name.0,
|
||||
value,
|
||||
})
|
||||
.collect::<Vec<_>>(),
|
||||
);
|
||||
}
|
||||
KmsPropertyKind::Blob => {
|
||||
data.set_flags(flags | DRM_MODE_PROP_BLOB);
|
||||
data.set_values_ptr(&[]);
|
||||
data.set_enum_blob_ptr(&[]);
|
||||
}
|
||||
KmsPropertyKind::Bitmask(bitmask_flags) => {
|
||||
data.set_flags(flags | DRM_MODE_PROP_BITMASK);
|
||||
data.set_values_ptr(
|
||||
&bitmask_flags
|
||||
.iter()
|
||||
.map(|&(_, value)| value)
|
||||
.collect::<Vec<_>>(),
|
||||
);
|
||||
data.set_enum_blob_ptr(
|
||||
&bitmask_flags
|
||||
.iter()
|
||||
.map(|&(name, value)| drm_mode_property_enum {
|
||||
name: name.0,
|
||||
value,
|
||||
})
|
||||
.collect::<Vec<_>>(),
|
||||
);
|
||||
}
|
||||
KmsPropertyKind::Object { type_ } => {
|
||||
data.set_flags(flags | DRM_MODE_PROP_OBJECT);
|
||||
data.set_values_ptr(&[u64::from(*type_)]);
|
||||
data.set_enum_blob_ptr(&[]);
|
||||
}
|
||||
&KmsPropertyKind::SignedRange(start, end) => {
|
||||
data.set_flags(flags | DRM_MODE_PROP_SIGNED_RANGE);
|
||||
data.set_values_ptr(&[start as u64, end as u64]);
|
||||
data.set_enum_blob_ptr(&[]);
|
||||
}
|
||||
}
|
||||
Ok(0)
|
||||
}),
|
||||
ipc::MODE_GET_PROP_BLOB => ipc::DrmModeGetBlob::with(payload, |mut data| {
|
||||
let blob = self.objects.get_blob(KmsObjectId(data.blob_id()))?;
|
||||
data.set_data(&blob);
|
||||
Ok(0)
|
||||
}),
|
||||
ipc::MODE_GET_FB => ipc::DrmModeFbCmd::with(payload, |mut data| {
|
||||
let fb = self.objects.get_framebuffer(KmsObjectId(data.fb_id()))?;
|
||||
|
||||
*next_id += 1;
|
||||
buffers.insert(*next_id, fb.buffer.clone());
|
||||
|
||||
data.set_width(fb.width);
|
||||
data.set_height(fb.height);
|
||||
data.set_pitch(fb.pitch);
|
||||
data.set_bpp(fb.bpp);
|
||||
data.set_depth(fb.depth);
|
||||
data.set_handle(*next_id);
|
||||
Ok(0)
|
||||
}),
|
||||
ipc::MODE_ADD_FB => ipc::DrmModeFbCmd::with(payload, |mut data| {
|
||||
let buffer = buffers.get(&data.handle()).ok_or(Error::new(EINVAL))?;
|
||||
|
||||
let fb = self.adapter.create_framebuffer(buffer);
|
||||
|
||||
let id = self.objects.add_framebuffer(objects::KmsFramebuffer {
|
||||
width: data.width(),
|
||||
height: data.height(),
|
||||
pitch: data.pitch(),
|
||||
bpp: data.bpp(),
|
||||
depth: data.depth(),
|
||||
buffer: buffer.clone(),
|
||||
driver_data: fb,
|
||||
});
|
||||
|
||||
data.set_fb_id(id.0);
|
||||
|
||||
Ok(0)
|
||||
}),
|
||||
ipc::MODE_RM_FB => ipc::StandinForUint::with(payload, |data| {
|
||||
let fb_id = KmsObjectId(data.inner());
|
||||
self.objects.remove_framebuffer(fb_id)?;
|
||||
|
||||
// Disable planes that use this framebuffer.
|
||||
for (vt, vt_data) in &mut self.vts {
|
||||
for (crtc_idx, crtc_state) in vt_data.crtc_state.iter_mut().enumerate() {
|
||||
if crtc_state.fb_id != Some(fb_id) {
|
||||
continue;
|
||||
}
|
||||
crtc_state.fb_id = None;
|
||||
|
||||
if *vt != self.active_vt {
|
||||
continue;
|
||||
}
|
||||
let crtc = self.objects.crtcs().nth(crtc_idx).unwrap();
|
||||
self.adapter
|
||||
.set_crtc(
|
||||
&self.objects,
|
||||
crtc,
|
||||
crtc_state.clone(),
|
||||
Damage {
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: 0,
|
||||
height: 0,
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
Ok(0)
|
||||
}),
|
||||
ipc::MODE_DIRTYFB => ipc::DrmModeFbDirtyCmd::with(payload, |data| {
|
||||
let fb = self.objects.get_framebuffer(KmsObjectId(data.fb_id()))?;
|
||||
|
||||
let damage = data
|
||||
.clips_ptr()
|
||||
.iter()
|
||||
.map(|rect| Damage {
|
||||
x: u32::from(rect.x1),
|
||||
y: u32::from(rect.y1),
|
||||
width: u32::from(rect.x2 - rect.x1),
|
||||
height: u32::from(rect.y2 - rect.y1),
|
||||
})
|
||||
.reduce(Damage::merge)
|
||||
.unwrap_or(Damage {
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: fb.width,
|
||||
height: fb.height,
|
||||
});
|
||||
|
||||
if *vt == self.active_vt {
|
||||
for crtc in self.objects.crtcs() {
|
||||
let state = crtc.lock().unwrap().state.clone();
|
||||
if state.fb_id == Some(KmsObjectId(data.fb_id())) {
|
||||
self.adapter.set_crtc(&self.objects, crtc, state, damage)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(0)
|
||||
}),
|
||||
ipc::MODE_CREATE_DUMB => ipc::DrmModeCreateDumb::with(payload, |mut data| {
|
||||
if data.bpp() != 32 || data.flags() != 0 {
|
||||
return Err(Error::new(EINVAL));
|
||||
}
|
||||
|
||||
let (buffer, pitch) =
|
||||
self.adapter.create_dumb_buffer(data.width(), data.height());
|
||||
|
||||
data.set_pitch(pitch);
|
||||
data.set_size(buffer.size() as u64);
|
||||
|
||||
*next_id += 1;
|
||||
buffers.insert(*next_id, Arc::new(buffer));
|
||||
data.set_handle(*next_id as u32);
|
||||
Ok(0)
|
||||
}),
|
||||
ipc::MODE_MAP_DUMB => ipc::DrmModeMapDumb::with(payload, |mut data| {
|
||||
if data.offset() != 0 {
|
||||
return Err(Error::new(EINVAL));
|
||||
}
|
||||
|
||||
let buffer_id = data.handle();
|
||||
|
||||
if !buffers.contains_key(&buffer_id) {
|
||||
return Err(Error::new(EINVAL));
|
||||
}
|
||||
|
||||
// FIXME use a better scheme for creating map offsets
|
||||
assert!(buffers[&buffer_id].size() < MAP_FAKE_OFFSET_MULTIPLIER);
|
||||
|
||||
data.set_offset((buffer_id as usize * MAP_FAKE_OFFSET_MULTIPLIER) as u64);
|
||||
|
||||
Ok(0)
|
||||
}),
|
||||
ipc::MODE_DESTROY_DUMB => ipc::DrmModeDestroyDumb::with(payload, |data| {
|
||||
if buffers.remove(&data.handle()).is_none() {
|
||||
return Err(Error::new(ENOENT));
|
||||
}
|
||||
Ok(0)
|
||||
}),
|
||||
ipc::MODE_GET_PLANE_RES => ipc::DrmModeGetPlaneRes::with(payload, |mut data| {
|
||||
let count = self.objects.crtc_ids().len();
|
||||
let mut ids = Vec::with_capacity(count);
|
||||
for i in 0..(count as u32) {
|
||||
ids.push(plane_id(i));
|
||||
}
|
||||
data.set_plane_id_ptr(&ids);
|
||||
Ok(0)
|
||||
}),
|
||||
ipc::MODE_GET_PLANE => ipc::DrmModeGetPlane::with(payload, |mut data| {
|
||||
let i = id_index(data.plane_id());
|
||||
let crtc_id = self.objects.crtc_ids()[i as usize];
|
||||
let crtc = self.objects.get_crtc(crtc_id).unwrap();
|
||||
data.set_crtc_id(crtc_id.0);
|
||||
data.set_fb_id(
|
||||
crtc.lock()
|
||||
.unwrap()
|
||||
.state
|
||||
.fb_id
|
||||
.unwrap_or(KmsObjectId::INVALID)
|
||||
.0,
|
||||
);
|
||||
data.set_possible_crtcs(1 << i);
|
||||
data.set_format_type_ptr(&[DrmFourcc::Argb8888 as u32]);
|
||||
Ok(0)
|
||||
}),
|
||||
ipc::MODE_OBJ_GET_PROPERTIES => {
|
||||
ipc::DrmModeObjGetProperties::with(payload, |mut data| {
|
||||
// FIXME remove once all drm objects are materialized in self.objects
|
||||
if data.obj_id() >= 1 << 11 {
|
||||
data.set_props_ptr(&[]);
|
||||
data.set_prop_values_ptr(&[]);
|
||||
return Ok(0);
|
||||
}
|
||||
|
||||
let (props, prop_vals) = self
|
||||
.objects
|
||||
.get_object_properties_data(KmsObjectId(data.obj_id()))?;
|
||||
data.set_props_ptr(&props);
|
||||
data.set_prop_values_ptr(&prop_vals);
|
||||
data.set_obj_type(self.objects.object_type(KmsObjectId(data.obj_id()))?);
|
||||
Ok(0)
|
||||
})
|
||||
}
|
||||
ipc::MODE_CURSOR2 => ipc::DrmModeCursor2::with(payload, |data| {
|
||||
let vt_state = self.vts.get_mut(vt).unwrap();
|
||||
|
||||
let cursor_plane = &mut vt_state.cursor_plane;
|
||||
|
||||
let update_buffer = data.flags() & DRM_MODE_CURSOR_BO != 0;
|
||||
if update_buffer {
|
||||
cursor_plane.buffer = if data.handle() == 0 {
|
||||
None
|
||||
} else if let Some(buffer) = buffers.get(&data.handle()) {
|
||||
Some(buffer.clone())
|
||||
} else {
|
||||
return Err(Error::new(EINVAL));
|
||||
};
|
||||
cursor_plane.hot_x = data.hot_x();
|
||||
cursor_plane.hot_y = data.hot_y();
|
||||
}
|
||||
|
||||
if data.flags() & DRM_MODE_CURSOR_MOVE != 0 {
|
||||
cursor_plane.x = data.x();
|
||||
cursor_plane.y = data.y();
|
||||
}
|
||||
|
||||
self.adapter.handle_cursor(cursor_plane, update_buffer);
|
||||
|
||||
Ok(0)
|
||||
}),
|
||||
ipc::MODE_GET_FB2 => ipc::DrmModeFbCmd2::with(payload, |mut data| {
|
||||
let fb = self.objects.get_framebuffer(KmsObjectId(data.fb_id()))?;
|
||||
|
||||
*next_id += 1;
|
||||
buffers.insert(*next_id, fb.buffer.clone());
|
||||
|
||||
data.set_width(fb.width);
|
||||
data.set_height(fb.height);
|
||||
data.set_pixel_format(DrmFourcc::Argb8888 as u32);
|
||||
data.set_handles([*next_id, 0, 0, 0]);
|
||||
data.set_pitches([fb.width * 4, 0, 0, 0]);
|
||||
data.set_offsets([0; 4]);
|
||||
data.set_modifier([0; 4]);
|
||||
Ok(0)
|
||||
}),
|
||||
_ => return Err(Error::new(EINVAL)),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn mmap_prep(
|
||||
&mut self,
|
||||
id: usize,
|
||||
offset: u64,
|
||||
_size: usize,
|
||||
_flags: MapFlags,
|
||||
_ctx: &CallerCtx,
|
||||
) -> syscall::Result<usize> {
|
||||
// log::trace!("KSMSG MMAP {} {:?} {} {}", id, _flags, _offset, _size);
|
||||
let (framebuffer, offset) = match self.handles.get(id)? {
|
||||
Handle::V2 {
|
||||
vt: _,
|
||||
next_id: _,
|
||||
buffers,
|
||||
} => (
|
||||
buffers
|
||||
.get(&((offset as usize / MAP_FAKE_OFFSET_MULTIPLIER) as u32))
|
||||
.ok_or(Error::new(EINVAL))
|
||||
.unwrap(),
|
||||
offset & (MAP_FAKE_OFFSET_MULTIPLIER as u64 - 1),
|
||||
),
|
||||
Handle::SchemeRoot => return Err(Error::new(EOPNOTSUPP)),
|
||||
};
|
||||
let ptr = T::map_dumb_buffer(&mut self.adapter, framebuffer);
|
||||
Ok(unsafe { ptr.add(offset as usize) } as usize)
|
||||
}
|
||||
|
||||
fn on_close(&mut self, id: usize) {
|
||||
self.handles.remove(id);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user