diff --git a/Cargo.lock b/Cargo.lock index 455a3d4100..6581dacbea 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1022,6 +1022,7 @@ dependencies = [ "log", "orbclient", "redox-daemon", + "redox-scheme 0.6.2", "redox_event", "redox_syscall", ] diff --git a/hwd/src/backend/devicetree.rs b/hwd/src/backend/devicetree.rs index d9ff5e2ca6..8a91d04e42 100644 --- a/hwd/src/backend/devicetree.rs +++ b/hwd/src/backend/devicetree.rs @@ -36,7 +36,9 @@ impl Backend for DeviceTreeBackend { fn probe(&mut self) -> Result<(), Box> { let dt = fdt::Fdt::new(&self.dtb).map_err(|err| format!("failed to parse dtb: {}", err))?; - let root = dt.find_node("/").ok_or_else(|| format!("failed to find root node"))?; + let root = dt + .find_node("/") + .ok_or_else(|| format!("failed to find root node"))?; Self::dump(&root, 0); Ok(()) } diff --git a/input/ps2d/Cargo.toml b/input/ps2d/Cargo.toml index e289ab0cae..e03d148ff1 100644 --- a/input/ps2d/Cargo.toml +++ b/input/ps2d/Cargo.toml @@ -10,6 +10,7 @@ orbclient = "0.3.27" redox_event = "0.4.1" redox_syscall = "0.5.10" redox-daemon = "0.1" +redox-scheme = "0.6.2" libredox = "0.1.3" common = { path = "../../common" } diff --git a/input/ps2d/src/main.rs b/input/ps2d/src/main.rs index 466943ee01..3a8922bbe4 100644 --- a/input/ps2d/src/main.rs +++ b/input/ps2d/src/main.rs @@ -13,11 +13,15 @@ use common::acquire_port_io_rights; use event::{user_data, EventQueue}; use inputd::ProducerHandle; use log::info; +use redox_scheme::{RequestKind, SignalBehavior, Socket}; +use syscall::{EAGAIN, EWOULDBLOCK}; +use crate::scheme::Ps2Scheme; use crate::state::Ps2d; mod controller; mod keymap; +mod scheme; mod state; mod vm; @@ -32,18 +36,11 @@ fn daemon(daemon: redox_daemon::Daemon) -> ! { acquire_port_io_rights().expect("ps2d: failed to get I/O permission"); - let (keymap, keymap_name): (fn(u8, bool) -> char, &str) = match env::args().skip(1).next() { - Some(k) => match k.to_lowercase().as_ref() { - "dvorak" => (keymap::dvorak::get_char, "dvorak"), - "us" => (keymap::us::get_char, "us"), - "gb" => (keymap::gb::get_char, "gb"), - "azerty" => (keymap::azerty::get_char, "azerty"), - "bepo" => (keymap::bepo::get_char, "bepo"), - "it" => (keymap::it::get_char, "it"), - &_ => (keymap::us::get_char, "us"), - }, - None => (keymap::us::get_char, "us"), - }; + let (mut keymap, mut keymap_name): (fn(u8, bool) -> char, &str) = + match env::args().skip(1).next() { + Some(k) => get_keymap_from_str(&k), + None => (keymap::us::get_char, "us"), + }; info!("ps2d: using keymap '{}'", keymap_name); @@ -53,6 +50,7 @@ fn daemon(daemon: redox_daemon::Daemon) -> ! { enum Source { Keyboard, Mouse, + Scheme, } } @@ -89,14 +87,29 @@ fn daemon(daemon: redox_daemon::Daemon) -> ! { ) .unwrap(); + let mut ps2d = Ps2d::new(input, keymap); + + let scheme_file = Socket::nonblock("ps2").expect("ps2d: failed to create ps2 scheme"); + + let mut scheme_handle = Ps2Scheme::new( + keymap_name.to_owned(), + vec!["dvorak", "us", "gb", "azerty", "bepo", "it"], + ); + + event_queue + .subscribe( + scheme_file.inner().raw(), + Source::Scheme, + event::EventFlags::READ, + ) + .unwrap(); + libredox::call::setrens(0, 0).expect("ps2d: failed to enter null namespace"); daemon .ready() .expect("ps2d: failed to mark daemon as ready"); - let mut ps2d = Ps2d::new(input, keymap); - let mut data = [0; 256]; for event in event_queue.map(|e| e.expect("ps2d: failed to get next event").user_data) { // There are some gotchas with ps/2 controllers that require this weird @@ -111,6 +124,39 @@ fn daemon(daemon: redox_daemon::Daemon) -> ! { let (file, keyboard) = match event { Source::Keyboard => (&mut key_file, true), Source::Mouse => (&mut mouse_file, false), + Source::Scheme => { + loop { + let request = match scheme_file.next_request(SignalBehavior::Interrupt) { + Ok(Some(request)) => request, + Ok(None) => { + // Scheme likely got unmounted + std::process::exit(0); + } + Err(err) if err.errno == EWOULDBLOCK || err.errno == EAGAIN => break, + Err(err) => panic!("ps2: failed to read scheme: {:?}", err), + }; + + match request.kind() { + RequestKind::Call(call) => { + let response = call.handle_sync(&mut scheme_handle); + + scheme_file + .write_response(response, SignalBehavior::Restart) + .expect("ps2: failed to write next scheme response"); + } + RequestKind::OnClose { id: _ } => {} + _ => (), + } + } + + if keymap_name != &scheme_handle.keymap { + (keymap, keymap_name) = get_keymap_from_str(&scheme_handle.keymap); + info!("ps2d: updating to new keymap '{:?}'", keymap_name); + ps2d.update_keymap(keymap); + } + + continue; + } }; loop { @@ -128,6 +174,18 @@ fn daemon(daemon: redox_daemon::Daemon) -> ! { process::exit(0); } +fn get_keymap_from_str(k: &str) -> (fn(u8, bool) -> char, &'static str) { + match k.to_lowercase().as_ref() { + "dvorak" => (keymap::dvorak::get_char, "dvorak"), + "us" => (keymap::us::get_char, "us"), + "gb" => (keymap::gb::get_char, "gb"), + "azerty" => (keymap::azerty::get_char, "azerty"), + "bepo" => (keymap::bepo::get_char, "bepo"), + "it" => (keymap::it::get_char, "it"), + &_ => (keymap::us::get_char, "us"), + } +} + fn main() { redox_daemon::Daemon::new(daemon).expect("ps2d: failed to create daemon"); } diff --git a/input/ps2d/src/scheme.rs b/input/ps2d/src/scheme.rs new file mode 100644 index 0000000000..058ec776c3 --- /dev/null +++ b/input/ps2d/src/scheme.rs @@ -0,0 +1,145 @@ +use redox_scheme::scheme::SchemeSync; +use redox_scheme::{CallerCtx, OpenResult}; +use std::convert::TryFrom; +use syscall::dirent::{DirEntry, DirentBuf, DirentKind}; +use syscall::schemev2::NewFdFlags; +use syscall::{ + Error, Result, EACCES, EINVAL, EMFILE, ENOENT, ENOTDIR, MODE_DIR, MODE_FILE, O_WRONLY, +}; + +pub struct Ps2Scheme { + pub keymap: String, + pub keymap_list: String, +} + +impl Ps2Scheme { + pub fn new(keymap: String, keymap_list: Vec<&str>) -> Ps2Scheme { + let scheme = Ps2Scheme { + keymap, + keymap_list: keymap_list.join("\n"), + }; + scheme + } +} + +impl SchemeSync for Ps2Scheme { + fn open(&mut self, path_str: &str, flags: usize, ctx: &CallerCtx) -> Result { + let path = path_str.trim_start_matches('/'); + if flags & O_WRONLY != 0 { + if ctx.uid != 0 || ctx.gid != 0 { + return Err(Error::new(EACCES)); + } else if path != "keymap" { + return Err(Error::new(EINVAL)); + } + } + + match path { + "" => Ok(OpenResult::ThisScheme { + number: 0, + flags: NewFdFlags::empty(), + }), + "keymap" => Ok(OpenResult::ThisScheme { + number: 1, + flags: NewFdFlags::POSITIONED, + }), + "keymap_list" => Ok(OpenResult::ThisScheme { + number: 2, + flags: NewFdFlags::POSITIONED, + }), + _ => Err(Error::new(ENOENT)), + } + } + fn getdents<'buf>( + &mut self, + id: usize, + mut buf: DirentBuf<&'buf mut [u8]>, + opaque_offset: u64, + ) -> Result> { + if id != 0 { + return Err(Error::new(ENOTDIR)); + } + let Ok(offset) = usize::try_from(opaque_offset) else { + return Ok(buf); + }; + for (this_idx, name) in ["keymap", "keymap_list"].iter().enumerate().skip(offset) { + buf.entry(DirEntry { + inode: this_idx as u64, + next_opaque_id: this_idx as u64 + 1, + kind: DirentKind::Regular, + name, + })?; + } + Ok(buf) + } + + fn fstat(&mut self, id: usize, stat: &mut syscall::Stat, _ctx: &CallerCtx) -> Result<()> { + stat.st_size = 0; + stat.st_mode = match id { + 0 => 0o555 | MODE_DIR, + 1 => 0o644 | MODE_FILE, + 2 => 0o444 | MODE_FILE, + _ => return Err(Error::new(ENOENT)), + }; + Ok(()) + } + + fn fpath(&mut self, _id: usize, buf: &mut [u8], _ctx: &CallerCtx) -> Result { + let path = b"/scheme/ps2"; + + let mut i = 0; + while i < buf.len() && i < path.len() { + buf[i] = path[i]; + i += 1; + } + + Ok(i) + } + + fn fsync(&mut self, _id: usize, _ctx: &CallerCtx) -> Result<()> { + Ok(()) + } + + fn read( + &mut self, + id: usize, + buf: &mut [u8], + offset: u64, + _fcntl_flags: u32, + _ctx: &CallerCtx, + ) -> Result { + if offset != 0 { + return Ok(0); + } + let value = match id { + 1 => self.keymap.as_bytes(), + 2 => self.keymap_list.as_bytes(), + _ => { + return Err(Error::new(ENOENT)); + } + }; + + if buf.len() + 2 < value.len() { + return Err(Error::new(EMFILE)); + } + buf[..value.len()].copy_from_slice(value); + buf[value.len()] = b'\n'; + buf[value.len() + 1] = b'\0'; + Ok(value.len() + 2) + } + + fn write( + &mut self, + id: usize, + buf: &[u8], + offset: u64, + _fcntl_flags: u32, + _ctx: &CallerCtx, + ) -> Result { + if offset != 0 || id != 1 { + return Ok(0); + } + let new_keymap = String::from_utf8(buf.to_vec()).map_err(|_| Error::new(EINVAL))?; + self.keymap = new_keymap.trim().to_string(); + Ok(buf.len()) + } +} diff --git a/input/ps2d/src/state.rs b/input/ps2d/src/state.rs index 6e35b33fe9..b8126bcb1c 100644 --- a/input/ps2d/src/state.rs +++ b/input/ps2d/src/state.rs @@ -67,6 +67,10 @@ impl char> Ps2d { } } + pub fn update_keymap(&mut self, keymap: F) { + self.get_char = keymap; + } + pub fn irq(&mut self) { while let Some((keyboard, data)) = self.ps2.next() { self.handle(keyboard, data); diff --git a/inputd/src/main.rs b/inputd/src/main.rs index 4b8646f4c5..6853a5769a 100644 --- a/inputd/src/main.rs +++ b/inputd/src/main.rs @@ -205,7 +205,7 @@ impl SchemeSync for InputScheme { "control" => Handle::Control, _ => { - log::error!("inputd: invalid path {path}"); + log::error!("inputd: invalid path '{path}'"); return Err(SysError::new(EINVAL)); } };