XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet

XFESystem

【Rust】XFE系统,基于Rust和汇编

公开
关注 0 Fork 0 Star 0
UTF-8
using System.Runtime.InteropServices;

namespace XFESystem.NativeKernel;

public unsafe struct UsbRing { public ulong Physical; public uint Index, Cycle; }
public unsafe struct UsbController
{
    public ulong Base, Bytes, Op, Doorbell, Runtime, Dcbaa, Events, Erst;
    public UsbRing Commands;
    public ulong Completed, CompletionParameter;
    public uint CompletionCode, EventIndex, EventCycle, Ports, Slots, ContextBytes, Pci, Running, Address64;
    public byte* RootSeen;
}
public unsafe struct UsbDevice
{
    public UsbController* Controller;
    public UsbDevice* Parent;
    public ulong Input, Output, Buffer;
    public UsbRing Control;
    public ulong Completed;
    public uint CompletionCode, CompletionRemaining;
    public uint Slot, RootPort, ParentPort, Speed, Route, Depth, TtSlot, TtPort, Packet, Connected;
    public uint Vendor, Product, HubPorts, HubSuper, HubTt, Configured;
    public byte* HubSeen;
}
public unsafe struct UsbInterruptEndpoint
{
    public UsbDevice* Device;
    public UsbRing Ring;
    public ulong Buffer, Pending;
    public uint Dci, Packet, Kind, Active, Reports, Errors;
    public UsbHidLayout Layout;
    public UsbHidState State;
}

// Independent C# xHCI 1.x polling driver, not the legacy Cosmos driver.
// One native scheduler worker owns all rings. No USB waits run in the UI or IRQ.
// The same worker serializes HID and the bounded boot-volume BOT storage path.
public static unsafe partial class UsbXhci
{
    const uint ControllerLimit = 8, DeviceLimit = 96, EndpointLimit = 96;
    static UsbController* _controllers;
    static UsbDevice* _devices;
    static UsbInterruptEndpoint* _endpoints;
    static uint _controllerCount, _deviceCount, _endpointCount;
    public static uint ReadyBits, State;
    public static ulong Reports, Failures;
    static bool _firstKey, _firstPointer;
    [DllImport("xfe_native", EntryPoint = "xfe_network_pci_read")] static extern uint PciRead(uint b, uint d, uint f, uint o);
    [DllImport("xfe_native", EntryPoint = "xfe_network_pci_write")] static extern int PciWrite(uint b, uint d, uint f, uint o, uint v);
    static ulong Now() => MonotonicClock.Nanoseconds() / 1000000;
    static void Sleep(uint ms) => Threads.Sleep(ms);
    static uint Read(ulong address) => Native.MmioRead32(address);
    static void Write(ulong address, uint value) => Native.MmioWrite32(address, value);
    static void Write64(ulong address, ulong value) { Write(address, (uint)value); Write(address + 4, (uint)(value >> 32)); }
    static ulong Phys(void* address) => (ulong)address - PhysicalMemory.Hhdm;
    static byte* At(ulong physical) => physical == 0 ? null : (byte*)PhysicalMemory.ToVirtual(physical);
    static ulong Allocate(uint pages = 1)
    {
        ulong flags = Native.IrqSave(); ulong value = PhysicalMemory.Allocate(pages); Native.IrqRestore(flags); return value;
    }
    static void Log(string message) { Serial.Write("XFE_USB|"); Serial.Line(message); }
    static void DeviceLog(UsbDevice* d, string message)
    {
        Serial.Write("XFE_USB_DEVICE|Slot|"); Serial.Number(d->Slot); Serial.Write("|Root|"); Serial.Number(d->RootPort);
        Serial.Write("|Depth|"); Serial.Number(d->Depth); Serial.Write("|Route|"); Serial.Hex(d->Route);
        Serial.Write("|Vendor|"); Serial.Hex(d->Vendor); Serial.Write("|Product|"); Serial.Hex(d->Product);
        Serial.Write("|"); Serial.Line(message);
    }
    public static bool Start()
    {
        State = 1;
        return Threads.Create(&Worker, null, 262144, 0, false) != 0;
    }
    [UnmanagedCallersOnly(EntryPoint = "xfe_usb_worker")]
    public static uint Worker(void* argument)
    {
        _controllers = (UsbController*)At(Allocate(2));
        _devices = (UsbDevice*)At(Allocate(8));
        _endpoints = (UsbInterruptEndpoint*)At(Allocate(8));
        if (_controllers == null || _devices == null || _endpoints == null) { State = 4; Log("STATE_ALLOCATION_FAILED"); return 1; }
        for (uint b = 0; b < 256; b++) for (uint d = 0; d < 32; d++)
        {
            if ((PciRead(b, d, 0, 0) & 65535) == 65535) continue;
            uint functions = (PciRead(b, d, 0, 12) & 0x800000) != 0 ? 8u : 1u;
            for (uint f = 0; f < functions; f++)
            {
                if ((PciRead(b, d, f, 0) & 65535) == 65535 || (PciRead(b, d, f, 8) >> 8) != 0x0c0330) continue;
                if (_controllerCount == ControllerLimit) { Log("CONTROLLER_LIMIT"); continue; }
                UsbController* c = &_controllers[_controllerCount++]; c->Pci = b << 16 | d << 8 | f;
                Serial.Write("XFE_USB_CONTROLLER|Pci|"); Serial.Hex(c->Pci); Serial.Write("|Id|"); Serial.Hex(PciRead(b,d,f,0)); Serial.Line("");
                if (!Initialize(c)) { Failures++; Log("CONTROLLER_INIT_FAILED"); continue; }
                for (uint p = 1; p <= c->Ports; p++) Root(c, p);
            }
        }
        State = 2;
        Log("INITIAL_ENUMERATION_COMPLETE");
        ulong nextScan = Now() + 2000;
        for (;;)
        {
            for (uint i = 0; i < _controllerCount; i++) if (_controllers[i].Running != 0) Events(&_controllers[i]);
            ProcessStorageRequest();
            if (Now() >= nextScan)
            {
                nextScan = Now() + 2000;
                for (uint i = 0; i < _controllerCount; i++)
                    if (_controllers[i].Running != 0) for (uint p = 1; p <= _controllers[i].Ports; p++) Root(&_controllers[i], p);
                for (uint i = 0; i < _deviceCount; i++) if (_devices[i].Connected != 0 && _devices[i].HubPorts != 0) ScanHub(&_devices[i]);
            }
            Sleep(1);
        }
    }
    static bool WaitBits(ulong address, uint mask, uint expected, uint timeout)
    {
        ulong end = Now() + timeout;
        do { if ((Read(address) & mask) == expected) return true; Sleep(1); } while (Now() < end);
        return false;
    }
    static bool RangeAllowed(ulong physical, ulong bytes)
    {
        if (physical < 0x100000 || bytes < 4096 || bytes > 2097152 || physical > ~0UL - bytes || (physical & 4095) != 0) return false;
        NativeBootInfo* boot = Kernel.Boot;
        for (ulong i = 0; i < boot->MemoryEntryCount; i++)
        {
            MemoryMapEntry* e = boot->MemoryEntries[i];
            // MMIO must not alias any owned or firmware/ACPI RAM, or scanout.
            if (e->Length != 0 && physical < e->Base + e->Length && e->Base < physical + bytes && e->Kind != 1 && e->Kind != 8) return false;
        }
        return true;
    }
    static bool Initialize(UsbController* c)
    {
        uint b = c->Pci >> 16, d = c->Pci >> 8 & 31, f = c->Pci & 7;
        uint low = PciRead(b,d,f,16), high = (low & 6) == 4 ? PciRead(b,d,f,20) : 0;
        if ((low & 1) != 0 || (low & 6) is not (0 or 4)) return false;
        ulong physical = (ulong)high << 32 | (low & ~15u);
        if (!RangeAllowed(physical, 4096)) return false;
        ulong irq = Native.IrqSave(); c->Base = VirtualMemory.MapDevice(physical, 4096); Native.IrqRestore(irq);
        if (c->Base == 0) return false;
        // Claim the firmware semaphore before taking control. Never force a
        // BIOS-owned controller, and never touch a host Windows controller.
        uint hcc = Read(c->Base + 16), ext = hcc >> 16;
        bool handoff = true;
        for (uint limit = 0; ext != 0 && limit < 64; limit++)
        {
            ulong offset = (ulong)ext * 4;
            if (offset > 262136) { handoff = false; break; }
            ulong page = (physical + offset) & ~4095UL;
            ulong capMapping = c->Base;
            bool extra = offset >= 4096;
            if (extra)
            {
                if (!RangeAllowed(page, 8192)) { handoff = false; break; }
                irq = Native.IrqSave(); capMapping = VirtualMemory.MapDevice(page, 8192); Native.IrqRestore(irq);
                if (capMapping == 0) { handoff = false; break; }
            }
            ulong capAddress = extra ? capMapping + ((physical + offset) & 4095) : c->Base + offset;
            uint cap = Read(capAddress);
            if ((cap & 255) == 1)
            {
                Write(capAddress, cap | (1u << 24));
                if (!WaitBits(capAddress, 1u << 16, 0, 1500)) handoff = false;
                else Write(capAddress + 4, 0xe0000000); // disable legacy SMI enables, acknowledge status
            }
            if (extra) { irq = Native.IrqSave(); VirtualMemory.UnmapDevice(capMapping,8192); Native.IrqRestore(irq); }
            if (!handoff) break;
            uint next = cap >> 8 & 255; if (next == 0) { ext = 0; break; } ext += next;
            if (limit == 63) handoff = false;
        }
        if (!handoff) { Log("FIRMWARE_HANDOFF_FAILED"); return false; }
        uint capLength = Read(c->Base) & 255;
        if (capLength < 32 || capLength > 256) return false;
        c->Op = c->Base + capLength;
        Write(c->Op, Read(c->Op) & ~1u);
        if (!WaitBits(c->Op + 4, 1, 1, 1000)) return false;
        // Size only this stopped, claimed xHCI BAR. Restore every PCI field
        // before mapping; no GPU/storage BAR probing or writes are performed.
        uint command = PciRead(b,d,f,4) & 65535;
        irq = Native.IrqSave();
        PciWrite(b,d,f,4,command & ~6u);
        PciWrite(b,d,f,16,0xffffffff); if ((low & 6) == 4) PciWrite(b,d,f,20,0xffffffff);
        ulong mask = (PciRead(b,d,f,16) & ~15u) | ((ulong)((low & 6) == 4 ? PciRead(b,d,f,20) : 0xffffffff) << 32);
        PciWrite(b,d,f,16,low); if ((low & 6) == 4) PciWrite(b,d,f,20,high);
        PciWrite(b,d,f,4,command); Native.IrqRestore(irq);
        ulong bytes = ~mask + 1;
        if ((bytes & (bytes - 1)) != 0 || !RangeAllowed(physical, bytes)) return false;
        irq = Native.IrqSave(); VirtualMemory.UnmapDevice(c->Base,4096);
        c->Base = VirtualMemory.MapDevice(physical,bytes); Native.IrqRestore(irq);
        if (c->Base == 0) return false; c->Bytes = bytes; c->Op = c->Base + capLength;
        c->Doorbell = Read(c->Base + 20) & ~3u; c->Runtime = Read(c->Base + 24) & ~31u;
        uint hcs = Read(c->Base + 4);
        c->Slots = hcs & 255; if (c->Slots > 32) c->Slots = 32;
        c->Ports = hcs >> 24; c->ContextBytes = (hcc & 4) != 0 ? 64u : 32u; c->Address64 = hcc & 1;
        if (c->Slots == 0 || c->Ports == 0 || c->Ports > 128 || c->Doorbell + (c->Slots + 1) * 4 > bytes ||
            c->Runtime + 64 > bytes || capLength + 0x400 + c->Ports * 16 > bytes) return false;
        c->Doorbell += c->Base; c->Runtime += c->Base;
        PciWrite(b,d,f,4,(command | 6 | 0x400) & ~0x100u); // bus master, memory; INTx disabled
        Write(c->Op,2);
        if (!WaitBits(c->Op,2,0,1000) || !WaitBits(c->Op+4,1u<<11,0,1000) || (Read(c->Op+8)&1)==0) return false;
        c->Dcbaa=Allocate(); c->Events=Allocate(); c->Erst=Allocate(); c->RootSeen=At(Allocate());
        if (c->Dcbaa==0 || c->Events==0 || c->Erst==0 || c->RootSeen==null || !NewRing(&c->Commands)) return false;
        uint hcs2=Read(c->Base+8), scratch=((hcs2>>27)&31)|((hcs2>>16)&0x3e0);
        if (scratch > 64) { Log("SCRATCHPAD_LIMIT"); return false; }
        if (scratch != 0)
        {
            ulong table=Allocate(); if(table==0)return false;
            for(uint i=0;i<scratch;i++){ulong page=Allocate();if(page==0)return false;((ulong*)At(table))[i]=page;}
            ((ulong*)At(c->Dcbaa))[0]=table;
        }
        if(c->Address64==0 && PhysicalMemory.AllocatedPages!=0 && (c->Dcbaa>=0x100000000 || c->Events>=0x100000000 || c->Commands.Physical>=0x100000000)) return false;
        Write64(c->Op+0x30,c->Dcbaa); Write64(c->Op+0x18,c->Commands.Physical|1);
        Write(c->Op+0x38,c->Slots);
        ulong* erst=(ulong*)At(c->Erst); erst[0]=c->Events; erst[1]=256;
        ulong intr=c->Runtime+0x20;
        Write(intr,1); Write(intr+4,0); Write(intr+8,1); Write64(intr+16,c->Erst); Write64(intr+24,c->Events);
        c->EventCycle=1; Native.MemoryBarrier(); Write(c->Op,1);
        if(!WaitBits(c->Op+4,1,0,1000)) return false;
        c->Running=1;
        Log("RUNNING"); return true;
    }
    static bool NewRing(UsbRing* ring)
    {
        ring->Physical=Allocate(); ring->Index=0; ring->Cycle=1;
        return ring->Physical!=0;
    }
    static ulong Put(UsbRing* ring, ulong parameter, uint status, uint control)
    {
        ulong physical=ring->Physical+ring->Index*16;
        uint* trb=(uint*)At(physical); ((ulong*)trb)[0]=parameter; trb[2]=status;
        Native.MemoryBarrier(); trb[3]=control|ring->Cycle; Native.MemoryBarrier();
        if(++ring->Index==255)
        {
            uint* link=(uint*)At(ring->Physical+255*16); ((ulong*)link)[0]=ring->Physical;link[2]=0;
            Native.MemoryBarrier();link[3]=(6u<<10)|2|ring->Cycle;Native.MemoryBarrier();ring->Index=0;ring->Cycle^=1;
        }
        return physical;
    }
    static bool Command(UsbController* c, uint type, ulong parameter, uint control, out uint slot)
    {
        slot=0;if(c->Running==0)return false;
        ulong pending=Put(&c->Commands,parameter,0,(type<<10)|control); c->Completed=0;
        Write(c->Doorbell,0); ulong end=Now()+1000;
        do {Events(c);if(c->Completed==pending){slot=(uint)(c->CompletionParameter>>24)&255;return c->CompletionCode==1;}Sleep(1);}while(Now()<end);
        Log("COMMAND_TIMEOUT");c->Running=0;Write(c->Op,0);Failures++;return false;
    }
    static void Events(UsbController* c)
    {
        if(c->Running==0)return;
        for(uint budget=0;budget<256;budget++)
        {
            uint* trb=(uint*)At(c->Events+c->EventIndex*16);
            uint flags=Read((ulong)(trb+3));if((flags&1)!=c->EventCycle)break;
            Native.MemoryBarrier();ulong parameter=((ulong*)trb)[0];uint status=trb[2],type=(flags>>10)&63,code=status>>24;
            if(type==33){c->Completed=parameter&~15UL;c->CompletionCode=code;c->CompletionParameter=flags;}
            else if(type==32)
            {
                uint slot=flags>>24,endpoint=(flags>>16)&31;
                for(uint i=0;i<_deviceCount;i++)
                {
                    UsbDevice* d=&_devices[i];if(d->Controller!=c || d->Slot!=slot || d->Connected==0)continue;
                    if(endpoint==1){d->Completed=parameter&~15UL;d->CompletionCode=code;d->CompletionRemaining=status&0xffffff;}
                    else if(!CompleteStorage(d,endpoint,parameter&~15UL,code,status&0xffffff))
                        CompleteInterrupt(d,endpoint,parameter&~15UL,code,status&0xffffff);
                    break;
                }
            }
            if(++c->EventIndex==256){c->EventIndex=0;c->EventCycle^=1;}
        }
        Write64(c->Runtime+0x38,(c->Events+c->EventIndex*16)|8);
    }
    static bool Control(UsbDevice* d, byte requestType, byte request, ushort value, ushort index, ushort length, out uint received)
    {
        received=0;if(d->Connected==0 || d->Controller->Running==0 || length>4096)return false;
        bool input=(requestType&128)!=0;
        ulong setup=requestType|((ulong)request<<8)|((ulong)value<<16)|((ulong)index<<32)|((ulong)length<<48);
        Put(&d->Control,setup,8,(2u<<10)|64|((length==0?0u:input?3u:2u)<<16));
        // A single Data Stage TRB is already its complete Data Stage TD.
        // CH must be clear; the Status Stage is a separate TD.
        if(length!=0)Put(&d->Control,d->Buffer,length,(3u<<10)|4|(input?1u<<16:0));
        ulong pending=Put(&d->Control,0,0,(4u<<10)|32|((length==0||!input)?1u<<16:0));d->Completed=0;
        Write(d->Controller->Doorbell+d->Slot*4,1);ulong end=Now()+1500;
        do
        {
            Events(d->Controller);
            if(d->Completed==pending)
            {if(d->CompletionCode!=1)return false;received=length;return true;}
            // A stalled EP0 must be reset with its dequeue pointer before the
            // next optional class request. A timeout is quarantined instead.
            if(d->Completed!=0 && d->CompletionCode!=1 && d->CompletionCode!=13)
            {
                uint slot; Command(d->Controller,14,0,d->Slot<<24|1u<<16,out slot);
                ulong dequeue=d->Control.Physical+d->Control.Index*16|d->Control.Cycle;
                Command(d->Controller,16,dequeue,d->Slot<<24|1u<<16,out slot);return false;
            }
            Sleep(1);
        }while(Now()<end);
        DeviceLog(d,"CONTROL_TIMEOUT");Disconnect(d);Failures++;return false;
    }
    static uint* Context(UsbDevice* d, uint index) => (uint*)(At(d->Input)+index*d->Controller->ContextBytes);
    static void SlotContext(UsbDevice* d,uint last)
    {
        uint* s=Context(d,1);s[0]=d->Route|(d->Speed<<20)|(last<<27);
        if(d->HubPorts!=0)s[0]|=1u<<26;
        s[1]=d->RootPort<<16|d->HubPorts<<24;s[2]=d->TtSlot|d->TtPort<<8|d->HubTt<<16;
    }
    static void CopyContext(UsbDevice* d,uint index)
    {
        uint* src=(uint*)(At(d->Output)+(index-1)*d->Controller->ContextBytes),dst=Context(d,index);
        for(uint i=0;i<d->Controller->ContextBytes/4;i++)dst[i]=src[i];
    }
    static void Root(UsbController* c,uint port)
    {
        ulong reg=c->Op+0x400+(port-1)*16;uint status=Read(reg);
        if((status&1)==0)
        {
            if(c->RootSeen[port]!=0)for(uint i=0;i<_deviceCount;i++)if(_devices[i].Controller==c&&_devices[i].RootPort==port&&_devices[i].Connected!=0)Disconnect(&_devices[i]);
            c->RootSeen[port]=0;
            if((status&512)==0)Write(reg,(status&0x0e00c200)|512);
            return;
        }
        if(c->RootSeen[port]!=0)return;c->RootSeen[port]=1;
        if((status&512)==0){Write(reg,(status&0x0e00c200)|512);Sleep(100);status=Read(reg);}
        if((status&2)==0)
        {
            uint speed=(status>>10)&15;
            Write(reg,(status&0x0e00c200)|512|(speed>=4?1u<<31:16));
            ulong end=Now()+500;
            do{Sleep(10);status=Read(reg);if((status&2)!=0&&(status&16)==0)break;}while(Now()<end);
        }
        if((status&3)!=3){Log("PORT_RESET_FAILED");Failures++;return;}
        Write(reg,(status&0x0e00c200)|0xfe0000);
        Sleep(20);Enumerate(c,null,port,0,(status>>10)&15);
    }
    static UsbDevice* Enumerate(UsbController* c,UsbDevice* parent,uint port,uint route,uint speed)
    {
        if(_deviceCount==DeviceLimit || speed==0 || speed>5 || parent!=null&&parent->Depth>=5)return null;
        uint slot;if(!Command(c,9,0,0,out slot)||slot==0||slot>c->Slots)return null;
        UsbDevice* d=&_devices[_deviceCount++];d->Controller=c;d->Parent=parent;d->Slot=slot;d->Connected=1;d->Speed=speed;
        d->RootPort=parent==null?port:parent->RootPort;d->ParentPort=parent==null?0:port;
        d->Route=route;d->Depth=parent==null?0:parent->Depth+1;
        if(parent!=null && speed<=2)
        {d->TtSlot=parent->Speed==3?parent->Slot:parent->TtSlot;d->TtPort=parent->Speed==3?port:parent->TtPort;}
        d->Input=Allocate();d->Output=Allocate();d->Buffer=Allocate();
        if(d->Input==0||d->Output==0||d->Buffer==0||!NewRing(&d->Control)){Disconnect(d);return null;}
        ((ulong*)At(c->Dcbaa))[slot]=d->Output;
        uint* ic=Context(d,0);ic[1]=3;SlotContext(d,1);
        d->Packet=speed>=4?512u:speed==3?64u:8u;
        uint* ep=Context(d,2);ep[1]=3u<<1|4u<<3|d->Packet<<16;((ulong*)(ep+2))[0]=d->Control.Physical|1;ep[4]=8;
        if(!Command(c,11,d->Input,slot<<24,out slot)){DeviceLog(d,"ADDRESS_FAILED");Disconnect(d);return null;}
        uint got;
        // Full-speed EP0 may actually use 64-byte packets. Request only the
        // first eight bytes before updating the provisional 8-byte context;
        // asking for all 18 first can cause a real controller babble error.
        if(!Control(d,128,6,0x100,0,8,out got)){DeviceLog(d,"DEVICE_DESCRIPTOR_PREFIX_FAILED");Disconnect(d);return null;}
        byte* desc=At(d->Buffer);
        if(desc[0]<18||desc[1]!=1){Disconnect(d);return null;}
        uint packet=d->Speed>=4?1u<<desc[7]:desc[7];
        if(packet is not (8 or 16 or 32 or 64 or 512) || d->Speed==2&&packet!=8 ||
            d->Speed==3&&packet!=64 || d->Speed>=4&&packet!=512 || d->Speed==1&&packet>64){Disconnect(d);return null;}
        if(packet!=d->Packet)
        {
            d->Packet=packet;ic[0]=0;ic[1]=2;CopyContext(d,2);Context(d,2)[1]=(Context(d,2)[1]&65535)|packet<<16;
            if(!Command(c,13,d->Input,d->Slot<<24,out slot)){Disconnect(d);return null;}
        }
        if(!Control(d,128,6,0x100,0,18,out got)){DeviceLog(d,"DEVICE_DESCRIPTOR_FAILED");Disconnect(d);return null;}
        desc=At(d->Buffer);if(desc[0]<18||desc[1]!=1){Disconnect(d);return null;}
        d->Vendor=desc[8]|(uint)desc[9]<<8;d->Product=desc[10]|(uint)desc[11]<<8;
        uint deviceClass=desc[4],protocol=desc[6];
        if(!Control(d,128,6,0x200,0,9,out got)){Disconnect(d);return null;}
        byte* config=At(d->Buffer);uint total=config[2]|(uint)config[3]<<8;
        if(config[0]<9||total<9||total>4096||!Control(d,128,6,0x200,0,(ushort)total,out got)){Disconnect(d);return null;}
        ulong saved=Allocate();if(saved==0){Disconnect(d);return null;}byte* all=At(saved);
        for(uint i=0;i<total;i++)all[i]=config[i];
        if(!Control(d,0,9,all[5],0,0,out got)){Disconnect(d);return null;}d->Configured=1;
        DeviceLog(d,"CONFIGURED");
        if(deviceClass==9){Hub(d,protocol);return d;}
        uint iface=0,subclass=0,hidProtocol=0,reportBytes=0,valid=0;
        for(uint p=0;p+2<=total;)
        {
            uint length=all[p];if(length<2||length>total-p)break;uint type=all[p+1];
            if(type==4&&length>=9){iface=all[p+2];subclass=all[p+6];hidProtocol=all[p+7];valid=all[p+5]==3&&all[p+3]==0?1u:0;reportBytes=0;}
            else if(type==0x21&&length>=9&&valid!=0){reportBytes=all[p+7]|(uint)all[p+8]<<8;}
            else if(type==5&&length>=7&&valid!=0&&(all[p+2]&128)!=0&&(all[p+3]&3)==3)
                AddInterrupt(d,iface,subclass,hidProtocol,reportBytes,all[p+2],(uint)(all[p+4]|all[p+5]<<8)&2047,all[p+6]);
            p+=length;
        }
        AddStorage(d,all,total);
        return d;
    }
    static void AddInterrupt(UsbDevice* d,uint iface,uint subclass,uint protocol,uint reportBytes,uint address,uint packet,uint interval)
    {
        if(_endpointCount==EndpointLimit||packet==0||packet>1024||interval==0)return;
        uint dci=(address&15)*2+1;if(dci<3||dci>31)return;
        UsbInterruptEndpoint* e=&_endpoints[_endpointCount++];e->Device=d;e->Dci=dci;e->Packet=packet;
        e->Buffer=Allocate();e->Layout.Fields=(UsbHidField*)At(Allocate(4));e->Layout.Capacity=512;
        if(e->Buffer==0||e->Layout.Fields==null||!NewRing(&e->Ring))return;
        uint got;
        bool parsed=reportBytes!=0&&reportBytes<=4096&&Control(d,129,6,0x2200,(ushort)iface,(ushort)reportBytes,out got)&&UsbHid.Parse(At(d->Buffer),reportBytes,&e->Layout);
        bool boot=subclass==1&&protocol==1;
        if(boot&&Control(d,0x21,11,0,(ushort)iface,0,out got))UsbHid.BootKeyboard(&e->Layout);
        else if(!parsed){DeviceLog(d,"UNSUPPORTED_HID_DESCRIPTOR");return;}
        Control(d,0x21,10,0,(ushort)iface,0,out got); // optional SET_IDLE, STALL is recovered
        e->Kind=e->Layout.Kind;
        uint* ic=Context(d,0);ic[0]=0;ic[1]=1u|(1u<<(int)dci);CopyContext(d,1);
        uint* slotContext=Context(d,1);uint last=(slotContext[0]>>27)&31;if(last<dci)slotContext[0]=(slotContext[0]&0x7ffffff)|dci<<27;
        uint exponent;
        if(d->Speed>=3){exponent=interval-1;if(exponent>15)return;}
        else{exponent=3;uint ms=1;while(ms<interval&&exponent<10){ms<<=1;exponent++;}}
        uint* ep=Context(d,dci+1);ep[0]=exponent<<16;ep[1]=3u<<1|7u<<3|packet<<16;
        ((ulong*)(ep+2))[0]=e->Ring.Physical|1;ep[4]=packet|packet<<16;
        uint slot;if(!Command(d->Controller,12,d->Input,d->Slot<<24,out slot)){DeviceLog(d,"ENDPOINT_CONFIGURATION_FAILED");return;}
        e->Active=1;RefreshCapabilities();
        Serial.Write("XFE_USB_HID_READY|Slot|");Serial.Number(d->Slot);Serial.Write("|Interface|");Serial.Number(iface);
        Serial.Write("|Dci|");Serial.Number(dci);Serial.Write("|Kind|");Serial.Number(e->Kind);Serial.Write("|BootKeyboard|");Serial.Number(boot?1UL:0UL);Serial.Line("");
        Arm(e);
    }
    static void Arm(UsbInterruptEndpoint* e)
    {
        if(e->Active==0||e->Device->Connected==0)return;
        e->Pending=Put(&e->Ring,e->Buffer,e->Packet,(1u<<10)|32|4);
        Write(e->Device->Controller->Doorbell+e->Device->Slot*4,e->Dci);
    }
    static void CompleteInterrupt(UsbDevice* d,uint dci,ulong pointer,uint code,uint remaining)
    {
        for(uint i=0;i<_endpointCount;i++)
        {
            UsbInterruptEndpoint* e=&_endpoints[i];if(e->Device!=d||e->Dci!=dci||e->Active==0||e->Pending!=pointer)continue;
            if((code==1||code==13)&&remaining<=e->Packet)
            {
                byte* reportBytes=stackalloc byte[2048]; NativeInputReport* reports=(NativeInputReport*)reportBytes;
                uint count=UsbHid.Decode(&e->Layout,&e->State,At(e->Buffer),e->Packet-remaining,reports,64);
                for(uint j=0;j<count;j++)
                {
                    Ps2Input.Enqueue(reports[j]);
                    if(!_firstKey&&reports[j].Kind==Ps2Decoder.KeyDown){_firstKey=true;Log("FIRST_HARDWARE_KEY_QUEUED");}
                    if(!_firstPointer&&reports[j].Kind==Ps2Decoder.Pointer&&(reports[j].DeltaX!=0||reports[j].DeltaY!=0||reports[j].Buttons!=0||reports[j].Wheel!=0))
                    {_firstPointer=true;Log("FIRST_HARDWARE_POINTER_QUEUED");}
                }
                e->Reports++;Reports++;
                if(e->Reports==1){Serial.Write("XFE_USB_FIRST_REPORT|Slot|");Serial.Number(d->Slot);Serial.Write("|Dci|");Serial.Number(dci);Serial.Write("|Events|");Serial.Number(count);Serial.Line("");}
                Arm(e);
            }
            else{e->Errors++;e->Active=0;RefreshCapabilities();Failures++;DeviceLog(d,"INTERRUPT_ENDPOINT_STOPPED");Ps2Input.InputLost();}
            return;
        }
    }
    static bool HubStatus(UsbDevice* d,uint port,out uint status)
    {
        status=0;uint got;if(!Control(d,0xa3,0,0,(ushort)port,4,out got))return false;status=*(uint*)At(d->Buffer);return true;
    }
    static void Hub(UsbDevice* d,uint protocol)
    {
        uint got;d->HubSuper=d->Speed>=4?1u:0;
        if(!Control(d,0xa0,6,(ushort)(d->HubSuper!=0?0x2a00:0x2900),0,(ushort)(d->HubSuper!=0?12:9),out got))return;
        byte* desc=At(d->Buffer);uint ports=desc[2];if(ports==0||ports>15){DeviceLog(d,"HUB_PORT_LIMIT");return;}
        d->HubPorts=ports;d->HubTt=d->Speed==3?(uint)(desc[3]>>5)&3:0;d->HubSeen=At(Allocate());if(d->HubSeen==null)return;
        uint powerWait=(uint)desc[5]*2+20;if(powerWait>1000)powerWait=1000;
        uint* ic=Context(d,0);ic[0]=0;ic[1]=1;CopyContext(d,1);
        uint* s=Context(d,1);s[0]|=1u<<26;s[1]=(s[1]&0xffffff)|ports<<24;s[2]=(s[2]&~0x30000u)|d->HubTt<<16;
        // xHCI 1.x initializes Hub/MTT/TTT/NumberOfPorts at the first
        // Configure Endpoint transition from Addressed to Configured. Evaluate
        // Context ignores these fields (it only evaluates latency/interrupt
        // targeting in the slot). A full-speed QEMU hub can hide that mistake;
        // real high-speed hubs need this context for their transaction translator.
        uint slot;if(!Command(d->Controller,12,d->Input,d->Slot<<24,out slot)){DeviceLog(d,"HUB_CONTEXT_FAILED");return;}
        Native.MemoryBarrier();uint* actual=(uint*)At(d->Output);
        if((actual[0]&(1u<<26))==0 || (actual[1]>>24)!=ports || ((actual[2]>>16)&3)!=d->HubTt)
        {
            // QEMU 11.1's own xhci_configure_slot only copies Context Entries
            // and Slot State, not Hub/NumberOfPorts/TTT. Keep that limitation
            // explicit. Never grant this exception to a physical PCI vendor.
            uint pci=d->Controller->Pci;
            if(PciRead(pci>>16,pci>>8&31,pci&7,0)!=0x000d1b36 || (actual[3]>>27)!=3)
            {DeviceLog(d,"HUB_CONTEXT_NOT_APPLIED");d->HubPorts=0;Failures++;return;}
            DeviceLog(d,"HUB_CONTEXT_QEMU_NOT_MODELED");
        }
        else DeviceLog(d,"HUB_CONTEXT_APPLIED");
        DeviceLog(d,"HUB_CONTEXT_CONFIGURED");
        if(d->HubSuper!=0)Control(d,0x20,12,(ushort)d->Depth,0,0,out got);
        for(uint port=1;port<=ports;port++)Control(d,0x23,3,8,(ushort)port,0,out got);
        Sleep(powerWait);DeviceLog(d,"HUB_POWERED");ScanHub(d);
    }
    static void ScanHub(UsbDevice* d)
    {
        for(uint p=1;p<=d->HubPorts&&d->Connected!=0;p++)
        {
            uint status,got;if(!HubStatus(d,p,out status))continue;
            if((status&1)==0)
            {
                if(d->HubSeen[p]!=0)for(uint i=0;i<_deviceCount;i++)if(_devices[i].Parent==d&&_devices[i].ParentPort==p)Disconnect(&_devices[i]);
                d->HubSeen[p]=0;continue;
            }
            if(d->HubSeen[p]!=0)continue;d->HubSeen[p]=1;
            if(!Control(d,0x23,3,4,(ushort)p,0,out got))continue;
            ulong end=Now()+500;
            do{Sleep(20);if(!HubStatus(d,p,out status))break;if((status&16)==0&&(status&2)!=0)break;}while(Now()<end);
            if((status&3)!=3){DeviceLog(d,"HUB_CHILD_RESET_FAILED");continue;}
            for(ushort change=16;change<=20;change++)if((status&(1u<<(change)))!=0)Control(d,0x23,1,change,(ushort)p,0,out got);
            uint speed=d->HubSuper!=0?4u:(status&0x400)!=0?3u:(status&0x200)!=0?2u:1u;
            uint route=d->Route|(p<<(int)(d->Depth*4));Sleep(20);Enumerate(d->Controller,d,p,route,speed);
        }
    }
    static void Disconnect(UsbDevice* d)
    {
        if(d->Connected==0)return;d->Connected=0;
        DisconnectStorage(d);
        for(uint i=0;i<_endpointCount;i++)if(_endpoints[i].Device==d)_endpoints[i].Active=0;
        for(uint i=0;i<_deviceCount;i++)if(_devices[i].Parent==d&&_devices[i].Connected!=0)Disconnect(&_devices[i]);
        RefreshCapabilities();
        uint slot;Command(d->Controller,10,0,d->Slot<<24,out slot);
        // DMA pages remain quarantined for this RAM session. Never recycle a
        // device buffer while an unresponsive controller may still own it.
        Ps2Input.InputLost();DeviceLog(d,"DISCONNECTED");
    }
    static void RefreshCapabilities()
    {
        uint bits=0;
        for(uint i=0;i<_endpointCount;i++)
        {
            UsbInterruptEndpoint* e=&_endpoints[i];if(e->Active==0||e->Device->Connected==0)continue;
            bits|=e->Kind;
            for(uint f=0;f<e->Layout.Count;f++)
                if(e->Layout.Fields[f].Page==1&&e->Layout.Fields[f].Minimum==0x38&&(e->Layout.Fields[f].Flags&4)!=0)bits|=4;
        }
        ReadyBits=bits;Ps2Input.SetUsbCapabilities(bits);
    }
    [UnmanagedCallersOnly(EntryPoint = "xfe_usb_info")]
    public static ulong Info(uint field) => field switch
    {0=>1,1=>State,2=>_controllerCount,3=>_deviceCount,4=>_endpointCount,5=>ReadyBits,6=>Reports,7=>Failures,_=>0};
}