From ce9c2a1fb48b437d8f6041ed23941e89ae8bbcdf Mon Sep 17 00:00:00 2001 From: Jeremy Soller Date: Thu, 29 Jan 2026 08:56:00 -0700 Subject: [PATCH] ps2d: add MouseTx abstraction and WIP touchpad identification --- drivers/input/ps2d/src/mouse.rs | 207 ++++++++++++++++++++++++-------- 1 file changed, 159 insertions(+), 48 deletions(-) diff --git a/drivers/input/ps2d/src/mouse.rs b/drivers/input/ps2d/src/mouse.rs index 6b3c4095b3..5702fd927f 100644 --- a/drivers/input/ps2d/src/mouse.rs +++ b/drivers/input/ps2d/src/mouse.rs @@ -21,9 +21,56 @@ enum MouseCommand { #[derive(Clone, Copy, Debug)] #[repr(u8)] enum MouseCommandData { + SetResolution = 0xE8, SetSampleRate = 0xF3, } +#[derive(Debug)] +struct MouseTx { + write: &'static [u8], + write_i: usize, + read: Vec, + read_bytes: usize, +} + +impl MouseTx { + fn new(write: &'static [u8], read_bytes: usize, ps2: &mut Ps2) -> Result { + let mut this = Self { + write, + write_i: 0, + read: Vec::with_capacity(read_bytes), + read_bytes, + }; + this.try_write(ps2)?; + Ok(this) + } + + fn try_write(&mut self, ps2: &mut Ps2) -> Result<(), ()> { + if let Some(write) = self.write.get(self.write_i) { + if let Err(err) = ps2.mouse_command_async(*write) { + log::error!("failed to write {:02X} to mouse: {:?}", write, err); + return Err(()); + } + } + Ok(()) + } + + fn handle(&mut self, data: u8, ps2: &mut Ps2) -> Result { + if self.write_i < self.write.len() { + if data == 0xFA { + self.write_i += 1; + self.try_write(ps2)?; + } else { + log::error!("unknown mouse response {:02X}", data); + return Err(()); + } + } else { + self.read.push(data); + } + Ok(self.write_i >= self.write.len() && self.read.len() >= self.read_bytes) + } +} + #[derive(Clone, Copy, Debug)] #[repr(u8)] #[allow(dead_code)] @@ -47,8 +94,12 @@ pub enum MouseState { Reset, /// BAT completion code returned Bat, + /// Identify touchpad + IdentifyTouchpad { tx: MouseTx }, /// Enable intellimouse features - EnableIntellimouse { index: usize, sent_command: bool }, + EnableIntellimouse { tx: MouseTx }, + /// Status request + Status { index: usize }, /// Device ID update DeviceId, /// Enable reporting command sent @@ -96,6 +147,20 @@ impl MouseState { } } + fn request_status(&mut self, ps2: &mut Ps2) -> MouseResult { + match ps2.mouse_command_async(MouseCommand::StatusRequest as u8) { + Ok(()) => { + *self = MouseState::Status { index: 0 }; + MouseResult::Timeout(COMMAND_TIMEOUT) + } + Err(err) => { + log::error!("failed to request mouse status: {:?}", err); + //TODO: reset mouse instead? + self.request_id(ps2) + } + } + } + fn request_id(&mut self, ps2: &mut Ps2) -> MouseResult { match ps2.mouse_command_async(MouseCommand::GetDeviceId as u8) { Ok(()) => { @@ -110,40 +175,45 @@ impl MouseState { } } + fn identify_touchpad(&mut self, ps2: &mut Ps2) -> MouseResult { + 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, 0, + MouseCommandData::SetResolution as u8, 0, + MouseCommandData::SetResolution as u8, 0, + // Status request + MouseCommand::StatusRequest as u8 + ], 3, ps2) { + Ok(tx) => { + *self = MouseState::IdentifyTouchpad { tx }; + MouseResult::Timeout(COMMAND_TIMEOUT) + }, + Err(()) => { + self.enable_intellimouse(ps2) + } + } + } + fn enable_intellimouse( &mut self, - index: usize, - sent_command: bool, ps2: &mut Ps2, ) -> MouseResult { - let magic = [200, 100, 80]; - if let Some(data) = magic.get(index) { - match ps2.mouse_command_async(if sent_command { - *data - } else { - MouseCommandData::SetSampleRate as u8 - }) { - Ok(()) => { - *self = if sent_command { - MouseState::EnableIntellimouse { - index: index + 1, - sent_command: false, - } - } else { - MouseState::EnableIntellimouse { - index, - sent_command: true, - } - }; - MouseResult::Timeout(COMMAND_TIMEOUT) - } - Err(err) => { - log::error!("failed to send intellimouse command: {:?}", err); - self.request_id(ps2) - } + match MouseTx::new(&[ + MouseCommandData::SetSampleRate as u8, 200, + MouseCommandData::SetSampleRate as u8, 100, + MouseCommandData::SetSampleRate as u8, 80 + ], 0, ps2) { + Ok(tx) => { + *self = MouseState::EnableIntellimouse { tx }; + MouseResult::Timeout(COMMAND_TIMEOUT) + }, + Err(()) => { + self.request_id(ps2) } - } else { - self.request_id(ps2) } } @@ -174,7 +244,7 @@ impl MouseState { if data == MouseId::Base as u8 { // Enable intellimouse features log::debug!("BAT mouse id {:02X} (base)", data); - self.enable_intellimouse(0, false, ps2) + self.identify_touchpad(ps2) } else if data == MouseId::Intellimouse1 as u8 { // Extra packet already enabled log::debug!("BAT mouse id {:02X} (intellimouse)", data); @@ -184,18 +254,50 @@ impl MouseState { MouseResult::Timeout(RESET_TIMEOUT) } } - MouseState::EnableIntellimouse { - index, - sent_command, - } => { - if data == 0xFA { - self.enable_intellimouse(index, sent_command, ps2) - } else { - log::warn!( - "unknown mouse response {:02X} while enabling intellimouse", - data - ); - self.request_id(ps2) + MouseState::IdentifyTouchpad { ref mut tx } => { + match tx.handle(data, ps2) { + Ok(done) => if done { + //TODO: handle touchpad identification + // If tx.read[1] == 0x47, this is a synaptics touchpad + self.request_status(ps2) + } else { + MouseResult::Timeout(COMMAND_TIMEOUT) + }, + Err(()) => { + self.enable_intellimouse(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_status(ps2) + } + } + } + MouseState::Status { index } => { + match index { + 0 => { + //TODO: check response + *self = MouseState::Status { index: 1 }; + MouseResult::Timeout(COMMAND_TIMEOUT) + } + 1 => { + *self = MouseState::Status { index: 2 }; + MouseResult::Timeout(COMMAND_TIMEOUT) + } + 2 => { + *self = MouseState::Status { index: 3 }; + MouseResult::Timeout(COMMAND_TIMEOUT) + } + _ => { + self.request_id(ps2) + } } } MouseState::DeviceId => { @@ -232,27 +334,36 @@ impl MouseState { self.reset(ps2) } MouseState::Reset => { - log::warn!("timeout while waiting for mouse reset"); + log::warn!("timeout waiting for mouse reset"); //TODO: retry reset? *self = MouseState::None; MouseResult::None } MouseState::Bat => { - log::warn!("timeout while waiting for BAT completion"); + log::warn!("timeout waiting for BAT completion"); //TODO: limit number of resets self.reset(ps2) } + MouseState::IdentifyTouchpad { .. } => { + //TODO: retry? + log::warn!("timeout identifying touchpad"); + self.request_status(ps2) + } MouseState::EnableIntellimouse { .. } => { //TODO: retry? - log::warn!("timeout while enabling intellimouse"); + log::warn!("timeout enabling intellimouse"); + self.request_status(ps2) + } + MouseState::Status { index } => { + log::warn!("timeout waiting for mouse status {}", index); self.request_id(ps2) } MouseState::DeviceId => { - log::warn!("timeout while requesting mouse id"); + log::warn!("timeout requesting mouse id"); self.enable_reporting(0, ps2) } MouseState::EnableReporting { id } => { - log::warn!("timeout while enabling reporting"); + log::warn!("timeout enabling reporting"); //TODO: limit number of retries self.enable_reporting(id, ps2) }