Merge branch 'virtio_gpu_resize' into 'master'

graphics: Handle virtio-gpu display resizing in fbbootlogd

See merge request redox-os/drivers!273
This commit is contained in:
Jeremy Soller
2025-07-02 09:33:48 -06:00
9 changed files with 568 additions and 53 deletions
+33
View File
@@ -226,4 +226,37 @@ impl TextScreen {
damage
}
pub fn resize(&mut self, old_map: &mut DisplayMap, new_map: &mut DisplayMap) {
// FIXME fold row when target is narrower and maybe unfold when it is wider
fn copy_row(
old_map: &mut DisplayMap,
new_map: &mut DisplayMap,
from_row: usize,
to_row: usize,
) {
for x in 0..cmp::min(old_map.width, new_map.width) {
let old_idx = from_row * old_map.width + x;
let new_idx = to_row * new_map.width + x;
unsafe {
(*new_map.offscreen)[new_idx] = (*old_map.offscreen)[old_idx];
}
}
}
if new_map.height >= old_map.height {
for row in 0..old_map.height {
copy_row(old_map, new_map, row, row);
}
} else {
let deleted_rows = (old_map.height - new_map.height).div_ceil(16);
for row in 0..new_map.height {
if row + (deleted_rows + 1) * 16 >= old_map.height {
break;
}
copy_row(old_map, new_map, row + deleted_rows * 16, row);
}
self.console.state.y = self.console.state.y.saturating_sub(deleted_rows);
}
}
}
+183 -17
View File
@@ -1,5 +1,8 @@
#![feature(slice_as_array)]
use std::collections::{BTreeMap, HashMap};
use std::io;
use std::mem::transmute;
use std::sync::Arc;
use graphics_ipc::v1::{CursorDamage, Damage};
@@ -8,7 +11,7 @@ use libredox::Fd;
use redox_scheme::scheme::SchemeSync;
use redox_scheme::{CallerCtx, OpenResult, RequestKind, SignalBehavior, Socket};
use syscall::schemev2::NewFdFlags;
use syscall::{Error, MapFlags, Result, EAGAIN, EBADF, EINVAL};
use syscall::{Error, MapFlags, Result, EAGAIN, EBADF, EINVAL, ENOENT, EOPNOTSUPP};
pub trait GraphicsAdapter {
type Framebuffer: Framebuffer;
@@ -52,7 +55,7 @@ pub struct GraphicsScheme<T: GraphicsAdapter> {
scheme_name: String,
socket: Socket,
next_id: usize,
handles: BTreeMap<usize, Handle>,
handles: BTreeMap<usize, Handle<T>>,
active_vt: usize,
vts: HashMap<usize, VtState<T>>,
@@ -63,8 +66,16 @@ struct VtState<T: GraphicsAdapter> {
cursor_plane: Option<CursorPlane<T::Cursor>>,
}
enum Handle {
V1Screen { vt: usize, screen: usize },
enum Handle<T: GraphicsAdapter> {
V1Screen {
vt: usize,
screen: usize,
},
V2 {
vt: usize,
next_id: usize,
fbs: HashMap<usize, Arc<T::Framebuffer>>,
},
}
impl<T: GraphicsAdapter> GraphicsScheme<T> {
@@ -100,6 +111,8 @@ impl<T: GraphicsAdapter> GraphicsScheme<T> {
VtEventKind::Activate => {
log::info!("activate {}", vt_event.vt);
self.active_vt = vt_event.vt;
let vt_state =
Self::get_or_create_vt(&mut self.adapter, &mut self.vts, vt_event.vt);
@@ -110,8 +123,6 @@ impl<T: GraphicsAdapter> GraphicsScheme<T> {
if let Some(cursor_plane) = &vt_state.cursor_plane {
self.adapter.handle_cursor(cursor_plane, true);
}
self.active_vt = vt_event.vt;
}
VtEventKind::Resize => {
@@ -120,6 +131,10 @@ impl<T: GraphicsAdapter> GraphicsScheme<T> {
}
}
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
@@ -200,22 +215,40 @@ impl<T: GraphicsAdapter> SchemeSync for GraphicsScheme<T> {
return Err(Error::new(EINVAL));
}
let mut parts = path.split('/');
let mut screen = parts.next().unwrap_or("").split('.');
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))?;
let vt = screen.next().unwrap_or("").parse::<usize>().unwrap();
let id = screen.next().unwrap_or("").parse::<usize>().unwrap_or(0);
// Ensure the VT exists such that the rest of the methods can freely access it.
Self::get_or_create_vt(&mut self.adapter, &mut self.vts, vt);
if id >= self.adapter.display_count() {
return Err(Error::new(EINVAL));
}
Handle::V2 {
vt,
next_id: 0,
fbs: HashMap::new(),
}
} else {
let mut parts = path.split('/');
let mut screen = parts.next().unwrap_or("").split('.');
// Ensure the VT exists such that the rest of the methods can freely access it.
Self::get_or_create_vt(&mut self.adapter, &mut self.vts, vt);
let vt = screen.next().unwrap_or("").parse::<usize>().unwrap();
let id = screen.next().unwrap_or("").parse::<usize>().unwrap_or(0);
if id >= self.adapter.display_count() {
return Err(Error::new(EINVAL));
}
// Ensure the VT exists such that the rest of the methods can freely access it.
Self::get_or_create_vt(&mut self.adapter, &mut self.vts, vt);
Handle::V1Screen { vt, screen: id }
};
self.next_id += 1;
self.handles
.insert(self.next_id, Handle::V1Screen { vt, screen: id });
self.handles.insert(self.next_id, handle);
Ok(OpenResult::ThisScheme {
number: self.next_id,
flags: NewFdFlags::empty(),
@@ -233,6 +266,11 @@ impl<T: GraphicsAdapter> SchemeSync for GraphicsScheme<T> {
framebuffer.height()
)
}
Handle::V2 {
vt,
next_id: _,
fbs: _,
} => format!("/scheme/{}/v2/{vt}", self.scheme_name),
};
buf[..path.len()].copy_from_slice(path.as_bytes());
Ok(path.len())
@@ -253,6 +291,7 @@ impl<T: GraphicsAdapter> SchemeSync for GraphicsScheme<T> {
);
Ok(())
}
Handle::V2 { .. } => Err(Error::new(EOPNOTSUPP)),
}
}
@@ -276,6 +315,7 @@ impl<T: GraphicsAdapter> SchemeSync for GraphicsScheme<T> {
Ok(1)
}
Handle::V2 { .. } => Err(Error::new(EOPNOTSUPP)),
}
}
@@ -354,6 +394,122 @@ impl<T: GraphicsAdapter> SchemeSync for GraphicsScheme<T> {
Ok(buf.len())
}
Handle::V2 { .. } => Err(Error::new(EOPNOTSUPP)),
}
}
fn call(&mut self, id: usize, payload: &mut [u8], metadata: &[u64]) -> Result<usize> {
use graphics_ipc::v2::ipc;
match self.handles.get_mut(&id).ok_or(Error::new(EBADF))? {
Handle::V1Screen { .. } => {
return Err(Error::new(EOPNOTSUPP));
}
Handle::V2 { vt, next_id, fbs } => match metadata[0] {
ipc::DISPLAY_COUNT => {
if payload.len() < size_of::<ipc::DisplayCount>() {
return Err(Error::new(EINVAL));
}
let payload = unsafe {
transmute::<&mut [u8; size_of::<ipc::DisplayCount>()], &mut ipc::DisplayCount>(
payload.as_mut_array().unwrap(),
)
};
payload.count = self.adapter.display_count();
Ok(size_of::<ipc::DisplayCount>())
}
ipc::DISPLAY_SIZE => {
if payload.len() < size_of::<ipc::DisplaySize>() {
return Err(Error::new(EINVAL));
}
let payload = unsafe {
transmute::<&mut [u8; size_of::<ipc::DisplaySize>()], &mut ipc::DisplaySize>(
payload.as_mut_array().unwrap(),
)
};
let display_id = payload.display_id;
if display_id >= self.adapter.display_count() {
return Err(Error::new(EINVAL));
}
let (width, height) = self.adapter.display_size(display_id);
payload.width = width;
payload.height = height;
Ok(size_of::<ipc::DisplaySize>())
}
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 < 0x1_000_000);
payload.offset = fb_id * 0x10_000_000;
Ok(size_of::<ipc::DumbFramebufferMapOffset>())
}
ipc::UPDATE_PLANE => {
if payload.len() < size_of::<ipc::UpdatePlane>() {
return Err(Error::new(EINVAL));
}
let payload = unsafe {
transmute::<&mut [u8; size_of::<ipc::UpdatePlane>()], &mut ipc::UpdatePlane>(
payload.as_mut_array().unwrap(),
)
};
let display_id = payload.display_id;
if display_id >= self.adapter.display_count() {
return Err(Error::new(EINVAL));
}
let Some(framebuffer) = fbs.get(&{ payload.fb_id }) else {
return Err(Error::new(EINVAL));
};
self.vts.get_mut(vt).unwrap().display_fbs[display_id] = framebuffer.clone();
if *vt == self.active_vt {
self.adapter
.update_plane(display_id, framebuffer, payload.damage);
}
Ok(size_of::<ipc::UpdatePlane>())
}
_ => return Err(Error::new(EINVAL)),
},
}
}
@@ -368,6 +524,16 @@ impl<T: GraphicsAdapter> SchemeSync for GraphicsScheme<T> {
// log::trace!("KSMSG MMAP {} {:?} {} {}", id, _flags, _offset, _size);
let (framebuffer, offset) = match self.handles.get(&id).ok_or(Error::new(EINVAL))? {
Handle::V1Screen { vt, screen } => (&self.vts[vt].display_fbs[*screen], offset),
Handle::V2 {
vt: _,
next_id: _,
fbs,
} => (
fbs.get(&(offset as usize / 0x10_000_000))
.ok_or(Error::new(EINVAL))
.unwrap(),
offset & (0x10_000_000 - 1),
),
};
let ptr = T::map_dumb_framebuffer(&mut self.adapter, framebuffer);
Ok(unsafe { ptr.add(offset as usize) } as usize)
+64 -6
View File
@@ -1,12 +1,15 @@
use std::collections::VecDeque;
use std::ptr;
use graphics_ipc::v1::V1GraphicsHandle;
use console_draw::TextScreen;
use graphics_ipc::v2::V2GraphicsHandle;
use inputd::ConsumerHandle;
use redox_scheme::Scheme;
use syscall::{Error, Result, EINVAL, ENOENT};
pub struct DisplayMap {
display_handle: V1GraphicsHandle,
display_handle: V2GraphicsHandle,
fb: usize,
inner: graphics_ipc::v1::DisplayMap,
}
@@ -30,18 +33,24 @@ impl FbbootlogScheme {
}
pub fn handle_handoff(&mut self) {
let new_display_handle = match self.input_handle.open_display() {
Ok(display) => V1GraphicsHandle::from_file(display).unwrap(),
let new_display_handle = match self.input_handle.open_display_v2() {
Ok(display) => V2GraphicsHandle::from_file(display).unwrap(),
Err(err) => {
eprintln!("fbbootlogd: No display present yet: {err}");
return;
}
};
match new_display_handle.map_display() {
let (width, height) = new_display_handle.display_size(0).unwrap();
let 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,
});
@@ -52,6 +61,53 @@ impl FbbootlogScheme {
}
}
}
fn handle_resize(map: &mut DisplayMap, text_screen: &mut TextScreen) {
let (width, height) = match map.display_handle.display_size(0) {
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)
}
};
if width as usize != map.inner.width() || height as usize != map.inner.height() {
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);
}
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(),
},
);
map.fb = fb;
map.inner = new_map;
eprintln!("fbbootlogd: mapped display");
}
Err(err) => {
eprintln!("fbbootlogd: failed to open display: {}", err);
}
},
Err(err) => {
eprintln!("fbbootlogd: failed to create framebuffer: {}", err);
}
}
}
}
}
impl Scheme for FbbootlogScheme {
@@ -91,6 +147,8 @@ impl Scheme for FbbootlogScheme {
fn write(&mut self, _id: usize, buf: &[u8], _offset: u64, _fcntl_flags: u32) -> Result<usize> {
if let Some(map) = &mut self.display_map {
Self::handle_resize(map, &mut self.text_screen);
let damage = self.text_screen.write(
&mut console_draw::DisplayMap {
offscreen: map.inner.ptr_mut(),
@@ -102,7 +160,7 @@ impl Scheme for FbbootlogScheme {
);
if let Some(map) = &self.display_map {
map.display_handle.sync_rect(damage).unwrap();
map.display_handle.update_plane(0, map.fb, damage).unwrap();
}
}
+1
View File
@@ -1,2 +1,3 @@
mod common;
pub mod v1;
pub mod v2;
+179
View File
@@ -0,0 +1,179 @@
use std::fs::File;
use std::os::unix::io::AsRawFd;
use std::{io, mem, ptr};
use libredox::flag;
pub use crate::common::{Damage, DisplayMap};
extern "C" {
fn redox_sys_call_v0(
fd: usize,
payload: *mut u8,
payload_len: usize,
flags: usize,
metadata: *const u64,
metadata_len: usize,
) -> usize;
}
unsafe fn sys_call<T>(
fd: &impl AsRawFd,
payload: &mut T,
flags: usize,
metadata: &[u64],
) -> libredox::error::Result<usize> {
libredox::error::Error::demux(redox_sys_call_v0(
fd.as_raw_fd() as usize,
payload as *mut T as *mut u8,
mem::size_of::<T>(),
flags,
metadata.as_ptr(),
metadata.len(),
))
}
/// A graphics handle using the (currently unstable) v2 graphics API.
///
/// The v2 graphics API allows creating framebuffers on the fly, using them for page flipping and
/// handles all displays using a single fd.
pub struct V2GraphicsHandle {
file: File,
}
impl V2GraphicsHandle {
pub fn from_file(file: File) -> io::Result<Self> {
Ok(V2GraphicsHandle { file })
}
pub fn display_count(&self) -> io::Result<usize> {
let mut cmd = ipc::DisplayCount { count: 0 };
unsafe {
sys_call(&self.file, &mut cmd, 0, &[ipc::DISPLAY_COUNT, 0, 0])?;
}
Ok(cmd.count)
}
pub fn display_size(&self, id: usize) -> io::Result<(u32, u32)> {
let mut cmd = ipc::DisplaySize {
display_id: id,
width: 0,
height: 0,
};
unsafe {
sys_call(&self.file, &mut cmd, 0, &[ipc::DISPLAY_SIZE, 0, 0])?;
}
Ok((cmd.width, cmd.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 map_dumb_framebuffer(
&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) })
}
pub fn update_plane(&self, display_id: usize, fb_id: usize, damage: Damage) -> io::Result<()> {
let mut cmd = ipc::UpdatePlane {
display_id,
fb_id,
damage,
};
unsafe {
sys_call(&self.file, &mut cmd, 0, &[ipc::UPDATE_PLANE, 0, 0])?;
}
Ok(())
}
}
pub mod ipc {
use crate::common::Damage;
pub const DISPLAY_COUNT: u64 = 1;
#[repr(C, packed)]
pub struct DisplayCount {
pub count: usize,
}
pub const DISPLAY_SIZE: u64 = 2;
#[repr(C, packed)]
pub struct DisplaySize {
pub display_id: usize,
pub width: u32,
pub height: u32,
}
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 UPDATE_PLANE: u64 = 5;
#[repr(C, packed)]
pub struct UpdatePlane {
pub display_id: usize,
pub fb_id: usize,
pub damage: Damage,
}
}
+29 -2
View File
@@ -31,7 +31,7 @@ use virtio_core::MSIX_PRIMARY_VECTOR;
mod scheme;
// const VIRTIO_GPU_EVENT_DISPLAY: u32 = 1 << 0;
const VIRTIO_GPU_EVENT_DISPLAY: u32 = 1 << 0;
const VIRTIO_GPU_MAX_SCANOUTS: usize = 16;
#[repr(C)]
@@ -457,6 +457,8 @@ fn deamon(deamon: redox_daemon::Daemon) -> anyhow::Result<()> {
.transport
.setup_queue(MSIX_PRIMARY_VECTOR, &device.irq_handle)?;
device.transport.setup_config_notify(MSIX_PRIMARY_VECTOR);
device.transport.run_device();
deamon.ready().unwrap();
@@ -471,6 +473,7 @@ fn deamon(deamon: redox_daemon::Daemon) -> anyhow::Result<()> {
enum Source {
Input,
Scheme,
Interrupt,
}
}
@@ -490,8 +493,15 @@ fn deamon(deamon: redox_daemon::Daemon) -> anyhow::Result<()> {
event::EventFlags::READ,
)
.unwrap();
event_queue
.subscribe(
device.irq_handle.as_raw_fd() as usize,
Source::Interrupt,
event::EventFlags::READ,
)
.unwrap();
let all = [Source::Input, Source::Scheme];
let all = [Source::Input, Source::Scheme, Source::Interrupt];
for event in all
.into_iter()
.chain(event_queue.map(|e| e.expect("virtio-gpud: failed to get next event").user_data))
@@ -510,6 +520,23 @@ fn deamon(deamon: redox_daemon::Daemon) -> anyhow::Result<()> {
.tick()
.expect("virtio-gpud: failed to process scheme events");
}
Source::Interrupt => loop {
let before_gen = device.transport.config_generation();
let events = config.events_read.get();
if events & VIRTIO_GPU_EVENT_DISPLAY != 0 {
futures::executor::block_on(scheme.adapter_mut().update_displays(config))
.unwrap();
scheme.notify_displays_changed();
config.events_clear.set(VIRTIO_GPU_EVENT_DISPLAY);
}
let after_gen = device.transport.config_generation();
if before_gen == after_gen {
break;
}
},
}
}
+36 -28
View File
@@ -64,6 +64,40 @@ pub struct VirtGpuAdapter<'a> {
}
impl VirtGpuAdapter<'_> {
pub async fn update_displays(&mut self, config: &mut GpuConfig) -> Result<(), Error> {
let mut display_info = self.get_display_info().await?;
let raw_displays = &mut display_info.display_info[..config.num_scanouts() as usize];
self.displays.resize(
raw_displays.len(),
Display {
width: 0,
height: 0,
active_resource: None,
},
);
for (i, info) in raw_displays.iter().enumerate() {
log::info!(
"virtio-gpu: display {i} ({}x{}px)",
info.rect.width,
info.rect.height
);
if info.rect.width == 0 || info.rect.height == 0 {
// QEMU gives all displays other than the first a zero width and height, but trying
// to attach a zero sized framebuffer to the display will result an error, so
// default to 640x480px.
self.displays[i].width = 640;
self.displays[i].height = 480;
} else {
self.displays[i].width = info.rect.width;
self.displays[i].height = info.rect.height;
}
}
Ok(())
}
async fn send_request<T>(&self, request: Dma<T>) -> Result<Dma<ControlHeader>, Error> {
let header = Dma::new(ControlHeader::default())?;
let command = ChainBuilder::new()
@@ -350,7 +384,7 @@ pub struct GpuScheme {}
impl<'a> GpuScheme {
pub async fn new(
config: &'a mut GpuConfig,
config: &mut GpuConfig,
control_queue: Arc<Queue<'a>>,
cursor_queue: Arc<Queue<'a>>,
transport: Arc<dyn Transport>,
@@ -362,33 +396,7 @@ impl<'a> GpuScheme {
displays: vec![],
};
let mut display_info = adapter.get_display_info().await?;
let raw_displays = &mut display_info.display_info[..config.num_scanouts() as usize];
for info in raw_displays.iter() {
log::info!(
"virtio-gpu: opening display ({}x{}px)",
info.rect.width,
info.rect.height
);
if info.rect.width == 0 || info.rect.height == 0 {
// QEMU gives all displays other than the first a zero width and height, but trying
// to attach a zero sized framebuffer to the display will result an error, so
// default to 640x480px.
adapter.displays.push(Display {
width: 640,
height: 480,
active_resource: None,
});
} else {
adapter.displays.push(Display {
width: info.rect.width,
height: info.rect.height,
active_resource: None,
});
}
}
adapter.update_displays(config).await?;
let scheme = GraphicsScheme::new(adapter, "display.virtio-gpu".to_owned());
let handle = DisplayHandle::new("virtio-gpu").unwrap();
+29
View File
@@ -3,6 +3,7 @@ use std::io::{self, Read, Write};
use std::mem::size_of;
use std::os::fd::{AsFd, AsRawFd, BorrowedFd, FromRawFd, RawFd};
use std::os::unix::fs::OpenOptionsExt;
use std::path::PathBuf;
use std::slice;
use libredox::flag::{O_CLOEXEC, O_NONBLOCK, O_RDWR};
@@ -71,6 +72,34 @@ impl ConsumerHandle {
Ok(display_file)
}
pub fn open_display_v2(&self) -> io::Result<File> {
let mut buffer = [0; 1024];
let fd = self.0.as_raw_fd();
let written = libredox::call::fpath(fd as usize, &mut buffer)?;
assert!(written <= buffer.len());
let mut display_path = PathBuf::from(
std::str::from_utf8(&buffer[..written])
.expect("init: display path UTF-8 check failed")
.to_owned(),
);
display_path.set_file_name(format!(
"v2/{}",
display_path.file_name().unwrap().to_str().unwrap()
));
let display_path = display_path.to_str().unwrap();
let display_file =
libredox::call::open(&display_path, (O_CLOEXEC | O_NONBLOCK | O_RDWR) as _, 0)
.map(|socket| unsafe { File::from_raw_fd(socket as RawFd) })
.unwrap_or_else(|err| {
panic!("failed to open display {}: {}", display_path, err);
});
Ok(display_file)
}
pub fn read_events<'a>(&self, events: &'a mut [Event]) -> io::Result<ConsumerHandleEvent<'a>> {
match read_to_slice(self.0.as_fd(), events) {
Ok(count) => Ok(ConsumerHandleEvent::Events(&events[..count])),
+14
View File
@@ -492,6 +492,12 @@ pub trait Transport: Sync + Send {
self.insert_status(DeviceStatusFlags::DRIVER_OK);
}
/// Request to be notified on configuration changes on the given MSI-X vector.
fn setup_config_notify(&self, vector: u16);
/// Each time the device configuration changes this number will be updated.
fn config_generation(&self) -> u32;
/// Creates a new queue.
///
/// ## Panics
@@ -601,6 +607,14 @@ impl Transport for StandardTransport<'_> {
assert!((confirm & DeviceStatusFlags::FEATURES_OK) == DeviceStatusFlags::FEATURES_OK);
}
fn setup_config_notify(&self, vector: u16) {
self.common.lock().unwrap().config_msix_vector.set(vector);
}
fn config_generation(&self) -> u32 {
u32::from(self.common.lock().unwrap().config_generation.get())
}
fn setup_queue(&self, vector: u16, irq_handle: &File) -> Result<Arc<Queue>, Error> {
let mut common = self.common.lock().unwrap();