//#define COSMOSDEBUG using Cosmos.Core; using Cosmos.Debug.Kernel; namespace Cosmos.HAL { /// /// This class describes the PS/2 mouse. /// public class PS2Mouse : MouseBase { enum Command : byte { SetScaling1_1 = 0xE6, SetScaling2_1 = 0xE7, SetResolution = 0xE8, StatusRequest = 0xE9, SetStreamMode = 0xEA, RequestSinglePacket = 0xEB, ResetWrapMode = 0xEC, SetWrapMode = 0xEE, SetRemoteMode = 0xF0, GetMouseID = 0xF2, SetSampleRate = 0xF3, EnablePacketStreaming = 0xF4, DisablePacketStreaming = 0xF5, SetDefaults = 0xF6, Resend = 0xFE, Reset = 0xFF } public byte PS2Port { get; } private bool HasScrollWheel { get { return mMouseID == 3 || mMouseID == 4; } } private Core.IOGroup.PS2Controller IO = Core.Global.BaseIOGroups.PS2Controller; private PS2Controller mPS2Controller = Global.PS2Controller; private Debugger mDebugger = new Debugger("HAL", "PS2Mouse"); private byte mMouseID = 0; internal PS2Mouse(byte aPort, byte aMouseID) { PS2Port = aPort; mMouseID = aMouseID; } /// /// This is the required call to start /// the mouse receiving interrupts. /// public override void Initialize() { SendCommand(Command.Reset); mPS2Controller.WaitForDeviceReset(); if (mMouseID == 0) { mMouseID = TryToEnableScrollWheel(); mDebugger.SendInternal("(PS/2 Mouse) Mouse ID: " + mMouseID); if (mMouseID == 3) { mMouseID = TryToEnableAdditionalButtons(); } mDebugger.SendInternal("(PS/2 Mouse) Mouse ID: " + mMouseID); } //SendCommand(Command.SetDefaults); //mPS2Controller.WaitForAck(); INTs.SetIrqHandler(12, HandleMouse); SendCommand(Command.EnablePacketStreaming); mPS2Controller.WaitForAck(); } /// /// Tries to enable the scroll wheel. /// /// Returns the mouse id. private byte TryToEnableScrollWheel() { SendCommand(Command.SetSampleRate, 200); SendCommand(Command.SetSampleRate, 100); SendCommand(Command.SetSampleRate, 80); SendCommand(Command.GetMouseID); return mPS2Controller.ReadByteAfterAck(); } /// /// Tries to enable additional buttons (buttons 4 and 5). /// /// Returns the mouse id. private byte TryToEnableAdditionalButtons() { SendCommand(Command.SetSampleRate, 200); SendCommand(Command.SetSampleRate, 200); SendCommand(Command.SetSampleRate, 80); SendCommand(Command.GetMouseID); return mPS2Controller.ReadByteAfterAck(); } private byte[] mMouseByte = new byte[4]; private static byte mMouseCycle = 0; public void HandleMouse(ref INTs.IRQContext context) { if (mMouseCycle == 0) { mMouseByte[0] = IO.Data.Byte; //Bit 3 of byte 0 is 1, then we have a good package if ((mMouseByte[0] & (1 << 3)) == (1 << 3)) { mMouseCycle++; } } else if (mMouseCycle == 1) { mMouseByte[1] = IO.Data.Byte; mMouseCycle++; } else if (mMouseCycle == 2) { mMouseByte[2] = IO.Data.Byte; if (HasScrollWheel) { mMouseCycle++; } } // TODO: move conditions to the if statement when stack corruption detection // works better for complex conditions var xTest1 = (mMouseCycle == 2 && !HasScrollWheel); var xTest2 = (mMouseCycle == 3 && HasScrollWheel); if (xTest1 || xTest2) { int xDeltaX = 0; int xDeltaY = 0; int xScrollWheel = 0; int xMouseState; if ((mMouseByte[0] & (1 << 4)) == (1 << 4)) { xDeltaX = (mMouseByte[1] | ~0xFF); } else { xDeltaX = mMouseByte[1]; } if ((mMouseByte[0] & (1 << 5)) == 1 << 5) { xDeltaY = -(mMouseByte[2] | ~0xFF); } else { xDeltaY = -mMouseByte[2]; } xMouseState = mMouseByte[0] & 0b0000_0111; if (HasScrollWheel) { var xScrollWheelByte = mMouseByte[3] & 0x0F; xScrollWheel = (xScrollWheelByte & 0b1000) == 0 ? xScrollWheelByte : xScrollWheelByte | ~0x0F; if (mMouseID == 4) { var xAdditionalButtonsByte = mMouseByte[3] & 0b0011_0000; xMouseState |= (xAdditionalButtonsByte >> 1); } } mDebugger.SendInternal($"(PS/2 Mouse) IRQ 12: Mouse State: ({xDeltaX}, {xDeltaY}, {xMouseState})"); OnMouseChanged?.Invoke(xDeltaX, xDeltaY, xMouseState, xScrollWheel); mMouseCycle = 0; } } private void SendCommand(Command aCommand, byte? aByte = null) { mDebugger.SendInternal("(PS/2 Mouse) Sending command:"); mDebugger.SendInternal("Command:"); mDebugger.SendInternal((byte)aCommand); if (PS2Port == 2) { mPS2Controller.PrepareSecondPortWrite(); } mPS2Controller.WaitToWrite(); IO.Data.Byte = (byte)aCommand; mPS2Controller.WaitForAck(); mDebugger.SendInternal("Command sent."); if (aByte.HasValue) { mDebugger.SendInternal("(PS/2 Mouse) Sending byte after command:"); mDebugger.SendInternal("Byte value:"); mDebugger.SendInternal(aByte.Value); if (PS2Port == 2) { mPS2Controller.PrepareSecondPortWrite(); } mPS2Controller.WaitToWrite(); IO.Data.Byte = aByte.Value; mPS2Controller.WaitForAck(); } } } }