The Pedigree Project 0.1
ps2-controller-regressions.cc
1/*
2 * Copyright (c) 2026, Pedigree Developers
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted.
6 */
7
8#include "pedigree/kernel/Log.h"
9#include "pedigree/kernel/machine/Ps2CaptureState.h"
10
11namespace {
12bool check(bool condition, const char* test, const char* detail) {
13 if (condition) {
14 return true;
15 }
16
17 ERROR("HOSTED-WAIT-TEST: FAIL " << test << ": " << detail);
18 return false;
19}
20
21bool oneShotAdmission() {
22 constexpr const char* Test = "ps2-one-shot-hard-admission";
24
25 const bool first = gate.tryAcquire();
26 const bool contended = !gate.tryAcquire();
27 const bool remainedOwned = gate.owned();
28 gate.release();
29 const bool reacquired = gate.tryAcquire();
30 gate.release();
31
32 const bool passed =
33 check(first && contended && remainedOwned && reacquired && !gate.owned(), Test,
34 "a failed hard-stage admission changed ownership or required a retry");
35 if (passed) {
36 NOTICE("HOSTED-WAIT-TEST: PASS ps2-one-shot-hard-admission");
37 }
38 return passed;
39}
40
41bool debuggerTransitionLockIndependent() {
42 constexpr const char* Test = "ps2-debug-state-lock-independent";
44 Ps2DebuggerPollingState debugState;
45
46 bool passed = check(gate.tryAcquire() && gate.owned() && !debugState.active(), Test,
47 "the test could not freeze an ordinary 8042 transaction");
48 debugState.set(true);
49 passed &= check(debugState.active() && gate.owned() && !gate.tryAcquire(), Test,
50 "debugger entry waited on or changed 8042 admission");
51 debugState.set(false);
52 passed &= check(!debugState.active() && gate.owned(), Test,
53 "debugger exit waited on or changed 8042 admission");
54 gate.release();
55
56 if (passed) {
57 NOTICE("HOSTED-WAIT-TEST: PASS ps2-debug-state-lock-independent");
58 }
59 return passed;
60}
61
62bool captureQueueFidelity() {
63 constexpr const char* Test = "ps2-capture-queue-fidelity";
64 Ps2CaptureQueue queue;
65 bool passed = true;
66
67 passed &= check(queue.tryPush(Ps2CapturedByte(0x1E, false)) &&
68 queue.tryPush(Ps2CapturedByte(0xA5, true)) && queue.pending() == 2,
69 Test, "captured keyboard and auxiliary bytes were not retained");
70
71 Ps2CapturedByte record;
72 passed &=
73 check(queue.pop(record) && record.value == 0x1E && !record.secondPort && queue.pop(record) &&
74 record.value == 0xA5 && record.secondPort && !queue.pop(record) && !queue.pending(),
75 Test, "capture order or status-based destination identity changed");
76
77 for (size_t i = 0; i < Ps2CaptureQueue::Capacity; ++i) {
78 passed &= queue.tryPush(Ps2CapturedByte(static_cast<uint8_t>(i), (i & 1) != 0));
79 }
80 passed &= check(!queue.hasCapacity() && !queue.tryPush(Ps2CapturedByte(0xFF, false)) &&
81 queue.pending() == Ps2CaptureQueue::Capacity,
82 Test, "a full hard-stage queue overwrote an unread byte");
83
84 for (size_t i = 0; i < Ps2CaptureQueue::Capacity; ++i) {
85 passed &= queue.pop(record) && record.value == static_cast<uint8_t>(i) &&
86 record.secondPort == ((i & 1) != 0);
87 }
88 passed &= check(!queue.pending() && queue.hasCapacity() &&
89 queue.tryPush(Ps2CapturedByte(0x42, false)) && queue.pop(record) &&
90 record.value == 0x42 && !record.secondPort,
91 Test, "the bounded queue did not recover after wraparound");
92
93 if (passed) {
94 NOTICE("HOSTED-WAIT-TEST: PASS ps2-capture-queue-fidelity");
95 }
96 return passed;
97}
98} // namespace
99
100bool runHostedPs2ControllerRegressions() {
101 return oneShotAdmission() && debuggerTransitionLockIndependent() && captureQueueFidelity();
102}
bool hasCapacity() const