/*
    * UsbDevice.cpp
    * USB device enumeration and configuration
    * Copyright (c) 2025 Daniel Hammer
*/

#include "UsbDevice.hpp"
#include "Xhci.hpp"
#include "HidKeyboard.hpp"
#include "HidMouse.hpp"
#include <Terminal/Terminal.hpp>
#include <CppLib/Stream.hpp>
#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;
    extern uint64_t* g_dcbaa;
}

namespace Drivers::USB::UsbDevice {

    // ---------------------------------------------------------------------------
    // Helpers
    // ---------------------------------------------------------------------------

    static uint16_t MaxPacketSizeForSpeed(uint32_t speed) {
        switch (speed) {
            case Xhci::SPEED_LOW:   return 8;
            case Xhci::SPEED_FULL:  return 8;
            case Xhci::SPEED_HIGH:  return 64;
            case Xhci::SPEED_SUPER: return 512;
            default:                return 64;
        }
    }

    // Map xHCI port speed to the slot context speed field value.
    // Per the xHCI spec the slot context speed field uses the same encoding
    // as PORTSC (1=Full, 2=Low, 3=High, 4=Super).
    static uint32_t SpeedToSlotContextValue(uint32_t speed) {
        return speed;  // Same encoding
    }

    // Convert USB endpoint bInterval to xHCI Endpoint Context Interval value.
    // HS/SS: bInterval is already in 2^(n-1) * 125µs encoding — use directly.
    // FS/LS: bInterval is in milliseconds (frames) — convert via fls(bInterval * 8).
    static uint32_t ConvertInterval(uint32_t speed, uint8_t bInterval) {
        if (bInterval == 0) return 0;

        if (speed == Xhci::SPEED_HIGH || speed == Xhci::SPEED_SUPER) {
            return bInterval;
        }

        // FS/LS: bInterval ms → microframes, then find highest set bit position
        uint32_t microframes = (uint32_t)bInterval * 8;
        uint32_t interval = 0;
        while (microframes > 0) {
            interval++;
            microframes >>= 1;
        }
        if (interval > 15) interval = 15;
        return interval;
    }

    // Convert xHCI port speed to a human-readable string
    static const char* SpeedToString(uint32_t speed) {
        switch (speed) {
            case Xhci::SPEED_LOW:   return "Low";
            case Xhci::SPEED_FULL:  return "Full";
            case Xhci::SPEED_HIGH:  return "High";
            case Xhci::SPEED_SUPER: return "Super";
            default:                return "Unknown";
        }
    }

    // ---------------------------------------------------------------------------
    // EnumerateDevice
    // ---------------------------------------------------------------------------

    uint8_t EnumerateDevice(uint8_t portId, uint32_t speed) {
        KernelLogStream(INFO, "USB") << "Enumerating device on port "
            << (uint64_t)portId << " speed=" << SpeedToString(speed);

        // -----------------------------------------------------------------
        // Step 1: Enable Slot
        // -----------------------------------------------------------------
        Xhci::TRB enableSlotTrb = {};
        enableSlotTrb.Control = (Xhci::TRB_ENABLE_SLOT << Xhci::TRB_TYPE_SHIFT);

        uint32_t cc = Xhci::SendCommand(enableSlotTrb);
        if (cc != Xhci::CC_SUCCESS) {
            KernelLogStream(ERROR, "USB") << "Enable Slot failed, cc=" << (uint64_t)cc;
            return 0;
        }

        uint8_t slotId = (uint8_t)Xhci::g_cmdCompletionSlotId;
        if (slotId == 0 || slotId > Xhci::MAX_SLOTS) {
            KernelLogStream(ERROR, "USB") << "Invalid slot ID: " << (uint64_t)slotId;
            return 0;
        }

        KernelLogStream(INFO, "USB") << "Slot " << (uint64_t)slotId << " enabled";

        // -----------------------------------------------------------------
        // Step 2: Allocate device output context and set DCBAA entry
        // -----------------------------------------------------------------
        auto* dev = Xhci::GetDevice(slotId);
        dev->Active = true;
        dev->PortId = portId;
        dev->Speed  = speed;

        // Allocate a zeroed page for the output DeviceContext
        auto* outputCtx = (Xhci::DeviceContext*)Memory::g_pfa->AllocateZeroed();
        dev->OutputContext     = outputCtx;
        dev->OutputContextPhys = Memory::SubHHDM(outputCtx);

        // Point DCBAA[slotId] to the output context physical address
        Xhci::g_dcbaa[slotId] = dev->OutputContextPhys;

        // -----------------------------------------------------------------
        // Step 3: Build Input Context for Address Device command
        // -----------------------------------------------------------------
        auto* inputCtx = (Xhci::InputContext*)Memory::g_pfa->AllocateZeroed();

        // Input Control Context: add Slot Context (bit 0) and EP0 (bit 1)
        inputCtx->ICC.AddFlags = 0x3;

        // Slot Context
        uint32_t speedVal    = SpeedToSlotContextValue(speed);
        uint32_t ctxEntries  = 1;  // Context Entries = 1 (Slot + EP0 only)
        inputCtx->Slot.Field0 = (ctxEntries << 27) | (speedVal << 20);
        inputCtx->Slot.Field1 = ((uint32_t)portId << 16);  // Root Hub Port Number

        // EP0 Context
        // Allocate EP0 transfer ring
        auto* ep0Ring = (Xhci::TRB*)Memory::g_pfa->AllocateZeroed();
        dev->EP0Ring        = ep0Ring;
        dev->EP0RingPhys    = Memory::SubHHDM(ep0Ring);
        dev->EP0RingEnqueue = 0;
        dev->EP0RingCCS     = true;

        // Set up Link TRB at last position to wrap back to start
        // (bit 1 = Toggle Cycle on Link TRBs)
        Xhci::TRB& ep0Link = ep0Ring[Xhci::XFER_RING_SIZE - 1];
        ep0Link.Parameter0 = (uint32_t)(dev->EP0RingPhys & 0xFFFFFFFF);
        ep0Link.Parameter1 = (uint32_t)(dev->EP0RingPhys >> 32);
        ep0Link.Status = 0;
        ep0Link.Control = (Xhci::TRB_LINK << Xhci::TRB_TYPE_SHIFT) | Xhci::TRB_ENT;

        uint16_t maxPacket = MaxPacketSizeForSpeed(speed);

        // Field1: CErr=3 (bits 2:1), EP Type=Control=4 (bits 5:3), Max Packet Size (bits 31:16)
        inputCtx->EP[0].Field1 = (3 << 1)
                                | (Xhci::EP_TYPE_CONTROL << 3)
                                | ((uint32_t)maxPacket << 16);
        // TR Dequeue Pointer with DCS=1
        inputCtx->EP[0].TRDequeuePtr = dev->EP0RingPhys | 1;
        // Average TRB Length = 8
        inputCtx->EP[0].Field2 = 8;

        // -----------------------------------------------------------------
        // 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;
            dev->Active = false;
            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, full 18 bytes)
        // -----------------------------------------------------------------
        DeviceDescriptor devDesc = {};
        cc = Xhci::ControlTransfer(slotId, REQTYPE_DEV_TO_HOST, REQ_GET_DESCRIPTOR,
                                   (DESC_DEVICE << 8), 0, sizeof(DeviceDescriptor),
                                   &devDesc, true);
        if (cc != Xhci::CC_SUCCESS && cc != Xhci::CC_SHORT_PACKET) {
            KernelLogStream(ERROR, "USB") << "GET_DESCRIPTOR(Device) failed, cc=" << (uint64_t)cc;
            dev->Active = false;
            return 0;
        }

        dev->VendorId  = devDesc.idVendor;
        dev->ProductId = devDesc.idProduct;

        KernelLogStream(INFO, "USB") << "Slot " << (uint64_t)slotId
            << ": VID:PID = " << base::hex << (uint64_t)devDesc.idVendor
            << ":" << (uint64_t)devDesc.idProduct << base::dec;

        // -----------------------------------------------------------------
        // Step 6: GET_DESCRIPTOR (Configuration) -- header first, then full
        // -----------------------------------------------------------------
        ConfigDescriptor cfgHdr = {};
        cc = Xhci::ControlTransfer(slotId, REQTYPE_DEV_TO_HOST, REQ_GET_DESCRIPTOR,
                                   (DESC_CONFIGURATION << 8), 0, sizeof(ConfigDescriptor),
                                   &cfgHdr, true);
        if (cc != Xhci::CC_SUCCESS && cc != Xhci::CC_SHORT_PACKET) {
            KernelLogStream(ERROR, "USB") << "GET_DESCRIPTOR(Config header) failed, cc=" << (uint64_t)cc;
            dev->Active = false;
            return 0;
        }

        uint8_t cfgBuf[256] = {};
        uint16_t totalLen = cfgHdr.wTotalLength;
        if (totalLen > 256) totalLen = 256;

        cc = Xhci::ControlTransfer(slotId, REQTYPE_DEV_TO_HOST, REQ_GET_DESCRIPTOR,
                                   (DESC_CONFIGURATION << 8), 0, totalLen,
                                   cfgBuf, true);
        if (cc != Xhci::CC_SUCCESS && cc != Xhci::CC_SHORT_PACKET) {
            KernelLogStream(ERROR, "USB") << "GET_DESCRIPTOR(Config full) failed, cc=" << (uint64_t)cc;
            dev->Active = false;
            return 0;
        }

        // -----------------------------------------------------------------
        // Step 7: Parse configuration descriptor blob
        // -----------------------------------------------------------------
        uint16_t offset = 0;
        bool foundHid = false;
        bool foundEp = false;
        uint16_t hidReportDescLen = 0;

        while (offset + 2 <= totalLen) {
            uint8_t len  = cfgBuf[offset];
            uint8_t type = cfgBuf[offset + 1];
            if (len == 0) break;

            if (type == DESC_INTERFACE && offset + sizeof(InterfaceDescriptor) <= totalLen) {
                auto* iface = (InterfaceDescriptor*)&cfgBuf[offset];
                // Reset foundHid at each new interface boundary so we don't
                // accidentally pick up endpoints from a different interface.
                foundHid = false;
                if (!foundEp &&
                    iface->bInterfaceClass == CLASS_HID &&
                    iface->bInterfaceSubClass == SUBCLASS_BOOT) {
                    dev->InterfaceClass    = iface->bInterfaceClass;
                    dev->InterfaceSubClass = iface->bInterfaceSubClass;
                    dev->InterfaceProtocol = iface->bInterfaceProtocol;
                    foundHid = true;
                }
            }

            // 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];
                if ((ep->bEndpointAddress & EP_DIR_IN) &&
                    (ep->bmAttributes & EP_XFER_TYPE_MASK) == EP_XFER_INTERRUPT) {
                    dev->InterruptEpNum     = ep->bEndpointAddress & 0x0F;
                    dev->InterruptMaxPacket = ep->wMaxPacketSize & 0x7FF;
                    dev->InterruptInterval  = ep->bInterval;
                    foundEp = true;
                }
            }

            offset += len;
        }

        // -----------------------------------------------------------------
        // Step 8: SET_CONFIGURATION
        // -----------------------------------------------------------------
        cc = Xhci::ControlTransfer(slotId, REQTYPE_HOST_TO_DEV, REQ_SET_CONFIGURATION,
                                   cfgHdr.bConfigurationValue, 0, 0, nullptr, false);
        if (cc != Xhci::CC_SUCCESS) {
            KernelLogStream(ERROR, "USB") << "SET_CONFIGURATION failed, cc=" << (uint64_t)cc;
            dev->Active = false;
            return 0;
        }

        // -----------------------------------------------------------------
        // Step 9: Configure Endpoint (if HID interrupt endpoint was found)
        // -----------------------------------------------------------------
        if (foundEp) {
            // Device Context Index for an IN endpoint:  DCI = EpNum * 2 + 1
            uint8_t dci = dev->InterruptEpNum * 2 + 1;

            auto* inputCtx2 = (Xhci::InputContext*)Memory::g_pfa->AllocateZeroed();

            // ICC: Add slot context (bit 0) and the interrupt endpoint (bit dci)
            inputCtx2->ICC.AddFlags = (1 << 0) | (1 << dci);

            // Copy the current slot context from the output context
            inputCtx2->Slot = dev->OutputContext->Slot;

            // Update Context Entries in slot context to at least cover this DCI
            uint32_t newCtxEntries = dci;
            inputCtx2->Slot.Field0 = (inputCtx2->Slot.Field0 & ~(0x1Fu << 27))
                                   | (newCtxEntries << 27);

            // Allocate interrupt transfer ring
            auto* intRing = (Xhci::TRB*)Memory::g_pfa->AllocateZeroed();
            dev->InterruptRing        = intRing;
            dev->InterruptRingPhys    = Memory::SubHHDM(intRing);
            dev->InterruptRingEnqueue = 0;
            dev->InterruptRingCCS     = true;

            // Set up Link TRB at last position
            Xhci::TRB& intLink = intRing[Xhci::XFER_RING_SIZE - 1];
            intLink.Parameter0 = (uint32_t)(dev->InterruptRingPhys & 0xFFFFFFFF);
            intLink.Parameter1 = (uint32_t)(dev->InterruptRingPhys >> 32);
            intLink.Status = 0;
            intLink.Control = (Xhci::TRB_LINK << Xhci::TRB_TYPE_SHIFT) | Xhci::TRB_ENT;

            // Endpoint Context for the interrupt IN endpoint
            auto& epCtx = inputCtx2->EP[dci - 1];  // EP array is 0-indexed, DCI 1 = EP[0]

            // Field0: Interval (bits 23:16) — convert bInterval to xHCI encoding
            uint32_t xhciInterval = ConvertInterval(speed, dev->InterruptInterval);
            epCtx.Field0 = (xhciInterval << 16);

            // Field1: CErr=3 (bits 2:1), EP Type=Interrupt IN=7 (bits 5:3),
            //         Max Packet Size (bits 31:16)
            epCtx.Field1 = (3 << 1)
                         | (Xhci::EP_TYPE_INTERRUPT_IN << 3)
                         | ((uint32_t)dev->InterruptMaxPacket << 16);

            // TR Dequeue Pointer with DCS=1
            epCtx.TRDequeuePtr = dev->InterruptRingPhys | 1;

            // Average TRB Length
            epCtx.Field2 = dev->InterruptMaxPacket;

            // Send Configure Endpoint command
            Xhci::TRB cfgTrb = {};
            uint64_t inputCtx2Phys = Memory::SubHHDM(inputCtx2);
            cfgTrb.Parameter0 = (uint32_t)(inputCtx2Phys & 0xFFFFFFFF);
            cfgTrb.Parameter1 = (uint32_t)(inputCtx2Phys >> 32);
            cfgTrb.Control    = (Xhci::TRB_CONFIGURE_ENDPOINT << Xhci::TRB_TYPE_SHIFT)
                              | ((uint32_t)slotId << 24);

            cc = Xhci::SendCommand(cfgTrb);
            if (cc != Xhci::CC_SUCCESS) {
                KernelLogStream(ERROR, "USB") << "Configure Endpoint failed, slot="
                    << (uint64_t)slotId << " cc=" << (uint64_t)cc;
                dev->Active = false;
                return 0;
            }

            KernelLogStream(INFO, "USB") << "Slot " << (uint64_t)slotId
                << ": Interrupt EP " << (uint64_t)dev->InterruptEpNum
                << " configured (DCI " << (uint64_t)dci << ")";
        }

        // -----------------------------------------------------------------
        // Step 10: SET_PROTOCOL -- Boot Protocol for keyboards only
        // -----------------------------------------------------------------
        // 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);
            if (cc != Xhci::CC_SUCCESS) {
                KernelLogStream(WARNING, "USB") << "SET_PROTOCOL(Boot) failed, cc=" << (uint64_t)cc;
                // Non-fatal: some devices only support boot protocol anyway
            }
        }

        // -----------------------------------------------------------------
        // 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(0) -- only report on changes (no idle reports)
        // -----------------------------------------------------------------
        if (foundEp && dev->InterfaceProtocol == PROTOCOL_KEYBOARD) {
            // wValue upper byte = duration (0 = indefinite), lower byte = report ID
            cc = Xhci::ControlTransfer(slotId, REQTYPE_CLASS_IFACE, REQ_SET_IDLE,
                                       (0 << 8), 0, 0, nullptr, false);
            if (cc != Xhci::CC_SUCCESS) {
                KernelLogStream(WARNING, "USB") << "SET_IDLE(0) failed, cc=" << (uint64_t)cc;
                // Non-fatal: not all devices support SET_IDLE
            }
        }

        // -----------------------------------------------------------------
        // Step 12: Queue first interrupt transfer
        // -----------------------------------------------------------------
        if (foundEp) {
            Xhci::QueueInterruptTransfer(slotId);
        }

        // -----------------------------------------------------------------
        // Step 13: Register with the appropriate HID driver
        // -----------------------------------------------------------------
        if (dev->InterfaceProtocol == PROTOCOL_KEYBOARD) {
            HidKeyboard::RegisterDevice(slotId);
            KernelLogStream(OK, "USB") << "Slot " << (uint64_t)slotId << ": HID Boot Keyboard";
        } else if (dev->InterfaceProtocol == PROTOCOL_MOUSE) {
            HidMouse::RegisterDevice(slotId);
            KernelLogStream(OK, "USB") << "Slot " << (uint64_t)slotId << ": HID Boot Mouse";
        } else if (foundEp) {
            KernelLogStream(INFO, "USB") << "Slot " << (uint64_t)slotId
                << ": HID device, protocol=" << (uint64_t)dev->InterfaceProtocol;
        } else {
            KernelLogStream(INFO, "USB") << "Slot " << (uint64_t)slotId
                << ": Non-HID device, class=" << (uint64_t)devDesc.bDeviceClass;
        }

        return slotId;
    }

}
