diff --git a/ps2d/src/main.rs b/ps2d/src/main.rs index 76ad116f95..be59ebc6d3 100644 --- a/ps2d/src/main.rs +++ b/ps2d/src/main.rs @@ -22,6 +22,7 @@ use controller::Ps2; mod controller; mod keymap; +mod vm; bitflags! { flags MousePacketFlags: u8 { @@ -38,6 +39,7 @@ bitflags! { struct Ps2d char> { ps2: Ps2, + vmmouse: bool, input: File, width: u32, height: u32, @@ -60,6 +62,8 @@ impl char> Ps2d { let mut ps2 = Ps2::new(); let extra_packet = ps2.init(); + let vmmouse = vm::enable(); + let mut width = 0; let mut height = 0; { @@ -74,6 +78,7 @@ impl char> Ps2d { Ps2d { ps2: ps2, + vmmouse: vmmouse, input: input, width: width, height: height, @@ -116,6 +121,65 @@ impl char> Ps2d { scancode: scancode, pressed: pressed }.to_event()).expect("ps2d: 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 { + println!("queue length not a multiple of 4: {}", queue_length); + break; + } + + let (status, dx, dy, dz, _, _) = unsafe { vm::cmd(vm::ABSPOINTER_DATA, 4) }; + + let (x, y) = if status & vm::RELATIVE_PACKET == vm::RELATIVE_PACKET { + ( + cmp::max(0, cmp::min(self.width as i32, self.mouse_x + dx as i32)), + cmp::max(0, cmp::min(self.height as i32, self.mouse_y - dy as i32)) + ) + } else { + ( + dx as i32 * self.width as i32 / 0xFFFF, + dy as i32 * self.height as i32 / 0xFFFF + ) + }; + + if x != self.mouse_x || y != self.mouse_y { + self.mouse_x = x; + self.mouse_y = y; + self.input.write(&MouseEvent { + x: x, + y: y, + }.to_event()).expect("ps2d: failed to write mouse event"); + } + + if dz != 0 { + self.input.write(&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.input.write(&ButtonEvent { + left: left, + middle: middle, + right: right, + }.to_event()).expect("ps2d: failed to write button event"); + } + } } else { self.packets[self.packet_i] = data; self.packet_i += 1; @@ -158,6 +222,13 @@ impl char> Ps2d { }.to_event()).expect("ps2d: failed to write mouse event"); } + if dz != 0 { + self.input.write(&ScrollEvent { + x: 0, + y: dz, + }.to_event()).expect("ps2d: failed to write scroll event"); + } + let left = flags.contains(LEFT_BUTTON); let middle = flags.contains(MIDDLE_BUTTON); let right = flags.contains(RIGHT_BUTTON); @@ -171,13 +242,6 @@ impl char> Ps2d { right: right, }.to_event()).expect("ps2d: failed to write button event"); } - - if dz != 0 { - self.input.write(&ScrollEvent { - x: 0, - y: dz, - }.to_event()).expect("ps2d: failed to write scroll event"); - } } else { println!("ps2d: overflow {:X} {:X} {:X} {:X}", self.packets[0], self.packets[1], self.packets[2], self.packets[3]); } diff --git a/ps2d/src/vm.rs b/ps2d/src/vm.rs new file mode 100644 index 0000000000..a772829aaf --- /dev/null +++ b/ps2d/src/vm.rs @@ -0,0 +1,82 @@ +// This code is informed by the QEMU implementation found here: +// https://github.com/qemu/qemu/blob/master/hw/input/vmmouse.c +// +// As well as the Linux implementation here: +// http://elixir.free-electrons.com/linux/v4.1/source/drivers/input/mouse/vmmouse.c + +const MAGIC: u32 = 0x564D5868; +const PORT: u16 = 0x5658; + +pub const ABSPOINTER_DATA: u32 = 39; +pub const ABSPOINTER_STATUS: u32 = 40; +pub const ABSPOINTER_COMMAND: u32 = 41; + +pub const CMD_ENABLE: u32 = 0x45414552; +pub const CMD_DISABLE: u32 = 0x000000f5; +pub const CMD_REQUEST_ABSOLUTE: u32 = 0x53424152; + +const VERSION: u32 = 0x3442554a; + +pub const RELATIVE_PACKET: u32 = 0x00010000; + +pub const LEFT_BUTTON: u32 = 0x20; +pub const RIGHT_BUTTON: u32 = 0x10; +pub const MIDDLE_BUTTON: u32 = 0x08; + +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +pub unsafe fn cmd(cmd: u32, arg: u32) -> (u32, u32, u32, u32, u32, u32) { + let a: u32; + let b: u32; + let c: u32; + let d: u32; + let si: u32; + let di: u32; + + asm!( + "in eax, dx" + : + "={eax}"(a), + "={ebx}"(b), + "={ecx}"(c), + "={edx}"(d), + "={esi}"(si), + "={edi}"(di) + : + "{eax}"(MAGIC), + "{ebx}"(arg), + "{ecx}"(cmd), + "{dx}"(PORT) + : + "memory" + : + "intel", "volatile" + ); + + (a, b, c, d, si, di) +} + +pub fn enable() -> bool { + println!("Enable"); + + unsafe { + let _ = cmd(ABSPOINTER_COMMAND, CMD_ENABLE); + + let (status, _, _, _, _, _) = cmd(ABSPOINTER_STATUS, 0); + if (status & 0x0000ffff) == 0 { + println!("No vmmouse"); + return false; + } + + let (version, _, _, _, _, _) = cmd(ABSPOINTER_DATA, 1); + if version != VERSION { + println!("Invalid vmmouse version: {} instead of {}", version, VERSION); + let _ = cmd(ABSPOINTER_COMMAND, CMD_DISABLE); + return false; + } + + cmd(ABSPOINTER_COMMAND, CMD_REQUEST_ABSOLUTE); + + } + + return true; +}