Files
RedBear-OS/drivers/graphics/driver-graphics/src/lib.rs
T

527 lines
16 KiB
Rust

#![feature(macro_metavar_expr)]
use std::cmp;
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 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::{
KmsCrtc, KmsCrtcDriver, KmsCrtcState, KmsObjectId, KmsObjects, KmsPlane, KmsPlaneDriver,
KmsPlaneState,
};
mod ioctl;
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 Plane: KmsPlaneDriver;
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_unique(&self) -> String;
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>,
) -> syscall::Result<()>;
fn set_plane(
&mut self,
objects: &KmsObjects<Self>,
plane: &Mutex<KmsPlane<Self>>,
new_plane_state: KmsPlaneState<Self>,
damage: Damage,
) -> syscall::Result<()>;
// FIXME replace this with an actual plane marked as cursor plane once we
// support planes internally.
fn has_cursor_plane(&self) -> bool;
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>>,
plane_state: Vec<KmsPlaneState<T>>,
cursor_plane: Option<CursorPlane<T::Buffer>>,
}
enum Handle<T: GraphicsAdapter> {
V2(DrmHandle<T>),
SchemeRoot,
}
struct DrmHandle<T: GraphicsAdapter> {
vt: usize,
unique: Option<String>,
next_id: u32,
buffers: HashMap<u32, Arc<T::Buffer>>,
}
impl<T: GraphicsAdapter> GraphicsSchemeInner<T> {
fn get_or_create_vt<'a>(
adapter: &T,
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(),
plane_state: objects
.planes()
.map(|plane| plane.lock().unwrap().state.clone())
.collect(),
cursor_plane: adapter.has_cursor_plane().then(|| 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.adapter, &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];
self.adapter
.set_crtc(&self.objects, crtc, crtc_state.clone())
.unwrap();
self.objects
.get_connector(connector_id)
.unwrap()
.lock()
.unwrap()
.state
.crtc_id = crtc_id;
}
for (plane_idx, plane_state) in vt_state.plane_state.iter().enumerate() {
let plane_id = self.objects.plane_ids()[plane_idx];
let plane = self.objects.get_plane(plane_id).unwrap();
let fb = plane_state.fb_id.map(|fb_id| {
self.objects
.get_framebuffer(fb_id)
.expect("removed framebuffers should be unset")
});
self.adapter
.set_plane(
&self.objects,
plane,
plane_state.clone(),
Damage {
x: 0,
y: 0,
width: fb.map_or(0, |fb| fb.width),
height: fb.map_or(0, |fb| fb.height),
},
)
.unwrap();
}
if let Some(cursor_plane) = &vt_state.cursor_plane {
self.adapter.handle_cursor(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.adapter, &self.objects, &mut self.vts, vt);
Handle::V2(DrmHandle {
vt,
unique: None,
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 fstat(&mut self, _id: usize, stat: &mut syscall::Stat, _ctx: &CallerCtx) -> Result<()> {
stat.st_dev = 226 /*DRM_MAJOR*/ << 8;
Ok(())
}
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(DrmHandle {
vt,
unique: _,
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> {
match self.handles.get_mut(id)? {
Handle::SchemeRoot => return Err(Error::new(EOPNOTSUPP)),
Handle::V2(handle) => ioctl::call_ioctl(
&mut self.adapter,
&mut self.objects,
self.active_vt,
&mut self.vts,
handle,
metadata[0],
payload,
),
}
}
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(DrmHandle {
vt: _,
unique: _,
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);
}
}