Files
RedBear-OS/local/patches/base/P7-ps2d-intellimouse2-leds-controller-init.patch
T
vasilito f6c2eb2a8e feat: ACPI Wave 1 boot-critical hardening (P19) + robust patch generation
- 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
2026-05-18 14:07:42 +03:00

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 {