virtio-gpu: start working on the scheme
Signed-off-by: Anhad Singh <andypython@protonmail.com>
This commit is contained in:
Generated
+9
-7
@@ -773,12 +773,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.17"
|
||||
version = "0.4.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
]
|
||||
checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4"
|
||||
|
||||
[[package]]
|
||||
name = "matches"
|
||||
@@ -1131,9 +1128,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.56"
|
||||
version = "1.0.63"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435"
|
||||
checksum = "7b368fba921b0dce7e60f5e04ec15e565b3303972b42bcfde1d0713b881959eb"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
@@ -1952,9 +1949,14 @@ dependencies = [
|
||||
name = "virtio-gpud"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"futures",
|
||||
"log",
|
||||
"orbclient",
|
||||
"pcid",
|
||||
"redox-daemon",
|
||||
"redox_syscall 0.3.5",
|
||||
"static_assertions",
|
||||
"virtio-core",
|
||||
]
|
||||
|
||||
|
||||
+1
-1
@@ -38,7 +38,7 @@ fn main() {
|
||||
|
||||
let mut scheme = BgaScheme {
|
||||
bga: bga,
|
||||
display: File::open("display:input").ok()
|
||||
display: File::open("display/vesa:input").ok()
|
||||
};
|
||||
|
||||
scheme.update_size();
|
||||
|
||||
@@ -11,3 +11,7 @@ popd
|
||||
pushd virtio-blkd
|
||||
cargo fmt
|
||||
popd
|
||||
|
||||
pushd virtio-gpud
|
||||
cargo fmt
|
||||
popd
|
||||
|
||||
+1
-1
@@ -45,7 +45,7 @@ fn daemon(daemon: redox_daemon::Daemon) -> ! {
|
||||
|
||||
let input = OpenOptions::new()
|
||||
.write(true)
|
||||
.open("display:input")
|
||||
.open("display/vesa:input")
|
||||
.expect("ps2d: failed to open display:input");
|
||||
|
||||
let mut event_file = OpenOptions::new()
|
||||
|
||||
+1
-1
@@ -382,7 +382,7 @@ fn main() {
|
||||
let report_ty = ReportTy::Input;
|
||||
let report_id = 0;
|
||||
|
||||
let mut display = File::open("display:input").expect("Failed to open orbital input socket");
|
||||
let mut display = File::open("display/vesa:input").expect("Failed to open orbital input socket");
|
||||
|
||||
//TODO: get dynamically
|
||||
let mut display_width = 0;
|
||||
|
||||
+1
-1
@@ -206,7 +206,7 @@ fn main() {
|
||||
|
||||
let mut width = 0;
|
||||
let mut height = 0;
|
||||
let mut display_opt = File::open("display:input").ok();
|
||||
let mut display_opt = File::open("display/vesa:input").ok();
|
||||
if let Some(ref display) = display_opt {
|
||||
let mut buf: [u8; 4096] = [0; 4096];
|
||||
if let Ok(count) = syscall::fpath(display.as_raw_fd() as usize, &mut buf) {
|
||||
|
||||
+1
-1
@@ -90,7 +90,7 @@ fn main() {
|
||||
redox_daemon::Daemon::new(|daemon| inner(daemon, framebuffers, &spec)).expect("failed to create daemon");
|
||||
}
|
||||
fn inner(daemon: redox_daemon::Daemon, framebuffers: Vec<FrameBuffer>, spec: &[bool]) -> ! {
|
||||
let mut socket = File::create(":display").expect("vesad: failed to create display scheme");
|
||||
let mut socket = File::create(":display/vesa").expect("vesad: failed to create display scheme");
|
||||
|
||||
let mut scheme = DisplayScheme::new(framebuffers, &spec);
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ use syscall::{Io, PHYSMAP_NO_CACHE, PHYSMAP_WRITE};
|
||||
|
||||
use crate::spec::*;
|
||||
use crate::transport::{Error, StandardTransport};
|
||||
use crate::utils::{VolatileCell, align_down};
|
||||
use crate::utils::{align_down, VolatileCell};
|
||||
|
||||
pub struct Device<'a> {
|
||||
pub transport: Arc<StandardTransport<'a>>,
|
||||
@@ -186,11 +186,7 @@ pub fn probe_device<'a>(pcid_handle: &mut PcidServerHandle) -> Result<Device<'a>
|
||||
|
||||
let size = offset + capability.length as usize;
|
||||
|
||||
let addr = syscall::physmap(
|
||||
aligned_addr,
|
||||
size,
|
||||
PHYSMAP_WRITE | PHYSMAP_NO_CACHE,
|
||||
)?;
|
||||
let addr = syscall::physmap(aligned_addr, size, PHYSMAP_WRITE | PHYSMAP_NO_CACHE)?;
|
||||
|
||||
addr + offset
|
||||
};
|
||||
|
||||
@@ -86,6 +86,12 @@ impl<'a> Future for PendingRequest<'a> {
|
||||
.insert(self.first_descriptor, cx.waker().clone());
|
||||
|
||||
let used_head = self.queue.used.head_index();
|
||||
|
||||
if used_head == self.queue.used_head.load(Ordering::SeqCst) {
|
||||
// No new requests have been completed.
|
||||
return Poll::Pending;
|
||||
}
|
||||
|
||||
let used_element = self.queue.used.get_element_at((used_head - 1) as usize);
|
||||
let written = used_element.written.get();
|
||||
|
||||
@@ -109,6 +115,8 @@ impl<'a> Future for PendingRequest<'a> {
|
||||
.lock()
|
||||
.unwrap()
|
||||
.remove(&self.first_descriptor);
|
||||
|
||||
self.queue.used_head.store(used_head, Ordering::SeqCst);
|
||||
return Poll::Ready(written);
|
||||
} else {
|
||||
return Poll::Pending;
|
||||
@@ -122,6 +130,7 @@ pub struct Queue<'a> {
|
||||
pub used: Used<'a>,
|
||||
pub descriptor: Dma<[Descriptor]>,
|
||||
pub available: Available<'a>,
|
||||
pub used_head: AtomicU16,
|
||||
|
||||
notification_bell: &'a mut AtomicU16,
|
||||
descriptor_stack: crossbeam_queue::SegQueue<u16>,
|
||||
@@ -148,6 +157,7 @@ impl<'a> Queue<'a> {
|
||||
waker: Mutex::new(std::collections::HashMap::new()),
|
||||
queue_index,
|
||||
descriptor_stack,
|
||||
used_head: AtomicU16::new(0),
|
||||
sref: sref.clone(),
|
||||
})
|
||||
}
|
||||
@@ -190,8 +200,6 @@ impl<'a> Queue<'a> {
|
||||
self.notification_bell
|
||||
.store(self.queue_index, Ordering::SeqCst);
|
||||
|
||||
assert_eq!(self.used.flags(), 0);
|
||||
|
||||
PendingRequest {
|
||||
queue: self.sref.upgrade().unwrap(),
|
||||
first_descriptor: first_descriptor as u32,
|
||||
@@ -470,6 +478,7 @@ impl<'a> StandardTransport<'a> {
|
||||
|
||||
event_queue
|
||||
.add(irq_fd, move |_| -> Result<Option<usize>, std::io::Error> {
|
||||
// Wake up the tasks waiting on the queue.
|
||||
for (_, task) in queue_copy.waker.lock().unwrap().iter() {
|
||||
task.wake_by_ref();
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use core::cell::UnsafeCell;
|
||||
use core::fmt::Debug;
|
||||
use core::marker::PhantomData;
|
||||
|
||||
#[derive(Debug)]
|
||||
#[repr(C)]
|
||||
pub struct VolatileCell<T> {
|
||||
value: UnsafeCell<T>,
|
||||
@@ -28,6 +28,17 @@ impl<T: Copy> VolatileCell<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Debug for VolatileCell<T>
|
||||
where
|
||||
T: Debug + Copy,
|
||||
{
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("VolatileCell")
|
||||
.field("value", &self.get())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<T> Sync for VolatileCell<T> {}
|
||||
|
||||
#[repr(C)]
|
||||
|
||||
@@ -6,8 +6,13 @@ authors = ["Anhad Singh <andypython@protonmail.com>"]
|
||||
|
||||
[dependencies]
|
||||
log = "0.4"
|
||||
static_assertions = "1.1.0"
|
||||
futures = { version = "0.3.28", features = ["executor"] }
|
||||
anyhow = "1.0.71"
|
||||
|
||||
virtio-core = { path = "../virtio-core" }
|
||||
pcid = { path = "../pcid" }
|
||||
|
||||
redox-daemon = "0.1"
|
||||
redox_syscall = "0.3"
|
||||
orbclient = "0.3.27"
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
diff --git a/src/main.rs b/src/main.rs
|
||||
index a99488a..ec97fdd 100644
|
||||
--- a/src/main.rs
|
||||
+++ b/src/main.rs
|
||||
@@ -78,6 +78,40 @@ pub fn run(file: &Path) -> Result<()> {
|
||||
println!("init: failed to run: no argument");
|
||||
},
|
||||
"run.d" => if let Some(new_dir) = args.next() {
|
||||
+ // On startup, the VESA display driver is started which basically makes use of the framebuffer
|
||||
+ // provided by the firmware. The GPU device are latter started by `pcid` (such as `virtio-gpu`).
|
||||
+ let mut devices = vec![];
|
||||
+ let schemes = std::fs::read_dir(":").unwrap();
|
||||
+
|
||||
+ for entry in schemes {
|
||||
+ let path = entry.unwrap().path();
|
||||
+ let path_str = path
|
||||
+ .into_os_string()
|
||||
+ .into_string()
|
||||
+ .expect("init: failed to convert path to string");
|
||||
+
|
||||
+ if path_str.contains("display") {
|
||||
+ println!("init: found display scheme {}", path_str);
|
||||
+ devices.push(path_str);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ let device = devices.iter().filter(|d| !d.contains("vesa")).collect::<Vec<_>>();
|
||||
+ let device = if device.is_empty() {
|
||||
+ // No GPU available, fallback to VESA display which *should* always be accessible via `display/vesa:`.
|
||||
+ "vesa"
|
||||
+ } else {
|
||||
+ // Parts:
|
||||
+ // :/display/virtio-gpu
|
||||
+ //
|
||||
+ // 1st: ":"
|
||||
+ // 2nd: "display"
|
||||
+ // 3rd: "virtio-gpu" (the one we want!)
|
||||
+ device[0].split("/").nth(2).unwrap()
|
||||
+ };
|
||||
+
|
||||
+ std::env::set_var("DISPLAY", &format!("display/{}:3/activate", device));
|
||||
+
|
||||
let mut entries = vec![];
|
||||
match read_dir(&new_dir) {
|
||||
Ok(list) => for entry_res in list {
|
||||
@@ -0,0 +1,13 @@
|
||||
diff --git a/src/main.rs b/src/main.rs
|
||||
index 1cdf9cf..3859ac0 100644
|
||||
--- a/src/main.rs
|
||||
+++ b/src/main.rs
|
||||
@@ -63,7 +63,7 @@ fn orbital(daemon: Daemon) -> Result<(), String> {
|
||||
.enable();
|
||||
|
||||
let mut args = env::args().skip(1);
|
||||
- let display_path = args.next().ok_or("no display argument")?;
|
||||
+ let display_path = env::var("DISPLAY").unwrap();
|
||||
let login_cmd = args.next().ok_or("no login manager argument")?;
|
||||
|
||||
core::fix_env(&display_path).map_err(|_| "error setting env vars")?;
|
||||
+180
-4
@@ -1,7 +1,154 @@
|
||||
use pcid_interface::PcidServerHandle;
|
||||
use virtio_core::transport::Error;
|
||||
//! `virtio-gpu` is a virtio based graphics adapter. It can operate in 2D mode and in 3D mode.
|
||||
//!
|
||||
//! XXX: 3D mode will offload rendering ops to the host gpu and therefore requires a GPU with 3D support
|
||||
//! on the host machine.
|
||||
|
||||
fn deamon(deamon: redox_daemon::Daemon) -> Result<(), Error> {
|
||||
#![feature(async_closure)]
|
||||
|
||||
use std::fs::File;
|
||||
use std::io::{Read, Write};
|
||||
|
||||
use pcid_interface::PcidServerHandle;
|
||||
|
||||
use syscall::{Packet, SchemeMut};
|
||||
use virtio_core::utils::VolatileCell;
|
||||
use virtio_core::MSIX_PRIMARY_VECTOR;
|
||||
|
||||
mod scheme;
|
||||
|
||||
// const VIRTIO_GPU_EVENT_DISPLAY: u32 = 1 << 0;
|
||||
const VIRTIO_GPU_MAX_SCANOUTS: usize = 16;
|
||||
|
||||
#[repr(C)]
|
||||
pub struct GpuConfig {
|
||||
/// Signals pending events to the driver.
|
||||
pub events_read: VolatileCell<u32>, // read-only
|
||||
/// Clears pending events in the device (write-to-clear).
|
||||
pub events_clear: VolatileCell<u32>, // write-only
|
||||
|
||||
pub min_scanouts: VolatileCell<u32>,
|
||||
pub num_capsets: VolatileCell<u32>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
#[repr(u32)]
|
||||
pub enum CommandTy {
|
||||
Undefined = 0,
|
||||
|
||||
// 2D commands
|
||||
GetDisplayInfo = 0x0100,
|
||||
ResourceCreate2d,
|
||||
ResourceUnref,
|
||||
SetScanout,
|
||||
ResourceFlush,
|
||||
TransferToHost2d,
|
||||
ResourceAttachBacking,
|
||||
ResourceDetachBacking,
|
||||
GetCapsetInfo,
|
||||
GetCapset,
|
||||
GetEdid,
|
||||
ResourceAssignUuid,
|
||||
ResourceCreateBlob,
|
||||
SetScanoutBlob,
|
||||
|
||||
// 3D commands
|
||||
CtxCreate = 0x0200,
|
||||
CtxDestroy,
|
||||
CtxAttachResource,
|
||||
CtxDetachResource,
|
||||
ResourceCreate3d,
|
||||
TransferToHost3d,
|
||||
TransferFromHost3d,
|
||||
Submit3d,
|
||||
ResourceMapBlob,
|
||||
ResourceUnmapBlob,
|
||||
|
||||
// cursor commands
|
||||
UpdateCursor = 0x0300,
|
||||
MoveCursor,
|
||||
|
||||
// success responses
|
||||
RespOkNodata = 0x1100,
|
||||
RespOkDisplayInfo,
|
||||
RespOkCapsetInfo,
|
||||
RespOkCapset,
|
||||
RespOkEdid,
|
||||
RespOkResourceUuid,
|
||||
RespOkMapInfo,
|
||||
|
||||
// error responses
|
||||
RespErrUnspec = 0x1200,
|
||||
RespErrOutOfMemory,
|
||||
RespErrInvalidScanoutId,
|
||||
RespErrInvalidResourceId,
|
||||
RespErrInvalidContextId,
|
||||
RespErrInvalidParameter,
|
||||
}
|
||||
|
||||
static_assertions::const_assert_eq!(core::mem::size_of::<CommandTy>(), 4);
|
||||
|
||||
#[derive(Debug)]
|
||||
#[repr(C)]
|
||||
pub struct ControlHeader {
|
||||
pub ty: VolatileCell<CommandTy>,
|
||||
pub flags: VolatileCell<u32>,
|
||||
pub fence_id: VolatileCell<u64>,
|
||||
pub ctx_id: VolatileCell<u32>,
|
||||
pub ring_index: VolatileCell<u8>,
|
||||
padding: [u8; 3],
|
||||
}
|
||||
|
||||
impl Default for ControlHeader {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
ty: VolatileCell::new(CommandTy::Undefined),
|
||||
flags: VolatileCell::new(0),
|
||||
fence_id: VolatileCell::new(0),
|
||||
ctx_id: VolatileCell::new(0),
|
||||
ring_index: VolatileCell::new(0),
|
||||
padding: [0; 3],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[repr(C)]
|
||||
pub struct GpuRect {
|
||||
pub x: VolatileCell<u32>,
|
||||
pub y: VolatileCell<u32>,
|
||||
pub width: VolatileCell<u32>,
|
||||
pub height: VolatileCell<u32>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[repr(C)]
|
||||
pub struct DisplayInfo {
|
||||
pub rect: GpuRect,
|
||||
pub enabled: VolatileCell<u32>,
|
||||
pub flags: VolatileCell<u32>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[repr(C)]
|
||||
pub struct GetDisplayInfo {
|
||||
pub header: ControlHeader,
|
||||
pub display_info: [DisplayInfo; VIRTIO_GPU_MAX_SCANOUTS],
|
||||
}
|
||||
|
||||
impl Default for GetDisplayInfo {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
header: ControlHeader {
|
||||
ty: VolatileCell::new(CommandTy::GetDisplayInfo),
|
||||
..Default::default()
|
||||
},
|
||||
|
||||
display_info: unsafe { core::mem::zeroed() },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn deamon(deamon: redox_daemon::Daemon) -> anyhow::Result<()> {
|
||||
let mut pcid_handle = PcidServerHandle::connect_default()?;
|
||||
|
||||
// Double check that we have the right device.
|
||||
@@ -13,7 +160,36 @@ fn deamon(deamon: redox_daemon::Daemon) -> Result<(), Error> {
|
||||
log::info!("virtio-gpu: initiating startup sequence :^)");
|
||||
|
||||
let device = virtio_core::probe_device(&mut pcid_handle)?;
|
||||
loop {}
|
||||
|
||||
// Negotiate features.
|
||||
device.transport.finalize_features();
|
||||
|
||||
// Queue for sending control commands.
|
||||
let control_queue = device
|
||||
.transport
|
||||
.setup_queue(MSIX_PRIMARY_VECTOR, &device.irq_handle)?;
|
||||
|
||||
// Queue for sending cursor updates.
|
||||
let cursor_queue = device
|
||||
.transport
|
||||
.setup_queue(MSIX_PRIMARY_VECTOR, &device.irq_handle)?;
|
||||
|
||||
device.transport.run_device();
|
||||
deamon.ready().unwrap();
|
||||
|
||||
let mut socket_file = File::create(":display/virtio-gpu")?;
|
||||
let mut scheme = scheme::Display::new(control_queue, cursor_queue);
|
||||
|
||||
loop {
|
||||
let mut packet = Packet::default();
|
||||
socket_file
|
||||
.read(&mut packet)
|
||||
.expect("virtio-gpud: failed to read disk scheme");
|
||||
scheme.handle(&mut packet);
|
||||
socket_file
|
||||
.write(&packet)
|
||||
.expect("virtio-gpud: failed to read disk scheme");
|
||||
}
|
||||
}
|
||||
|
||||
fn daemon_runner(redox_daemon: redox_daemon::Daemon) -> ! {
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
use std::{
|
||||
collections::BTreeMap,
|
||||
sync::{
|
||||
atomic::{AtomicUsize, Ordering},
|
||||
Arc,
|
||||
},
|
||||
};
|
||||
|
||||
use orbclient::Event;
|
||||
use syscall::{Dma, Error as SysError, SchemeMut, EINVAL, EPERM};
|
||||
use virtio_core::{
|
||||
spec::{Buffer, ChainBuilder, DescriptorFlags},
|
||||
transport::{Error, Queue},
|
||||
utils::VolatileCell,
|
||||
};
|
||||
|
||||
use crate::{CommandTy, ControlHeader, GetDisplayInfo};
|
||||
|
||||
pub enum Handle {
|
||||
Input(InputHandle),
|
||||
Screen(ScreenHandle),
|
||||
}
|
||||
|
||||
pub struct InputHandle {}
|
||||
pub struct ScreenHandle {
|
||||
id: usize,
|
||||
}
|
||||
|
||||
pub struct Display<'a> {
|
||||
control_queue: Arc<Queue<'a>>,
|
||||
cursor_queue: Arc<Queue<'a>>,
|
||||
|
||||
display_id: usize,
|
||||
handles: BTreeMap<usize, Handle>,
|
||||
next_id: AtomicUsize,
|
||||
}
|
||||
|
||||
impl<'a> Display<'a> {
|
||||
pub fn new(control_queue: Arc<Queue<'a>>, cursor_queue: Arc<Queue<'a>>) -> Self {
|
||||
Self {
|
||||
control_queue,
|
||||
cursor_queue,
|
||||
|
||||
display_id: 0,
|
||||
handles: BTreeMap::new(),
|
||||
next_id: AtomicUsize::new(0),
|
||||
}
|
||||
}
|
||||
|
||||
async fn get_display_info(&self) -> Result<Dma<GetDisplayInfo>, Error> {
|
||||
let header = Dma::new(ControlHeader {
|
||||
ty: VolatileCell::new(CommandTy::GetDisplayInfo),
|
||||
..Default::default()
|
||||
})?;
|
||||
|
||||
let response = Dma::new(GetDisplayInfo::default())?;
|
||||
let command = ChainBuilder::new()
|
||||
.chain(Buffer::new(&header))
|
||||
.chain(Buffer::new(&response).flags(DescriptorFlags::WRITE_ONLY))
|
||||
.build();
|
||||
|
||||
self.control_queue.send(command).await;
|
||||
assert!(response.header.ty.get() == CommandTy::RespOkDisplayInfo);
|
||||
|
||||
Ok(response)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> SchemeMut for Display<'a> {
|
||||
fn open(&mut self, path: &str, flags: usize, uid: u32, gid: u32) -> syscall::Result<usize> {
|
||||
if path == "input" {
|
||||
if uid != 0 {
|
||||
return Err(SysError::new(EPERM));
|
||||
}
|
||||
|
||||
let fd = self.next_id.fetch_add(1, Ordering::SeqCst);
|
||||
self.handles.insert(fd, Handle::Input(InputHandle {}));
|
||||
|
||||
Ok(fd)
|
||||
} else {
|
||||
let mut parts = path.split('/');
|
||||
let screen = parts.next().unwrap_or("").split('.');
|
||||
dbg!(screen);
|
||||
|
||||
todo!();
|
||||
}
|
||||
}
|
||||
|
||||
fn read(&mut self, id: usize, buf: &mut [u8]) -> syscall::Result<usize> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn write(&mut self, id: usize, buf: &[u8]) -> syscall::Result<usize> {
|
||||
let handle = self.handles.get_mut(&id).ok_or(SysError::new(EINVAL))?;
|
||||
|
||||
match handle {
|
||||
Handle::Input(_) => todo!(),
|
||||
Handle::Screen(_) => {
|
||||
let size = buf.len() / core::mem::size_of::<Event>();
|
||||
let events =
|
||||
unsafe { core::slice::from_raw_parts(buf.as_ptr().cast::<Event>(), size) };
|
||||
|
||||
dbg!(events);
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user