drivers: add inputd
Take out the input coalescing from `vesad` into `inputd`. Signed-off-by: Anhad Singh <andypython@protonmail.com>
This commit is contained in:
Generated
+13
-2
@@ -674,6 +674,17 @@ dependencies = [
|
||||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "inputd"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"log",
|
||||
"redox-daemon",
|
||||
"redox-log",
|
||||
"redox_syscall 0.3.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "instant"
|
||||
version = "0.1.12"
|
||||
@@ -742,9 +753,9 @@ checksum = "a6f08839bc70ef4a3fe1d566d5350f519c5912ea86be0df1740a7d247c7fc0ef"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.141"
|
||||
version = "0.2.147"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3304a64d199bb964be99741b7a14d26972741915b3649639149b2479bb46f4b5"
|
||||
checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3"
|
||||
|
||||
[[package]]
|
||||
name = "link-cplusplus"
|
||||
|
||||
@@ -22,6 +22,7 @@ members = [
|
||||
"usbctl",
|
||||
"usbhidd",
|
||||
"usbscsid",
|
||||
"inputd",
|
||||
|
||||
"virtio-blkd",
|
||||
"virtio-netd",
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
#!/usr/bin/bash
|
||||
|
||||
pushd virtio-core
|
||||
cargo fmt
|
||||
popd
|
||||
function fmt() {
|
||||
for dir in "$@"
|
||||
do
|
||||
pushd $dir
|
||||
printf "\e[1;32mFormatting\e[0m $dir\n"
|
||||
cargo fmt
|
||||
popd
|
||||
done
|
||||
}
|
||||
|
||||
pushd virtio-netd
|
||||
cargo fmt
|
||||
popd
|
||||
|
||||
pushd virtio-blkd
|
||||
cargo fmt
|
||||
popd
|
||||
|
||||
pushd virtio-gpud
|
||||
cargo fmt
|
||||
popd
|
||||
fmt virtio-core \
|
||||
virtio-netd \
|
||||
virtio-blkd \
|
||||
virtio-gpud \
|
||||
inputd
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
[package]
|
||||
name = "inputd"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
authors = ["Anhad Singh <andypython@protonmail.com>"]
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.71"
|
||||
log = "0.4.19"
|
||||
redox-daemon = "0.1.0"
|
||||
redox-log = "0.1.1"
|
||||
redox_syscall = "0.3.5"
|
||||
@@ -0,0 +1,224 @@
|
||||
//! `:input`
|
||||
//!
|
||||
//! A seperate scheme is required since all of the input from different input devices is required
|
||||
//! to be combined into a single stream which is later going to be processed by the "consumer"
|
||||
//! which usually is Orbital.
|
||||
//!
|
||||
//! ## Input Device ("producer")
|
||||
//! Write events to `input:producer`.
|
||||
//!
|
||||
//! ## Input Consumer ("consumer")
|
||||
//! Read events from `input:consumer`. Optionally, set the `EVENT_READ` flag to be notified when
|
||||
//! events are available.
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
use std::fs::File;
|
||||
use std::io::{Read, Write};
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
|
||||
use syscall::{Error as SysError, EventFlags, Packet, SchemeMut, EINVAL};
|
||||
|
||||
#[derive(Debug)]
|
||||
enum Handle {
|
||||
Producer,
|
||||
Consumer {
|
||||
events: EventFlags,
|
||||
pending: Vec<u8>,
|
||||
notified: bool
|
||||
},
|
||||
}
|
||||
|
||||
impl Handle {
|
||||
pub fn is_producer(&self) -> bool {
|
||||
matches!(self, Handle::Producer)
|
||||
}
|
||||
}
|
||||
|
||||
struct InputScheme {
|
||||
handles: BTreeMap<usize, Handle>,
|
||||
next_id: AtomicUsize,
|
||||
}
|
||||
|
||||
impl InputScheme {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
next_id: AtomicUsize::new(0),
|
||||
handles: BTreeMap::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SchemeMut for InputScheme {
|
||||
fn open(&mut self, path: &str, _flags: usize, _uid: u32, _gid: u32) -> syscall::Result<usize> {
|
||||
let handle_ty = match path {
|
||||
"producer" => Handle::Producer,
|
||||
"consumer" => Handle::Consumer {
|
||||
events: EventFlags::empty(),
|
||||
pending: Vec::new(),
|
||||
notified: false
|
||||
},
|
||||
|
||||
_ => unreachable!("inputd: invalid path {path}"),
|
||||
};
|
||||
|
||||
log::info!("inputd: {path} channel has been opened");
|
||||
|
||||
let fd = self.next_id.fetch_add(1, Ordering::SeqCst);
|
||||
self.handles.insert(fd, handle_ty);
|
||||
|
||||
Ok(fd)
|
||||
}
|
||||
|
||||
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());
|
||||
|
||||
for (i, byte) in pending.drain(..copy).enumerate() {
|
||||
buf[i] = byte;
|
||||
}
|
||||
|
||||
Ok(copy)
|
||||
} else {
|
||||
// A producer cannot read from the channel.
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
|
||||
fn write(&mut self, id: usize, buf: &[u8]) -> syscall::Result<usize> {
|
||||
if buf.len() == 1 && buf[0] > 0xf4 {
|
||||
return Ok(1);
|
||||
}
|
||||
|
||||
let handle = self.handles.get_mut(&id).ok_or(SysError::new(EINVAL))?;
|
||||
assert!(handle.is_producer());
|
||||
|
||||
for handle in self.handles.values_mut() {
|
||||
match handle {
|
||||
Handle::Consumer {
|
||||
ref mut pending, ref mut notified, ..
|
||||
} => {
|
||||
pending.extend_from_slice(buf);
|
||||
*notified = false;
|
||||
},
|
||||
_ => continue,
|
||||
}
|
||||
}
|
||||
|
||||
Ok(buf.len())
|
||||
}
|
||||
|
||||
fn fevent(
|
||||
&mut self,
|
||||
id: usize,
|
||||
flags: syscall::EventFlags,
|
||||
) -> syscall::Result<syscall::EventFlags> {
|
||||
let handle = self.handles.get_mut(&id).ok_or(SysError::new(EINVAL))?;
|
||||
|
||||
match handle {
|
||||
Handle::Consumer { ref mut events, ref mut notified, .. } => {
|
||||
*events = flags;
|
||||
*notified = false;},
|
||||
_ => unreachable!(),
|
||||
}
|
||||
|
||||
Ok(EventFlags::empty())
|
||||
}
|
||||
|
||||
fn close(&mut self, _id: usize) -> syscall::Result<usize> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
fn deamon(deamon: redox_daemon::Daemon) -> anyhow::Result<()> {
|
||||
// Create the ":input" scheme.
|
||||
let mut socket_file = File::create(":input")?;
|
||||
let mut scheme = InputScheme::new();
|
||||
|
||||
deamon.ready().unwrap();
|
||||
|
||||
loop {
|
||||
let mut should_handle = false;
|
||||
let mut packet = Packet::default();
|
||||
socket_file.read(&mut packet)?;
|
||||
|
||||
// The producer has written to the channel; the consumers should be notified.
|
||||
if packet.a == syscall::SYS_WRITE {
|
||||
should_handle = true;
|
||||
}
|
||||
|
||||
scheme.handle(&mut packet);
|
||||
socket_file.write(&packet)?;
|
||||
|
||||
if !should_handle {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (id, handle) in scheme.handles.iter_mut() {
|
||||
if let Handle::Consumer { events, pending, ref mut notified } = handle {
|
||||
if pending.is_empty() || *notified || !events.contains(EventFlags::EVENT_READ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Notify the consumer that we have some events to read. Yum yum.
|
||||
let mut event_packet = Packet::default();
|
||||
event_packet.a = syscall::SYS_FEVENT;
|
||||
event_packet.b = *id;
|
||||
event_packet.c = EventFlags::EVENT_READ.bits();
|
||||
// Specifies the number of bytes that can be read non-blocking.
|
||||
event_packet.d = pending.len();
|
||||
socket_file.write(&event_packet)?;
|
||||
|
||||
*notified = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn daemon_runner(redox_daemon: redox_daemon::Daemon) -> ! {
|
||||
deamon(redox_daemon).unwrap();
|
||||
unreachable!();
|
||||
}
|
||||
|
||||
#[cfg(target_os = "redox")]
|
||||
pub fn setup_logging(level: log::LevelFilter, name: &str) {
|
||||
use redox_log::{OutputBuilder, RedoxLogger};
|
||||
|
||||
let mut logger = RedoxLogger::new().with_output(
|
||||
OutputBuilder::stderr()
|
||||
.with_filter(level)
|
||||
.with_ansi_escape_codes()
|
||||
.flush_on_newline(true)
|
||||
.build(),
|
||||
);
|
||||
|
||||
match OutputBuilder::in_redox_logging_scheme("disk", "pcie", format!("{name}.log")) {
|
||||
Ok(builder) => {
|
||||
logger = logger.with_output(builder.with_filter(level).flush_on_newline(true).build())
|
||||
}
|
||||
Err(err) => eprintln!("inputd: failed to create log: {}", err),
|
||||
}
|
||||
|
||||
match OutputBuilder::in_redox_logging_scheme("disk", "pcie", format!("{name}.ansi.log")) {
|
||||
Ok(builder) => {
|
||||
logger = logger.with_output(
|
||||
builder
|
||||
.with_filter(level)
|
||||
.with_ansi_escape_codes()
|
||||
.flush_on_newline(true)
|
||||
.build(),
|
||||
)
|
||||
}
|
||||
Err(err) => eprintln!("inputd: failed to create ANSI log: {}", err),
|
||||
}
|
||||
|
||||
logger.enable().unwrap();
|
||||
log::info!("inputd: enabled logger");
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
#[cfg(target_os = "redox")]
|
||||
setup_logging(log::LevelFilter::Trace, "inputd");
|
||||
redox_daemon::Daemon::new(daemon_runner).expect("virtio-core: failed to daemonize");
|
||||
}
|
||||
+2
-2
@@ -45,8 +45,8 @@ fn daemon(daemon: redox_daemon::Daemon) -> ! {
|
||||
|
||||
let input = OpenOptions::new()
|
||||
.write(true)
|
||||
.open("display/vesa:input")
|
||||
.expect("ps2d: failed to open display:input");
|
||||
.open("input:producer")
|
||||
.expect("ps2d: failed to open input:producer");
|
||||
|
||||
let mut event_file = OpenOptions::new()
|
||||
.read(true)
|
||||
|
||||
@@ -252,7 +252,7 @@ impl<'a> Available<'a> {
|
||||
self.ring
|
||||
.elements
|
||||
.as_slice(self.queue_size)
|
||||
.get(index % 256)
|
||||
.get(index % self.queue_size)
|
||||
.expect("virtio-core::available: index out of bounds")
|
||||
}
|
||||
}
|
||||
@@ -318,7 +318,7 @@ impl<'a> Used<'a> {
|
||||
self.ring
|
||||
.elements
|
||||
.as_slice(self.queue_size)
|
||||
.get(index % 256)
|
||||
.get(index % self.queue_size)
|
||||
.expect("virtio-core::used: index out of bounds")
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user