Merge branch 'ps2-keymap-config' into 'master'

Add ps2 scheme to make the keymap configurable

See merge request redox-os/drivers!293
This commit is contained in:
Jeremy Soller
2025-10-20 11:30:24 -06:00
7 changed files with 227 additions and 16 deletions
Generated
+1
View File
@@ -1022,6 +1022,7 @@ dependencies = [
"log",
"orbclient",
"redox-daemon",
"redox-scheme 0.6.2",
"redox_event",
"redox_syscall",
]
+3 -1
View File
@@ -36,7 +36,9 @@ impl Backend for DeviceTreeBackend {
fn probe(&mut self) -> Result<(), Box<dyn Error>> {
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(())
}
+1
View File
@@ -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" }
+72 -14
View File
@@ -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");
}
+145
View File
@@ -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<OpenResult> {
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<DirentBuf<&'buf mut [u8]>> {
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<usize> {
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<usize> {
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<usize> {
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())
}
}
+4
View File
@@ -67,6 +67,10 @@ impl<F: Fn(u8, bool) -> char> Ps2d<F> {
}
}
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);
+1 -1
View File
@@ -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));
}
};