f6c2eb2a8e
- P19-init-startup-hardening: Replace panic-grade expect/unwrap in init startup paths (getns, register_scheme_to_ns, setrens, filename parsing) with graceful error handling and logging - P19-acpid-startup-hardening: Replace panic-grade calls in acpid with graceful degradation (rxsdt read failure → warn + exit 0, SDT parse → error + exit 1, I/O privilege → fatal, scheme registration → fatal, setrens → warn + continue, event loop errors → log + continue) - P18-9-msi-allocation-resilience: Regenerate with git diff -U0 -w format for maximum context resilience - fetch.rs: Change --fuzz=0 to --fuzz=3 for resilient patch application - AGENTS.md: Document robust patch generation technique as mandatory - Add P4/P5/P6/P7 patches (estale, dmi, i2c, ps2d hardening) - Add P21 kernel x2apic SMP fix patch - Multiple local recipe source improvements (redox-drm, driver-manager, driver-acpi, thermald) - Config updates for redbear-mini and redbear-device-services - Subsystem assessment document
758 lines
29 KiB
Diff
758 lines
29 KiB
Diff
diff --git a/drivers/input/ps2d/src/controller.rs b/drivers/input/ps2d/src/controller.rs
|
|
index d7af4cba..061ef2cf 100644
|
|
--- a/drivers/input/ps2d/src/controller.rs
|
|
+++ b/drivers/input/ps2d/src/controller.rs
|
|
@@ -97,6 +97,14 @@ enum KeyboardCommandData {
|
|
const DEFAULT_TIMEOUT: u64 = 50_000;
|
|
// Reset timeout in microseconds
|
|
const RESET_TIMEOUT: u64 = 1_000_000;
|
|
+// Maximum bytes to drain during flush (Linux: I8042_BUFFER_SIZE)
|
|
+const FLUSH_LIMIT: usize = 4096;
|
|
+// Controller self-test pass value (Linux: I8042_RET_CTL_TEST)
|
|
+const SELFTEST_PASS: u8 = 0x55;
|
|
+// Controller self-test retries (Linux: 5 attempts)
|
|
+const SELFTEST_RETRIES: usize = 5;
|
|
+// AUX port test pass value (Linux returns 0x00 on success)
|
|
+const AUX_TEST_PASS: u8 = 0x00;
|
|
|
|
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
|
pub struct Ps2 {
|
|
@@ -129,7 +137,15 @@ impl Ps2 {
|
|
|
|
#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
|
|
pub fn new() -> Self {
|
|
- unimplemented!()
|
|
+ // PS/2 controller is x86-only hardware. On other architectures, construct
|
|
+ // a zeroed struct; init() will fail at the controller self-test and the
|
|
+ // daemon will log an error and stop attempting keyboard/mouse operations.
|
|
+ Ps2 {
|
|
+ data: Mmio::new(0),
|
|
+ status: ReadOnly::new(Mmio::new(0)),
|
|
+ command: WriteOnly::new(Mmio::new(0)),
|
|
+ mouse_resets: 0,
|
|
+ }
|
|
}
|
|
|
|
fn status(&mut self) -> StatusFlags {
|
|
@@ -261,6 +277,30 @@ impl Ps2 {
|
|
self.write(command as u8)
|
|
}
|
|
|
|
+ pub fn set_leds(&mut self, caps: bool, num: bool, scroll: bool) {
|
|
+ let mut led_byte = 0u8;
|
|
+ if scroll { led_byte |= 1; }
|
|
+ if num { led_byte |= 2; }
|
|
+ if caps { led_byte |= 4; }
|
|
+ if let Err(err) = self.keyboard_command_inner(0xED) {
|
|
+ log::debug!("ps2d: LED command 0xED not supported: {:?}", err);
|
|
+ return;
|
|
+ }
|
|
+ match self.read_timeout(DEFAULT_TIMEOUT) {
|
|
+ Ok(0xFA) => {
|
|
+ if let Err(err) = self.write(led_byte) {
|
|
+ log::debug!("ps2d: failed to send LED byte {:02X}: {:?}", led_byte, err);
|
|
+ }
|
|
+ }
|
|
+ Ok(val) => {
|
|
+ log::debug!("ps2d: LED command ACK expected 0xFA, got {:02X}", val);
|
|
+ }
|
|
+ Err(err) => {
|
|
+ log::debug!("ps2d: LED command ACK timeout: {:?}", err);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
pub fn next(&mut self) -> Option<(bool, u8)> {
|
|
let status = self.status();
|
|
if status.contains(StatusFlags::OUTPUT_FULL) {
|
|
@@ -271,6 +311,50 @@ impl Ps2 {
|
|
}
|
|
}
|
|
|
|
+ /// Drain all pending bytes from the controller output buffer.
|
|
+ /// Borrowed from Linux i8042_flush(): stale firmware/BIOS bytes can be
|
|
+ /// misinterpreted as device responses during initialization.
|
|
+ fn flush(&mut self) -> usize {
|
|
+ let mut count = 0;
|
|
+ while self.status().contains(StatusFlags::OUTPUT_FULL) {
|
|
+ if count >= FLUSH_LIMIT {
|
|
+ warn!("flush: exceeded limit, controller may be stuck");
|
|
+ break;
|
|
+ }
|
|
+ let data = self.data.read();
|
|
+ trace!("flush: discarded {:02X}", data);
|
|
+ count += 1;
|
|
+ }
|
|
+ if count > 0 {
|
|
+ debug!("flushed {} stale bytes from controller", count);
|
|
+ }
|
|
+ count
|
|
+ }
|
|
+
|
|
+ /// Test the AUX (mouse) port via controller command 0xA9.
|
|
+ /// Borrowed from Linux: verifies electrical connectivity before
|
|
+ /// attempting to talk to the mouse. Returns true if the port passed.
|
|
+ fn test_aux_port(&mut self) -> bool {
|
|
+ if let Err(err) = self.command(Command::TestSecond) {
|
|
+ warn!("aux port test command failed: {:?}", err);
|
|
+ return false;
|
|
+ }
|
|
+ match self.read() {
|
|
+ Ok(AUX_TEST_PASS) => {
|
|
+ debug!("aux port test passed");
|
|
+ true
|
|
+ }
|
|
+ Ok(val) => {
|
|
+ warn!("aux port test failed: {:02X}", val);
|
|
+ false
|
|
+ }
|
|
+ Err(err) => {
|
|
+ warn!("aux port test read timeout: {:?}", err);
|
|
+ false
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
pub fn init_keyboard(&mut self) -> Result<(), Error> {
|
|
let mut b;
|
|
|
|
@@ -308,66 +392,125 @@ impl Ps2 {
|
|
}
|
|
|
|
pub fn init(&mut self) -> Result<(), Error> {
|
|
+ // Linux i8042_controller_check(): verify controller is present by
|
|
+ // flushing any stale data. A stuck output buffer means no controller.
|
|
+ self.flush();
|
|
+
|
|
+ // Bare-metal controllers may be slow after firmware handoff.
|
|
+ // Give the controller a moment to finish POST before sending commands.
|
|
+ std::thread::sleep(std::time::Duration::from_millis(50));
|
|
+
|
|
{
|
|
- // Disable devices
|
|
- self.command(Command::DisableFirst)?;
|
|
- self.command(Command::DisableSecond)?;
|
|
+ // Disable both ports first — use retry because the controller
|
|
+ // may still be settling or temporarily unresponsive.
|
|
+ // Failure here is non-fatal: we continue and attempt the rest
|
|
+ // of initialization. A truly absent controller will fail later
|
|
+ // at self-test or keyboard reset.
|
|
+ if let Err(err) = self.retry(
|
|
+ format_args!("disable first port"),
|
|
+ 3,
|
|
+ |x| x.command(Command::DisableFirst),
|
|
+ ) {
|
|
+ warn!("disable first port failed: {:?}", err);
|
|
+ }
|
|
+ if let Err(err) = self.retry(
|
|
+ format_args!("disable second port"),
|
|
+ 3,
|
|
+ |x| x.command(Command::DisableSecond),
|
|
+ ) {
|
|
+ warn!("disable second port failed: {:?}", err);
|
|
+ }
|
|
}
|
|
|
|
- // Disable clocks, disable interrupts, and disable translate
|
|
+ // Flush again after disabling — firmware may have queued more bytes
|
|
+ self.flush();
|
|
+
|
|
+ // Linux i8042_controller_init() step 1: write a known-safe config
|
|
+ // (interrupts off, both ports disabled) so stale config can't cause
|
|
+ // spurious interrupts during the rest of init.
|
|
{
|
|
- // Since the default config may have interrupts enabled, and the kernel may eat up
|
|
- // our data in that case, we will write a config without reading the current one
|
|
let config = ConfigFlags::POST_PASSED
|
|
| ConfigFlags::FIRST_DISABLED
|
|
| ConfigFlags::SECOND_DISABLED;
|
|
self.set_config(config)?;
|
|
}
|
|
|
|
- // The keyboard seems to still collect bytes even when we disable
|
|
- // the port, so we must disable the keyboard too
|
|
+ // Linux i8042_controller_selftest(): retry up to 5 times with delay.
|
|
+ // "On some really fragile systems this does not take the first time."
|
|
+ {
|
|
+ let mut passed = false;
|
|
+ for attempt in 0..SELFTEST_RETRIES {
|
|
+ if let Err(err) = self.command(Command::TestController) {
|
|
+ warn!("self-test command failed (attempt {}): {:?}", attempt + 1, err);
|
|
+ continue;
|
|
+ }
|
|
+ match self.read() {
|
|
+ Ok(SELFTEST_PASS) => {
|
|
+ passed = true;
|
|
+ break;
|
|
+ }
|
|
+ Ok(val) => {
|
|
+ warn!(
|
|
+ "self-test unexpected value {:02X} (attempt {}/{})",
|
|
+ val,
|
|
+ attempt + 1,
|
|
+ SELFTEST_RETRIES
|
|
+ );
|
|
+ }
|
|
+ Err(err) => {
|
|
+ warn!("self-test read timeout (attempt {}): {:?}", attempt + 1, err);
|
|
+ }
|
|
+ }
|
|
+ // Linux: msleep(50) between retries
|
|
+ std::thread::sleep(std::time::Duration::from_millis(50));
|
|
+ }
|
|
+ if !passed {
|
|
+ // Linux on x86: "giving up on controller selftest, continuing anyway"
|
|
+ warn!("controller self-test did not pass after {} attempts, continuing", SELFTEST_RETRIES);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ // Flush any bytes the self-test may have left behind
|
|
+ self.flush();
|
|
+
|
|
+ // Linux i8042_controller_init() step 2: set keyboard defaults
|
|
+ // (disable scanning so keyboard doesn't send scancodes during init)
|
|
self.retry(format_args!("keyboard defaults"), 4, |x| {
|
|
- // Set defaults and disable scanning
|
|
let b = x.keyboard_command(KeyboardCommand::SetDefaultsDisable)?;
|
|
if b != 0xFA {
|
|
error!("keyboard failed to set defaults: {:02X}", b);
|
|
return Err(Error::CommandRetry);
|
|
}
|
|
-
|
|
Ok(b)
|
|
})?;
|
|
|
|
- {
|
|
- // Perform the self test
|
|
- self.command(Command::TestController)?;
|
|
- let r = self.read()?;
|
|
- if r != 0x55 {
|
|
- warn!("self test unexpected value: {:02X}", r);
|
|
- }
|
|
- }
|
|
-
|
|
// Initialize keyboard
|
|
if let Err(err) = self.init_keyboard() {
|
|
error!("failed to initialize keyboard: {:?}", err);
|
|
return Err(err);
|
|
}
|
|
|
|
- // Enable second device
|
|
- let enable_mouse = match self.command(Command::EnableSecond) {
|
|
- Ok(()) => true,
|
|
- Err(err) => {
|
|
- error!("failed to initialize mouse: {:?}", err);
|
|
- false
|
|
+ // Linux: test AUX port (command 0xA9) before enabling.
|
|
+ // Skips mouse init entirely if the port is not electrically present.
|
|
+ let aux_ok = self.test_aux_port();
|
|
+
|
|
+ // Enable second device (mouse) only if AUX port tested OK
|
|
+ let enable_mouse = if aux_ok {
|
|
+ match self.command(Command::EnableSecond) {
|
|
+ Ok(()) => true,
|
|
+ Err(err) => {
|
|
+ warn!("failed to enable aux port after test passed: {:?}", err);
|
|
+ false
|
|
+ }
|
|
}
|
|
+ } else {
|
|
+ info!("skipping mouse init: aux port test did not pass");
|
|
+ false
|
|
};
|
|
|
|
{
|
|
- // Enable keyboard data reporting
|
|
- // Use inner function to prevent retries
|
|
- // Response is ignored since scanning is now on
|
|
if let Err(err) = self.keyboard_command_inner(KeyboardCommand::EnableReporting as u8) {
|
|
error!("failed to initialize keyboard reporting: {:?}", err);
|
|
- //TODO: fix by using interrupts?
|
|
}
|
|
}
|
|
|
|
diff --git a/drivers/input/ps2d/src/main.rs b/drivers/input/ps2d/src/main.rs
|
|
index db17de2a..86f903bf 100644
|
|
--- a/drivers/input/ps2d/src/main.rs
|
|
+++ b/drivers/input/ps2d/src/main.rs
|
|
@@ -11,7 +11,7 @@ use std::process;
|
|
|
|
use common::acquire_port_io_rights;
|
|
use event::{user_data, EventQueue};
|
|
-use inputd::ProducerHandle;
|
|
+use inputd::InputProducer;
|
|
|
|
use crate::state::Ps2d;
|
|
|
|
@@ -31,7 +31,8 @@ fn daemon(daemon: daemon::Daemon) -> ! {
|
|
|
|
acquire_port_io_rights().expect("ps2d: failed to get I/O permission");
|
|
|
|
- let input = ProducerHandle::new().expect("ps2d: failed to open input producer");
|
|
+ let keyboard_input = InputProducer::new_named_or_fallback("ps2-keyboard").expect("ps2d: failed to open keyboard input");
|
|
+ let mouse_input = InputProducer::new_named_or_fallback("ps2-mouse").expect("ps2d: failed to open mouse input");
|
|
|
|
user_data! {
|
|
enum Source {
|
|
@@ -93,7 +94,7 @@ fn daemon(daemon: daemon::Daemon) -> ! {
|
|
|
|
daemon.ready();
|
|
|
|
- let mut ps2d = Ps2d::new(input, time_file);
|
|
+ let mut ps2d = Ps2d::new(keyboard_input, mouse_input, time_file);
|
|
|
|
let mut data = [0; 256];
|
|
for event in event_queue.map(|e| e.expect("ps2d: failed to get next event").user_data) {
|
|
diff --git a/drivers/input/ps2d/src/mouse.rs b/drivers/input/ps2d/src/mouse.rs
|
|
index 9e95ab88..23099493 100644
|
|
--- a/drivers/input/ps2d/src/mouse.rs
|
|
+++ b/drivers/input/ps2d/src/mouse.rs
|
|
@@ -1,8 +1,8 @@
|
|
use crate::controller::Ps2;
|
|
use std::time::Duration;
|
|
|
|
-pub const RESET_RETRIES: usize = 10;
|
|
-pub const RESET_TIMEOUT: Duration = Duration::from_millis(1000);
|
|
+pub const RESET_RETRIES: usize = 3;
|
|
+pub const RESET_TIMEOUT: Duration = Duration::from_millis(250);
|
|
pub const COMMAND_TIMEOUT: Duration = Duration::from_millis(100);
|
|
|
|
#[derive(Clone, Copy, Debug)]
|
|
@@ -61,6 +61,10 @@ impl MouseTx {
|
|
if data == 0xFA {
|
|
self.write_i += 1;
|
|
self.try_write(ps2)?;
|
|
+ } else if data == 0xFE {
|
|
+ // PS/2 RESEND: mouse asks us to resend the current command byte
|
|
+ log::debug!("mouse requested resend for byte {:02X}, resending", self.write.get(self.write_i).unwrap_or(&0));
|
|
+ self.try_write(ps2)?;
|
|
} else {
|
|
log::error!("unknown mouse response {:02X}", data);
|
|
return Err(());
|
|
@@ -80,8 +84,7 @@ enum MouseId {
|
|
Base = 0x00,
|
|
/// Mouse sends fourth byte with scroll
|
|
Intellimouse1 = 0x03,
|
|
- /// Mouse sends fourth byte with scroll, button 4, and button 5
|
|
- //TODO: support this mouse type
|
|
+ /// Mouse sends fourth byte with scroll and buttons 4/5
|
|
Intellimouse2 = 0x04,
|
|
}
|
|
|
|
@@ -94,25 +97,16 @@ pub enum TouchpadCommand {
|
|
|
|
#[derive(Debug)]
|
|
pub enum MouseState {
|
|
- /// No mouse found
|
|
None,
|
|
- /// Ready to initialize mouse
|
|
Init,
|
|
- /// Reset command is sent
|
|
Reset,
|
|
- /// BAT completion code returned
|
|
Bat,
|
|
- /// Identify touchpad
|
|
IdentifyTouchpad { tx: MouseTx },
|
|
- /// Enable intellimouse features
|
|
EnableIntellimouse { tx: MouseTx },
|
|
- /// Status request
|
|
+ EnableIntellimouse2 { tx: MouseTx },
|
|
Status { index: usize },
|
|
- /// Device ID update
|
|
DeviceId,
|
|
- /// Enable reporting command sent
|
|
EnableReporting { id: u8 },
|
|
- /// Mouse is streaming
|
|
Streaming { id: u8 },
|
|
}
|
|
|
|
@@ -194,9 +188,7 @@ impl MouseState {
|
|
let cmd = TouchpadCommand::Identify as u8;
|
|
match MouseTx::new(
|
|
&[
|
|
- // Ensure command alignment
|
|
MouseCommand::SetScaling1To1 as u8,
|
|
- // Send special identify touchpad command
|
|
MouseCommandData::SetResolution as u8,
|
|
0,
|
|
MouseCommandData::SetResolution as u8,
|
|
@@ -205,7 +197,6 @@ impl MouseState {
|
|
0,
|
|
MouseCommandData::SetResolution as u8,
|
|
0,
|
|
- // Status request
|
|
MouseCommand::StatusRequest as u8,
|
|
],
|
|
3,
|
|
@@ -215,7 +206,7 @@ impl MouseState {
|
|
*self = MouseState::IdentifyTouchpad { tx };
|
|
MouseResult::Timeout(COMMAND_TIMEOUT)
|
|
}
|
|
- Err(()) => self.enable_intellimouse(ps2),
|
|
+ Err(()) => self.enable_intellimouse2(ps2),
|
|
}
|
|
}
|
|
|
|
@@ -240,6 +231,27 @@ impl MouseState {
|
|
}
|
|
}
|
|
|
|
+ fn enable_intellimouse2(&mut self, ps2: &mut Ps2) -> MouseResult {
|
|
+ match MouseTx::new(
|
|
+ &[
|
|
+ MouseCommandData::SetSampleRate as u8,
|
|
+ 200,
|
|
+ MouseCommandData::SetSampleRate as u8,
|
|
+ 200,
|
|
+ MouseCommandData::SetSampleRate as u8,
|
|
+ 80,
|
|
+ ],
|
|
+ 0,
|
|
+ ps2,
|
|
+ ) {
|
|
+ Ok(tx) => {
|
|
+ *self = MouseState::EnableIntellimouse2 { tx };
|
|
+ MouseResult::Timeout(COMMAND_TIMEOUT)
|
|
+ }
|
|
+ Err(()) => self.enable_intellimouse(ps2),
|
|
+ }
|
|
+ }
|
|
+
|
|
pub fn handle(&mut self, data: u8, ps2: &mut Ps2) -> MouseResult {
|
|
match *self {
|
|
MouseState::None | MouseState::Init => {
|
|
@@ -260,17 +272,22 @@ impl MouseState {
|
|
MouseResult::Timeout(COMMAND_TIMEOUT)
|
|
} else {
|
|
log::warn!("unknown mouse response {:02X} after reset", data);
|
|
- self.reset(ps2)
|
|
+ *self = MouseState::None;
|
|
+ MouseResult::None
|
|
}
|
|
}
|
|
MouseState::Bat => {
|
|
if data == MouseId::Base as u8 {
|
|
- // Enable intellimouse features
|
|
+ // Base mouse - enable intellimouse features
|
|
log::debug!("BAT mouse id {:02X} (base)", data);
|
|
self.identify_touchpad(ps2)
|
|
} else if data == MouseId::Intellimouse1 as u8 {
|
|
- // Extra packet already enabled
|
|
- log::debug!("BAT mouse id {:02X} (intellimouse)", data);
|
|
+ // Scroll wheel already enabled
|
|
+ log::debug!("BAT mouse id {:02X} (intellimouse1)", data);
|
|
+ self.enable_reporting(data, ps2)
|
|
+ } else if data == MouseId::Intellimouse2 as u8 {
|
|
+ // Scroll wheel + buttons 4/5 already enabled
|
|
+ log::debug!("BAT mouse id {:02X} (intellimouse2)", data);
|
|
self.enable_reporting(data, ps2)
|
|
} else {
|
|
log::warn!("unknown mouse id {:02X} after BAT", data);
|
|
@@ -291,7 +308,17 @@ impl MouseState {
|
|
Err(()) => self.enable_intellimouse(ps2),
|
|
}
|
|
}
|
|
- MouseState::EnableIntellimouse { ref mut tx } => match tx.handle(data, ps2) {
|
|
+MouseState::EnableIntellimouse { ref mut tx } => match tx.handle(data, ps2) {
|
|
+ Ok(done) => {
|
|
+ if done {
|
|
+ self.request_status(ps2)
|
|
+ } else {
|
|
+ MouseResult::Timeout(COMMAND_TIMEOUT)
|
|
+ }
|
|
+ }
|
|
+ Err(()) => self.request_id(ps2),
|
|
+ },
|
|
+ MouseState::EnableIntellimouse2 { ref mut tx } => match tx.handle(data, ps2) {
|
|
Ok(done) => {
|
|
if done {
|
|
self.request_status(ps2)
|
|
@@ -299,7 +326,7 @@ impl MouseState {
|
|
MouseResult::Timeout(COMMAND_TIMEOUT)
|
|
}
|
|
}
|
|
- Err(()) => self.request_status(ps2),
|
|
+ Err(()) => self.enable_intellimouse(ps2),
|
|
},
|
|
MouseState::Status { index } => {
|
|
match index {
|
|
@@ -324,7 +351,7 @@ impl MouseState {
|
|
// Command OK response
|
|
//TODO: handle this separately?
|
|
MouseResult::Timeout(COMMAND_TIMEOUT)
|
|
- } else if data == MouseId::Base as u8 || data == MouseId::Intellimouse1 as u8 {
|
|
+ } else if data == MouseId::Base as u8 || data == MouseId::Intellimouse1 as u8 || data == MouseId::Intellimouse2 as u8 {
|
|
log::debug!("mouse id {:02X}", data);
|
|
self.enable_reporting(data, ps2)
|
|
} else {
|
|
@@ -339,11 +366,15 @@ impl MouseState {
|
|
MouseResult::None
|
|
}
|
|
MouseState::Streaming { id } => {
|
|
- MouseResult::Packet(data, id == MouseId::Intellimouse1 as u8)
|
|
+ MouseResult::Packet(data, id == MouseId::Intellimouse1 as u8 || id == MouseId::Intellimouse2 as u8)
|
|
}
|
|
}
|
|
}
|
|
|
|
+ pub fn streaming_is_intellimouse2(&self) -> bool {
|
|
+ matches!(self, MouseState::Streaming { id } if *id == MouseId::Intellimouse2 as u8)
|
|
+ }
|
|
+
|
|
pub fn handle_timeout(&mut self, ps2: &mut Ps2) -> MouseResult {
|
|
match *self {
|
|
MouseState::None | MouseState::Streaming { .. } => MouseResult::None,
|
|
@@ -352,12 +383,14 @@ impl MouseState {
|
|
self.reset(ps2)
|
|
}
|
|
MouseState::Reset => {
|
|
- log::warn!("timeout waiting for mouse reset");
|
|
- self.reset(ps2)
|
|
+ log::debug!("timeout waiting for mouse reset, fast-failing");
|
|
+ *self = MouseState::None;
|
|
+ MouseResult::None
|
|
}
|
|
MouseState::Bat => {
|
|
- log::warn!("timeout waiting for BAT completion");
|
|
- self.reset(ps2)
|
|
+ log::debug!("timeout waiting for BAT completion, fast-failing");
|
|
+ *self = MouseState::None;
|
|
+ MouseResult::None
|
|
}
|
|
MouseState::IdentifyTouchpad { .. } => {
|
|
//TODO: retry?
|
|
@@ -365,10 +398,13 @@ impl MouseState {
|
|
self.request_status(ps2)
|
|
}
|
|
MouseState::EnableIntellimouse { .. } => {
|
|
- //TODO: retry?
|
|
log::warn!("timeout enabling intellimouse");
|
|
self.request_status(ps2)
|
|
}
|
|
+ MouseState::EnableIntellimouse2 { .. } => {
|
|
+ log::warn!("timeout enabling intellimouse2, falling back to intellimouse");
|
|
+ self.enable_intellimouse(ps2)
|
|
+ }
|
|
MouseState::Status { index } => {
|
|
log::warn!("timeout waiting for mouse status {}", index);
|
|
self.request_id(ps2)
|
|
diff --git a/drivers/input/ps2d/src/state.rs b/drivers/input/ps2d/src/state.rs
|
|
index 9018dc6b..8f5832f6 100644
|
|
--- a/drivers/input/ps2d/src/state.rs
|
|
+++ b/drivers/input/ps2d/src/state.rs
|
|
@@ -1,4 +1,4 @@
|
|
-use inputd::ProducerHandle;
|
|
+use inputd::InputProducer;
|
|
use log::{error, warn};
|
|
use orbclient::{ButtonEvent, KeyEvent, MouseEvent, MouseRelativeEvent, ScrollEvent};
|
|
use std::{
|
|
@@ -44,7 +44,8 @@ pub struct Ps2d {
|
|
ps2: Ps2,
|
|
vmmouse: bool,
|
|
vmmouse_relative: bool,
|
|
- input: ProducerHandle,
|
|
+ keyboard_input: InputProducer,
|
|
+ mouse_input: InputProducer,
|
|
time_file: File,
|
|
extended: bool,
|
|
mouse_x: i32,
|
|
@@ -52,16 +53,24 @@ pub struct Ps2d {
|
|
mouse_left: bool,
|
|
mouse_middle: bool,
|
|
mouse_right: bool,
|
|
+ mouse_button_4: bool,
|
|
+ mouse_button_5: 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(input: ProducerHandle, time_file: File) -> Self {
|
|
+ pub fn new(keyboard_input: InputProducer, mouse_input: InputProducer, time_file: File) -> Self {
|
|
let mut ps2 = Ps2::new();
|
|
- ps2.init().expect("failed to initialize");
|
|
+ if let Err(err) = ps2.init() {
|
|
+ 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;
|
|
@@ -77,7 +86,8 @@ impl Ps2d {
|
|
ps2,
|
|
vmmouse,
|
|
vmmouse_relative,
|
|
- input,
|
|
+ keyboard_input,
|
|
+ mouse_input,
|
|
time_file,
|
|
extended: false,
|
|
mouse_x: 0,
|
|
@@ -85,10 +95,16 @@ impl Ps2d {
|
|
mouse_left: false,
|
|
mouse_middle: false,
|
|
mouse_right: false,
|
|
+ mouse_button_4: false,
|
|
+ mouse_button_5: 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 {
|
|
@@ -96,6 +112,12 @@ impl Ps2d {
|
|
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
|
|
}
|
|
|
|
@@ -272,8 +294,21 @@ impl Ps2d {
|
|
}
|
|
};
|
|
|
|
+ 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.input
|
|
+ self.keyboard_input
|
|
.write_event(
|
|
KeyEvent {
|
|
character: '\0',
|
|
@@ -304,7 +339,7 @@ impl Ps2d {
|
|
|
|
if self.vmmouse_relative {
|
|
if dx != 0 || dy != 0 {
|
|
- self.input
|
|
+ self.mouse_input
|
|
.write_event(
|
|
MouseRelativeEvent {
|
|
dx: dx as i32,
|
|
@@ -320,14 +355,14 @@ impl Ps2d {
|
|
if x != self.mouse_x || y != self.mouse_y {
|
|
self.mouse_x = x;
|
|
self.mouse_y = y;
|
|
- self.input
|
|
+ self.mouse_input
|
|
.write_event(MouseEvent { x, y }.to_event())
|
|
.expect("ps2d: failed to write mouse event");
|
|
}
|
|
};
|
|
|
|
if dz != 0 {
|
|
- self.input
|
|
+ self.mouse_input
|
|
.write_event(
|
|
ScrollEvent {
|
|
x: 0,
|
|
@@ -348,7 +383,7 @@ impl Ps2d {
|
|
self.mouse_left = left;
|
|
self.mouse_middle = middle;
|
|
self.mouse_right = right;
|
|
- self.input
|
|
+ self.mouse_input
|
|
.write_event(
|
|
ButtonEvent {
|
|
left,
|
|
@@ -432,22 +467,35 @@ impl Ps2d {
|
|
}
|
|
|
|
let mut dz = 0;
|
|
+ let mut button_4 = false;
|
|
+ let mut button_5 = false;
|
|
if extra_packet {
|
|
- let mut scroll = (self.packets[3] & 0xF) as i8;
|
|
- if scroll & (1 << 3) == 1 << 3 {
|
|
- scroll -= 16;
|
|
+ let fourth = self.packets[3];
|
|
+ if self.mouse_state.streaming_is_intellimouse2() {
|
|
+ let mut scroll = (fourth & 0x0F) as i8;
|
|
+ if scroll & 0x08 != 0 {
|
|
+ scroll -= 16;
|
|
+ }
|
|
+ dz = -(scroll as i32);
|
|
+ button_4 = (fourth & 0x10) != 0;
|
|
+ button_5 = (fourth & 0x20) != 0;
|
|
+ } else {
|
|
+ let mut scroll = (fourth & 0xF) as i8;
|
|
+ if scroll & (1 << 3) == 1 << 3 {
|
|
+ scroll -= 16;
|
|
+ }
|
|
+ dz = -scroll as i32;
|
|
}
|
|
- dz = -scroll as i32;
|
|
}
|
|
|
|
if dx != 0 || dy != 0 {
|
|
- self.input
|
|
+ self.mouse_input
|
|
.write_event(MouseRelativeEvent { dx, dy }.to_event())
|
|
.expect("ps2d: failed to write mouse event");
|
|
}
|
|
|
|
if dz != 0 {
|
|
- self.input
|
|
+ self.mouse_input
|
|
.write_event(ScrollEvent { x: 0, y: dz }.to_event())
|
|
.expect("ps2d: failed to write scroll event");
|
|
}
|
|
@@ -458,11 +506,15 @@ impl Ps2d {
|
|
if left != self.mouse_left
|
|
|| middle != self.mouse_middle
|
|
|| right != self.mouse_right
|
|
+ || button_4 != self.mouse_button_4
|
|
+ || button_5 != self.mouse_button_5
|
|
{
|
|
self.mouse_left = left;
|
|
self.mouse_middle = middle;
|
|
self.mouse_right = right;
|
|
- self.input
|
|
+ self.mouse_button_4 = button_4;
|
|
+ self.mouse_button_5 = button_5;
|
|
+ self.mouse_input
|
|
.write_event(
|
|
ButtonEvent {
|
|
left,
|
|
diff --git a/drivers/input/ps2d/src/vm.rs b/drivers/input/ps2d/src/vm.rs
|
|
index 71b71417..769a78e9 100644
|
|
--- a/drivers/input/ps2d/src/vm.rs
|
|
+++ b/drivers/input/ps2d/src/vm.rs
|
|
@@ -64,8 +64,8 @@ pub unsafe fn cmd(cmd: u32, arg: u32) -> (u32, u32, u32, u32) {
|
|
}
|
|
|
|
#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
|
|
-pub unsafe fn cmd(cmd: u32, arg: u32) -> (u32, u32, u32, u32) {
|
|
- unimplemented!()
|
|
+pub unsafe fn cmd(_cmd: u32, _arg: u32) -> (u32, u32, u32, u32) {
|
|
+ (0, 0, 0, 0)
|
|
}
|
|
|
|
pub fn enable(relative: bool) -> bool {
|