diff --git a/graphics/console-draw/src/lib.rs b/graphics/console-draw/src/lib.rs index 2cf93db76c..b1551d9c79 100644 --- a/graphics/console-draw/src/lib.rs +++ b/graphics/console-draw/src/lib.rs @@ -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); + } + } } diff --git a/graphics/driver-graphics/src/lib.rs b/graphics/driver-graphics/src/lib.rs index 94860f3103..e9971323fb 100644 --- a/graphics/driver-graphics/src/lib.rs +++ b/graphics/driver-graphics/src/lib.rs @@ -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 { scheme_name: String, socket: Socket, next_id: usize, - handles: BTreeMap, + handles: BTreeMap>, active_vt: usize, vts: HashMap>, @@ -63,8 +66,16 @@ struct VtState { cursor_plane: Option>, } -enum Handle { - V1Screen { vt: usize, screen: usize }, +enum Handle { + V1Screen { + vt: usize, + screen: usize, + }, + V2 { + vt: usize, + next_id: usize, + fbs: HashMap>, + }, } impl GraphicsScheme { @@ -100,6 +111,8 @@ impl GraphicsScheme { 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 GraphicsScheme { 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 GraphicsScheme { } } + 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 SchemeSync for GraphicsScheme { 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::() + .map_err(|_| Error::new(EINVAL))?; - let vt = screen.next().unwrap_or("").parse::().unwrap(); - let id = screen.next().unwrap_or("").parse::().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::().unwrap(); + let id = screen.next().unwrap_or("").parse::().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 SchemeSync for GraphicsScheme { 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 SchemeSync for GraphicsScheme { ); Ok(()) } + Handle::V2 { .. } => Err(Error::new(EOPNOTSUPP)), } } @@ -276,6 +315,7 @@ impl SchemeSync for GraphicsScheme { Ok(1) } + Handle::V2 { .. } => Err(Error::new(EOPNOTSUPP)), } } @@ -354,6 +394,122 @@ impl SchemeSync for GraphicsScheme { Ok(buf.len()) } + Handle::V2 { .. } => Err(Error::new(EOPNOTSUPP)), + } + } + + fn call(&mut self, id: usize, payload: &mut [u8], metadata: &[u64]) -> Result { + 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::() { + return Err(Error::new(EINVAL)); + } + let payload = unsafe { + transmute::<&mut [u8; size_of::()], &mut ipc::DisplayCount>( + payload.as_mut_array().unwrap(), + ) + }; + payload.count = self.adapter.display_count(); + Ok(size_of::()) + } + ipc::DISPLAY_SIZE => { + if payload.len() < size_of::() { + return Err(Error::new(EINVAL)); + } + let payload = unsafe { + transmute::<&mut [u8; size_of::()], &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::CREATE_DUMB_FRAMEBUFFER => { + if payload.len() < size_of::() { + return Err(Error::new(EINVAL)); + } + let payload = unsafe { + transmute::< + &mut [u8; size_of::()], + &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::DUMB_FRAMEBUFFER_MAP_OFFSET => { + if payload.len() < size_of::() { + return Err(Error::new(EINVAL)); + } + let payload = unsafe { + transmute::< + &mut [u8; size_of::()], + &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::UPDATE_PLANE => { + if payload.len() < size_of::() { + return Err(Error::new(EINVAL)); + } + let payload = unsafe { + transmute::<&mut [u8; size_of::()], &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::()) + } + _ => return Err(Error::new(EINVAL)), + }, } } @@ -368,6 +524,16 @@ impl SchemeSync for GraphicsScheme { // 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) diff --git a/graphics/fbbootlogd/src/scheme.rs b/graphics/fbbootlogd/src/scheme.rs index 17166c43af..647999fb3b 100644 --- a/graphics/fbbootlogd/src/scheme.rs +++ b/graphics/fbbootlogd/src/scheme.rs @@ -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 { 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(); } } diff --git a/graphics/graphics-ipc/src/lib.rs b/graphics/graphics-ipc/src/lib.rs index 99b52a835b..ed4b73a675 100644 --- a/graphics/graphics-ipc/src/lib.rs +++ b/graphics/graphics-ipc/src/lib.rs @@ -1,2 +1,3 @@ mod common; pub mod v1; +pub mod v2; diff --git a/graphics/graphics-ipc/src/v2.rs b/graphics/graphics-ipc/src/v2.rs new file mode 100644 index 0000000000..fa316d96b0 --- /dev/null +++ b/graphics/graphics-ipc/src/v2.rs @@ -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( + fd: &impl AsRawFd, + payload: &mut T, + flags: usize, + metadata: &[u64], +) -> libredox::error::Result { + libredox::error::Error::demux(redox_sys_call_v0( + fd.as_raw_fd() as usize, + payload as *mut T as *mut u8, + mem::size_of::(), + 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 { + Ok(V2GraphicsHandle { file }) + } + + pub fn display_count(&self) -> io::Result { + 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 { + 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 { + 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, + } +} diff --git a/graphics/virtio-gpud/src/main.rs b/graphics/virtio-gpud/src/main.rs index 7f95d9d572..4b9654fe17 100644 --- a/graphics/virtio-gpud/src/main.rs +++ b/graphics/virtio-gpud/src/main.rs @@ -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; + } + }, } } diff --git a/graphics/virtio-gpud/src/scheme.rs b/graphics/virtio-gpud/src/scheme.rs index b7707ab5f1..c6656c5398 100644 --- a/graphics/virtio-gpud/src/scheme.rs +++ b/graphics/virtio-gpud/src/scheme.rs @@ -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(&self, request: Dma) -> Result, 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>, cursor_queue: Arc>, transport: Arc, @@ -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(); diff --git a/inputd/src/lib.rs b/inputd/src/lib.rs index 0a2559beb9..e7e7e47fa7 100644 --- a/inputd/src/lib.rs +++ b/inputd/src/lib.rs @@ -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 { + 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> { match read_to_slice(self.0.as_fd(), events) { Ok(count) => Ok(ConsumerHandleEvent::Events(&events[..count])), diff --git a/virtio-core/src/transport.rs b/virtio-core/src/transport.rs index b2782bdc5c..5f3ecaa91f 100644 --- a/virtio-core/src/transport.rs +++ b/virtio-core/src/transport.rs @@ -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, Error> { let mut common = self.common.lock().unwrap();