diff --git a/Cargo.lock b/Cargo.lock index afb50c966b..9960ef6c71 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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" diff --git a/Cargo.toml b/Cargo.toml index 2ccefbc9b4..b992570d25 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,6 +22,7 @@ members = [ "usbctl", "usbhidd", "usbscsid", + "inputd", "virtio-blkd", "virtio-netd", diff --git a/fmt.sh b/fmt.sh index 450e518f26..c9f29df18f 100755 --- a/fmt.sh +++ b/fmt.sh @@ -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 diff --git a/inputd/Cargo.toml b/inputd/Cargo.toml new file mode 100644 index 0000000000..75c1a048a5 --- /dev/null +++ b/inputd/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "inputd" +version = "0.1.0" +edition = "2021" +authors = ["Anhad Singh "] + +[dependencies] +anyhow = "1.0.71" +log = "0.4.19" +redox-daemon = "0.1.0" +redox-log = "0.1.1" +redox_syscall = "0.3.5" diff --git a/inputd/src/main.rs b/inputd/src/main.rs new file mode 100644 index 0000000000..f349f6dec8 --- /dev/null +++ b/inputd/src/main.rs @@ -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, + notified: bool + }, +} + +impl Handle { + pub fn is_producer(&self) -> bool { + matches!(self, Handle::Producer) + } +} + +struct InputScheme { + handles: BTreeMap, + 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 { + 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 { + 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 { + 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 { + 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 { + 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"); +} diff --git a/ps2d/src/main.rs b/ps2d/src/main.rs index 96df72a2b0..f9cdba3b6b 100644 --- a/ps2d/src/main.rs +++ b/ps2d/src/main.rs @@ -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) diff --git a/virtio-core/src/transport.rs b/virtio-core/src/transport.rs index 5bcc693f59..bc56de2149 100644 --- a/virtio-core/src/transport.rs +++ b/virtio-core/src/transport.rs @@ -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") } }