drivers/graphics: Use drm dumb buffer ioctls

This commit is contained in:
bjorn3
2025-12-17 23:06:39 +01:00
parent e8bf0d180b
commit a4a5d86efa
9 changed files with 215 additions and 269 deletions
Generated
+3
View File
@@ -719,6 +719,7 @@ version = "0.1.0"
dependencies = [
"console-draw",
"daemon",
"drm",
"graphics-ipc",
"inputd",
"libredox",
@@ -736,6 +737,7 @@ dependencies = [
"common",
"console-draw",
"daemon",
"drm",
"graphics-ipc",
"inputd",
"libredox",
@@ -925,6 +927,7 @@ version = "0.1.0"
dependencies = [
"common",
"drm",
"drm-fourcc",
"libredox",
"log",
"redox-ioctl",
+52 -68
View File
@@ -83,8 +83,8 @@ enum Handle<T: GraphicsAdapter> {
},
V2 {
vt: usize,
next_id: usize,
fbs: HashMap<usize, Arc<T::Framebuffer>>,
next_id: u32,
fbs: HashMap<u32, Arc<T::Framebuffer>>,
},
}
@@ -463,6 +463,10 @@ impl<T: GraphicsAdapter> SchemeSync for GraphicsScheme<T> {
id_index(i) | (1 << 13)
}
fn dumb_buffer_id(i: u32) -> u32 {
id_index(i) | (1 << 14)
}
fn modeinfo_for_size(width: u32, height: u32) -> drm_sys::drm_mode_modeinfo {
let mut modeinfo = drm_sys::drm_mode_modeinfo {
// The actual visible display size
@@ -596,6 +600,50 @@ impl<T: GraphicsAdapter> SchemeSync for GraphicsScheme<T> {
data.set_handle(fb_handle_id(i));
Ok(0)
}),
ipc::MODE_CREATE_DUMB => ipc::DrmModeCreateDumb::with(payload, |mut data| {
if data.bpp() != 32 {
return Err(Error::new(EINVAL));
}
let fb = self
.adapter
.create_dumb_framebuffer(data.width(), data.height());
*next_id += 1;
fbs.insert(*next_id, Arc::new(fb));
data.set_handle(dumb_buffer_id(*next_id as u32));
data.set_pitch(data.width() * 4);
data.set_size(u64::from(data.width()) * u64::from(data.height()) * 4);
Ok(0)
}),
ipc::MODE_MAP_DUMB => ipc::DrmModeMapDumb::with(payload, |mut data| {
if data.offset() != 0 {
return Err(Error::new(EINVAL));
}
let fb_id = id_index(data.handle());
if !fbs.contains_key(&fb_id) {
return Err(Error::new(EINVAL));
}
// FIXME use a better scheme for creating map offsets
assert!(
((fbs[&fb_id].width() * fbs[&fb_id].height() * 4) as usize)
< MAP_FAKE_OFFSET_MULTIPLIER
);
data.set_offset((fb_id as usize * MAP_FAKE_OFFSET_MULTIPLIER) as u64);
Ok(0)
}),
ipc::MODE_DESTROY_DUMB => ipc::DrmModeDestroyDumb::with(payload, |data| {
let fb_id = id_index(data.handle());
if fbs.remove(&fb_id).is_none() {
return Err(Error::new(ENOENT));
}
Ok(0)
}),
ipc::MODE_GET_PLANE_RES => ipc::DrmModeGetPlaneRes::with(payload, |mut data| {
let count = self.adapter.display_count();
let mut ids = Vec::with_capacity(count);
@@ -634,70 +682,6 @@ impl<T: GraphicsAdapter> SchemeSync for GraphicsScheme<T> {
data.set_modifier([0; 4]);
Ok(0)
}),
ipc::CREATE_DUMB_FRAMEBUFFER => {
if payload.len() < size_of::<ipc::CreateDumbFramebuffer>() {
return Err(Error::new(EINVAL));
}
let payload = unsafe {
transmute::<
&mut [u8; size_of::<ipc::CreateDumbFramebuffer>()],
&mut ipc::CreateDumbFramebuffer,
>(payload.as_mut_array().unwrap())
};
let fb = self
.adapter
.create_dumb_framebuffer(payload.width, payload.height);
*next_id += 1;
fbs.insert(*next_id, Arc::new(fb));
payload.fb_id = *next_id;
Ok(size_of::<ipc::CreateDumbFramebuffer>())
}
ipc::DUMB_FRAMEBUFFER_MAP_OFFSET => {
if payload.len() < size_of::<ipc::DumbFramebufferMapOffset>() {
return Err(Error::new(EINVAL));
}
let payload = unsafe {
transmute::<
&mut [u8; size_of::<ipc::DumbFramebufferMapOffset>()],
&mut ipc::DumbFramebufferMapOffset,
>(payload.as_mut_array().unwrap())
};
let fb_id = payload.fb_id;
if !fbs.contains_key(&fb_id) {
return Err(Error::new(EINVAL));
}
// FIXME use a better scheme for creating map offsets
assert!(
((fbs[&fb_id].width() * fbs[&fb_id].height() * 4) as usize)
< MAP_FAKE_OFFSET_MULTIPLIER
);
payload.offset = fb_id * MAP_FAKE_OFFSET_MULTIPLIER;
Ok(size_of::<ipc::DumbFramebufferMapOffset>())
}
ipc::DESTROY_DUMB_FRAMEBUFFER => {
if payload.len() < size_of::<ipc::DestroyDumbFramebuffer>() {
return Err(Error::new(EINVAL));
}
let payload = unsafe {
transmute::<
&mut [u8; size_of::<ipc::DestroyDumbFramebuffer>()],
&mut ipc::DestroyDumbFramebuffer,
>(payload.as_mut_array().unwrap())
};
if fbs.remove(&{ payload.fb_id }).is_none() {
return Err(Error::new(ENOENT));
}
Ok(size_of::<ipc::DestroyDumbFramebuffer>())
}
ipc::UPDATE_PLANE => {
if payload.len() < size_of::<ipc::UpdatePlane>() {
return Err(Error::new(EINVAL));
@@ -713,7 +697,7 @@ impl<T: GraphicsAdapter> SchemeSync for GraphicsScheme<T> {
return Err(Error::new(EINVAL));
}
let Some(framebuffer) = fbs.get(&{ payload.fb_id }) else {
let Some(framebuffer) = fbs.get(&id_index(payload.fb_id)) else {
return Err(Error::new(EINVAL));
};
@@ -747,7 +731,7 @@ impl<T: GraphicsAdapter> SchemeSync for GraphicsScheme<T> {
next_id: _,
fbs,
} => (
fbs.get(&(offset as usize / MAP_FAKE_OFFSET_MULTIPLIER))
fbs.get(&((offset as usize / MAP_FAKE_OFFSET_MULTIPLIER) as u32))
.ok_or(Error::new(EINVAL))
.unwrap(),
offset & (MAP_FAKE_OFFSET_MULTIPLIER as u64 - 1),
+1
View File
@@ -4,6 +4,7 @@ version = "0.1.0"
edition = "2021"
[dependencies]
drm = "0.14.1"
orbclient = "0.3.27"
ransid = "0.4"
redox_event = "0.4"
+74 -56
View File
@@ -1,7 +1,9 @@
use std::collections::VecDeque;
use std::{cmp, ptr};
use std::{cmp, mem, ptr};
use console_draw::TextScreen;
use drm::buffer::Buffer;
use drm::control::dumbbuffer::{DumbBuffer, DumbMapping};
use graphics_ipc::v2::V2GraphicsHandle;
use inputd::ConsumerHandle;
use orbclient::{Event, EventOption};
@@ -12,8 +14,21 @@ use syscall::{Error, Result, EINVAL, ENOENT};
pub struct DisplayMap {
display_handle: V2GraphicsHandle,
fb: usize,
inner: graphics_ipc::v1::DisplayMap,
fb: DumbBuffer,
mapping: DumbMapping<'static>,
}
impl DisplayMap {
unsafe fn console_map(&mut self) -> console_draw::DisplayMap {
console_draw::DisplayMap {
offscreen: ptr::slice_from_raw_parts_mut(
self.mapping.as_mut_ptr() as *mut u32,
self.mapping.len() / 4,
),
width: self.fb.size().0 as usize,
height: self.fb.size().1 as usize,
}
}
}
pub struct FbbootlogScheme {
@@ -55,24 +70,27 @@ impl FbbootlogScheme {
let (width, height) = new_display_handle
.display_size(new_display_handle.first_display().unwrap())
.unwrap();
let fb = new_display_handle
let mut fb = new_display_handle
.create_dumb_framebuffer(width, height)
.unwrap();
match new_display_handle.map_dumb_framebuffer(fb, width, height) {
Ok(display_map) => {
self.display_map = Some(DisplayMap {
display_handle: new_display_handle,
fb,
inner: display_map,
});
eprintln!("fbbootlogd: mapped display");
}
let display_map = match new_display_handle.map_dumb_framebuffer(&mut fb) {
Ok(display_map) => unsafe {
mem::transmute::<DumbMapping<'_>, DumbMapping<'static>>(display_map)
},
Err(err) => {
eprintln!("fbbootlogd: failed to open display: {}", err);
return;
}
}
};
self.display_map = Some(DisplayMap {
display_handle: new_display_handle,
mapping: display_map,
fb,
});
eprintln!("fbbootlogd: mapped display");
}
pub fn handle_input(&mut self, ev: &Event) {
@@ -127,11 +145,7 @@ impl FbbootlogScheme {
return;
};
let buffer_len = self.text_buffer.lines.len();
let dmap = &mut console_draw::DisplayMap {
offscreen: map.inner.ptr_mut(),
width: map.inner.width(),
height: map.inner.height(),
};
let dmap = unsafe { &mut map.console_map() };
// for both extra space on wrapping text and a scrollback indicator
let spare_lines = 3;
self.is_scrollback = true;
@@ -151,11 +165,15 @@ impl FbbootlogScheme {
if i == buffer_len || yd + spare_lines * 16 > dmap.height {
// render until end of screen
damage.height = (dmap.height as u32) - damage.y;
map.display_handle.update_plane(0, map.fb, damage).unwrap();
map.display_handle
.update_plane(0, u32::from(map.fb.handle()), damage)
.unwrap();
self.is_scrollback = i < buffer_len;
break;
} else {
map.display_handle.update_plane(0, map.fb, damage).unwrap();
map.display_handle
.update_plane(0, u32::from(map.fb.handle()), damage)
.unwrap();
}
}
}
@@ -169,45 +187,47 @@ impl FbbootlogScheme {
Ok((width, height)) => (width, height),
Err(err) => {
eprintln!("fbbootlogd: failed to get display size: {}", err);
(map.inner.width() as u32, map.inner.height() as u32)
map.fb.size()
}
};
if width as usize != map.inner.width() || height as usize != map.inner.height() {
if (width, height) != map.fb.size() {
match map.display_handle.create_dumb_framebuffer(width, height) {
Ok(fb) => match map.display_handle.map_dumb_framebuffer(fb, width, height) {
Ok(mut new_map) => {
let count = new_map.ptr().len();
unsafe {
ptr::write_bytes(new_map.ptr_mut() as *mut u32, 0, count);
Ok(mut fb) => {
let mut new_map = match map.display_handle.map_dumb_framebuffer(&mut fb) {
Ok(new_map) => unsafe {
mem::transmute::<DumbMapping<'_>, DumbMapping<'static>>(new_map)
},
Err(err) => {
eprintln!("fbbootlogd: failed to open display: {}", err);
return;
}
};
text_screen.resize(
&mut console_draw::DisplayMap {
offscreen: map.inner.ptr_mut(),
width: map.inner.width(),
height: map.inner.height(),
},
&mut console_draw::DisplayMap {
offscreen: new_map.ptr_mut(),
width: new_map.width(),
height: new_map.height(),
},
);
new_map.fill(0);
let _ = map.display_handle.destroy_dumb_framebuffer(map.fb);
text_screen.resize(
unsafe { &mut map.console_map() },
&mut console_draw::DisplayMap {
offscreen: ptr::slice_from_raw_parts_mut(
new_map.as_mut_ptr() as *mut u32,
new_map.len() / 4,
),
width: fb.size().0 as usize,
height: fb.size().1 as usize,
},
);
map.fb = fb;
map.inner = new_map;
let old_fb = mem::replace(&mut map.fb, fb);
map.mapping = new_map;
eprintln!("fbbootlogd: mapped display");
}
Err(err) => {
eprintln!("fbbootlogd: failed to open display: {}", err);
}
},
let _ = map.display_handle.destroy_dumb_framebuffer(old_fb);
eprintln!("fbbootlogd: mapped display");
}
Err(err) => {
eprintln!("fbbootlogd: failed to create framebuffer: {}", err);
return;
}
}
}
@@ -267,17 +287,15 @@ impl SchemeSync for FbbootlogScheme {
if !self.is_scrollback {
let damage = self.text_screen.write(
&mut console_draw::DisplayMap {
offscreen: map.inner.ptr_mut(),
width: map.inner.width(),
height: map.inner.height(),
},
unsafe { &mut map.console_map() },
buf,
&mut VecDeque::new(),
);
if let Some(map) = &self.display_map {
map.display_handle.update_plane(0, map.fb, damage).unwrap();
map.display_handle
.update_plane(0, u32::from(map.fb.handle()), damage)
.unwrap();
}
}
}
+1
View File
@@ -4,6 +4,7 @@ version = "0.1.0"
edition = "2021"
[dependencies]
drm = "0.14.1"
log = "0.4"
orbclient = "0.3.27"
ransid = "0.4"
+67 -48
View File
@@ -1,7 +1,9 @@
use console_draw::TextScreen;
use drm::buffer::Buffer;
use drm::control::dumbbuffer::{DumbBuffer, DumbMapping};
use graphics_ipc::v2::{Damage, V2GraphicsHandle};
use inputd::ConsumerHandle;
use std::{io, ptr};
use std::{io, mem, ptr};
pub struct Display {
pub input_handle: ConsumerHandle,
@@ -10,8 +12,21 @@ pub struct Display {
pub struct DisplayMap {
display_handle: V2GraphicsHandle,
fb: usize,
pub inner: graphics_ipc::v2::DisplayMap,
fb: DumbBuffer,
mapping: DumbMapping<'static>,
}
impl DisplayMap {
pub unsafe fn console_map(&mut self) -> console_draw::DisplayMap {
console_draw::DisplayMap {
offscreen: ptr::slice_from_raw_parts_mut(
self.mapping.as_mut_ptr() as *mut u32,
self.mapping.len() / 4,
),
width: self.fb.size().0 as usize,
height: self.fb.size().1 as usize,
}
}
}
impl Display {
@@ -36,28 +51,29 @@ impl Display {
let (width, height) = new_display_handle
.display_size(new_display_handle.first_display().unwrap())
.unwrap();
let fb = new_display_handle
let mut fb = new_display_handle
.create_dumb_framebuffer(width, height)
.unwrap();
match new_display_handle.map_dumb_framebuffer(fb, width, height) {
Ok(map) => {
log::debug!(
"fbcond: Mapped new display with size {}x{}",
map.width(),
map.height()
);
self.map = Some(DisplayMap {
display_handle: new_display_handle,
fb,
inner: map,
});
}
let map = match new_display_handle.map_dumb_framebuffer(&mut fb) {
Ok(map) => unsafe { mem::transmute::<DumbMapping<'_>, DumbMapping<'static>>(map) },
Err(err) => {
log::error!("failed to map display: {}", err);
return;
}
}
};
log::debug!(
"fbcond: Mapped new display with size {}x{}",
fb.size().0,
fb.size().1,
);
self.map = Some(DisplayMap {
display_handle: new_display_handle,
fb,
mapping: map,
});
}
pub fn handle_resize(map: &mut DisplayMap, text_screen: &mut TextScreen) {
@@ -69,43 +85,44 @@ impl Display {
Ok((width, height)) => (width, height),
Err(err) => {
log::error!("fbcond: failed to get display size: {}", err);
(map.inner.width() as u32, map.inner.height() as u32)
map.fb.size()
}
};
if width as usize != map.inner.width() || height as usize != map.inner.height() {
if (width, height) != map.fb.size() {
match map.display_handle.create_dumb_framebuffer(width, height) {
Ok(fb) => match map.display_handle.map_dumb_framebuffer(fb, width, height) {
Ok(mut new_map) => {
let count = new_map.ptr().len();
unsafe {
ptr::write_bytes(new_map.ptr_mut() as *mut u32, 0, count);
Ok(mut fb) => {
let mut new_map = match map.display_handle.map_dumb_framebuffer(&mut fb) {
Ok(new_map) => unsafe {
mem::transmute::<DumbMapping<'_>, DumbMapping<'static>>(new_map)
},
Err(err) => {
eprintln!("fbcond: failed to open display: {}", err);
return;
}
};
text_screen.resize(
&mut console_draw::DisplayMap {
offscreen: map.inner.ptr_mut(),
width: map.inner.width(),
height: map.inner.height(),
},
&mut console_draw::DisplayMap {
offscreen: new_map.ptr_mut(),
width: new_map.width(),
height: new_map.height(),
},
);
new_map.fill(0);
let _ = map.display_handle.destroy_dumb_framebuffer(map.fb);
text_screen.resize(
unsafe { &mut map.console_map() },
&mut console_draw::DisplayMap {
offscreen: ptr::slice_from_raw_parts_mut(
new_map.as_mut_ptr() as *mut u32,
new_map.len() / 4,
),
width: fb.size().0 as usize,
height: fb.size().1 as usize,
},
);
map.fb = fb;
map.inner = new_map;
let old_fb = mem::replace(&mut map.fb, fb);
map.mapping = new_map;
log::debug!("fbcond: mapped display");
}
Err(err) => {
log::error!("fbcond: failed to open display: {}", err);
}
},
let _ = map.display_handle.destroy_dumb_framebuffer(old_fb);
eprintln!("fbcond: mapped display");
}
Err(err) => {
log::error!("fbcond: failed to create framebuffer: {}", err);
}
@@ -115,7 +132,9 @@ impl Display {
pub fn sync_rect(&mut self, damage: Damage) {
if let Some(map) = &self.map {
map.display_handle.update_plane(0, map.fb, damage).unwrap();
map.display_handle
.update_plane(0, u32::from(map.fb.handle()), damage)
.unwrap();
}
}
}
+3 -9
View File
@@ -124,15 +124,9 @@ impl TextScreen {
if let Some(map) = &mut self.display.map {
Display::handle_resize(map, &mut self.inner);
let damage = self.inner.write(
&mut console_draw::DisplayMap {
offscreen: map.inner.ptr_mut(),
width: map.inner.width(),
height: map.inner.height(),
},
buf,
&mut self.input,
);
let damage = self
.inner
.write(unsafe { &mut map.console_map() }, buf, &mut self.input);
self.display.sync_rect(damage);
}
+1
View File
@@ -5,6 +5,7 @@ edition = "2021"
[dependencies]
drm = "0.14"
drm-fourcc = "2.2.0"
log = "0.4"
libredox = "0.1.3"
redox-ioctl = { git = "https://gitlab.redox-os.org/redox-os/relibc.git" }
+13 -88
View File
@@ -1,12 +1,13 @@
use std::fs::File;
use std::os::fd::{AsFd, BorrowedFd};
use std::os::unix::io::AsRawFd;
use std::{io, mem, ptr};
use std::{io, mem};
use drm::control::connector::{self, State};
use drm::control::dumbbuffer::{DumbBuffer, DumbMapping};
use drm::control::Device as _;
use drm::{ClientCapability, Device as _, DriverCapability};
use libredox::flag;
use drm_fourcc::DrmFourcc;
pub use crate::common::{Damage, DisplayMap};
@@ -79,75 +80,22 @@ impl V2GraphicsHandle {
Ok((u32::from(width), u32::from(height)))
}
pub fn create_dumb_framebuffer(&self, width: u32, height: u32) -> io::Result<usize> {
let mut cmd = ipc::CreateDumbFramebuffer {
width,
height,
fb_id: 0,
};
unsafe {
sys_call(
&self.file,
&mut cmd,
0,
&[ipc::CREATE_DUMB_FRAMEBUFFER, 0, 0],
)?;
}
Ok(cmd.fb_id)
pub fn create_dumb_framebuffer(&self, width: u32, height: u32) -> io::Result<DumbBuffer> {
self.create_dumb_buffer((width, height), DrmFourcc::Argb8888, 32)
}
pub fn map_dumb_framebuffer(
pub fn map_dumb_framebuffer<'a>(
&self,
id: usize,
width: u32,
height: u32,
) -> io::Result<DisplayMap> {
let mut cmd = ipc::DumbFramebufferMapOffset {
fb_id: id,
offset: 0,
};
unsafe {
sys_call(
&self.file,
&mut cmd,
0,
&[ipc::DUMB_FRAMEBUFFER_MAP_OFFSET, 0, 0],
)?;
}
let display_ptr = unsafe {
libredox::call::mmap(libredox::call::MmapArgs {
fd: self.file.as_raw_fd() as usize,
offset: cmd.offset as u64,
length: (width * height * 4) as usize,
prot: flag::PROT_READ | flag::PROT_WRITE,
flags: flag::MAP_SHARED,
addr: core::ptr::null_mut(),
})?
};
let offscreen = ptr::slice_from_raw_parts_mut(
display_ptr as *mut u32,
width as usize * height as usize,
);
Ok(unsafe { DisplayMap::new(offscreen, width as usize, height as usize) })
buffer: &'a mut DumbBuffer,
) -> io::Result<DumbMapping<'a>> {
self.map_dumb_buffer(buffer)
}
pub fn destroy_dumb_framebuffer(&self, id: usize) -> io::Result<usize> {
let mut cmd = ipc::DestroyDumbFramebuffer { fb_id: id };
unsafe {
sys_call(
&self.file,
&mut cmd,
0,
&[ipc::DESTROY_DUMB_FRAMEBUFFER, 0, 0],
)?;
}
Ok(cmd.fb_id)
pub fn destroy_dumb_framebuffer(&self, buffer: DumbBuffer) -> io::Result<()> {
self.destroy_dumb_buffer(buffer)
}
pub fn update_plane(&self, display_id: usize, fb_id: usize, damage: Damage) -> io::Result<()> {
pub fn update_plane(&self, display_id: usize, fb_id: u32, damage: Damage) -> io::Result<()> {
let mut cmd = ipc::UpdatePlane {
display_id,
fb_id,
@@ -166,34 +114,11 @@ pub mod ipc {
pub use redox_ioctl::drm::*;
// FIXME replace these with proper drm interfaces
pub const CREATE_DUMB_FRAMEBUFFER: u64 = 3;
#[repr(C, packed)]
pub struct CreateDumbFramebuffer {
pub width: u32,
pub height: u32,
pub fb_id: usize,
}
pub const DUMB_FRAMEBUFFER_MAP_OFFSET: u64 = 4;
#[repr(C, packed)]
pub struct DumbFramebufferMapOffset {
pub fb_id: usize,
pub offset: usize,
}
pub const DESTROY_DUMB_FRAMEBUFFER: u64 = 5;
#[repr(C, packed)]
pub struct DestroyDumbFramebuffer {
pub fb_id: usize,
}
pub const UPDATE_PLANE: u64 = 6;
#[repr(C, packed)]
pub struct UpdatePlane {
pub display_id: usize,
pub fb_id: usize,
pub fb_id: u32,
pub damage: Damage,
}
}