PS2 mouse refactor
This commit is contained in:
@@ -13,7 +13,7 @@ use common::io::Pio;
|
||||
#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
|
||||
use common::io::Mmio;
|
||||
|
||||
use log::{debug, error, trace, warn};
|
||||
use log::{debug, error, info, trace, warn};
|
||||
|
||||
use std::fmt;
|
||||
|
||||
@@ -25,8 +25,6 @@ pub enum Error {
|
||||
WriteTimeout,
|
||||
CommandTimeout(Command),
|
||||
WriteConfigTimeout(ConfigFlags),
|
||||
MouseCommandFail(MouseCommand),
|
||||
MouseCommandDataFail(MouseCommandData),
|
||||
KeyboardCommandFail(KeyboardCommand),
|
||||
KeyboardCommandDataFail(KeyboardCommandData),
|
||||
}
|
||||
@@ -94,30 +92,10 @@ enum KeyboardCommandData {
|
||||
ScancodeSet = 0xF0,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[repr(u8)]
|
||||
#[allow(dead_code)]
|
||||
enum MouseCommand {
|
||||
SetScaling1To1 = 0xE6,
|
||||
SetScaling2To1 = 0xE7,
|
||||
StatusRequest = 0xE9,
|
||||
GetDeviceId = 0xF2,
|
||||
EnableReporting = 0xF4,
|
||||
SetDefaultsDisable = 0xF5,
|
||||
SetDefaults = 0xF6,
|
||||
Reset = 0xFF,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[repr(u8)]
|
||||
enum MouseCommandData {
|
||||
SetSampleRate = 0xF3,
|
||||
}
|
||||
|
||||
// Default timeout in microseconds
|
||||
const DEFAULT_TIMEOUT: u64 = 50_000;
|
||||
// Reset timeout in microseconds
|
||||
const RESET_TIMEOUT: u64 = 500_000;
|
||||
const RESET_TIMEOUT: u64 = 1_000_000;
|
||||
|
||||
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
||||
pub struct Ps2 {
|
||||
@@ -195,13 +173,13 @@ impl Ps2 {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn retry<F: Fn(&mut Self) -> Result<u8, Error>>(
|
||||
fn retry<T, F: Fn(&mut Self) -> Result<T, Error>>(
|
||||
&mut self,
|
||||
name: fmt::Arguments,
|
||||
retries: usize,
|
||||
f: F,
|
||||
) -> Result<u8, Error> {
|
||||
trace!("ps2d: {}", name);
|
||||
) -> Result<T, Error> {
|
||||
trace!("{}", name);
|
||||
let mut res = Err(Error::NoMoreTries);
|
||||
for retry in 0..retries {
|
||||
res = f(self);
|
||||
@@ -210,7 +188,7 @@ impl Ps2 {
|
||||
return Ok(ok);
|
||||
}
|
||||
Err(ref err) => {
|
||||
debug!("ps2d: {}: retry {}/{}: {:?}", name, retry + 1, retries, err);
|
||||
debug!("{}: retry {}/{}: {:?}", name, retry + 1, retries, err);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -263,7 +241,7 @@ impl Ps2 {
|
||||
.keyboard_command_inner(command as u8)
|
||||
.map_err(|_| Error::KeyboardCommandDataFail(command))?;
|
||||
if res != 0xFA {
|
||||
warn!("ps2d: keyboard incorrect result of set command: {command:?} {res:02X}");
|
||||
warn!("keyboard incorrect result of set command: {command:?} {res:02X}");
|
||||
return Ok(res);
|
||||
}
|
||||
x.write(data);
|
||||
@@ -272,39 +250,9 @@ impl Ps2 {
|
||||
)
|
||||
}
|
||||
|
||||
fn mouse_command_inner(&mut self, command: u8) -> Result<u8, Error> {
|
||||
pub fn mouse_command_async(&mut self, command: u8) -> Result<(), Error> {
|
||||
self.command(Command::WriteSecond)?;
|
||||
self.write(command as u8)?;
|
||||
match self.read()? {
|
||||
0xFE => Err(Error::CommandRetry),
|
||||
value => Ok(value),
|
||||
}
|
||||
}
|
||||
|
||||
fn mouse_command(&mut self, command: MouseCommand) -> Result<u8, Error> {
|
||||
self.retry(format_args!("mouse command {:?}", command), 4, |x| {
|
||||
x.mouse_command_inner(command as u8)
|
||||
.map_err(|_| Error::MouseCommandFail(command))
|
||||
})
|
||||
}
|
||||
|
||||
fn mouse_command_data(&mut self, command: MouseCommandData, data: u8) -> Result<u8, Error> {
|
||||
self.retry(
|
||||
format_args!("mouse command {:?} {:#x}", command, data),
|
||||
4,
|
||||
|x| {
|
||||
let res = x
|
||||
.mouse_command_inner(command as u8)
|
||||
.map_err(|_| Error::MouseCommandDataFail(command))?;
|
||||
if res != 0xFA {
|
||||
warn!("ps2d: mouse incorrect result of set command: {command:?} {res:02X}");
|
||||
return Ok(res);
|
||||
}
|
||||
x.command(Command::WriteSecond)?;
|
||||
x.write(data as u8)?;
|
||||
x.read()
|
||||
},
|
||||
)
|
||||
self.write(command as u8)
|
||||
}
|
||||
|
||||
pub fn next(&mut self) -> Option<(bool, u8)> {
|
||||
@@ -331,10 +279,10 @@ impl Ps2 {
|
||||
if b == 0xFA {
|
||||
b = self.read().unwrap_or(0);
|
||||
if b != 0xAA {
|
||||
error!("ps2d: keyboard failed self test: {:02X}", b);
|
||||
error!("keyboard failed self test: {:02X}", b);
|
||||
}
|
||||
} else {
|
||||
error!("ps2d: keyboard failed to reset: {:02X}", b);
|
||||
error!("keyboard failed to reset: {:02X}", b);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -342,7 +290,7 @@ impl Ps2 {
|
||||
// Set defaults and disable scanning
|
||||
let b = x.keyboard_command(KeyboardCommand::SetDefaultsDisable)?;
|
||||
if b != 0xFA {
|
||||
error!("ps2d: keyboard failed to set defaults: {:02X}", b);
|
||||
error!("keyboard failed to set defaults: {:02X}", b);
|
||||
return Err(Error::CommandRetry);
|
||||
}
|
||||
|
||||
@@ -355,7 +303,7 @@ impl Ps2 {
|
||||
b = self.keyboard_command_data(KeyboardCommandData::ScancodeSet, scancode_set)?;
|
||||
if b != 0xFA {
|
||||
error!(
|
||||
"ps2d: keyboard failed to set scancode set {}: {:02X}",
|
||||
"keyboard failed to set scancode set {}: {:02X}",
|
||||
scancode_set, b
|
||||
);
|
||||
}
|
||||
@@ -364,86 +312,7 @@ impl Ps2 {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn init_mouse(&mut self) -> Result<bool, Error> {
|
||||
{
|
||||
// Enable second device
|
||||
self.command(Command::EnableSecond)?;
|
||||
}
|
||||
|
||||
self.retry(format_args!("mouse reset"), 4, |x| {
|
||||
// Reset mouse
|
||||
let mut b = x.mouse_command(MouseCommand::Reset)?;
|
||||
if b == 0xFA {
|
||||
b = x.read_timeout(RESET_TIMEOUT)?;
|
||||
if b != 0xAA {
|
||||
error!("ps2d: mouse failed self test 1: {:02X}", b);
|
||||
return Err(Error::CommandRetry);
|
||||
}
|
||||
|
||||
b = x.read_timeout(RESET_TIMEOUT)?;
|
||||
if b != 0x00 {
|
||||
error!("ps2d: mouse failed self test 2: {:02X}", b);
|
||||
return Err(Error::CommandRetry);
|
||||
}
|
||||
} else {
|
||||
error!("ps2d: mouse failed to reset: {:02X}", b);
|
||||
return Err(Error::CommandRetry);
|
||||
}
|
||||
|
||||
Ok(b)
|
||||
})?;
|
||||
|
||||
{
|
||||
// Enable extra packet on mouse
|
||||
//TODO: show error return values
|
||||
if self.mouse_command_data(MouseCommandData::SetSampleRate, 200)? != 0xFA
|
||||
|| self.mouse_command_data(MouseCommandData::SetSampleRate, 100)? != 0xFA
|
||||
|| self.mouse_command_data(MouseCommandData::SetSampleRate, 80)? != 0xFA
|
||||
{
|
||||
error!("ps2d: mouse failed to enable extra packet");
|
||||
}
|
||||
}
|
||||
|
||||
let b = self.mouse_command(MouseCommand::GetDeviceId)?;
|
||||
let mouse_extra = if b == 0xFA {
|
||||
self.read()? == 3
|
||||
} else {
|
||||
error!("ps2d: mouse failed to get device id: {:02X}", b);
|
||||
false
|
||||
};
|
||||
|
||||
{
|
||||
// Set sample rate to maximum
|
||||
let sample_rate = 200;
|
||||
let b = self.mouse_command_data(MouseCommandData::SetSampleRate, sample_rate)?;
|
||||
if b != 0xFA {
|
||||
error!(
|
||||
"ps2d: mouse failed to set sample rate to {}: {:02X}",
|
||||
sample_rate, b
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
let b = self.mouse_command(MouseCommand::StatusRequest)?;
|
||||
if b != 0xFA {
|
||||
error!("ps2d: mouse failed to request status: {:02X}", b);
|
||||
} else {
|
||||
let a = self.read()?;
|
||||
let b = self.read()?;
|
||||
let c = self.read()?;
|
||||
|
||||
debug!(
|
||||
"ps2d: mouse status {:#x} resolution {} sample rate {}",
|
||||
a, b, c
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(mouse_extra)
|
||||
}
|
||||
|
||||
pub fn init(&mut self) -> Result<bool, Error> {
|
||||
pub fn init(&mut self) -> Result<(), Error> {
|
||||
{
|
||||
// Disable devices
|
||||
self.command(Command::DisableFirst)?;
|
||||
@@ -465,22 +334,22 @@ impl Ps2 {
|
||||
self.command(Command::TestController)?;
|
||||
let r = self.read()?;
|
||||
if r != 0x55 {
|
||||
warn!("ps2d: self test unexpected value: {:02X}", r);
|
||||
warn!("self test unexpected value: {:02X}", r);
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize keyboard
|
||||
if let Err(err) = self.init_keyboard() {
|
||||
error!("ps2d: failed to initialize keyboard: {:?}", err);
|
||||
error!("failed to initialize keyboard: {:?}", err);
|
||||
return Err(err);
|
||||
}
|
||||
|
||||
// Initialize mouse
|
||||
let (mouse_found, mouse_extra) = match self.init_mouse() {
|
||||
Ok(ok) => (true, ok),
|
||||
// Enable second device
|
||||
let enable_mouse = match self.command(Command::EnableSecond) {
|
||||
Ok(()) => true,
|
||||
Err(err) => {
|
||||
error!("ps2d: failed to initialize mouse: {:?}", err);
|
||||
(false, false)
|
||||
error!("failed to initialize mouse: {:?}", err);
|
||||
false
|
||||
}
|
||||
};
|
||||
|
||||
@@ -492,20 +361,12 @@ impl Ps2 {
|
||||
//TODO: fix by using interrupts?
|
||||
}
|
||||
|
||||
if mouse_found {
|
||||
// Enable mouse data reporting
|
||||
// Use inner function to prevent retries
|
||||
self.mouse_command_inner(MouseCommand::EnableReporting as u8)?;
|
||||
// Response is ignored since scanning is now on
|
||||
//TODO: fix by using interrupts?
|
||||
}
|
||||
|
||||
// Enable clocks and interrupts
|
||||
{
|
||||
let config = ConfigFlags::POST_PASSED
|
||||
| ConfigFlags::FIRST_INTERRUPT
|
||||
| ConfigFlags::FIRST_TRANSLATE
|
||||
| if mouse_found {
|
||||
| if enable_mouse {
|
||||
ConfigFlags::SECOND_INTERRUPT
|
||||
} else {
|
||||
ConfigFlags::SECOND_DISABLED
|
||||
@@ -513,6 +374,6 @@ impl Ps2 {
|
||||
self.set_config(config)?;
|
||||
}
|
||||
|
||||
Ok(mouse_extra)
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ use syscall::{EAGAIN, EWOULDBLOCK};
|
||||
use crate::state::Ps2d;
|
||||
|
||||
mod controller;
|
||||
mod mouse;
|
||||
mod state;
|
||||
mod vm;
|
||||
|
||||
@@ -39,6 +40,7 @@ fn daemon(daemon: daemon::Daemon) -> ! {
|
||||
enum Source {
|
||||
Keyboard,
|
||||
Mouse,
|
||||
Time,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,11 +77,26 @@ fn daemon(daemon: daemon::Daemon) -> ! {
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let mut time_file = OpenOptions::new()
|
||||
.read(true)
|
||||
.write(true)
|
||||
.custom_flags(syscall::O_NONBLOCK as i32)
|
||||
.open(format!("/scheme/time/{}", syscall::CLOCK_MONOTONIC))
|
||||
.expect("ps2d: failed to open /scheme/time");
|
||||
|
||||
event_queue
|
||||
.subscribe(
|
||||
time_file.as_raw_fd() as usize,
|
||||
Source::Time,
|
||||
event::EventFlags::READ,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
libredox::call::setrens(0, 0).expect("ps2d: failed to enter null namespace");
|
||||
|
||||
daemon.ready();
|
||||
|
||||
let mut ps2d = Ps2d::new(input);
|
||||
let mut ps2d = Ps2d::new(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) {
|
||||
@@ -95,6 +112,10 @@ fn daemon(daemon: daemon::Daemon) -> ! {
|
||||
let (file, keyboard) = match event {
|
||||
Source::Keyboard => (&mut key_file, true),
|
||||
Source::Mouse => (&mut mouse_file, false),
|
||||
Source::Time => {
|
||||
ps2d.time_event();
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
loop {
|
||||
|
||||
@@ -0,0 +1,261 @@
|
||||
use crate::controller::Ps2;
|
||||
use std::time::Duration;
|
||||
|
||||
pub const RESET_TIMEOUT: Duration = Duration::from_millis(1000);
|
||||
pub const COMMAND_TIMEOUT: Duration = Duration::from_millis(100);
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[repr(u8)]
|
||||
#[allow(dead_code)]
|
||||
enum MouseCommand {
|
||||
SetScaling1To1 = 0xE6,
|
||||
SetScaling2To1 = 0xE7,
|
||||
StatusRequest = 0xE9,
|
||||
GetDeviceId = 0xF2,
|
||||
EnableReporting = 0xF4,
|
||||
SetDefaultsDisable = 0xF5,
|
||||
SetDefaults = 0xF6,
|
||||
Reset = 0xFF,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[repr(u8)]
|
||||
enum MouseCommandData {
|
||||
SetSampleRate = 0xF3,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[repr(u8)]
|
||||
#[allow(dead_code)]
|
||||
enum MouseId {
|
||||
/// Mouse sends three bytes
|
||||
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
|
||||
Intellimouse2 = 0x04,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum MouseState {
|
||||
/// No mouse found
|
||||
None,
|
||||
/// Ready to initialize mouse
|
||||
Init,
|
||||
/// Reset command is sent
|
||||
Reset,
|
||||
/// BAT completion code returned
|
||||
Bat,
|
||||
/// Enable intellimouse features
|
||||
EnableIntellimouse { index: usize, sent_command: bool },
|
||||
/// Device ID update
|
||||
DeviceId,
|
||||
/// Enable reporting command sent
|
||||
EnableReporting { id: u8 },
|
||||
/// Mouse is streaming
|
||||
Streaming { id: u8 },
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[must_use]
|
||||
pub enum MouseResult {
|
||||
None,
|
||||
Packet(u8, bool),
|
||||
Timeout(Duration),
|
||||
}
|
||||
|
||||
impl MouseState {
|
||||
pub fn reset(&mut self, ps2: &mut Ps2) -> MouseResult {
|
||||
match ps2.mouse_command_async(MouseCommand::Reset as u8) {
|
||||
Ok(()) => {
|
||||
*self = MouseState::Reset;
|
||||
MouseResult::Timeout(RESET_TIMEOUT)
|
||||
}
|
||||
Err(err) => {
|
||||
log::error!("failed to send mouse reset command: {:?}", err);
|
||||
//TODO: retry reset?
|
||||
*self = MouseState::None;
|
||||
MouseResult::None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn enable_reporting(&mut self, id: u8, ps2: &mut Ps2) -> MouseResult {
|
||||
match ps2.mouse_command_async(MouseCommand::EnableReporting as u8) {
|
||||
Ok(()) => {
|
||||
*self = MouseState::EnableReporting { id };
|
||||
MouseResult::Timeout(COMMAND_TIMEOUT)
|
||||
}
|
||||
Err(err) => {
|
||||
log::error!("failed to enable mouse reporting: {:?}", err);
|
||||
//TODO: reset mouse?
|
||||
*self = MouseState::None;
|
||||
MouseResult::None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn request_id(&mut self, ps2: &mut Ps2) -> MouseResult {
|
||||
match ps2.mouse_command_async(MouseCommand::GetDeviceId as u8) {
|
||||
Ok(()) => {
|
||||
*self = MouseState::DeviceId;
|
||||
MouseResult::Timeout(COMMAND_TIMEOUT)
|
||||
}
|
||||
Err(err) => {
|
||||
log::error!("failed to request mouse id: {:?}", err);
|
||||
//TODO: reset mouse instead?
|
||||
self.enable_reporting(MouseId::Base as u8, 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)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
self.request_id(ps2)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle(&mut self, data: u8, ps2: &mut Ps2) -> MouseResult {
|
||||
match *self {
|
||||
MouseState::None | MouseState::Init => {
|
||||
//TODO: enable port in this case, mouse hotplug may send 0xAA 0x00
|
||||
log::error!(
|
||||
"received mouse byte {:02X} when mouse not initialized",
|
||||
data
|
||||
);
|
||||
MouseResult::None
|
||||
}
|
||||
MouseState::Reset => {
|
||||
if data == 0xFA {
|
||||
log::debug!("mouse reset ok");
|
||||
MouseResult::Timeout(RESET_TIMEOUT)
|
||||
} else if data == 0xAA {
|
||||
log::debug!("BAT completed");
|
||||
*self = MouseState::Bat;
|
||||
MouseResult::Timeout(COMMAND_TIMEOUT)
|
||||
} else {
|
||||
log::warn!("unknown mouse response {:02X} after reset", data);
|
||||
self.reset(ps2)
|
||||
}
|
||||
}
|
||||
MouseState::Bat => {
|
||||
if data == MouseId::Base as u8 {
|
||||
// Enable intellimouse features
|
||||
log::debug!("BAT mouse id {:02X} (base)", data);
|
||||
self.enable_intellimouse(0, false, ps2)
|
||||
} else if data == MouseId::Intellimouse1 as u8 {
|
||||
// Extra packet already enabled
|
||||
log::debug!("BAT mouse id {:02X} (intellimouse)", data);
|
||||
self.enable_reporting(data, ps2)
|
||||
} else {
|
||||
log::warn!("unknown mouse id {:02X} after BAT", data);
|
||||
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::DeviceId => {
|
||||
if data == 0xFA {
|
||||
// Command OK response
|
||||
//TODO: handle this separately?
|
||||
MouseResult::Timeout(COMMAND_TIMEOUT)
|
||||
} else if data == MouseId::Base as u8 || data == MouseId::Intellimouse1 as u8 {
|
||||
log::debug!("mouse id {:02X}", data);
|
||||
self.enable_reporting(data, ps2)
|
||||
} else {
|
||||
log::warn!("unknown mouse id {:02X} after requesting id", data);
|
||||
self.reset(ps2)
|
||||
}
|
||||
}
|
||||
MouseState::EnableReporting { id } => {
|
||||
log::debug!("mouse id {:02X} enable reporting {:02X}", id, data);
|
||||
//TODO: handle response ok/error
|
||||
*self = MouseState::Streaming { id };
|
||||
MouseResult::None
|
||||
}
|
||||
MouseState::Streaming { id } => {
|
||||
MouseResult::Packet(data, id == MouseId::Intellimouse1 as u8)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle_timeout(&mut self, ps2: &mut Ps2) -> MouseResult {
|
||||
let mut res = MouseResult::None;
|
||||
match *self {
|
||||
MouseState::None | MouseState::Streaming { .. } => MouseResult::None,
|
||||
MouseState::Init => {
|
||||
// The state uses a timeout on init to request a reset
|
||||
self.reset(ps2)
|
||||
}
|
||||
MouseState::Reset => {
|
||||
log::warn!("timeout while waiting for mouse reset");
|
||||
//TODO: retry reset?
|
||||
*self = MouseState::None;
|
||||
MouseResult::None
|
||||
}
|
||||
MouseState::Bat => {
|
||||
log::warn!("timeout while waiting for BAT completion");
|
||||
//TODO: limit number of resets
|
||||
self.reset(ps2)
|
||||
}
|
||||
MouseState::EnableIntellimouse { .. } => {
|
||||
//TODO: retry?
|
||||
log::warn!("timeout while enabling intellimouse");
|
||||
self.request_id(ps2)
|
||||
}
|
||||
MouseState::DeviceId => {
|
||||
log::warn!("timeout while requesting mouse id");
|
||||
self.enable_reporting(0, ps2)
|
||||
}
|
||||
MouseState::EnableReporting { id } => {
|
||||
log::warn!("timeout while enabling reporting");
|
||||
//TODO: limit number of retries
|
||||
self.enable_reporting(id, ps2)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+171
-83
@@ -1,8 +1,16 @@
|
||||
use inputd::ProducerHandle;
|
||||
use log::{error, warn};
|
||||
use log::{error, info, 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! {
|
||||
@@ -18,46 +26,69 @@ bitflags! {
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
input: ProducerHandle,
|
||||
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,
|
||||
extra_packet: bool,
|
||||
}
|
||||
|
||||
impl Ps2d {
|
||||
pub fn new(input: ProducerHandle) -> Self {
|
||||
pub fn new(input: ProducerHandle, time_file: File) -> Self {
|
||||
let mut ps2 = Ps2::new();
|
||||
let extra_packet = ps2.init().expect("ps2d: failed to initialize");
|
||||
ps2.init().expect("failed to initialize");
|
||||
|
||||
// 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);
|
||||
|
||||
Ps2d {
|
||||
let mut this = Ps2d {
|
||||
ps2,
|
||||
vmmouse,
|
||||
vmmouse_relative,
|
||||
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,
|
||||
extra_packet,
|
||||
}
|
||||
};
|
||||
|
||||
// This triggers initializing the mouse
|
||||
this.handle_mouse(None);
|
||||
|
||||
this
|
||||
}
|
||||
|
||||
pub fn irq(&mut self) {
|
||||
@@ -66,6 +97,24 @@ impl Ps2d {
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
@@ -109,7 +158,7 @@ impl Ps2d {
|
||||
/* 0x80 to 0xFF used for press/release detection */
|
||||
_ => {
|
||||
if pressed {
|
||||
warn!("ps2d: unknown extended scancode {:02X}", ps2_scancode);
|
||||
warn!("unknown extended scancode {:02X}", ps2_scancode);
|
||||
}
|
||||
0
|
||||
}
|
||||
@@ -208,7 +257,7 @@ impl Ps2d {
|
||||
/* 0x80 to 0xFF used for press/release detection */
|
||||
_ => {
|
||||
if pressed {
|
||||
warn!("ps2d: unknown scancode {:02X}", ps2_scancode);
|
||||
warn!("unknown scancode {:02X}", ps2_scancode);
|
||||
}
|
||||
0
|
||||
}
|
||||
@@ -225,7 +274,7 @@ impl Ps2d {
|
||||
}
|
||||
.to_event(),
|
||||
)
|
||||
.expect("ps2d: failed to write key event");
|
||||
.expect("failed to write key event");
|
||||
}
|
||||
}
|
||||
} else if self.vmmouse {
|
||||
@@ -239,7 +288,7 @@ impl Ps2d {
|
||||
}
|
||||
|
||||
if queue_length % 4 != 0 {
|
||||
error!("ps2d: queue length not a multiple of 4: {}", queue_length);
|
||||
error!("queue length not a multiple of 4: {}", queue_length);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -304,83 +353,122 @@ impl Ps2d {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
self.packets[self.packet_i] = data;
|
||||
self.packet_i += 1;
|
||||
self.handle_mouse(Some(data));
|
||||
}
|
||||
}
|
||||
|
||||
let flags = MousePacketFlags::from_bits_truncate(self.packets[0]);
|
||||
if !flags.contains(MousePacketFlags::ALWAYS_ON) {
|
||||
error!("ps2d: mouse misalign {:X}", self.packets[0]);
|
||||
|
||||
self.packets = [0; 4];
|
||||
self.packet_i = 0;
|
||||
} else if self.packet_i >= self.packets.len()
|
||||
|| (!self.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;
|
||||
pub fn handle_mouse(&mut self, data_opt: Option<u8>) {
|
||||
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;
|
||||
}
|
||||
|
||||
let mut dy = -(self.packets[2] as i32);
|
||||
if flags.contains(MousePacketFlags::Y_SIGN) {
|
||||
dy += 0x100;
|
||||
}
|
||||
|
||||
let mut dz = 0;
|
||||
if self.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.input
|
||||
.write_event(MouseRelativeEvent { dx, dy }.to_event())
|
||||
.expect("ps2d: failed to write mouse event");
|
||||
}
|
||||
|
||||
if dz != 0 {
|
||||
self.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.input
|
||||
.write_event(
|
||||
ButtonEvent {
|
||||
left,
|
||||
middle,
|
||||
right,
|
||||
}
|
||||
.to_event(),
|
||||
)
|
||||
.expect("ps2d: failed to write button event");
|
||||
}
|
||||
} else {
|
||||
warn!(
|
||||
"ps2d: 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;
|
||||
// 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.input
|
||||
.write_event(MouseRelativeEvent { dx, dy }.to_event())
|
||||
.expect("ps2d: failed to write mouse event");
|
||||
}
|
||||
|
||||
if dz != 0 {
|
||||
self.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.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,12 +69,12 @@ pub unsafe fn cmd(cmd: u32, arg: u32) -> (u32, u32, u32, u32) {
|
||||
}
|
||||
|
||||
pub fn enable(relative: bool) -> bool {
|
||||
trace!("ps2d: Enable vmmouse");
|
||||
trace!("Enable vmmouse");
|
||||
|
||||
unsafe {
|
||||
let (eax, ebx, _, _) = cmd(GETVERSION, 0);
|
||||
if ebx != MAGIC || eax == 0xFFFFFFFF {
|
||||
info!("ps2d: No vmmouse support");
|
||||
info!("No vmmouse support");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -82,14 +82,14 @@ pub fn enable(relative: bool) -> bool {
|
||||
|
||||
let (status, _, _, _) = cmd(ABSPOINTER_STATUS, 0);
|
||||
if (status & 0x0000ffff) == 0 {
|
||||
info!("ps2d: No vmmouse");
|
||||
info!("No vmmouse");
|
||||
return false;
|
||||
}
|
||||
|
||||
let (version, _, _, _) = cmd(ABSPOINTER_DATA, 1);
|
||||
if version != VERSION {
|
||||
error!(
|
||||
"ps2d: Invalid vmmouse version: {} instead of {}",
|
||||
"Invalid vmmouse version: {} instead of {}",
|
||||
version, VERSION
|
||||
);
|
||||
let _ = cmd(ABSPOINTER_COMMAND, CMD_DISABLE);
|
||||
|
||||
Reference in New Issue
Block a user