fix: GUI and kernel fixes, Wikipedia app rewrite, among other things
This commit is contained in:
@@ -109,7 +109,7 @@ namespace Drivers::Graphics::IntelGPU {
|
||||
} else {
|
||||
// Unknown device ID - accept generically but warn
|
||||
g_gpuInfo.gen = 7; // Assume gen 7 as a safe default
|
||||
g_gpuInfo.name = "Unknown Intel GPU";
|
||||
g_gpuInfo.name = "Intel GPU";
|
||||
|
||||
KernelLogStream(WARNING, "IntelGPU") << "Unknown Intel display controller "
|
||||
<< "(device " << base::hex << (uint64_t)found->DeviceId << ")"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* HidMouse.cpp
|
||||
* USB HID Boot Protocol Mouse driver
|
||||
* USB HID Mouse driver (Report Protocol with descriptor parsing)
|
||||
* Copyright (c) 2025 Daniel Hammer
|
||||
*/
|
||||
|
||||
@@ -18,6 +18,173 @@ namespace Drivers::USB::HidMouse {
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
static uint8_t g_SlotId = 0;
|
||||
static MouseReportFormat g_Format = {};
|
||||
static bool g_FormatValid = false;
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// HID Report Descriptor parsing
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
// HID item size lookup: bSize field (bits 0-1) → byte count
|
||||
static uint8_t ItemDataSize(uint8_t bSize) {
|
||||
static const uint8_t sizes[] = {0, 1, 2, 4};
|
||||
return sizes[bSize & 0x03];
|
||||
}
|
||||
|
||||
// Read an unsigned value from 1-4 bytes (little-endian)
|
||||
static uint32_t ReadItemData(const uint8_t* p, uint8_t size) {
|
||||
switch (size) {
|
||||
case 1: return p[0];
|
||||
case 2: return (uint32_t)p[0] | ((uint32_t)p[1] << 8);
|
||||
case 4: return (uint32_t)p[0] | ((uint32_t)p[1] << 8)
|
||||
| ((uint32_t)p[2] << 16) | ((uint32_t)p[3] << 24);
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// HID usage page constants
|
||||
static constexpr uint16_t USAGE_PAGE_GENERIC_DESKTOP = 0x01;
|
||||
static constexpr uint16_t USAGE_PAGE_BUTTON = 0x09;
|
||||
|
||||
// HID usage constants (Generic Desktop)
|
||||
static constexpr uint16_t USAGE_X = 0x30;
|
||||
static constexpr uint16_t USAGE_Y = 0x31;
|
||||
static constexpr uint16_t USAGE_WHEEL = 0x38;
|
||||
|
||||
// Maximum local usages we track between Input items
|
||||
static constexpr int MAX_USAGES = 16;
|
||||
|
||||
void ParseReportDescriptor(const uint8_t* desc, uint16_t length) {
|
||||
g_FormatValid = false;
|
||||
|
||||
MouseReportFormat fmt = {};
|
||||
|
||||
// Global state
|
||||
uint16_t usagePage = 0;
|
||||
uint32_t reportSize = 0; // bits per field
|
||||
uint32_t reportCount = 0; // number of fields
|
||||
|
||||
// Local state (reset after each Main item)
|
||||
uint16_t usages[MAX_USAGES] = {};
|
||||
int usageCount = 0;
|
||||
bool hasUsageRange = false;
|
||||
|
||||
// Running bit offset within the report (excluding report ID byte)
|
||||
uint16_t bitOffset = 0;
|
||||
|
||||
uint16_t pos = 0;
|
||||
while (pos < length) {
|
||||
uint8_t header = desc[pos];
|
||||
|
||||
// Long items (0xFE prefix) — skip
|
||||
if (header == 0xFE) {
|
||||
if (pos + 1 >= length) break;
|
||||
uint8_t dataSize = desc[pos + 1];
|
||||
pos += 3 + dataSize;
|
||||
continue;
|
||||
}
|
||||
|
||||
uint8_t bSize = header & 0x03;
|
||||
uint8_t bType = (header >> 2) & 0x03;
|
||||
uint8_t bTag = (header >> 4) & 0x0F;
|
||||
uint8_t dataSize = ItemDataSize(bSize);
|
||||
|
||||
if (pos + 1 + dataSize > length) break;
|
||||
uint32_t data = ReadItemData(&desc[pos + 1], dataSize);
|
||||
pos += 1 + dataSize;
|
||||
|
||||
if (bType == 1) {
|
||||
// Global items
|
||||
switch (bTag) {
|
||||
case 0: usagePage = (uint16_t)data; break; // Usage Page
|
||||
case 7: reportSize = data; break; // Report Size
|
||||
case 8: fmt.hasReportId = true; // Report ID
|
||||
fmt.reportId = (uint8_t)data; break;
|
||||
case 9: reportCount = data; break; // Report Count
|
||||
}
|
||||
} else if (bType == 2) {
|
||||
// Local items
|
||||
switch (bTag) {
|
||||
case 0: // Usage
|
||||
if (usageCount < MAX_USAGES)
|
||||
usages[usageCount++] = (uint16_t)data;
|
||||
break;
|
||||
case 1: hasUsageRange = true; break; // Usage Minimum
|
||||
case 2: break; // Usage Maximum
|
||||
}
|
||||
} else if (bType == 0) {
|
||||
// Main items
|
||||
if (bTag == 8 || bTag == 9) {
|
||||
// Input (tag 8) or Output (tag 9)
|
||||
bool isConstant = (data & 0x01);
|
||||
|
||||
if (bTag == 8 && !isConstant) {
|
||||
// Data input fields — map usages to bit offsets
|
||||
if (usagePage == USAGE_PAGE_BUTTON && hasUsageRange) {
|
||||
fmt.buttonBitOffset = bitOffset;
|
||||
fmt.buttonCount = (uint8_t)reportCount;
|
||||
} else if (usagePage == USAGE_PAGE_GENERIC_DESKTOP) {
|
||||
for (uint32_t i = 0; i < reportCount && i < (uint32_t)usageCount; i++) {
|
||||
uint16_t u = usages[i];
|
||||
uint16_t off = bitOffset + (uint16_t)(i * reportSize);
|
||||
if (u == USAGE_X) {
|
||||
fmt.xBitOffset = off;
|
||||
fmt.xBitSize = (uint8_t)reportSize;
|
||||
} else if (u == USAGE_Y) {
|
||||
fmt.yBitOffset = off;
|
||||
fmt.yBitSize = (uint8_t)reportSize;
|
||||
} else if (u == USAGE_WHEEL) {
|
||||
fmt.scrollBitOffset = off;
|
||||
fmt.scrollBitSize = (uint8_t)reportSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Advance bit offset for all input fields (data and constant/padding)
|
||||
bitOffset += (uint16_t)(reportSize * reportCount);
|
||||
}
|
||||
|
||||
// Reset local state after any Main item
|
||||
usageCount = 0;
|
||||
hasUsageRange = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Validate: we need at least X and Y
|
||||
if (fmt.xBitSize > 0 && fmt.yBitSize > 0) {
|
||||
g_Format = fmt;
|
||||
g_FormatValid = true;
|
||||
|
||||
KernelLogStream(INFO, "USB/Mouse")
|
||||
<< "Report format: buttons=" << (uint64_t)fmt.buttonCount
|
||||
<< " X@" << (uint64_t)fmt.xBitOffset << ":" << (uint64_t)fmt.xBitSize
|
||||
<< " Y@" << (uint64_t)fmt.yBitOffset << ":" << (uint64_t)fmt.yBitSize
|
||||
<< " scroll=" << (uint64_t)fmt.scrollBitSize
|
||||
<< (fmt.hasReportId ? " (has report ID)" : "");
|
||||
} else {
|
||||
KernelLogStream(WARNING, "USB/Mouse") << "Could not parse report descriptor, using boot protocol fallback";
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Bit-field extraction
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
static int32_t ExtractSigned(const uint8_t* data, uint16_t bitOffset, uint8_t bitSize) {
|
||||
int32_t value = 0;
|
||||
for (uint8_t i = 0; i < bitSize; i++) {
|
||||
uint16_t byteIdx = (bitOffset + i) / 8;
|
||||
uint8_t bitIdx = (bitOffset + i) % 8;
|
||||
if (data[byteIdx] & (1 << bitIdx))
|
||||
value |= (1 << i);
|
||||
}
|
||||
// Sign extend
|
||||
if (bitSize < 32 && (value & (1 << (bitSize - 1)))) {
|
||||
value |= ~((1 << bitSize) - 1);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Public API
|
||||
@@ -25,25 +192,53 @@ namespace Drivers::USB::HidMouse {
|
||||
|
||||
void RegisterDevice(uint8_t slotId) {
|
||||
g_SlotId = slotId;
|
||||
|
||||
KernelLogStream(OK, "USB/Mouse") << "Registered HID mouse on slot " << (uint64_t)slotId;
|
||||
}
|
||||
|
||||
void ProcessReport(const uint8_t* data, uint16_t length) {
|
||||
if (length < 3) return;
|
||||
|
||||
// Boot protocol mouse report:
|
||||
// Byte 0: Buttons (bit 0 = left, bit 1 = right, bit 2 = middle)
|
||||
// Byte 1: X displacement (signed int8_t)
|
||||
// Byte 2: Y displacement (signed int8_t)
|
||||
// Byte 3: Scroll wheel (signed int8_t, optional)
|
||||
if (!g_FormatValid) {
|
||||
// Fallback: assume boot protocol format
|
||||
uint8_t buttons = data[0] & 0x07;
|
||||
int8_t deltaX = (int8_t)data[1];
|
||||
int8_t deltaY = (int8_t)data[2];
|
||||
int8_t scroll = (length >= 4) ? (int8_t)data[3] : 0;
|
||||
Drivers::PS2::Mouse::InjectMouseReport(buttons, deltaX, deltaY, scroll);
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t buttons = data[0] & 0x07;
|
||||
int8_t deltaX = (int8_t)data[1];
|
||||
int8_t deltaY = (int8_t)data[2];
|
||||
int8_t scroll = (length >= 4) ? (int8_t)data[3] : 0;
|
||||
// Skip report ID byte if present
|
||||
uint16_t byteOffset = g_Format.hasReportId ? 8 : 0;
|
||||
|
||||
Drivers::PS2::Mouse::InjectMouseReport(buttons, deltaX, deltaY, scroll);
|
||||
// Extract buttons
|
||||
uint8_t buttons = 0;
|
||||
for (uint8_t i = 0; i < g_Format.buttonCount && i < 8; i++) {
|
||||
uint16_t bit = byteOffset + g_Format.buttonBitOffset + i;
|
||||
if (data[bit / 8] & (1 << (bit % 8)))
|
||||
buttons |= (1 << i);
|
||||
}
|
||||
|
||||
// Extract X, Y
|
||||
int32_t rawX = ExtractSigned(data, byteOffset + g_Format.xBitOffset, g_Format.xBitSize);
|
||||
int32_t rawY = ExtractSigned(data, byteOffset + g_Format.yBitOffset, g_Format.yBitSize);
|
||||
|
||||
// Clamp to int8_t range for InjectMouseReport
|
||||
if (rawX > 127) rawX = 127;
|
||||
if (rawX < -128) rawX = -128;
|
||||
if (rawY > 127) rawY = 127;
|
||||
if (rawY < -128) rawY = -128;
|
||||
|
||||
// Extract scroll wheel
|
||||
int8_t scroll = 0;
|
||||
if (g_Format.scrollBitSize > 0) {
|
||||
int32_t rawScroll = ExtractSigned(data, byteOffset + g_Format.scrollBitOffset, g_Format.scrollBitSize);
|
||||
if (rawScroll > 127) rawScroll = 127;
|
||||
if (rawScroll < -128) rawScroll = -128;
|
||||
scroll = (int8_t)rawScroll;
|
||||
}
|
||||
|
||||
Drivers::PS2::Mouse::InjectMouseReport(buttons, (int8_t)rawX, (int8_t)rawY, scroll);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* HidMouse.hpp
|
||||
* USB HID Boot Protocol Mouse driver
|
||||
* USB HID Mouse driver (Report Protocol with descriptor parsing)
|
||||
* Copyright (c) 2025 Daniel Hammer
|
||||
*/
|
||||
|
||||
@@ -9,10 +9,28 @@
|
||||
|
||||
namespace Drivers::USB::HidMouse {
|
||||
|
||||
// Parsed layout of a mouse HID report
|
||||
struct MouseReportFormat {
|
||||
bool hasReportId;
|
||||
uint8_t reportId;
|
||||
uint8_t buttonBitOffset; // bit offset of first button
|
||||
uint8_t buttonCount;
|
||||
uint8_t xBitOffset;
|
||||
uint8_t xBitSize; // 8 or 16 typically
|
||||
uint8_t yBitOffset;
|
||||
uint8_t yBitSize;
|
||||
uint8_t scrollBitOffset;
|
||||
uint8_t scrollBitSize; // 0 = no scroll wheel
|
||||
};
|
||||
|
||||
// Register a mouse device by slot ID
|
||||
void RegisterDevice(uint8_t slotId);
|
||||
|
||||
// Process a 3-4 byte boot protocol mouse report
|
||||
// Parse a HID Report Descriptor to determine the report layout.
|
||||
// Must be called before ProcessReport for Report Protocol mice.
|
||||
void ParseReportDescriptor(const uint8_t* desc, uint16_t length);
|
||||
|
||||
// Process an incoming mouse report using the parsed format
|
||||
void ProcessReport(const uint8_t* data, uint16_t length);
|
||||
|
||||
};
|
||||
|
||||
@@ -339,6 +339,7 @@ namespace Drivers::USB::UsbDevice {
|
||||
uint16_t offset = 0;
|
||||
bool foundHid = false;
|
||||
bool foundEp = false;
|
||||
uint16_t hidReportDescLen = 0;
|
||||
|
||||
while (offset + 2 <= totalLen) {
|
||||
uint8_t len = cfgBuf[offset];
|
||||
@@ -360,6 +361,12 @@ namespace Drivers::USB::UsbDevice {
|
||||
}
|
||||
}
|
||||
|
||||
// HID descriptor (0x21): extract report descriptor length
|
||||
if (type == DESC_HID && foundHid && !foundEp && len >= 9 && offset + 8 < totalLen) {
|
||||
hidReportDescLen = (uint16_t)cfgBuf[offset + 7]
|
||||
| ((uint16_t)cfgBuf[offset + 8] << 8);
|
||||
}
|
||||
|
||||
if (type == DESC_ENDPOINT && foundHid && !foundEp &&
|
||||
offset + sizeof(EndpointDescriptor) <= totalLen) {
|
||||
auto* ep = (EndpointDescriptor*)&cfgBuf[offset];
|
||||
@@ -463,8 +470,9 @@ namespace Drivers::USB::UsbDevice {
|
||||
// -----------------------------------------------------------------
|
||||
// Step 10: SET_PROTOCOL -- Boot Protocol for keyboards only
|
||||
// -----------------------------------------------------------------
|
||||
// Boot Protocol constrains mice to 3-byte reports (no scroll wheel).
|
||||
// Keep mice in Report Protocol (the default) so scroll data is included.
|
||||
// Set Boot Protocol for keyboards only.
|
||||
// Mice stay in Report Protocol (the default) for scroll wheel support;
|
||||
// HidMouse parses the HID Report Descriptor to handle variable formats.
|
||||
if (foundEp && dev->InterfaceProtocol == PROTOCOL_KEYBOARD) {
|
||||
cc = Xhci::ControlTransfer(slotId, REQTYPE_CLASS_IFACE, REQ_SET_PROTOCOL,
|
||||
0, 0, 0, nullptr, false);
|
||||
@@ -474,6 +482,24 @@ namespace Drivers::USB::UsbDevice {
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
// Step 10b: Fetch HID Report Descriptor for mice
|
||||
// -----------------------------------------------------------------
|
||||
if (foundEp && dev->InterfaceProtocol == PROTOCOL_MOUSE && hidReportDescLen > 0) {
|
||||
uint8_t rdBuf[256] = {};
|
||||
uint16_t rdLen = hidReportDescLen;
|
||||
if (rdLen > 256) rdLen = 256;
|
||||
|
||||
cc = Xhci::ControlTransfer(slotId, REQTYPE_STD_IFACE_IN, REQ_GET_DESCRIPTOR,
|
||||
(DESC_HID_REPORT << 8), 0, rdLen,
|
||||
rdBuf, true);
|
||||
if (cc == Xhci::CC_SUCCESS || cc == Xhci::CC_SHORT_PACKET) {
|
||||
HidMouse::ParseReportDescriptor(rdBuf, rdLen);
|
||||
} else {
|
||||
KernelLogStream(WARNING, "USB") << "GET_DESCRIPTOR(HID Report) failed, cc=" << (uint64_t)cc;
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
// Step 11: SET_IDLE(4) -- 16ms idle rate for software typematic
|
||||
// -----------------------------------------------------------------
|
||||
|
||||
@@ -71,6 +71,8 @@ namespace Drivers::USB::UsbDevice {
|
||||
constexpr uint8_t DESC_CONFIGURATION = 2;
|
||||
constexpr uint8_t DESC_INTERFACE = 4;
|
||||
constexpr uint8_t DESC_ENDPOINT = 5;
|
||||
constexpr uint8_t DESC_HID = 0x21;
|
||||
constexpr uint8_t DESC_HID_REPORT = 0x22;
|
||||
|
||||
// USB class codes
|
||||
constexpr uint8_t CLASS_HID = 0x03;
|
||||
@@ -91,6 +93,7 @@ namespace Drivers::USB::UsbDevice {
|
||||
constexpr uint8_t REQTYPE_DEV_TO_HOST = 0x80;
|
||||
constexpr uint8_t REQTYPE_HOST_TO_DEV = 0x00;
|
||||
constexpr uint8_t REQTYPE_CLASS_IFACE = 0x21; // Host-to-device, class, interface
|
||||
constexpr uint8_t REQTYPE_STD_IFACE_IN = 0x81; // Dev-to-host, standard, interface
|
||||
|
||||
// Endpoint direction mask
|
||||
constexpr uint8_t EP_DIR_IN = 0x80;
|
||||
|
||||
Reference in New Issue
Block a user