Merge branch 'gpu_drm43' into 'main'

drivers/graphics/driver-graphics: Extract ioctl impls into a separate module

See merge request redox-os/base!268
This commit is contained in:
Jeremy Soller
2026-05-14 15:02:06 -06:00
5 changed files with 773 additions and 639 deletions
@@ -0,0 +1,94 @@
use std::collections::HashMap;
use drm_sys::{DRM_MODE_CURSOR_BO, DRM_MODE_CURSOR_MOVE};
use syscall::{Error, ENOENT, ENXIO};
use crate::{DrmHandle, GraphicsAdapter, VtState};
pub(super) fn mode_cursor<T: GraphicsAdapter>(
adapter: &mut T,
vts: &mut HashMap<usize, VtState<T>>,
handle: &mut DrmHandle<T>,
data: redox_ioctl::drm::DrmModeCursor<'_>,
) -> Result<usize, Error> {
cursor_inner(
adapter,
vts,
handle,
data.flags(),
data.crtc_id(),
data.x(),
data.y(),
data.width(),
data.height(),
data.handle(),
0,
0,
)
}
pub(super) fn mode_cursor2<T: GraphicsAdapter>(
adapter: &mut T,
vts: &mut HashMap<usize, VtState<T>>,
handle: &mut DrmHandle<T>,
data: redox_ioctl::drm::DrmModeCursor2<'_>,
) -> Result<usize, Error> {
cursor_inner(
adapter,
vts,
handle,
data.flags(),
data.crtc_id(),
data.x(),
data.y(),
data.width(),
data.height(),
data.handle(),
data.hot_x(),
data.hot_y(),
)
}
fn cursor_inner<T: GraphicsAdapter>(
adapter: &mut T,
vts: &mut HashMap<usize, VtState<T>>,
handle: &mut DrmHandle<T>,
flags: u32,
_crtc_id: u32,
x: i32,
y: i32,
_width: u32,
_height: u32,
handle_id: u32,
hot_x: i32,
hot_y: i32,
) -> Result<usize, Error> {
let vt_state = vts.get_mut(&handle.vt).unwrap();
let Some(cursor_plane) = &mut vt_state.cursor_plane else {
return Err(Error::new(ENXIO));
};
let update_buffer = flags & DRM_MODE_CURSOR_BO != 0;
if update_buffer {
cursor_plane.buffer = if handle_id == 0 {
None
} else if let Some(buffer) = handle.buffers.get(&handle_id) {
Some(buffer.clone())
} else {
return Err(Error::new(ENOENT));
};
cursor_plane.hot_x = hot_x;
cursor_plane.hot_y = hot_y;
}
if flags & DRM_MODE_CURSOR_MOVE != 0 {
cursor_plane.x = x;
cursor_plane.y = y;
}
adapter.handle_cursor(cursor_plane, update_buffer);
Ok(0)
}
@@ -0,0 +1,196 @@
use std::collections::HashMap;
use drm_fourcc::DrmFourcc;
use syscall::{Error, EINVAL};
use crate::kms::objects::{KmsFramebuffer, KmsObjectId, KmsObjects};
use crate::{Damage, DrmHandle, GraphicsAdapter, VtState};
pub(super) fn mode_get_fb<T: GraphicsAdapter>(
objects: &mut KmsObjects<T>,
handle: &mut DrmHandle<T>,
mut data: redox_ioctl::drm::DrmModeFbCmd<'_>,
) -> Result<usize, Error> {
let fb = objects.get_framebuffer(KmsObjectId(data.fb_id()))?;
let (bpp, depth) = match fb.pixel_format {
DrmFourcc::Xrgb8888 => (32, 24),
DrmFourcc::Argb8888 => (32, 32),
_ => todo!(),
};
handle.next_id += 1;
handle.buffers.insert(handle.next_id, fb.buffer.clone());
data.set_width(fb.width);
data.set_height(fb.height);
data.set_pitch(fb.pitch);
data.set_bpp(bpp);
data.set_depth(depth);
data.set_handle(handle.next_id);
Ok(0)
}
pub(super) fn mode_add_fb<T: GraphicsAdapter>(
adapter: &mut T,
objects: &mut KmsObjects<T>,
handle: &mut DrmHandle<T>,
mut data: redox_ioctl::drm::DrmModeFbCmd<'_>,
) -> Result<usize, Error> {
let buffer = handle
.buffers
.get(&data.handle())
.ok_or(Error::new(EINVAL))?;
if data.bpp() != 32 {
return Err(Error::new(EINVAL));
}
let pixel_format = match data.depth() {
24 => DrmFourcc::Xrgb8888,
32 => DrmFourcc::Argb8888,
_ => return Err(Error::new(EINVAL)),
};
let fb = adapter.create_framebuffer(buffer);
let id = objects.add_framebuffer(KmsFramebuffer {
width: data.width(),
height: data.height(),
pixel_format,
pitch: data.pitch(),
buffer: buffer.clone(),
driver_data: fb,
});
data.set_fb_id(id.0);
Ok(0)
}
pub(super) fn mode_rm_fb<T: GraphicsAdapter>(
adapter: &mut T,
objects: &mut KmsObjects<T>,
active_vt: usize,
vts: &mut HashMap<usize, VtState<T>>,
data: redox_ioctl::drm::StandinForUint<'_>,
) -> Result<usize, Error> {
let fb_id = KmsObjectId(data.inner());
objects.remove_framebuffer(fb_id)?;
// Disable planes that use this framebuffer.
for (vt, vt_data) in vts {
for (plane_idx, plane_state) in vt_data.plane_state.iter_mut().enumerate() {
if plane_state.fb_id != Some(fb_id) {
continue;
}
plane_state.fb_id = None;
if *vt != active_vt {
continue;
}
let plane = objects.planes().nth(plane_idx).unwrap();
adapter
.set_plane(
&objects,
plane,
plane_state.clone(),
Damage {
x: 0,
y: 0,
width: 0,
height: 0,
},
)
.unwrap();
}
}
Ok(0)
}
pub(super) fn mode_dirtyfb<T: GraphicsAdapter>(
adapter: &mut T,
objects: &mut KmsObjects<T>,
active_vt: usize,
handle: &mut DrmHandle<T>,
data: redox_ioctl::drm::DrmModeFbDirtyCmd<'_>,
) -> Result<usize, Error> {
let fb = 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 handle.vt == active_vt {
for plane in objects.planes() {
let state = plane.lock().unwrap().state.clone();
if state.fb_id == Some(KmsObjectId(data.fb_id())) {
adapter.set_plane(&objects, plane, state, damage)?;
}
}
}
Ok(0)
}
pub(super) fn mode_add_fb2<T: GraphicsAdapter>(
adapter: &mut T,
objects: &mut KmsObjects<T>,
handle: &mut DrmHandle<T>,
mut data: redox_ioctl::drm::DrmModeFbCmd2<'_>,
) -> Result<usize, Error> {
// FIXME handle multi-plane framebuffers
let buffer = handle
.buffers
.get(&data.handles()[0])
.ok_or(Error::new(EINVAL))?;
let fb = adapter.create_framebuffer(buffer);
let id = objects.add_framebuffer(KmsFramebuffer {
width: data.width(),
height: data.height(),
pixel_format: DrmFourcc::try_from(data.pixel_format()).map_err(|_| Error::new(EINVAL))?,
pitch: data.pitches()[0],
buffer: buffer.clone(),
driver_data: fb,
});
data.set_fb_id(id.0);
Ok(0)
}
pub(super) fn mode_get_fb2<T: GraphicsAdapter>(
objects: &mut KmsObjects<T>,
handle: &mut DrmHandle<T>,
mut data: redox_ioctl::drm::DrmModeFbCmd2<'_>,
) -> Result<usize, Error> {
let fb = objects.get_framebuffer(KmsObjectId(data.fb_id()))?;
handle.next_id += 1;
handle.buffers.insert(handle.next_id, fb.buffer.clone());
data.set_width(fb.width);
data.set_height(fb.height);
data.set_pixel_format(fb.pixel_format as u32);
data.set_handles([handle.next_id, 0, 0, 0]);
data.set_pitches([fb.pitch, 0, 0, 0]);
data.set_offsets([0; 4]);
data.set_modifier([0; 4]);
Ok(0)
}
@@ -0,0 +1,370 @@
use std::collections::HashMap;
use std::ffi::c_char;
use std::mem;
use std::sync::Arc;
use drm_fourcc::DrmFourcc;
use syscall::{Error, EINVAL, ENOENT};
use crate::kms::objects::{KmsObjectId, KmsObjects, KmsRect};
use crate::{Buffer, Damage, DrmHandle, GraphicsAdapter, VtState, MAP_FAKE_OFFSET_MULTIPLIER};
mod cursor;
mod framebuffer;
mod property;
pub(crate) fn call_ioctl<T: GraphicsAdapter>(
adapter: &mut T,
objects: &mut KmsObjects<T>,
active_vt: usize,
vts: &mut HashMap<usize, VtState<T>>,
handle: &mut DrmHandle<T>,
cmd: u64,
payload: &mut [u8],
) -> syscall::Result<usize> {
use redox_ioctl::drm as ipc;
match cmd {
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(adapter.name()) });
data.set_date(unsafe { mem::transmute(&b"0"[..]) });
data.set_desc(unsafe { mem::transmute(adapter.desc()) });
Ok(0)
}),
ipc::GET_UNIQUE => ipc::DrmUnique::with(payload, |mut data| {
if let Some(unique) = &handle.unique {
data.set_unique(unsafe { mem::transmute::<&[u8], &[c_char]>(unique.as_bytes()) });
} else {
data.set_unique_len(0);
}
Ok(0)
}),
ipc::SET_VERSION => ipc::DrmSetVersion::with(payload, |mut data| {
// We only support version 1.4 currently
if data.drm_di_major() != 0 || data.drm_di_minor() != 4 {
return Err(Error::new(EINVAL));
}
if data.drm_dd_major() != 0 || data.drm_dd_minor() != 4 {
return Err(Error::new(EINVAL));
}
data.set_drm_di_major(1);
data.set_drm_di_minor(4);
data.set_drm_dd_major(1);
data.set_drm_dd_minor(4);
handle.unique = Some(adapter.get_unique());
Ok(0)
}),
ipc::GET_CAP => ipc::DrmGetCap::with(payload, |mut data| {
data.set_value(
adapter.get_cap(
data.capability()
.try_into()
.map_err(|_| Error::new(EINVAL))?,
)?,
);
Ok(0)
}),
ipc::SET_CLIENT_CAP => ipc::DrmSetClientCap::with(payload, |data| {
adapter.set_client_cap(
data.capability()
.try_into()
.map_err(|_| Error::new(EINVAL))?,
data.value(),
)?;
Ok(0)
}),
ipc::MODE_CARD_RES => ipc::DrmModeCardRes::with(payload, |mut data| {
let conn_ids = objects
.connector_ids()
.iter()
.map(|id| id.0)
.collect::<Vec<_>>();
let crtc_ids = objects.crtc_ids().iter().map(|id| id.0).collect::<Vec<_>>();
let enc_ids = objects
.encoder_ids()
.iter()
.map(|id| id.0)
.collect::<Vec<_>>();
let fb_ids = 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 = 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(
objects
.get_plane(crtc.primary_plane)
.unwrap()
.lock()
.unwrap()
.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_id = KmsObjectId(data.crtc_id());
let crtc = objects.get_crtc(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 primary_plane_id = crtc.lock().unwrap().primary_plane;
let plane = objects.get_plane(primary_plane_id)?;
let mut new_crtc_state = crtc.lock().unwrap().state.clone();
new_crtc_state.mode = mode;
let mut new_plane_state = plane.lock().unwrap().state.clone();
new_plane_state.fb_id = fb_id;
new_plane_state.crtc_id = Some(crtc_id);
if handle.vt == active_vt {
adapter.set_crtc(&objects, crtc, new_crtc_state.clone())?;
adapter.set_plane(
&objects,
plane,
new_plane_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 {
objects
.get_connector(connector)?
.lock()
.unwrap()
.state
.crtc_id = crtc_id
}
}
crtc.lock().unwrap().state = new_crtc_state.clone();
plane.lock().unwrap().state = new_plane_state.clone();
vts.get_mut(&handle.vt).unwrap().crtc_state[crtc.lock().unwrap().crtc_index as usize] =
new_crtc_state;
vts.get_mut(&handle.vt).unwrap().plane_state
[plane.lock().unwrap().plane_index as usize] = new_plane_state;
Ok(0)
}),
ipc::MODE_CURSOR => ipc::DrmModeCursor::with(payload, |data| {
cursor::mode_cursor(adapter, vts, handle, data)
}),
ipc::MODE_GET_ENCODER => ipc::DrmModeGetEncoder::with(payload, |mut data| {
let encoder = 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 {
adapter.probe_connector(objects, KmsObjectId(data.connector_id()));
}
let connector = 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) =
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, |data| {
property::mode_get_property(objects, data)
}),
ipc::MODE_GET_PROP_BLOB => {
ipc::DrmModeGetBlob::with(payload, |data| property::mode_get_prop_blob(objects, data))
}
ipc::MODE_GET_FB => ipc::DrmModeFbCmd::with(payload, |data| {
framebuffer::mode_get_fb(objects, handle, data)
}),
ipc::MODE_ADD_FB => ipc::DrmModeFbCmd::with(payload, |data| {
framebuffer::mode_add_fb(adapter, objects, handle, data)
}),
ipc::MODE_RM_FB => ipc::StandinForUint::with(payload, |data| {
framebuffer::mode_rm_fb(adapter, objects, active_vt, vts, data)
}),
ipc::MODE_DIRTYFB => ipc::DrmModeFbDirtyCmd::with(payload, |data| {
framebuffer::mode_dirtyfb(adapter, objects, active_vt, handle, data)
}),
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) = adapter.create_dumb_buffer(data.width(), data.height());
data.set_pitch(pitch);
data.set_size(buffer.size() as u64);
handle.next_id += 1;
handle.buffers.insert(handle.next_id, Arc::new(buffer));
data.set_handle(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 !handle.buffers.contains_key(&buffer_id) {
return Err(Error::new(ENOENT));
}
// FIXME use a better scheme for creating map offsets
assert!(handle.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 handle.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 ids = objects
.plane_ids()
.iter()
.map(|id| id.0)
.collect::<Vec<_>>();
data.set_plane_id_ptr(&ids);
Ok(0)
}),
ipc::MODE_SET_PLANE => ipc::DrmModeSetPlane::with(payload, |data| {
let plane_id = KmsObjectId(data.plane_id());
let plane = objects.get_plane(plane_id)?;
let crtc_id = KmsObjectId(data.crtc_id());
let crtc_index = objects.get_crtc(crtc_id)?.lock().unwrap().crtc_index;
let mut new_state = {
let plane = plane.lock().unwrap();
if plane.possible_crtcs & (1 << crtc_index) == 0 {
return Err(Error::new(EINVAL));
}
plane.state.clone()
};
let fb_id = if data.fb_id() != 0 {
KmsObjectId(data.fb_id())
} else {
KmsObjectId::INVALID
};
new_state.fb_id = Some(fb_id);
new_state.crtc_id = Some(crtc_id);
new_state.src_rect = KmsRect {
x: data.src_x(),
y: data.src_y(),
width: data.src_w(),
height: data.src_h(),
};
new_state.crtc_rect = KmsRect {
x: data.crtc_x() as i32,
y: data.crtc_y() as i32,
width: data.crtc_w(),
height: data.crtc_h(),
};
if handle.vt == active_vt {
adapter.set_plane(
&objects,
plane,
new_state.clone(),
Damage {
x: 0,
y: 0,
width: 0,
height: 0,
},
)?;
}
vts.get_mut(&handle.vt).unwrap().plane_state
[plane.lock().unwrap().plane_index as usize] = new_state;
Ok(0)
}),
ipc::MODE_GET_PLANE => ipc::DrmModeGetPlane::with(payload, |mut data| {
let plane = objects
.get_plane(KmsObjectId(data.plane_id()))
.unwrap()
.lock()
.unwrap();
data.set_crtc_id(plane.state.crtc_id.map_or(0, |id| id.0));
data.set_fb_id(plane.state.fb_id.unwrap_or(KmsObjectId::INVALID).0);
data.set_possible_crtcs(plane.possible_crtcs);
data.set_format_type_ptr(&[DrmFourcc::Argb8888 as u32]);
Ok(0)
}),
ipc::MODE_ADD_FB2 => ipc::DrmModeFbCmd2::with(payload, |data| {
framebuffer::mode_add_fb2(adapter, objects, handle, data)
}),
ipc::MODE_OBJ_GET_PROPERTIES => ipc::DrmModeObjGetProperties::with(payload, |data| {
property::mode_obj_get_properties(objects, data)
}),
ipc::MODE_CURSOR2 => ipc::DrmModeCursor2::with(payload, |data| {
cursor::mode_cursor2(adapter, vts, handle, data)
}),
ipc::MODE_GET_FB2 => ipc::DrmModeFbCmd2::with(payload, |data| {
framebuffer::mode_get_fb2(objects, handle, data)
}),
_ => return Err(Error::new(EINVAL)),
}
}
@@ -0,0 +1,99 @@
use drm_sys::{
drm_mode_property_enum, 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 syscall::Error;
use crate::kms::objects::{KmsObjectId, KmsObjects};
use crate::kms::properties::KmsPropertyKind;
use crate::GraphicsAdapter;
pub(super) fn mode_get_property<T: GraphicsAdapter>(
objects: &mut KmsObjects<T>,
mut data: redox_ioctl::drm::DrmModeGetProperty<'_>,
) -> Result<usize, Error> {
let property = 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)
}
pub(super) fn mode_get_prop_blob<T: GraphicsAdapter>(
objects: &mut KmsObjects<T>,
mut data: redox_ioctl::drm::DrmModeGetBlob<'_>,
) -> Result<usize, Error> {
let blob = objects.get_blob(KmsObjectId(data.blob_id()))?;
data.set_data(&blob);
Ok(0)
}
pub(super) fn mode_obj_get_properties<T: GraphicsAdapter>(
objects: &mut KmsObjects<T>,
mut data: redox_ioctl::drm::DrmModeObjGetProperties<'_>,
) -> Result<usize, Error> {
let (props, prop_vals) = 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(objects.object_type(KmsObjectId(data.obj_id()))?);
Ok(0)
}
+14 -639
View File
@@ -1,35 +1,28 @@
#![feature(macro_metavar_expr)]
use std::cmp;
use std::collections::HashMap;
use std::ffi::c_char;
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, ENXIO, EOPNOTSUPP};
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, KmsPlane, KmsPlaneDriver,
KmsPlaneState, KmsRect,
KmsCrtc, KmsCrtcDriver, KmsCrtcState, KmsObjectId, KmsObjects, KmsPlane, KmsPlaneDriver,
KmsPlaneState,
};
use crate::kms::properties::KmsPropertyKind;
mod ioctl;
pub mod kms;
#[derive(Debug, Copy, Clone)]
@@ -485,635 +478,17 @@ impl<T: GraphicsAdapter> SchemeSync for GraphicsSchemeInner<T> {
metadata: &[u64],
_ctx: &CallerCtx,
) -> Result<usize> {
use redox_ioctl::drm as ipc;
match self.handles.get_mut(id)? {
Handle::SchemeRoot => return Err(Error::new(EOPNOTSUPP)),
Handle::V2(handle) => 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_UNIQUE => ipc::DrmUnique::with(payload, |mut data| {
if let Some(unique) = &handle.unique {
data.set_unique(unsafe {
mem::transmute::<&[u8], &[c_char]>(unique.as_bytes())
});
} else {
data.set_unique_len(0);
}
Ok(0)
}),
ipc::SET_VERSION => ipc::DrmSetVersion::with(payload, |mut data| {
// We only support version 1.4 currently
if data.drm_di_major() != 0 || data.drm_di_minor() != 4 {
return Err(Error::new(EINVAL));
}
if data.drm_dd_major() != 0 || data.drm_dd_minor() != 4 {
return Err(Error::new(EINVAL));
}
data.set_drm_di_major(1);
data.set_drm_di_minor(4);
data.set_drm_dd_major(1);
data.set_drm_dd_minor(4);
handle.unique = Some(self.adapter.get_unique());
Ok(0)
}),
ipc::GET_CAP => ipc::DrmGetCap::with(payload, |mut data| {
data.set_value(
self.adapter.get_cap(
data.capability()
.try_into()
.map_err(|_| 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(|_| 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(
self.objects
.get_plane(crtc.primary_plane)
.unwrap()
.lock()
.unwrap()
.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_id = KmsObjectId(data.crtc_id());
let crtc = self.objects.get_crtc(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 primary_plane_id = crtc.lock().unwrap().primary_plane;
let plane = self.objects.get_plane(primary_plane_id)?;
let mut new_crtc_state = crtc.lock().unwrap().state.clone();
new_crtc_state.mode = mode;
let mut new_plane_state = plane.lock().unwrap().state.clone();
new_plane_state.fb_id = fb_id;
new_plane_state.crtc_id = Some(crtc_id);
if handle.vt == self.active_vt {
self.adapter
.set_crtc(&self.objects, crtc, new_crtc_state.clone())?;
self.adapter.set_plane(
&self.objects,
plane,
new_plane_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 = crtc_id
}
}
crtc.lock().unwrap().state = new_crtc_state.clone();
plane.lock().unwrap().state = new_plane_state.clone();
self.vts.get_mut(&handle.vt).unwrap().crtc_state
[crtc.lock().unwrap().crtc_index as usize] = new_crtc_state;
self.vts.get_mut(&handle.vt).unwrap().plane_state
[plane.lock().unwrap().plane_index as usize] = new_plane_state;
Ok(0)
}),
ipc::MODE_CURSOR => ipc::DrmModeCursor::with(payload, |data| {
let vt_state = self.vts.get_mut(&handle.vt).unwrap();
let Some(cursor_plane) = &mut vt_state.cursor_plane else {
return Err(Error::new(ENXIO));
};
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) = handle.buffers.get(&data.handle()) {
Some(buffer.clone())
} else {
return Err(Error::new(ENOENT));
};
}
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()))?;
let (bpp, depth) = match fb.pixel_format {
DrmFourcc::Xrgb8888 => (32, 24),
DrmFourcc::Argb8888 => (32, 32),
_ => todo!(),
};
handle.next_id += 1;
handle.buffers.insert(handle.next_id, fb.buffer.clone());
data.set_width(fb.width);
data.set_height(fb.height);
data.set_pitch(fb.pitch);
data.set_bpp(bpp);
data.set_depth(depth);
data.set_handle(handle.next_id);
Ok(0)
}),
ipc::MODE_ADD_FB => ipc::DrmModeFbCmd::with(payload, |mut data| {
let buffer = handle
.buffers
.get(&data.handle())
.ok_or(Error::new(EINVAL))?;
if data.bpp() != 32 {
return Err(Error::new(EINVAL));
}
let pixel_format = match data.depth() {
24 => DrmFourcc::Xrgb8888,
32 => DrmFourcc::Argb8888,
_ => return Err(Error::new(EINVAL)),
};
let fb = self.adapter.create_framebuffer(buffer);
let id = self.objects.add_framebuffer(objects::KmsFramebuffer {
width: data.width(),
height: data.height(),
pixel_format,
pitch: data.pitch(),
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 (plane_idx, plane_state) in vt_data.plane_state.iter_mut().enumerate() {
if plane_state.fb_id != Some(fb_id) {
continue;
}
plane_state.fb_id = None;
if *vt != self.active_vt {
continue;
}
let plane = self.objects.planes().nth(plane_idx).unwrap();
self.adapter
.set_plane(
&self.objects,
plane,
plane_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 handle.vt == self.active_vt {
for plane in self.objects.planes() {
let state = plane.lock().unwrap().state.clone();
if state.fb_id == Some(KmsObjectId(data.fb_id())) {
self.adapter
.set_plane(&self.objects, plane, 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);
handle.next_id += 1;
handle.buffers.insert(handle.next_id, Arc::new(buffer));
data.set_handle(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 !handle.buffers.contains_key(&buffer_id) {
return Err(Error::new(ENOENT));
}
// FIXME use a better scheme for creating map offsets
assert!(handle.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 handle.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 ids = self
.objects
.plane_ids()
.iter()
.map(|id| id.0)
.collect::<Vec<_>>();
data.set_plane_id_ptr(&ids);
Ok(0)
}),
ipc::MODE_SET_PLANE => ipc::DrmModeSetPlane::with(payload, |data| {
let plane_id = KmsObjectId(data.plane_id());
let plane = self.objects.get_plane(plane_id)?;
let crtc_id = KmsObjectId(data.crtc_id());
let crtc_index = self.objects.get_crtc(crtc_id)?.lock().unwrap().crtc_index;
let mut new_state = {
let plane = plane.lock().unwrap();
if plane.possible_crtcs & (1 << crtc_index) == 0 {
return Err(Error::new(EINVAL));
}
plane.state.clone()
};
let fb_id = if data.fb_id() != 0 {
KmsObjectId(data.fb_id())
} else {
KmsObjectId::INVALID
};
new_state.fb_id = Some(fb_id);
new_state.crtc_id = Some(crtc_id);
new_state.src_rect = KmsRect {
x: data.src_x(),
y: data.src_y(),
width: data.src_w(),
height: data.src_h(),
};
new_state.crtc_rect = KmsRect {
x: data.crtc_x() as i32,
y: data.crtc_y() as i32,
width: data.crtc_w(),
height: data.crtc_h(),
};
if handle.vt == self.active_vt {
self.adapter.set_plane(
&self.objects,
plane,
new_state.clone(),
Damage {
x: 0,
y: 0,
width: 0,
height: 0,
},
)?;
}
self.vts.get_mut(&handle.vt).unwrap().plane_state
[plane.lock().unwrap().plane_index as usize] = new_state;
Ok(0)
}),
ipc::MODE_GET_PLANE => ipc::DrmModeGetPlane::with(payload, |mut data| {
let plane = self
.objects
.get_plane(KmsObjectId(data.plane_id()))
.unwrap()
.lock()
.unwrap();
data.set_crtc_id(plane.state.crtc_id.map_or(0, |id| id.0));
data.set_fb_id(plane.state.fb_id.unwrap_or(KmsObjectId::INVALID).0);
data.set_possible_crtcs(plane.possible_crtcs);
data.set_format_type_ptr(&[DrmFourcc::Argb8888 as u32]);
Ok(0)
}),
ipc::MODE_ADD_FB2 => ipc::DrmModeFbCmd2::with(payload, |mut data| {
// FIXME handle multi-plane framebuffers
let buffer = handle
.buffers
.get(&data.handles()[0])
.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(),
pixel_format: DrmFourcc::try_from(data.pixel_format())
.map_err(|_| Error::new(EINVAL))?,
pitch: data.pitches()[0],
buffer: buffer.clone(),
driver_data: fb,
});
data.set_fb_id(id.0);
Ok(0)
}),
ipc::MODE_OBJ_GET_PROPERTIES => {
ipc::DrmModeObjGetProperties::with(payload, |mut data| {
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(&handle.vt).unwrap();
let Some(cursor_plane) = &mut vt_state.cursor_plane else {
return Err(Error::new(ENXIO));
};
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) = handle.buffers.get(&data.handle()) {
Some(buffer.clone())
} else {
return Err(Error::new(ENOENT));
};
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()))?;
handle.next_id += 1;
handle.buffers.insert(handle.next_id, fb.buffer.clone());
data.set_width(fb.width);
data.set_height(fb.height);
data.set_pixel_format(fb.pixel_format as u32);
data.set_handles([handle.next_id, 0, 0, 0]);
data.set_pitches([fb.pitch, 0, 0, 0]);
data.set_offsets([0; 4]);
data.set_modifier([0; 4]);
Ok(0)
}),
_ => return Err(Error::new(EINVAL)),
},
Handle::V2(handle) => ioctl::call_ioctl(
&mut self.adapter,
&mut self.objects,
self.active_vt,
&mut self.vts,
handle,
metadata[0],
payload,
),
}
}