Files
RedBear-OS/drivers/input/ps2d/src/state.rs
T
Red Bear OS 45452c5a8e acpid+ps2d: wire SystemQuirkFlags consumers (LG Gram Round 1)
acpid: load SystemQuirkFlags from in-memory DMI at init via
redox-driver-sys::quirks::toml_loader::load_dmi_system_quirks.
Store as a field on AcpiContext and expose via system_quirks().
Wire two consumers:

  - FORCE_S2IDLE in set_global_s_state(3): routes S3 entry to
    s2idle preparation instead. LG Gram 16Z90TP, Framework 16,
    late Dell XPS advertise \_S3 in DSDT but the firmware refuses
    the SLP_TYP write and hangs / silently no-ops.

  - NO_LEGACY_PM1B in set_global_s_state(*): skips the PM1b write
    even when FADT reports a non-zero pm1b_control_block. LG
    firmware reports a PM1b block that does not exist on the
    platform; writes wedge the controller.

ps2d: load SystemQuirkFlags from /scheme/acpi/dmi at init via
redox_driver_sys::quirks::system_quirks(). Wire one consumer:

  - KBD_DEACTIVATE_FIXUP in Ps2::init(): skips SetDefaultsDisable
    (0xF5) during keyboard init. LG Gram + some Dell/HP/Lenovo
    keyboards wedge or drop keys when 0xF5 is sent (Linux
    atkbd.c keyboard_broken[] / atkbd_deactivate_input tables).

acpid/src/dmi.rs: reframe misleading 'stub' docstring on
try_load_existing() — the function is a real round-trip reader
for /scheme/acpi/dmi, not a stub.

Cargo.lock regenerated to include the new redox-driver-sys path
dependency in acpid and ps2d.

This is the consumer-wiring deliverable for LG Gram Round 1
(local/docs/evidence/lg-gram/ASSESSMENT-2026-07-26.md). The
matching redox-driver-sys stub-replacement work
(load_dmi_acpi_quirks real loader, PANEL_ORIENTATION_TABLE
populated with Linux DRM entries, ACPI_FLAG_NAMES + TOML parser)
lands in the parent repo in the same Round 1 push.

acpi_irq1_skip_override remains documented as needing kernel IRQ
setup work (out of scope for Round 1).
2026-07-26 16:30:50 +09:00

524 lines
19 KiB
Rust

use inputd::InputProducer;
use log::{error, warn};
use orbclient::{ButtonEvent, KeyEvent, MouseEvent, MouseRelativeEvent, ScrollEvent};
use std::{
convert::TryInto,
fs::File,
io::{Read, Write},
time::Duration,
};
use syscall::TimeSpec;
use crate::controller::Ps2;
use crate::mouse::{MouseResult, MouseState};
use crate::vm;
bitflags! {
pub struct MousePacketFlags: u8 {
const LEFT_BUTTON = 1;
const RIGHT_BUTTON = 1 << 1;
const MIDDLE_BUTTON = 1 << 2;
const ALWAYS_ON = 1 << 3;
const X_SIGN = 1 << 4;
const Y_SIGN = 1 << 5;
const X_OVERFLOW = 1 << 6;
const Y_OVERFLOW = 1 << 7;
}
}
fn timespec_from_duration(duration: Duration) -> TimeSpec {
TimeSpec {
tv_sec: duration.as_secs().try_into().unwrap(),
tv_nsec: duration.subsec_nanos().try_into().unwrap(),
}
}
fn duration_from_timespec(timespec: TimeSpec) -> Duration {
Duration::new(
timespec.tv_sec.try_into().unwrap(),
timespec.tv_nsec.try_into().unwrap(),
)
}
pub struct Ps2d {
ps2: Ps2,
vmmouse: bool,
vmmouse_relative: bool,
keyboard_input: InputProducer,
mouse_input: InputProducer,
time_file: File,
extended: bool,
mouse_x: i32,
mouse_y: i32,
mouse_left: bool,
mouse_middle: bool,
mouse_right: bool,
mouse_state: MouseState,
mouse_timeout: Option<TimeSpec>,
packets: [u8; 4],
packet_i: usize,
caps_lock: bool,
num_lock: bool,
scroll_lock: bool,
leds_dirty: bool,
}
impl Ps2d {
pub fn new(
keyboard_input: InputProducer,
mouse_input: InputProducer,
time_file: File,
kbd_deactivate_fixup: bool,
) -> Self {
let mut ps2 = Ps2::new();
if let Err(err) = ps2.init(kbd_deactivate_fixup) {
log::error!("ps2d: controller init failed: {:?}", err);
}
// FIXME add an option for orbital to disable this when an app captures the mouse.
let vmmouse_relative = false;
let vmmouse = vm::enable(vmmouse_relative);
// TODO: QEMU hack, maybe do this when Init timed out?
if vmmouse {
// 3 = MouseId::Intellimouse1
MouseState::Bat.handle(3, &mut ps2);
}
let mut this = Ps2d {
ps2,
vmmouse,
vmmouse_relative,
keyboard_input,
mouse_input,
time_file,
extended: false,
mouse_x: 0,
mouse_y: 0,
mouse_left: false,
mouse_middle: false,
mouse_right: false,
mouse_state: MouseState::Init,
mouse_timeout: None,
packets: [0; 4],
packet_i: 0,
caps_lock: false,
num_lock: true,
scroll_lock: false,
leds_dirty: true,
};
if !this.vmmouse {
// This triggers initializing the mouse
this.handle_mouse(None);
}
// Flush initial LED state (Num Lock on by default)
if this.leds_dirty {
this.leds_dirty = false;
this.ps2.set_leds(this.caps_lock, this.num_lock, this.scroll_lock);
}
this
}
pub fn irq(&mut self) {
while let Some((keyboard, data)) = self.ps2.next() {
self.handle(keyboard, data);
}
}
pub fn time_event(&mut self) {
let mut time = TimeSpec::default();
match self.time_file.read(&mut time) {
Ok(_count) => {}
Err(err) => {
log::error!("failed to read time file: {}", err);
return;
}
}
if let Some(mouse_timeout) = self.mouse_timeout {
if time.tv_sec > mouse_timeout.tv_sec
|| (time.tv_sec == mouse_timeout.tv_sec && time.tv_nsec >= mouse_timeout.tv_nsec)
{
self.handle_mouse(None);
}
}
}
pub fn handle(&mut self, keyboard: bool, data: u8) {
if keyboard {
if data == 0xE0 {
self.extended = true;
} else {
let (ps2_scancode, pressed) = if data >= 0x80 {
(data - 0x80, false)
} else {
(data, true)
};
let scancode = if self.extended {
self.extended = false;
match ps2_scancode {
0x1C => orbclient::K_NUM_ENTER,
0x1D => orbclient::K_RIGHT_CTRL,
0x20 => orbclient::K_VOLUME_TOGGLE,
0x22 => orbclient::K_MEDIA_PLAY_PAUSE,
0x24 => orbclient::K_MEDIA_STOP,
0x10 => orbclient::K_MEDIA_REWIND,
0x19 => orbclient::K_MEDIA_FAST_FORWARD,
0x2E => orbclient::K_VOLUME_DOWN,
0x30 => orbclient::K_VOLUME_UP,
0x35 => orbclient::K_NUM_SLASH,
0x38 => orbclient::K_ALT_GR,
0x47 => orbclient::K_HOME,
0x48 => orbclient::K_UP,
0x49 => orbclient::K_PGUP,
0x4B => orbclient::K_LEFT,
0x4D => orbclient::K_RIGHT,
0x4F => orbclient::K_END,
0x50 => orbclient::K_DOWN,
0x51 => orbclient::K_PGDN,
0x52 => orbclient::K_INS,
0x53 => orbclient::K_DEL,
0x5B => orbclient::K_LEFT_SUPER,
0x5C => orbclient::K_RIGHT_SUPER,
0x5D => orbclient::K_APP,
0x5E => orbclient::K_POWER,
0x5F => orbclient::K_SLEEP,
/* 0x80 to 0xFF used for press/release detection */
_ => {
if pressed {
warn!("unknown extended scancode {:02X}", ps2_scancode);
}
0
}
}
} else {
match ps2_scancode {
/* 0x00 unused */
0x01 => orbclient::K_ESC,
0x02 => orbclient::K_1,
0x03 => orbclient::K_2,
0x04 => orbclient::K_3,
0x05 => orbclient::K_4,
0x06 => orbclient::K_5,
0x07 => orbclient::K_6,
0x08 => orbclient::K_7,
0x09 => orbclient::K_8,
0x0A => orbclient::K_9,
0x0B => orbclient::K_0,
0x0C => orbclient::K_MINUS,
0x0D => orbclient::K_EQUALS,
0x0E => orbclient::K_BKSP,
0x0F => orbclient::K_TAB,
0x10 => orbclient::K_Q,
0x11 => orbclient::K_W,
0x12 => orbclient::K_E,
0x13 => orbclient::K_R,
0x14 => orbclient::K_T,
0x15 => orbclient::K_Y,
0x16 => orbclient::K_U,
0x17 => orbclient::K_I,
0x18 => orbclient::K_O,
0x19 => orbclient::K_P,
0x1A => orbclient::K_BRACE_OPEN,
0x1B => orbclient::K_BRACE_CLOSE,
0x1C => orbclient::K_ENTER,
0x1D => orbclient::K_CTRL,
0x1E => orbclient::K_A,
0x1F => orbclient::K_S,
0x20 => orbclient::K_D,
0x21 => orbclient::K_F,
0x22 => orbclient::K_G,
0x23 => orbclient::K_H,
0x24 => orbclient::K_J,
0x25 => orbclient::K_K,
0x26 => orbclient::K_L,
0x27 => orbclient::K_SEMICOLON,
0x28 => orbclient::K_QUOTE,
0x29 => orbclient::K_TICK,
0x2A => orbclient::K_LEFT_SHIFT,
0x2B => orbclient::K_BACKSLASH,
0x2C => orbclient::K_Z,
0x2D => orbclient::K_X,
0x2E => orbclient::K_C,
0x2F => orbclient::K_V,
0x30 => orbclient::K_B,
0x31 => orbclient::K_N,
0x32 => orbclient::K_M,
0x33 => orbclient::K_COMMA,
0x34 => orbclient::K_PERIOD,
0x35 => orbclient::K_SLASH,
0x36 => orbclient::K_RIGHT_SHIFT,
0x37 => orbclient::K_NUM_ASTERISK,
0x38 => orbclient::K_ALT,
0x39 => orbclient::K_SPACE,
0x3A => orbclient::K_CAPS,
0x3B => orbclient::K_F1,
0x3C => orbclient::K_F2,
0x3D => orbclient::K_F3,
0x3E => orbclient::K_F4,
0x3F => orbclient::K_F5,
0x40 => orbclient::K_F6,
0x41 => orbclient::K_F7,
0x42 => orbclient::K_F8,
0x43 => orbclient::K_F9,
0x44 => orbclient::K_F10,
0x45 => orbclient::K_NUM,
0x46 => orbclient::K_SCROLL,
0x47 => orbclient::K_NUM_7,
0x48 => orbclient::K_NUM_8,
0x49 => orbclient::K_NUM_9,
0x4A => orbclient::K_NUM_MINUS,
0x4B => orbclient::K_NUM_4,
0x4C => orbclient::K_NUM_5,
0x4D => orbclient::K_NUM_6,
0x4E => orbclient::K_NUM_PLUS,
0x4F => orbclient::K_NUM_1,
0x50 => orbclient::K_NUM_2,
0x51 => orbclient::K_NUM_3,
0x52 => orbclient::K_NUM_0,
0x53 => orbclient::K_NUM_PERIOD,
/* 0x54 to 0x55 unused */
0x56 => 0x56, // UK Backslash
0x57 => orbclient::K_F11,
0x58 => orbclient::K_F12,
/* 0x59 to 0x7F unused */
/* 0x80 to 0xFF used for press/release detection */
_ => {
if pressed {
warn!("unknown scancode {:02X}", ps2_scancode);
}
0
}
}
};
if scancode != 0 && pressed {
match scancode {
orbclient::K_CAPS => { self.caps_lock = !self.caps_lock; self.leds_dirty = true; },
orbclient::K_NUM => { self.num_lock = !self.num_lock; self.leds_dirty = true; },
orbclient::K_SCROLL => { self.scroll_lock = !self.scroll_lock; self.leds_dirty = true; },
_ => (),
}
}
if self.leds_dirty {
self.leds_dirty = false;
self.ps2.set_leds(self.caps_lock, self.num_lock, self.scroll_lock);
}
if scancode != 0 {
self.keyboard_input
.write_event(
KeyEvent {
character: '\0',
scancode,
pressed,
}
.to_event(),
)
.expect("failed to write key event");
}
}
} else if self.vmmouse {
for _i in 0..256 {
let (status, _, _, _) = unsafe { vm::cmd(vm::ABSPOINTER_STATUS, 0) };
//TODO if ((status & VMMOUSE_ERROR) == VMMOUSE_ERROR)
let queue_length = status & 0xffff;
if queue_length == 0 {
break;
}
if queue_length % 4 != 0 {
error!("queue length not a multiple of 4: {}", queue_length);
break;
}
let (status, dx, dy, dz) = unsafe { vm::cmd(vm::ABSPOINTER_DATA, 4) };
if self.vmmouse_relative {
if dx != 0 || dy != 0 {
self.mouse_input
.write_event(
MouseRelativeEvent {
dx: dx as i32,
dy: dy as i32,
}
.to_event(),
)
.expect("ps2d: failed to write mouse event");
}
} else {
let x = dx as i32;
let y = dy as i32;
if x != self.mouse_x || y != self.mouse_y {
self.mouse_x = x;
self.mouse_y = y;
self.mouse_input
.write_event(MouseEvent { x, y }.to_event())
.expect("ps2d: failed to write mouse event");
}
};
if dz != 0 {
self.mouse_input
.write_event(
ScrollEvent {
x: 0,
y: -(dz as i32),
}
.to_event(),
)
.expect("ps2d: failed to write scroll event");
}
let left = status & vm::LEFT_BUTTON == vm::LEFT_BUTTON;
let middle = status & vm::MIDDLE_BUTTON == vm::MIDDLE_BUTTON;
let right = status & vm::RIGHT_BUTTON == vm::RIGHT_BUTTON;
if left != self.mouse_left
|| middle != self.mouse_middle
|| right != self.mouse_right
{
self.mouse_left = left;
self.mouse_middle = middle;
self.mouse_right = right;
self.mouse_input
.write_event(
ButtonEvent {
left,
middle,
right,
}
.to_event(),
)
.expect("ps2d: failed to write button event");
}
}
} else {
self.handle_mouse(Some(data));
}
}
pub fn handle_mouse(&mut self, data_opt: Option<u8>) {
// log::trace!(
// "handle_mouse state {:?} data {:?}",
// self.mouse_state,
// data_opt
// );
let mouse_res = match data_opt {
Some(data) => self.mouse_state.handle(data, &mut self.ps2),
None => self.mouse_state.handle_timeout(&mut self.ps2),
};
self.mouse_timeout = None;
let (packet_data, extra_packet) = match mouse_res {
MouseResult::None => {
return;
}
MouseResult::Packet(packet_data, extra_packet) => (packet_data, extra_packet),
MouseResult::Timeout(duration) => {
// Read current time
let mut time = TimeSpec::default();
match self.time_file.read(&mut time) {
Ok(_count) => {}
Err(err) => {
log::error!("failed to read time file: {}", err);
return;
}
}
// Add duration to time
time = timespec_from_duration(duration_from_timespec(time) + duration);
// Write next time
match self.time_file.write(&time) {
Ok(_count) => {}
Err(err) => {
log::error!("failed to write time file: {}", err);
}
}
self.mouse_timeout = Some(time);
return;
}
};
self.packets[self.packet_i] = packet_data;
self.packet_i += 1;
let flags = MousePacketFlags::from_bits_truncate(self.packets[0]);
if !flags.contains(MousePacketFlags::ALWAYS_ON) {
error!("mouse misalign {:X}", self.packets[0]);
self.packets = [0; 4];
self.packet_i = 0;
} else if self.packet_i >= self.packets.len() || (!extra_packet && self.packet_i >= 3) {
if !flags.contains(MousePacketFlags::X_OVERFLOW)
&& !flags.contains(MousePacketFlags::Y_OVERFLOW)
{
let mut dx = self.packets[1] as i32;
if flags.contains(MousePacketFlags::X_SIGN) {
dx -= 0x100;
}
let mut dy = -(self.packets[2] as i32);
if flags.contains(MousePacketFlags::Y_SIGN) {
dy += 0x100;
}
let mut dz = 0;
if extra_packet {
let mut scroll = (self.packets[3] & 0xF) as i8;
if scroll & (1 << 3) == 1 << 3 {
scroll -= 16;
}
dz = -scroll as i32;
}
if dx != 0 || dy != 0 {
self.mouse_input
.write_event(MouseRelativeEvent { dx, dy }.to_event())
.expect("ps2d: failed to write mouse event");
}
if dz != 0 {
self.mouse_input
.write_event(ScrollEvent { x: 0, y: dz }.to_event())
.expect("ps2d: failed to write scroll event");
}
let left = flags.contains(MousePacketFlags::LEFT_BUTTON);
let middle = flags.contains(MousePacketFlags::MIDDLE_BUTTON);
let right = flags.contains(MousePacketFlags::RIGHT_BUTTON);
if left != self.mouse_left
|| middle != self.mouse_middle
|| right != self.mouse_right
{
self.mouse_left = left;
self.mouse_middle = middle;
self.mouse_right = right;
self.mouse_input
.write_event(
ButtonEvent {
left,
middle,
right,
}
.to_event(),
)
.expect("ps2d: failed to write button event");
}
} else {
warn!(
"overflow {:X} {:X} {:X} {:X}",
self.packets[0], self.packets[1], self.packets[2], self.packets[3]
);
}
self.packets = [0; 4];
self.packet_i = 0;
}
}
}