Generated
+4
@@ -680,6 +680,7 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"log",
|
||||
"orbclient",
|
||||
"redox-daemon",
|
||||
"redox-log",
|
||||
"redox_syscall 0.3.5",
|
||||
@@ -1921,6 +1922,7 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
||||
name = "vesad"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"inputd",
|
||||
"orbclient",
|
||||
"ransid",
|
||||
"redox-daemon",
|
||||
@@ -1968,12 +1970,14 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"futures",
|
||||
"inputd",
|
||||
"log",
|
||||
"orbclient",
|
||||
"paste",
|
||||
"pcid",
|
||||
"redox-daemon",
|
||||
"redox_syscall 0.3.5",
|
||||
"spin",
|
||||
"static_assertions",
|
||||
"virtio-core",
|
||||
]
|
||||
|
||||
@@ -10,3 +10,4 @@ log = "0.4.19"
|
||||
redox-daemon = "0.1.0"
|
||||
redox-log = "0.1.1"
|
||||
redox_syscall = "0.3.5"
|
||||
orbclient = "0.3.27"
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
use std::fs::File;
|
||||
use std::io::{Error, Read};
|
||||
|
||||
pub struct Handle(File);
|
||||
|
||||
impl Handle {
|
||||
pub fn new<S: Into<String>>(device_name: S) -> Result<Self, Error> {
|
||||
let path = format!("input:handle/{}", device_name.into());
|
||||
Ok(Self(File::open(path)?))
|
||||
}
|
||||
|
||||
// The return value is the display identifier. It will be used to uniquely
|
||||
// identify the display on activation events.
|
||||
pub fn register(&mut self) -> Result<usize, Error> {
|
||||
Ok(dbg!(self.0.read(&mut [])?))
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(packed)]
|
||||
pub struct Damage {
|
||||
pub x: i32,
|
||||
pub y: i32,
|
||||
pub width: i32,
|
||||
pub height: i32,
|
||||
}
|
||||
+106
-10
@@ -15,10 +15,11 @@ use std::collections::BTreeMap;
|
||||
use std::fs::File;
|
||||
use std::io::{Read, Write};
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
use std::sync::Arc;
|
||||
|
||||
use orbclient::{Event, EventOption};
|
||||
use syscall::{Error as SysError, EventFlags, Packet, SchemeMut, EINVAL};
|
||||
|
||||
#[derive(Debug)]
|
||||
enum Handle {
|
||||
Producer,
|
||||
Consumer {
|
||||
@@ -26,6 +27,7 @@ enum Handle {
|
||||
pending: Vec<u8>,
|
||||
notified: bool,
|
||||
},
|
||||
Device(Arc<String>),
|
||||
}
|
||||
|
||||
impl Handle {
|
||||
@@ -36,27 +38,46 @@ impl Handle {
|
||||
|
||||
struct InputScheme {
|
||||
handles: BTreeMap<usize, Handle>,
|
||||
|
||||
next_id: AtomicUsize,
|
||||
next_vt_id: AtomicUsize,
|
||||
|
||||
vts: BTreeMap<usize, Arc<String>>,
|
||||
super_key: bool,
|
||||
active_vt: Option<(Arc<String>, File)>,
|
||||
}
|
||||
|
||||
impl InputScheme {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
next_id: AtomicUsize::new(0),
|
||||
next_vt_id: AtomicUsize::new(1),
|
||||
|
||||
handles: BTreeMap::new(),
|
||||
vts: BTreeMap::new(),
|
||||
super_key: false,
|
||||
active_vt: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SchemeMut for InputScheme {
|
||||
fn open(&mut self, path: &str, _flags: usize, _uid: u32, _gid: u32) -> syscall::Result<usize> {
|
||||
let handle_ty = match path {
|
||||
let mut path_parts = path.split('/');
|
||||
|
||||
let command = path_parts.next().ok_or(SysError::new(EINVAL))?;
|
||||
|
||||
let handle_ty = match command {
|
||||
"producer" => Handle::Producer,
|
||||
"consumer" => Handle::Consumer {
|
||||
events: EventFlags::empty(),
|
||||
pending: Vec::new(),
|
||||
notified: false,
|
||||
},
|
||||
"handle" => {
|
||||
let value = path_parts.next().ok_or(SysError::new(EINVAL))?;
|
||||
Handle::Device(Arc::new(value.to_string()))
|
||||
}
|
||||
|
||||
_ => unreachable!("inputd: invalid path {path}"),
|
||||
};
|
||||
@@ -72,17 +93,24 @@ impl SchemeMut for InputScheme {
|
||||
fn read(&mut self, id: usize, buf: &mut [u8]) -> syscall::Result<usize> {
|
||||
let handle = self.handles.get_mut(&id).ok_or(SysError::new(EINVAL))?;
|
||||
|
||||
if let Handle::Consumer { pending, .. } = handle {
|
||||
let copy = core::cmp::min(pending.len(), buf.len());
|
||||
match handle {
|
||||
Handle::Consumer { pending, .. } => {
|
||||
let copy = core::cmp::min(pending.len(), buf.len());
|
||||
|
||||
for (i, byte) in pending.drain(..copy).enumerate() {
|
||||
buf[i] = byte;
|
||||
for (i, byte) in pending.drain(..copy).enumerate() {
|
||||
buf[i] = byte;
|
||||
}
|
||||
|
||||
Ok(copy)
|
||||
}
|
||||
|
||||
Ok(copy)
|
||||
} else {
|
||||
// A producer cannot read from the channel.
|
||||
unreachable!()
|
||||
Handle::Device(device) => {
|
||||
let vt = self.next_vt_id.fetch_add(1, Ordering::SeqCst);
|
||||
self.vts.insert(vt, device.clone());
|
||||
Ok(vt)
|
||||
}
|
||||
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,6 +119,74 @@ impl SchemeMut for InputScheme {
|
||||
return Ok(1);
|
||||
}
|
||||
|
||||
let events = unsafe {
|
||||
core::slice::from_raw_parts(
|
||||
buf.as_ptr() as *const Event,
|
||||
buf.len() / core::mem::size_of::<Event>(),
|
||||
)
|
||||
};
|
||||
|
||||
for event in events.iter() {
|
||||
let mut new_active_opt = None;
|
||||
match event.to_option() {
|
||||
EventOption::Key(key_event) => match key_event.scancode {
|
||||
f @ 0x3B..=0x44 if self.super_key => {
|
||||
// F1 through F10
|
||||
new_active_opt = Some((f - 0x3A) as usize);
|
||||
}
|
||||
|
||||
0x57 if self.super_key => {
|
||||
// F11
|
||||
new_active_opt = Some(11);
|
||||
}
|
||||
|
||||
0x58 if self.super_key => {
|
||||
// F12
|
||||
new_active_opt = Some(12);
|
||||
}
|
||||
|
||||
0x5B => {
|
||||
// Super
|
||||
self.super_key = key_event.pressed;
|
||||
}
|
||||
|
||||
_ => (),
|
||||
},
|
||||
|
||||
_ => continue,
|
||||
}
|
||||
|
||||
if let Some(new_active) = new_active_opt {
|
||||
if let Some(vt) = self.vts.get(&new_active) {
|
||||
// Result: display/virtio-gpu:3/acvtivate
|
||||
let activate = format!("display/{vt}:{new_active}/activate");
|
||||
log::info!("inputd: switching to VT #{new_active} ({activate})");
|
||||
|
||||
let file = File::open(&activate).unwrap();
|
||||
// Drop the old active VT first.
|
||||
//
|
||||
// TODO(andypython): This can be drastically improved by introducting something
|
||||
// like ioctl() to the display scheme.
|
||||
for (vt, device) in self.vts.iter() {
|
||||
if vt == &new_active {
|
||||
continue;
|
||||
}
|
||||
|
||||
let deactivate = format!("display/{device}:{}/deactivate", vt);
|
||||
let _ = File::open(&deactivate);
|
||||
|
||||
if device.contains("virtio") {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
self.active_vt = Some((vt.clone(), file));
|
||||
} else {
|
||||
log::warn!("inputd: switch to non-existent VT #{new_active} was requested");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let handle = self.handles.get_mut(&id).ok_or(SysError::new(EINVAL))?;
|
||||
assert!(handle.is_producer());
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ ransid = "0.4"
|
||||
rusttype = { version = "0.2", optional = true }
|
||||
redox_syscall = "0.3"
|
||||
redox-daemon = "0.1"
|
||||
inputd = { path = "../inputd" }
|
||||
|
||||
[features]
|
||||
default = []
|
||||
|
||||
+11
-5
@@ -38,10 +38,13 @@ pub struct DisplayScheme {
|
||||
next_id: usize,
|
||||
pub handles: BTreeMap<usize, Handle>,
|
||||
super_key: bool,
|
||||
inputd_handle: inputd::Handle,
|
||||
}
|
||||
|
||||
impl DisplayScheme {
|
||||
pub fn new(mut framebuffers: Vec<FrameBuffer>, spec: &[bool]) -> DisplayScheme {
|
||||
let mut inputd_handle = inputd::Handle::new("vesa").unwrap();
|
||||
|
||||
let mut onscreens = Vec::new();
|
||||
for fb in framebuffers.iter_mut() {
|
||||
onscreens.push(unsafe {
|
||||
@@ -51,7 +54,6 @@ impl DisplayScheme {
|
||||
|
||||
let mut vts = BTreeMap::<VtIndex, BTreeMap<ScreenIndex, Box<dyn Screen>>>::new();
|
||||
|
||||
let mut vt_i = 1;
|
||||
for &vt_type in spec.iter() {
|
||||
let mut screens = BTreeMap::<ScreenIndex, Box<dyn Screen>>::new();
|
||||
for fb_i in 0..framebuffers.len() {
|
||||
@@ -62,8 +64,7 @@ impl DisplayScheme {
|
||||
Box::new(TextScreen::new(Display::new(fb.width, fb.height)))
|
||||
});
|
||||
}
|
||||
vts.insert(VtIndex(vt_i), screens);
|
||||
vt_i += 1;
|
||||
vts.insert(VtIndex(inputd_handle.register().unwrap()), screens);
|
||||
}
|
||||
|
||||
DisplayScheme {
|
||||
@@ -74,6 +75,7 @@ impl DisplayScheme {
|
||||
next_id: 0,
|
||||
handles: BTreeMap::new(),
|
||||
super_key: false,
|
||||
inputd_handle
|
||||
}
|
||||
}
|
||||
|
||||
@@ -168,11 +170,15 @@ impl SchemeMut for DisplayScheme {
|
||||
let screen_i = ScreenIndex(
|
||||
vt_screen.next().unwrap_or("").parse::<usize>().unwrap_or(0)
|
||||
);
|
||||
if let Some(screens) = self.vts.get(&vt_i) {
|
||||
if screens.contains_key(&screen_i) {
|
||||
if let Some(screens) = self.vts.get_mut(&vt_i) {
|
||||
if let Some(screen) = screens.get_mut(&screen_i) {
|
||||
for cmd in parts {
|
||||
if cmd == "activate" {
|
||||
self.active = vt_i;
|
||||
screen.redraw(
|
||||
self.onscreens[screen_i.0],
|
||||
self.framebuffers[screen_i.0].stride
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -380,6 +380,11 @@ impl<'a> StandardTransport<'a> {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn reset(&self) {
|
||||
let mut common = self.common.lock().unwrap();
|
||||
common.device_status.set(DeviceStatusFlags::empty());
|
||||
}
|
||||
|
||||
pub fn check_device_feature(&self, feature: u32) -> bool {
|
||||
let mut common = self.common.lock().unwrap();
|
||||
|
||||
|
||||
@@ -13,7 +13,9 @@ paste = "1.0.13"
|
||||
|
||||
virtio-core = { path = "../virtio-core" }
|
||||
pcid = { path = "../pcid" }
|
||||
inputd = { path = "../inputd" }
|
||||
|
||||
redox-daemon = "0.1"
|
||||
redox_syscall = "0.3"
|
||||
orbclient = "0.3.27"
|
||||
spin = "0.9.8"
|
||||
|
||||
+71
-30
@@ -22,6 +22,7 @@
|
||||
|
||||
#![feature(int_roundings)]
|
||||
|
||||
use std::cell::UnsafeCell;
|
||||
use std::fs::File;
|
||||
use std::io::{Read, Write};
|
||||
|
||||
@@ -144,6 +145,15 @@ pub struct ControlHeader {
|
||||
padding: [u8; 3],
|
||||
}
|
||||
|
||||
impl ControlHeader {
|
||||
pub fn with_ty(ty: CommandTy) -> Self {
|
||||
Self {
|
||||
ty: VolatileCell::new(ty),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for ControlHeader {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
@@ -157,44 +167,41 @@ impl Default for ControlHeader {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
#[repr(C)]
|
||||
pub struct GpuRect {
|
||||
pub x: VolatileCell<u32>,
|
||||
pub y: VolatileCell<u32>,
|
||||
pub width: VolatileCell<u32>,
|
||||
pub height: VolatileCell<u32>,
|
||||
pub x: u32,
|
||||
pub y: u32,
|
||||
pub width: u32,
|
||||
pub height: u32,
|
||||
}
|
||||
|
||||
impl GpuRect {
|
||||
pub fn new(x: u32, y: u32, width: u32, height: u32) -> Self {
|
||||
Self {
|
||||
x: VolatileCell::new(x),
|
||||
y: VolatileCell::new(y),
|
||||
width: VolatileCell::new(width),
|
||||
height: VolatileCell::new(height),
|
||||
x,
|
||||
y,
|
||||
width,
|
||||
height,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn width(&self) -> u32 {
|
||||
self.width.get()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn height(&self) -> u32 {
|
||||
self.height.get()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[repr(C)]
|
||||
pub struct DisplayInfo {
|
||||
pub rect: GpuRect,
|
||||
rect: UnsafeCell<GpuRect>,
|
||||
pub enabled: VolatileCell<u32>,
|
||||
pub flags: VolatileCell<u32>,
|
||||
}
|
||||
|
||||
impl DisplayInfo {
|
||||
pub fn rect(&self) -> &GpuRect {
|
||||
// SAFETY: We never give out mutable references to `self.rect`.
|
||||
unsafe { &*self.rect.get() }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[repr(C)]
|
||||
pub struct GetDisplayInfo {
|
||||
@@ -274,16 +281,31 @@ pub struct AttachBacking {
|
||||
impl AttachBacking {
|
||||
pub fn new(resource_id: u32, num_entries: u32) -> Self {
|
||||
Self {
|
||||
header: ControlHeader {
|
||||
ty: VolatileCell::new(CommandTy::ResourceAttachBacking),
|
||||
..Default::default()
|
||||
},
|
||||
header: ControlHeader::with_ty(CommandTy::ResourceAttachBacking),
|
||||
resource_id,
|
||||
num_entries,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[repr(C)]
|
||||
pub struct DetachBacking {
|
||||
pub header: ControlHeader,
|
||||
pub resource_id: u32,
|
||||
pub padding: u32,
|
||||
}
|
||||
|
||||
impl DetachBacking {
|
||||
pub fn new(resource_id: u32) -> Self {
|
||||
Self {
|
||||
header: ControlHeader::with_ty(CommandTy::ResourceDetachBacking),
|
||||
resource_id,
|
||||
padding: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[repr(C)]
|
||||
pub struct ResourceFlush {
|
||||
@@ -296,11 +318,7 @@ pub struct ResourceFlush {
|
||||
impl ResourceFlush {
|
||||
pub fn new(resource_id: u32, rect: GpuRect) -> Self {
|
||||
Self {
|
||||
header: ControlHeader {
|
||||
ty: VolatileCell::new(CommandTy::ResourceFlush),
|
||||
..Default::default()
|
||||
},
|
||||
|
||||
header: ControlHeader::with_ty(CommandTy::ResourceFlush),
|
||||
rect,
|
||||
resource_id,
|
||||
padding: 0,
|
||||
@@ -308,6 +326,24 @@ impl ResourceFlush {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[repr(C)]
|
||||
pub struct ResourceUnref {
|
||||
pub header: ControlHeader,
|
||||
pub resource_id: u32,
|
||||
pub padding: u32,
|
||||
}
|
||||
|
||||
impl ResourceUnref {
|
||||
pub fn new(resource_id: u32) -> Self {
|
||||
Self {
|
||||
header: ControlHeader::with_ty(CommandTy::ResourceUnref),
|
||||
resource_id,
|
||||
padding: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug)]
|
||||
pub struct SetScanout {
|
||||
@@ -389,7 +425,12 @@ fn deamon(deamon: redox_daemon::Daemon) -> anyhow::Result<()> {
|
||||
deamon.ready().unwrap();
|
||||
|
||||
let mut socket_file = File::create(":display/virtio-gpu")?;
|
||||
let mut scheme = scheme::Scheme::new(config, control_queue.clone(), cursor_queue.clone())?;
|
||||
let mut scheme = futures::executor::block_on(scheme::Scheme::new(
|
||||
config,
|
||||
control_queue.clone(),
|
||||
cursor_queue.clone(),
|
||||
device.transport.clone(),
|
||||
))?;
|
||||
|
||||
loop {
|
||||
let mut packet = Packet::default();
|
||||
|
||||
+149
-66
@@ -1,23 +1,38 @@
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use std::sync::atomic::{AtomicU32, AtomicUsize, Ordering};
|
||||
use std::sync::atomic::{AtomicU32, Ordering};
|
||||
use std::sync::Arc;
|
||||
|
||||
use inputd::Damage;
|
||||
use syscall::{Dma, Error as SysError, SchemeMut, EINVAL};
|
||||
|
||||
use virtio_core::spec::{Buffer, ChainBuilder, DescriptorFlags};
|
||||
use virtio_core::transport::{Error, Queue};
|
||||
use virtio_core::transport::{Error, Queue, StandardTransport};
|
||||
use virtio_core::utils::VolatileCell;
|
||||
|
||||
use crate::*;
|
||||
|
||||
static RESOURCE_ALLOC: AtomicU32 = AtomicU32::new(1); // XXX: 0 is reserved for whatever that takes `resource_id`.
|
||||
|
||||
impl Into<GpuRect> for &Damage {
|
||||
fn into(self) -> GpuRect {
|
||||
GpuRect {
|
||||
x: self.x as u32,
|
||||
y: self.y as u32,
|
||||
width: self.width as u32,
|
||||
height: self.height as u32,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Display<'a> {
|
||||
control_queue: Arc<Queue<'a>>,
|
||||
cursor_queue: Arc<Queue<'a>>,
|
||||
transport: Arc<StandardTransport<'a>>,
|
||||
|
||||
mapped: Option<usize>,
|
||||
// TODO(andypython): Remove the need for the spin crate after the `once_cell`
|
||||
// API is stabilized.
|
||||
mapped: spin::Once<usize>,
|
||||
|
||||
width: u32,
|
||||
height: u32,
|
||||
@@ -30,24 +45,25 @@ impl<'a> Display<'a> {
|
||||
pub fn new(
|
||||
control_queue: Arc<Queue<'a>>,
|
||||
cursor_queue: Arc<Queue<'a>>,
|
||||
_display_info: &mut DisplayInfo,
|
||||
transport: Arc<StandardTransport<'a>>,
|
||||
id: usize,
|
||||
) -> Self {
|
||||
Self {
|
||||
control_queue,
|
||||
cursor_queue,
|
||||
|
||||
mapped: None,
|
||||
mapped: spin::Once::new(),
|
||||
|
||||
width: 1920,
|
||||
height: 1080,
|
||||
transport,
|
||||
|
||||
id,
|
||||
resource_id: RESOURCE_ALLOC.fetch_add(1, Ordering::SeqCst),
|
||||
}
|
||||
}
|
||||
|
||||
async fn get_fpath(&mut self, buffer: &mut [u8]) -> Result<usize, Error> {
|
||||
async fn get_fpath(&self, buffer: &mut [u8]) -> Result<usize, Error> {
|
||||
let path = format!("display/virtio-gpu:3.0/{}/{}", self.width, self.height);
|
||||
|
||||
// Copy the path into the target buffer.
|
||||
@@ -55,7 +71,7 @@ impl<'a> Display<'a> {
|
||||
Ok(path.len())
|
||||
}
|
||||
|
||||
async fn send_request<T>(&mut self, request: Dma<T>) -> Result<Dma<ControlHeader>, Error> {
|
||||
async fn send_request<T>(&self, request: Dma<T>) -> Result<Dma<ControlHeader>, Error> {
|
||||
let header = Dma::new(ControlHeader::default())?;
|
||||
let command = ChainBuilder::new()
|
||||
.chain(Buffer::new(&request))
|
||||
@@ -66,15 +82,15 @@ impl<'a> Display<'a> {
|
||||
Ok(header)
|
||||
}
|
||||
|
||||
async fn flush_resource(&mut self, flush: ResourceFlush) -> Result<(), Error> {
|
||||
async fn flush_resource(&self, flush: ResourceFlush) -> Result<(), Error> {
|
||||
let header = self.send_request(Dma::new(flush)?).await?;
|
||||
assert_eq!(header.ty.get(), CommandTy::RespOkNodata);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn map_screen(&mut self, offset: usize) -> Result<usize, Error> {
|
||||
if let Some(mapped) = self.mapped {
|
||||
async fn map_screen(&self, offset: usize) -> Result<usize, Error> {
|
||||
if let Some(mapped) = self.mapped.get() {
|
||||
return Ok(mapped + offset);
|
||||
}
|
||||
|
||||
@@ -124,74 +140,128 @@ impl<'a> Display<'a> {
|
||||
|
||||
self.control_queue.send(command).await;
|
||||
assert_eq!(header.ty.get(), CommandTy::RespOkNodata);
|
||||
self.flush().await?;
|
||||
self.mapped = Some(mapped);
|
||||
Ok(mapped + offset)
|
||||
}
|
||||
|
||||
async fn flush(&mut self) -> Result<(), Error> {
|
||||
let rect = GpuRect::new(0, 0, self.width, self.height);
|
||||
let scanout_request = Dma::new(SetScanout::new(self.id as u32, self.resource_id, rect))?;
|
||||
let scanout_request = Dma::new(SetScanout::new(
|
||||
self.id as u32,
|
||||
self.resource_id,
|
||||
GpuRect::new(0, 0, self.width, self.height),
|
||||
))?;
|
||||
let header = self.send_request(scanout_request).await?;
|
||||
assert_eq!(header.ty.get(), CommandTy::RespOkNodata);
|
||||
|
||||
let rect = GpuRect::new(0, 0, self.width, self.height);
|
||||
let req = Dma::new(XferToHost2d::new(self.resource_id, rect))?;
|
||||
self.flush(None).await?;
|
||||
self.mapped.call_once(|| mapped);
|
||||
|
||||
Ok(mapped + offset)
|
||||
}
|
||||
|
||||
/// If `damage` is `None`, the entire screen is flushed.
|
||||
async fn flush(&self, damage: Option<&Damage>) -> Result<(), Error> {
|
||||
let damage = if let Some(damage) = damage {
|
||||
damage.into()
|
||||
} else {
|
||||
GpuRect {
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: self.width,
|
||||
height: self.height,
|
||||
}
|
||||
};
|
||||
|
||||
let req = Dma::new(XferToHost2d::new(
|
||||
self.resource_id,
|
||||
GpuRect {
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: self.width,
|
||||
height: self.height,
|
||||
},
|
||||
))?;
|
||||
let header = self.send_request(req).await?;
|
||||
assert_eq!(header.ty.get(), CommandTy::RespOkNodata);
|
||||
|
||||
let rect = GpuRect::new(0, 0, self.width, self.height);
|
||||
self.flush_resource(ResourceFlush::new(self.resource_id, rect))
|
||||
self.flush_resource(ResourceFlush::new(self.resource_id, damage.clone()))
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// This detaches any backing pages from the display and unrefs the resource. Also resets the
|
||||
/// device, which is required to go back to legacy mode.
|
||||
async fn detach(&self) -> Result<(), Error> {
|
||||
let request = Dma::new(DetachBacking::new(self.resource_id))?;
|
||||
let header = self.send_request(request).await?;
|
||||
assert_eq!(header.ty.get(), CommandTy::RespOkNodata);
|
||||
|
||||
let request = Dma::new(ResourceUnref::new(self.resource_id))?;
|
||||
let header = self.send_request(request).await?;
|
||||
assert_eq!(header.ty.get(), CommandTy::RespOkNodata);
|
||||
|
||||
// Go back to legacy mode.
|
||||
self.transport.reset();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Scheme<'a> {
|
||||
control_queue: Arc<Queue<'a>>,
|
||||
cursor_queue: Arc<Queue<'a>>,
|
||||
config: &'a mut GpuConfig,
|
||||
/// File descriptor allocator.
|
||||
next_id: AtomicUsize,
|
||||
handles: BTreeMap<usize /* file descriptor */, Display<'a>>,
|
||||
handles: BTreeMap<usize /* file descriptor */, Arc<Display<'a>>>,
|
||||
inputd_handle: inputd::Handle,
|
||||
displays: Vec<Arc<Display<'a>>>,
|
||||
}
|
||||
|
||||
impl<'a> Scheme<'a> {
|
||||
pub fn new(
|
||||
pub async fn new(
|
||||
config: &'a mut GpuConfig,
|
||||
control_queue: Arc<Queue<'a>>,
|
||||
cursor_queue: Arc<Queue<'a>>,
|
||||
) -> Result<Self, Error> {
|
||||
Ok(Self {
|
||||
control_queue,
|
||||
cursor_queue,
|
||||
transport: Arc<StandardTransport<'a>>,
|
||||
) -> Result<Scheme<'a>, Error> {
|
||||
let displays = Self::probe(
|
||||
control_queue.clone(),
|
||||
cursor_queue.clone(),
|
||||
transport.clone(),
|
||||
config,
|
||||
next_id: AtomicUsize::new(0),
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(Self {
|
||||
handles: BTreeMap::new(),
|
||||
inputd_handle: inputd::Handle::new("virtio-gpu").unwrap(),
|
||||
displays,
|
||||
})
|
||||
}
|
||||
|
||||
async fn open_display(&self, id: usize) -> Result<Display<'a>, Error> {
|
||||
let mut display_info = self.get_display_info().await?;
|
||||
let displays = &mut display_info.display_info[..self.config.num_scanouts() as usize];
|
||||
async fn probe(
|
||||
control_queue: Arc<Queue<'a>>,
|
||||
cursor_queue: Arc<Queue<'a>>,
|
||||
transport: Arc<StandardTransport<'a>>,
|
||||
config: &GpuConfig,
|
||||
) -> Result<Vec<Arc<Display<'a>>>, Error> {
|
||||
let mut display_info = Self::get_display_info(control_queue.clone()).await?;
|
||||
let displays = &mut display_info.display_info[..config.num_scanouts() as usize];
|
||||
|
||||
let display = displays.get_mut(id).ok_or(SysError::new(syscall::ENOENT))?;
|
||||
let mut result = vec![];
|
||||
|
||||
log::info!(
|
||||
"virtio-gpu: opening display ({}x{}px)",
|
||||
display.rect.width(),
|
||||
display.rect.height()
|
||||
);
|
||||
for (id, info) in displays.iter().enumerate() {
|
||||
log::info!(
|
||||
"virtio-gpu: opening display ({}x{}px)",
|
||||
info.rect().width,
|
||||
info.rect().height
|
||||
);
|
||||
|
||||
Ok(Display::new(
|
||||
self.control_queue.clone(),
|
||||
self.cursor_queue.clone(),
|
||||
display,
|
||||
id,
|
||||
))
|
||||
let display = Display::new(
|
||||
control_queue.clone(),
|
||||
cursor_queue.clone(),
|
||||
transport.clone(),
|
||||
id,
|
||||
);
|
||||
|
||||
result.push(Arc::new(display));
|
||||
}
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
async fn get_display_info(&self) -> Result<Dma<GetDisplayInfo>, Error> {
|
||||
async fn get_display_info(control_queue: Arc<Queue<'a>>) -> Result<Dma<GetDisplayInfo>, Error> {
|
||||
let header = Dma::new(ControlHeader {
|
||||
ty: VolatileCell::new(CommandTy::GetDisplayInfo),
|
||||
..Default::default()
|
||||
@@ -203,7 +273,7 @@ impl<'a> Scheme<'a> {
|
||||
.chain(Buffer::new(&response).flags(DescriptorFlags::WRITE_ONLY))
|
||||
.build();
|
||||
|
||||
self.control_queue.send(command).await;
|
||||
control_queue.send(command).await;
|
||||
assert!(response.header.ty.get() == CommandTy::RespOkDisplayInfo);
|
||||
|
||||
Ok(response)
|
||||
@@ -217,16 +287,30 @@ impl<'a> SchemeMut for Scheme<'a> {
|
||||
let mut parts = path.split('/');
|
||||
let mut screen = parts.next().unwrap_or("").split('.');
|
||||
|
||||
let vt_index = screen.next().unwrap_or("").parse::<usize>().unwrap_or(1);
|
||||
static YES: std::sync::atomic::AtomicBool = std::sync::atomic::AtomicBool::new(true);
|
||||
if YES.load(Ordering::SeqCst) {
|
||||
YES.store(false, Ordering::SeqCst);
|
||||
}
|
||||
|
||||
let fd = screen.next().unwrap_or("").parse::<usize>().unwrap();
|
||||
|
||||
if path.contains("deactivate") {
|
||||
log::info!("virtio-gpu: deactivating display at file #{}", fd);
|
||||
|
||||
let handle = self.handles.get(&fd).unwrap();
|
||||
futures::executor::block_on(handle.detach()).unwrap();
|
||||
|
||||
return Ok(0);
|
||||
}
|
||||
|
||||
let id = screen.next().unwrap_or("").parse::<usize>().unwrap_or(0);
|
||||
|
||||
dbg!(&vt_index, &id);
|
||||
dbg!(&id);
|
||||
|
||||
let fd = self.next_id.fetch_add(1, Ordering::SeqCst);
|
||||
let display = futures::executor::block_on(self.open_display(id))
|
||||
.map_err(|_| SysError::new(syscall::ENOENT))?;
|
||||
let display = self.displays.get(id).ok_or(SysError::new(EINVAL))?;
|
||||
|
||||
self.handles.insert(fd, display);
|
||||
let fd = self.inputd_handle.register().unwrap();
|
||||
self.handles.insert(fd, display.clone());
|
||||
Ok(fd)
|
||||
}
|
||||
|
||||
@@ -269,7 +353,7 @@ impl<'a> SchemeMut for Scheme<'a> {
|
||||
|
||||
fn fsync(&mut self, id: usize) -> syscall::Result<usize> {
|
||||
let handle = self.handles.get_mut(&id).ok_or(SysError::new(EINVAL))?;
|
||||
futures::executor::block_on(handle.flush()).unwrap();
|
||||
futures::executor::block_on(handle.flush(None)).unwrap();
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
@@ -281,17 +365,16 @@ impl<'a> SchemeMut for Scheme<'a> {
|
||||
|
||||
fn write(&mut self, id: usize, buf: &[u8]) -> syscall::Result<usize> {
|
||||
let handle = self.handles.get_mut(&id).ok_or(SysError::new(EINVAL))?;
|
||||
let damages = unsafe {
|
||||
core::slice::from_raw_parts(
|
||||
buf.as_ptr() as *const Damage,
|
||||
buf.len() / core::mem::size_of::<Damage>(),
|
||||
)
|
||||
};
|
||||
|
||||
// SAFETY: lmao
|
||||
unsafe {
|
||||
core::ptr::copy_nonoverlapping(
|
||||
buf.as_ptr(),
|
||||
handle.mapped.unwrap() as *mut u8,
|
||||
buf.len(),
|
||||
);
|
||||
for damage in damages {
|
||||
futures::executor::block_on(handle.flush(Some(damage))).unwrap();
|
||||
}
|
||||
|
||||
futures::executor::block_on(handle.flush()).unwrap();
|
||||
Ok(buf.len())
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user