The Pedigree Project 0.1
hid-report-regressions.cc
1/* Copyright (c) 2026, Pedigree Developers. SPDX-License-Identifier: ISC */
2#include "pedigree/kernel/LockGuard.h"
3#include "pedigree/kernel/Log.h"
4#include "pedigree/kernel/machine/InputManager.h"
5#include "pedigree/kernel/process/Mutex.h"
6#include "pedigree/kernel/process/Semaphore.h"
7
8#include "modules/drivers/common/hid/HidReport.h"
9#include "modules/drivers/common/hid/HidUtils.h"
10
11namespace {
12struct Capture {
13 Mutex lock;
14 Semaphore received{0, false};
15 int values[32]{};
16 size_t count = 0;
17};
18void input(InputManager::InputNotification& notification) {
19 auto* capture = static_cast<Capture*>(notification.meta);
20 int value = 0;
21 if (notification.type == InputManager::RawKey &&
22 (notification.data.rawkey.scancode == 4 || notification.data.rawkey.scancode == 5 ||
23 notification.data.rawkey.scancode == 0xe1)) {
24 value = notification.data.rawkey.scancode * (notification.data.rawkey.keyUp ? -1 : 1);
25 } else if (notification.type == InputManager::Mouse && notification.data.pointy.relx == -1) {
26 value = 100;
27 } else if (notification.type == InputManager::Mouse) {
28 value = 200 + notification.data.pointy.buttons[0] + 2 * notification.data.pointy.buttons[1];
29 }
30 if (!value)
31 return;
32 LockGuard<Mutex> lock(capture->lock);
33 if (capture->count < 32) {
34 capture->values[capture->count++] = value;
35 }
36 capture->received.release();
37}
38} // namespace
39
40EXPORTED_PUBLIC bool runHostedHidReportRegressions() {
41 uint8_t descriptor[] = {0x05, 1, 0x09, 6, 0xa1, 1, 0x85, 1, 0x05, 7, 0x09, 4,
42 0x15, 0, 0x25, 1, 0x75, 1, 0x95, 1, 0x81, 2, 0x75, 7,
43 0x81, 3, 0xc0, 0x05, 1, 0x09, 6, 0xa1, 1, 0x85, 2, 0x05,
44 7, 0x09, 5, 0x15, 0, 0x25, 1, 0x75, 1, 0x95, 1, 0x81,
45 2, 0x75, 7, 0x81, 3, 0xc0, 0x05, 1, 0x09, 2, 0xa1, 1,
46 0x85, 3, 0x09, 0x30, 0x16, 0, 0xf8, 0x26, 0xff, 7, 0x75, 12,
47 0x95, 1, 0x81, 6, 0x75, 4, 0x81, 3, 0xc0};
48 HidReport report;
49 report.parseDescriptor(descriptor, sizeof(descriptor));
50 if (!report.valid())
51 return false;
52 Capture capture;
53 InputManager::instance().installCallback(InputManager::RawKey | InputManager::Mouse, input,
54 &capture);
55 uint8_t packets[][3] = {{1, 1}, {2, 1}, {1}, {1, 0}, {2, 0}, {3, 0xff, 0x0f}};
56 const size_t lengths[] = {2, 2, 1, 2, 2, 3};
57 for (size_t i = 0; i < 6; ++i)
58 report.feedInput(packets[i], nullptr, lengths[i]);
59 bool delivered = capture.received.acquireForCompletion(5, 2);
60
61 uint8_t hotplugDescriptor[] = {0x05, 1, 0x09, 6, 0xa1, 1, 0x85, 1, 0x05, 7, 0x09,
62 4, 0x09, 0xe1, 0x15, 0, 0x25, 1, 0x75, 1, 0x95, 2,
63 0x81, 2, 0x75, 6, 0x95, 1, 0x81, 3, 0xc0, 0x05, 1,
64 0x09, 2, 0xa1, 1, 0x85, 2, 0x05, 9, 0x19, 1, 0x29,
65 2, 0x15, 0, 0x25, 1, 0x75, 1, 0x95, 2, 0x81, 2,
66 0x75, 6, 0x95, 1, 0x81, 3, 0xc0};
67 auto* first = new HidReport;
68 auto* second = new HidReport;
69 first->parseDescriptor(hotplugDescriptor, sizeof(hotplugDescriptor));
70 second->parseDescriptor(hotplugDescriptor, sizeof(hotplugDescriptor));
71 uint8_t heldKeys[] = {1, 3};
72 uint8_t left[] = {2, 1};
73 uint8_t both[] = {2, 3};
74 first->feedInput(heldKeys, nullptr, sizeof(heldKeys));
75 delivered &= capture.received.acquireForCompletion(2, 2);
76 first->feedInput(left, nullptr, sizeof(left));
77 delivered &= capture.received.acquireForCompletion(1, 2);
78 second->feedInput(heldKeys, nullptr, sizeof(heldKeys));
79 second->feedInput(left, nullptr, sizeof(left));
80 delivered &= capture.received.acquireForCompletion(1, 2);
81 second->feedInput(both, nullptr, sizeof(both));
82 delivered &= capture.received.acquireForCompletion(1, 2);
83 delete first;
84 delivered &= capture.received.acquireForCompletion(1, 2);
85 uint8_t unshifted[] = {1, 1};
86 uint8_t right[] = {2, 2};
87 second->feedInput(unshifted, nullptr, sizeof(unshifted));
88 delivered &= capture.received.acquireForCompletion(1, 2);
89 second->feedInput(right, nullptr, sizeof(right));
90 delivered &= capture.received.acquireForCompletion(1, 2);
91 delete second;
92 delivered &= capture.received.acquireForCompletion(2, 2);
93 {
94 HidReport reconnected;
95 reconnected.parseDescriptor(hotplugDescriptor, sizeof(hotplugDescriptor));
96 reconnected.feedInput(unshifted, nullptr, sizeof(unshifted));
97 }
98 const int expected[] = {4, 5, -4, -5, 100, 4, 0xe1, 201, 201,
99 203, 203, -0xe1, 202, -4, 200, 4, -4};
100 const size_t expectedCount = sizeof(expected) / sizeof(expected[0]);
101 delivered &= capture.received.acquireForCompletion(2, 2);
102 InputManager::instance().removeCallback(input, &capture);
103 bool valid = delivered && capture.count == expectedCount;
104 for (size_t i = 0; valid && i < expectedCount; ++i)
105 valid = capture.values[i] == expected[i];
106 uint8_t truncated[] = {0x27, 0};
107 HidReport malformed;
108 malformed.parseDescriptor(truncated, sizeof(truncated));
109 valid &= !malformed.valid();
110 uint8_t nested[34];
111 for (size_t i = 0; i < 34; i += 2) {
112 nested[i] = 0xa1;
113 nested[i + 1] = 1;
114 }
115 HidReport deep;
116 deep.parseDescriptor(nested, sizeof(nested));
117 valid &= !deep.valid();
118 uint8_t field[] = {0xf0, 0x3f};
119 valid &= HidUtils::getBufferField(field, 4, 10) == 0x3ff;
120 if (valid) {
121 NOTICE("HOSTED-WAIT-TEST: PASS hid-report-ids-short-signed-bounds-and-hotplug");
122 } else {
123 ERROR("HOSTED-WAIT-TEST: FAIL hid-report-ids-short-signed-bounds-and-hotplug");
124 }
125 return valid;
126}
void parseDescriptor(uint8_t *pDescriptor, size_t nDescriptorLength)
Parses a HID report descriptor and stores the resulted data.
Definition HidReport.cc:69
void feedInput(uint8_t *pBuffer, uint8_t *pOldBuffer, size_t nBufferSize)
Feeds the input interpreter with a new input buffer.
Definition HidReport.cc:309
static InputManager & instance()
Singleton design.
void installCallback(CallbackType filter, callback_t callback, void *meta=0)
Installs a callback.
void removeCallback(callback_t callback, void *meta=0)
Removes a callback.
Definition Mutex.h:56
uint64_t getBufferField(uint8_t *pBuffer, size_t nStart, size_t nLength)
Retrieves a field in a buffer.
Definition HidUtils.cc:65
bool keyUp
Whether this is a keyUp event or not.
uint8_t scancode
Machine-specific scancode for the key.