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.
// Only enumerates hubs and HID; never issues storage commands to USB disks.
public static unsafe 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]);
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 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;
}
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;
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};
}
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.
// Only enumerates hubs and HID; never issues storage commands to USB disks.
public static unsafe 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]);
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 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;
}
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;
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};
}