using System.Runtime.InteropServices; namespace XFESystem.NativeKernel; // 8042 hardware port and bounded queue. Decoder is the same production source // linked into the host regressions, with no injected event path in this driver. public static unsafe class Ps2Input { const uint Capacity = 256, PollLimit = 100000; static NativeInputReport* _reports; static uint _head, _tail, _lost; static Ps2DecodeState _decoder; static uint _buttons; public static uint ReadyBits; static uint _ps2ReadyBits; public static ulong HardwareBytes, QueuedReports; static bool Write(ushort port, byte value) { for (uint poll = 0; poll < PollLimit; poll++) { if ((Native.In8(0x64) & 2) == 0) { Native.Out8(port, value); return true; } Native.Pause(); } return false; } static bool Read(out byte value, bool mouse, uint limit = PollLimit) { value = 0; ulong deadline = limit > PollLimit && MonotonicClock.Ready ? MonotonicClock.Nanoseconds() + 1000000000 : 0; for (uint poll = 0; poll < limit; poll++) { if (deadline != 0 && MonotonicClock.Nanoseconds() >= deadline) return false; byte status = Native.In8(0x64); if (status == 255) return false; if ((status & 1) != 0) { byte data = Native.In8(0x60); if ((status & 0xc0) != 0) return false; if (((status & 32) != 0) == mouse) { value = data; return true; } bool otherMouse = (status & 32) != 0; if ((ReadyBits & (otherMouse ? 2u : 1u)) != 0) Decode(otherMouse, data); } Native.Pause(); } return false; } static bool Device(byte value, bool mouse) { for (uint attempt = 0; attempt < 3; attempt++) { if (mouse && !Write(0x64, 0xd4)) return false; if (!Write(0x60, value) || !Read(out byte ack, mouse)) return false; if (ack == 0xfa) return true; if (ack != 0xfe) return false; } return false; } static bool ResetMouseDevice() { // F6 does not reset an IntelliMouse's packet ID. A successful FF BAT // and ID0 reply are required before selecting the standard fallback. if (!Device(0xff, true) || !Read(out byte bat, true, PollLimit * 1000) || bat != 0xaa || !Read(out byte id, true) || id != 0) return false; return Ps2Decoder.SetMouseDeviceId(ref _decoder, id); } static bool SampleRate(byte rate) => Device(0xf3, true) && Device(rate, true); static bool InitializeMouse() { if (!Write(0x64, 0xa8) || !ResetMouseDevice()) return false; bool identified = SampleRate(200) && SampleRate(100) && SampleRate(80) && Device(0xf2, true) && Read(out byte id, true) && Ps2Decoder.SetMouseDeviceId(ref _decoder, id); // A failed/unknown reply leaves the hardware protocol uncertain. Never // parse a four-byte device as three bytes just by changing software. if (!identified && !ResetMouseDevice()) return false; if (!Device(0xea, true) || !Device(0xf4, true)) return false; ReadyBits |= 2; if (_decoder.MouseDeviceId == 3) ReadyBits |= 4; return true; } public static void Initialize() { // The LAPIC poller must not consume ACK/BAT bytes while the keyboard // is already enabled and the mouse is negotiating its packet format. ulong flags = Native.IrqSave(); InitializeDevices(); _ps2ReadyBits = ReadyBits; Native.IrqRestore(flags); } static void InitializeDevices() { _reports = (NativeInputReport*)Native.Storage(10); _head = _tail = _lost = 0; ReadyBits = 0; _decoder = new Ps2DecodeState(); if (_reports == null || Native.In8(0x64) == 255) return; if (!Write(0x64, 0xad) || !Write(0x64, 0xa7)) return; for (uint i = 0; i < 64 && (Native.In8(0x64) & 1) != 0; i++) Native.In8(0x60); if (!Write(0x64, 0x20) || !Read(out byte config, false)) return; // Translation converts the keyboard's set 2 into set 1; IRQs stay masked // because the independent LAPIC tick services both device queues. config = (byte)((config | 0x40) & ~0x33); if (!Write(0x64, 0x60) || !Write(0x60, config) || !Write(0x64, 0xae)) return; if (Device(0xf5, false) && Device(0xf0, false) && Device(2, false) && Device(0xf4, false)) ReadyBits |= 1; InitializeMouse(); Serial.Write("XFE_INPUT_READY keyboard="); Serial.Number((ReadyBits & 1) != 0 ? 1UL : 0UL); Serial.Write(" mouse="); Serial.Number((ReadyBits & 2) != 0 ? 1UL : 0UL); Serial.Write(" wheel="); Serial.Number((ReadyBits & 4) != 0 ? 1UL : 0UL); Serial.Write(" id="); Serial.Number(_decoder.MouseDeviceId); Serial.Line(""); } static void Decode(bool mouse, byte value) { NativeInputReport report; bool complete = mouse ? Ps2Decoder.DecodeMouse(ref _decoder, value, &report) : Ps2Decoder.DecodeKeyboard(ref _decoder, value, &report); if (!complete) return; if (mouse) _buttons = report.Buttons; Enqueue(report); } // Both physical backends share this ABI and queue. xHCI calls it from its // native worker; IRQ masking excludes the PS/2 tick producer and consumer. public static void Enqueue(NativeInputReport report) { ulong flags = Native.IrqSave(); if (_reports == null) { Native.IrqRestore(flags); return; } if (report.Kind == Ps2Decoder.Pointer) _buttons = report.Buttons; uint next = (_tail + 1) % Capacity; if (next == _head) { _lost++; _head = _tail = 0; Ps2Decoder.ResetKeyboard(ref _decoder); Ps2Decoder.ResetMouse(ref _decoder); Native.IrqRestore(flags); return; } _reports[_tail] = report; _tail = next; QueuedReports++; Native.IrqRestore(flags); } public static void InputLost() { ulong flags = Native.IrqSave(); _lost++; _head = _tail = 0; _buttons = 0; Native.IrqRestore(flags); } public static void SetUsbCapabilities(uint bits) { ulong flags = Native.IrqSave(); ReadyBits = _ps2ReadyBits | (bits & 7); Native.IrqRestore(flags); } public static void Poll() { if (_reports == null || _ps2ReadyBits == 0) return; for (uint i = 0; i < 64; i++) { byte status = Native.In8(0x64); if (status == 255 || (status & 1) == 0) return; byte value = Native.In8(0x60); HardwareBytes++; if ((status & 0xc0) != 0) { _lost++; _head = _tail = 0; Ps2Decoder.ResetKeyboard(ref _decoder); Ps2Decoder.ResetMouse(ref _decoder); continue; } bool mouse = (status & 32) != 0; if ((_ps2ReadyBits & (mouse ? 2u : 1u)) != 0) Decode(mouse, value); } } [UnmanagedCallersOnly(EntryPoint = "xfe_runtime_input_ready")] public static int Ready() => (int)ReadyBits; [UnmanagedCallersOnly(EntryPoint = "xfe_runtime_input_read")] public static int ReadReport(NativeInputReport* output) { if (output == null || _reports == null) return 0; ulong flags = Native.IrqSave(); if (_lost != 0) { *output = new NativeInputReport { Kind = 4, KeyCode = _lost, Buttons = _buttons }; _lost = 0; Native.IrqRestore(flags); return 1; } if (_head == _tail) { Native.IrqRestore(flags); return 0; } *output = _reports[_head]; _head = (_head + 1) % Capacity; Native.IrqRestore(flags); return 1; } }