feat: Fix USB mouse support, GUI wkipedia app, icon refresh, file manager improvements, more

This commit is contained in:
2026-02-20 11:33:06 +01:00
parent 359ef3ba53
commit 4c6b783832
21 changed files with 1513 additions and 160 deletions
+107 -2
View File
@@ -13,9 +13,28 @@
#include <Memory/HHDM.hpp>
#include <Memory/PageFrameAllocator.hpp>
#include <Libraries/Memory.hpp>
#include <Timekeeping/ApicTimer.hpp>
using namespace Kt;
static void BusyWaitMs(uint64_t ms) {
uint64_t flags;
asm volatile("pushfq; pop %0" : "=r"(flags));
if (flags & (1 << 9)) {
// Interrupts enabled — use timer-based delay
uint64_t start = Timekeeping::GetMilliseconds();
while (Timekeeping::GetMilliseconds() - start < ms) {
asm volatile("pause" ::: "memory");
}
} else {
// Interrupts disabled (e.g. timer tick context) — use I/O port delay
// Each outb to port 0x80 takes ~1µs on x86
for (uint64_t i = 0; i < ms * 1000; i++) {
asm volatile("outb %%al, $0x80" ::: "memory");
}
}
}
// Access xHCI internal state needed during enumeration
namespace Drivers::USB::Xhci {
extern volatile uint32_t g_cmdCompletionSlotId;
@@ -163,16 +182,99 @@ namespace Drivers::USB::UsbDevice {
inputCtx->EP[0].Field2 = 8;
// -----------------------------------------------------------------
// Step 4: Address Device command
// Step 4a: Address Device (BSR=1) — initialize slot without SET_ADDRESS
// -----------------------------------------------------------------
Xhci::TRB addrTrb = {};
uint64_t inputCtxPhys = Memory::SubHHDM(inputCtx);
addrTrb.Parameter0 = (uint32_t)(inputCtxPhys & 0xFFFFFFFF);
addrTrb.Parameter1 = (uint32_t)(inputCtxPhys >> 32);
addrTrb.Control = (Xhci::TRB_ADDRESS_DEVICE << Xhci::TRB_TYPE_SHIFT)
| Xhci::TRB_BSR
| ((uint32_t)slotId << 24);
cc = Xhci::SendCommand(addrTrb);
if (cc != Xhci::CC_SUCCESS) {
KernelLogStream(ERROR, "USB") << "Address Device (BSR=1) failed, slot="
<< (uint64_t)slotId << " cc=" << (uint64_t)cc;
dev->Active = false;
return 0;
}
KernelLogStream(INFO, "USB") << "Slot " << (uint64_t)slotId << " initialized (BSR=1)";
// -----------------------------------------------------------------
// Step 4b: GET_DESCRIPTOR (Device, 8 bytes) — read bMaxPacketSize0
// -----------------------------------------------------------------
uint8_t partialDesc[8] = {};
cc = Xhci::ControlTransfer(slotId, REQTYPE_DEV_TO_HOST, REQ_GET_DESCRIPTOR,
(DESC_DEVICE << 8), 0, 8,
partialDesc, true);
if (cc != Xhci::CC_SUCCESS && cc != Xhci::CC_SHORT_PACKET) {
KernelLogStream(ERROR, "USB") << "GET_DESCRIPTOR(8-byte) failed, cc=" << (uint64_t)cc;
dev->Active = false;
return 0;
}
uint8_t bMaxPacketSize0 = partialDesc[7];
if (bMaxPacketSize0 == 0) bMaxPacketSize0 = maxPacket; // fallback
KernelLogStream(INFO, "USB") << "Slot " << (uint64_t)slotId
<< ": bMaxPacketSize0=" << (uint64_t)bMaxPacketSize0;
// -----------------------------------------------------------------
// Step 4c: Evaluate Context — update EP0 max packet size if needed
// -----------------------------------------------------------------
if (bMaxPacketSize0 != maxPacket) {
auto* evalCtx = (Xhci::InputContext*)Memory::g_pfa->AllocateZeroed();
// Only updating EP0 — set AddFlags bit 1 (EP0), no slot context needed
evalCtx->ICC.AddFlags = (1 << 1);
// Copy current EP0 context and update max packet size
evalCtx->EP[0] = dev->OutputContext->EP[0];
evalCtx->EP[0].Field1 = (evalCtx->EP[0].Field1 & 0x0000FFFF)
| ((uint32_t)bMaxPacketSize0 << 16);
Xhci::TRB evalTrb = {};
uint64_t evalCtxPhys = Memory::SubHHDM(evalCtx);
evalTrb.Parameter0 = (uint32_t)(evalCtxPhys & 0xFFFFFFFF);
evalTrb.Parameter1 = (uint32_t)(evalCtxPhys >> 32);
evalTrb.Control = (Xhci::TRB_EVALUATE_CONTEXT << Xhci::TRB_TYPE_SHIFT)
| ((uint32_t)slotId << 24);
cc = Xhci::SendCommand(evalTrb);
if (cc != Xhci::CC_SUCCESS) {
KernelLogStream(WARNING, "USB") << "Evaluate Context failed, slot="
<< (uint64_t)slotId << " cc=" << (uint64_t)cc;
// Non-fatal: continue with original max packet size
} else {
KernelLogStream(INFO, "USB") << "Slot " << (uint64_t)slotId
<< ": EP0 max packet updated to " << (uint64_t)bMaxPacketSize0;
}
}
// -----------------------------------------------------------------
// Step 4d: Address Device (BSR=0) — actually send SET_ADDRESS
// -----------------------------------------------------------------
// Update input context EP0 to current ring position and actual max
// packet size. BSR=0 re-initializes the output EP0 context from the
// input context, so both fields must reflect reality.
uint64_t curDeq = dev->EP0RingPhys + (uint64_t)dev->EP0RingEnqueue * sizeof(Xhci::TRB);
if (dev->EP0RingCCS) {
curDeq |= 1; // DCS bit
}
inputCtx->EP[0].TRDequeuePtr = curDeq;
inputCtx->EP[0].Field1 = (3 << 1)
| (Xhci::EP_TYPE_CONTROL << 3)
| ((uint32_t)bMaxPacketSize0 << 16);
Xhci::TRB addrTrb2 = {};
addrTrb2.Parameter0 = (uint32_t)(inputCtxPhys & 0xFFFFFFFF);
addrTrb2.Parameter1 = (uint32_t)(inputCtxPhys >> 32);
addrTrb2.Control = (Xhci::TRB_ADDRESS_DEVICE << Xhci::TRB_TYPE_SHIFT)
| ((uint32_t)slotId << 24);
cc = Xhci::SendCommand(addrTrb2);
if (cc != Xhci::CC_SUCCESS) {
KernelLogStream(ERROR, "USB") << "Address Device failed, slot="
<< (uint64_t)slotId << " cc=" << (uint64_t)cc;
@@ -180,10 +282,13 @@ namespace Drivers::USB::UsbDevice {
return 0;
}
// Set-address recovery time (USB spec requires >= 2ms, use 10ms for safety)
BusyWaitMs(10);
KernelLogStream(INFO, "USB") << "Slot " << (uint64_t)slotId << " addressed";
// -----------------------------------------------------------------
// Step 5: GET_DESCRIPTOR (Device)
// Step 5: GET_DESCRIPTOR (Device, full 18 bytes)
// -----------------------------------------------------------------
DeviceDescriptor devDesc = {};
cc = Xhci::ControlTransfer(slotId, REQTYPE_DEV_TO_HOST, REQ_GET_DESCRIPTOR,
+28 -4
View File
@@ -16,9 +16,28 @@
#include <Memory/PageFrameAllocator.hpp>
#include <Libraries/Memory.hpp>
#include <Hal/Apic/Interrupts.hpp>
#include <Timekeeping/ApicTimer.hpp>
using namespace Kt;
static void BusyWaitMs(uint64_t ms) {
uint64_t flags;
asm volatile("pushfq; pop %0" : "=r"(flags));
if (flags & (1 << 9)) {
// Interrupts enabled — use timer-based delay
uint64_t start = Timekeeping::GetMilliseconds();
while (Timekeeping::GetMilliseconds() - start < ms) {
asm volatile("pause" ::: "memory");
}
} else {
// Interrupts disabled (e.g. timer tick context) — use I/O port delay
// Each outb to port 0x80 takes ~1µs on x86
for (uint64_t i = 0; i < ms * 1000; i++) {
asm volatile("outb %%al, $0x80" ::: "memory");
}
}
}
namespace Drivers::USB::Xhci {
// -------------------------------------------------------------------------
@@ -568,8 +587,9 @@ namespace Drivers::USB::Xhci {
if (alreadyActive) continue;
if (portsc & PORTSC_PED) {
// Already enabled — enumerate directly
// Already enabled — enumerate after recovery delay
uint32_t speed = (portsc >> 10) & 0xF;
BusyWaitMs(10);
UsbDevice::EnumerateDevice(port + 1, speed);
} else {
// Need port reset first
@@ -602,6 +622,9 @@ namespace Drivers::USB::Xhci {
WriteOp(OP_PORTSC_BASE + port * OP_PORTSC_STRIDE,
(portsc & PORTSC_PRESERVE) | PORTSC_CHANGE_BITS);
// Post-reset recovery delay (USB spec requires >= 10ms)
BusyWaitMs(10);
UsbDevice::EnumerateDevice(port + 1, speed);
}
} else {
@@ -901,9 +924,7 @@ namespace Drivers::USB::Xhci {
}
}
// Wait for port power to stabilize (~20ms)
for (int i = 0; i < 20000000; i++) {
asm volatile("" ::: "memory");
}
BusyWaitMs(20);
KernelLogStream(OK, "xHCI") << "All ports powered";
// -----------------------------------------------------------------
@@ -962,6 +983,9 @@ namespace Drivers::USB::Xhci {
KernelLogStream(OK, "xHCI") << "Port " << base::dec << (uint64_t)(port + 1)
<< ": reset complete, speed=" << speedStr;
// Post-reset recovery delay (USB spec requires >= 10ms)
BusyWaitMs(10);
// Enumerate the device (port IDs are 1-based)
UsbDevice::EnumerateDevice(port + 1, speed);
}