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[8]{};
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 value = notification.data.rawkey.scancode * (notification.data.rawkey.keyUp ? -1 : 1);
24 else if (notification.type == InputManager::Mouse && notification.data.pointy.relx == -1)
25 value = 100;
26 if (!value)
27 return;
28 LockGuard<Mutex> lock(capture->lock);
29 if (capture->count < 8)
30 capture->values[capture->count++] = value;
31 capture->received.release();
32}
33} // namespace
34
35EXPORTED_PUBLIC bool runHostedHidReportRegressions() {
36 uint8_t descriptor[] = {0x05, 1, 0x09, 6, 0xa1, 1, 0x85, 1, 0x05, 7, 0x09, 4,
37 0x15, 0, 0x25, 1, 0x75, 1, 0x95, 1, 0x81, 2, 0x75, 7,
38 0x81, 3, 0xc0, 0x05, 1, 0x09, 6, 0xa1, 1, 0x85, 2, 0x05,
39 7, 0x09, 5, 0x15, 0, 0x25, 1, 0x75, 1, 0x95, 1, 0x81,
40 2, 0x75, 7, 0x81, 3, 0xc0, 0x05, 1, 0x09, 2, 0xa1, 1,
41 0x85, 3, 0x09, 0x30, 0x16, 0, 0xf8, 0x26, 0xff, 7, 0x75, 12,
42 0x95, 1, 0x81, 6, 0x75, 4, 0x81, 3, 0xc0};
43 HidReport report;
44 report.parseDescriptor(descriptor, sizeof(descriptor));
45 if (!report.valid())
46 return false;
47 Capture capture;
48 InputManager::instance().installCallback(InputManager::RawKey | InputManager::Mouse, input,
49 &capture);
50 uint8_t packets[][3] = {{1, 1}, {2, 1}, {1}, {1, 0}, {2, 0}, {3, 0xff, 0x0f}};
51 const size_t lengths[] = {2, 2, 1, 2, 2, 3};
52 for (size_t i = 0; i < 6; ++i)
53 report.feedInput(packets[i], nullptr, lengths[i]);
54 const bool delivered = capture.received.acquireForCompletion(5, 2);
55 InputManager::instance().removeCallback(input, &capture);
56 const int expected[] = {4, 5, -4, -5, 100};
57 bool valid = delivered && capture.count == 5;
58 for (size_t i = 0; valid && i < 5; ++i)
59 valid = capture.values[i] == expected[i];
60 uint8_t truncated[] = {0x27, 0};
61 HidReport malformed;
62 malformed.parseDescriptor(truncated, sizeof(truncated));
63 valid &= !malformed.valid();
64 uint8_t nested[34];
65 for (size_t i = 0; i < 34; i += 2) {
66 nested[i] = 0xa1;
67 nested[i + 1] = 1;
68 }
69 HidReport deep;
70 deep.parseDescriptor(nested, sizeof(nested));
71 valid &= !deep.valid();
72 uint8_t field[] = {0xf0, 0x3f};
73 valid &= HidUtils::getBufferField(field, 4, 10) == 0x3ff;
74 if (valid)
75 NOTICE("HOSTED-WAIT-TEST: PASS hid-report-ids-short-signed-and-bounds");
76 else
77 ERROR("HOSTED-WAIT-TEST: FAIL hid-report-ids-short-signed-and-bounds");
78 return valid;
79}
void parseDescriptor(uint8_t *pDescriptor, size_t nDescriptorLength)
Parses a HID report descriptor and stores the resulted data.
Definition HidReport.cc:65
void feedInput(uint8_t *pBuffer, uint8_t *pOldBuffer, size_t nBufferSize)
Feeds the input interpreter with a new input buffer.
Definition HidReport.cc:304
static InputManager & instance()
Singleton design.
void installCallback(CallbackType filter, callback_t callback, void *meta=0, Thread *pThread=0, uintptr_t param=0)
Installs a callback.
void removeCallback(callback_t callback, void *meta=0, Thread *pThread=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:26
bool keyUp
Whether this is a keyUp event or not.
uint8_t scancode
Machine-specific scancode for the key.