Inhaltsverzeichnis

Lyx OS – XHCI-Treiber

Der XHCI-Treiber (kernel/xhci.lyx) implementiert den USB 3.x Host-Controller-Treiber für Lyx OS. Er läuft vollständig in Ring-0 und wird von keiner externen USB-Stack-Library abhängig. Die Ring-3-Syscalls für USB sind noch nicht vergeben — Syscalls 150–155 sind durch Vega-Fensterverwaltung (150–152), RAM-Disk (153–154) und IOFS-mkfs (155) belegt. USB-Syscalls werden im XHCI-WP in einem freien Bereich ab 156+ zugewiesen.

Syscall-ABI · Kernel-Interna · Übersicht


1. Überblick

XHCI (Extensible Host Controller Interface) ist der USB-Host-Controller-Standard ab USB 3.0. Er ersetzt EHCI (USB 2.0), OHCI und UHCI und unterstützt alle USB-Generationen (1.0–3.2) in einem einzigen Controller.

Zuständigkeiten des Treibers:

Schicht Zuständigkeit
PCI-Erkennung XHCI-Controller auf dem PCI-Bus finden (Class 0x0C / Sub 0x03 / ProgIF 0x30)
MMIO-Mapping Capability-, Operational-, Runtime- und Doorbell-Register in Kernel-Address-Space mappen
HC-Initialisierung Controller-Reset, Slot-Enable, Command-Ring und Event-Ring einrichten
Port-Management Port-Reset, Speed-Erkennung, Gerät adressieren
Transfer-Engine TRB-Ringe für Control-, Bulk- und Interrupt-Transfers verwalten
Ring-3-Interface USB-Syscalls TBD (ab 156+, erst mit XHCI-WP vergeben; 150–155 = Vega/RAM-Disk/IOFS)

Quelldatei: kernel/xhci.lyx


2. PCI-Erkennung

Der XHCI-Controller wird als PCI-Gerät erkannt:

Feld Wert Bedeutung
Class Code 0x0C Serial Bus Controller
Subclass 0x03 USB Controller
Prog IF 0x30 xHCI

// PCI-Scan: XHCI-Controller finden
con XHCI_PCI_CLASS  : int64 := 0x0C;
con XHCI_PCI_SUB    : int64 := 0x03;
con XHCI_PCI_PROGIF : int64 := 0x30;

Der Treiber liest die BAR0-Basisadresse aus dem PCI-Konfigurationsraum (BAR0 ist immer MMIO, 64-bit). Die physische Adresse wird via sys_mmap_device in den Kernel-Address-Space gemappt.


3. MMIO-Register-Layout

XHCI kennt vier Register-Bereiche, alle relativ zur BAR0-Basisadresse:

BAR0 ──┬── Capability Registers    (Offset 0, Länge = CAPLENGTH)
       ├── Operational Registers   (Offset = CAPLENGTH)
       ├── Runtime Registers       (Offset = RTSOFF, aus CapRegs)
       └── Doorbell Array          (Offset = DBOFF, aus CapRegs)

3.1 Capability Registers (CapRegs)

Nur lesbar. Beschreiben die Hardware-Eigenschaften des Controllers.

Offset Name Beschreibung
+0 CAPLENGTH (uint8) Länge des CapRegs-Blocks; Startadresse der OpRegs
+2 HCIVERSION (uint16) xHCI-Spezifikationsversion (z.B. 0x0100 = v1.0)
+4 HCSPARAMS1 (uint32) Bits[7:0]=MaxSlots, Bits[18:8]=MaxIntrs, Bits[31:24]=MaxPorts
+8 HCSPARAMS2 (uint32) IST, ERST_MAX, MAX_SCRATCHPAD_BUFS
+12 HCSPARAMS3 (uint32) U1/U2-Exit-Latenzzeiten
+16 HCCPARAMS1 (uint32) Capability-Bits: 64-bit Addressing, BNC, AC64, CSZ usw.
+20 DBOFF (uint32) Byte-Offset der Doorbell-Array-Base relativ zu BAR0
+24 RTSOFF (uint32) Byte-Offset der Runtime-Register-Base relativ zu BAR0
+28 HCCPARAMS2 (uint32) Erweiterte Capability-Bits (USB 3.1+)

con XHCI_CAP_CAPLENGTH  : int64 := 0;
con XHCI_CAP_HCIVERSION : int64 := 2;
con XHCI_CAP_HCSPARAMS1 : int64 := 4;
con XHCI_CAP_HCSPARAMS2 : int64 := 8;
con XHCI_CAP_HCCPARAMS1 : int64 := 16;
con XHCI_CAP_DBOFF      : int64 := 20;
con XHCI_CAP_RTSOFF     : int64 := 24;

3.2 Operational Registers (OpRegs)

Lese-/schreibbar. Steuern den Controller-Betrieb.

Offset Name Beschreibung
+0 USBCMD (uint32) Run/Stop (Bit 0), HC-Reset (Bit 1), Interrupt-Enable (Bit 2)
+4 USBSTS (uint32) HCHalted (Bit 0), Host-Error (Bit 2), Event-Int (Bit 3)
+8 PAGESIZE (uint32) Bits[15:0]: unterstützte Page-Größen (Bit N = 2(N+12) Bytes)
+20 DNCTRL (uint32) Device-Notification-Control
+24 CRCR (uint64) Command-Ring-Control: RCS, CS, CA, CRR, Ring-Pointer
+48 DCBAAP (uint64) Device-Context-Base-Address-Array-Pointer
+56 CONFIG (uint32) Bits[7:0]: MaxSlotsEn (max. aktivierte Slots)

con XHCI_OP_USBCMD  : int64 := 0;
con XHCI_OP_USBSTS  : int64 := 4;
con XHCI_OP_PAGESIZE: int64 := 8;
con XHCI_OP_DNCTRL  : int64 := 20;
con XHCI_OP_CRCR    : int64 := 24;
con XHCI_OP_DCBAAP  : int64 := 48;
con XHCI_OP_CONFIG  : int64 := 56;

// USBCMD-Bits
con XHCI_CMD_RUN    : int64 := 1;    // Run/Stop
con XHCI_CMD_HCRST  : int64 := 2;    // HC-Reset
con XHCI_CMD_INTE   : int64 := 4;    // Interrupter-Enable
con XHCI_CMD_HSEE   : int64 := 8;    // Host-System-Error-Enable

// USBSTS-Bits
con XHCI_STS_HCH    : int64 := 1;    // HC Halted
con XHCI_STS_HSE    : int64 := 4;    // Host System Error
con XHCI_STS_EINT   : int64 := 8;    // Event Interrupt
con XHCI_STS_PCD    : int64 := 16;   // Port Change Detected
con XHCI_STS_CNR    : int64 := 0x800; // Controller Not Ready

3.3 Runtime Registers

Offset Name Beschreibung
+0 MFINDEX (uint32) Microframe-Index (125 µs-Ticks)
+32 IR[0] Interrupter-Register-Set 0 (IMAN, IMOD, ERSTSZ, ERSTBA, ERDP)

Interrupter-0-Offsets (relativ zu IR[0]-Basis = RTSOFF + 32):

Offset Name Beschreibung
+0 IMAN (uint32) Interrupt-Management: IE (Bit 1), IP (Bit 0)
+4 IMOD (uint32) Interrupt-Moderation: IMODI (Bits[15:0])
+8 ERSTSZ (uint32) Event-Ring-Segment-Table-Size
+16 ERSTBA (uint64) Event-Ring-Segment-Table-Base-Address
+24 ERDP (uint64) Event-Ring-Dequeue-Pointer

con XHCI_RT_MFINDEX : int64 := 0;
con XHCI_RT_IR0     : int64 := 32;   // Basis des ersten Interrupters

// Offsets innerhalb IR[0]
con XHCI_IR_IMAN    : int64 := 0;
con XHCI_IR_IMOD    : int64 := 4;
con XHCI_IR_ERSTSZ  : int64 := 8;
con XHCI_IR_ERSTBA  : int64 := 16;
con XHCI_IR_ERDP    : int64 := 24;

3.4 Doorbell Array

Ein 32-bit-Eintrag pro Slot (Slot 0 = Host-Controller-Command-Ring). Schreiben löst einen Transfer aus.

con XHCI_DB_HC      : int64 := 0;    // Doorbell 0: Command-Ring
// Doorbell N (N = Slot-ID): DB_TARGET = Endpoint-Index (Bits[7:0])


4. Datenstrukturen

4.1 Transfer Request Block (TRB)

Jede Operation in XHCI ist ein 16-Byte-TRB. TRBs werden in Ringen arrangiert.

Byte  0– 7: Parameter  (Adresse, Daten, abhängig vom TRB-Typ)
Byte  8–11: Status     (Byte-Count, Interrupter-Target)
Byte 12–15: Control    (Bits[9:0]=TRB-Typ, Bit15=C=Cycle-Bit, typ-spezifische Flags)

con XHCI_TRB_SIZE : int64 := 16;

// TRB-Typen (Control-Bits[9:0])
con TRB_NORMAL        : int64 := 1;   // Bulk / Interrupt Transfer
con TRB_SETUP         : int64 := 2;   // Control Setup-Paket
con TRB_DATA          : int64 := 3;   // Control Data-Phase
con TRB_STATUS        : int64 := 4;   // Control Status-Phase
con TRB_ISOCH         : int64 := 5;   // Isochroner Transfer
con TRB_LINK          : int64 := 6;   // Ring-Link (letzter TRB im Ring)
con TRB_EVT_DATA      : int64 := 7;   // Event-Data
con TRB_NOOP          : int64 := 8;   // No-Op Transfer
con TRB_ENABLE_SLOT   : int64 := 9;   // Command: Slot aktivieren
con TRB_DISABLE_SLOT  : int64 := 10;  // Command: Slot deaktivieren
con TRB_ADDRESS_DEV   : int64 := 11;  // Command: Gerät adressieren
con TRB_CONFIG_EP     : int64 := 12;  // Command: Endpoint konfigurieren
con TRB_EVAL_CTX      : int64 := 13;  // Command: Context evaluieren
con TRB_NOOP_CMD      : int64 := 23;  // No-Op Command
con TRB_XFER_EVENT    : int64 := 32;  // Event: Transfer abgeschlossen
con TRB_CMD_COMPL     : int64 := 33;  // Event: Command abgeschlossen
con TRB_PORT_STATUS   : int64 := 34;  // Event: Port-Status geändert

// TRB-Control-Bits
con TRB_C             : int64 := 1;        // Cycle-Bit
con TRB_TC            : int64 := 2;        // Toggle-Cycle (nur LINK-TRB)
con TRB_ISP           : int64 := 4;        // Interrupt-on-Short-Packet
con TRB_IOC           : int64 := 0x20;     // Interrupt-on-Completion
con TRB_IDT           : int64 := 0x40;     // Immediate-Data (≤8 Bytes direkt im TRB)

4.2 Event-Ring-Segment-Table-Eintrag (ERSTE)

con XHCI_ERST_SIZE  : int64 := 16;
// +0  (uint64): Ring-Segment-Base-Address (4K-aligned)
// +8  (uint32): Ring-Segment-Size (Anzahl TRBs im Segment)
// +12 (uint32): reserviert

4.3 Device-Context-Base-Address-Array (DCBAA)

Ein Array von uint64-Zeigern (Slot 0 bis MaxSlots). Slot 0 zeigt auf den Scratchpad-Buffer-Array (oder 0 wenn MaxScratchpad=0). Jeder weitere Eintrag zeigt auf den Output-Device-Context des entsprechenden Slots.

4.4 Input-Context

Wird für ADDRESS_DEVICE- und CONFIGURE_ENDPOINT-Commands genutzt. Besteht aus:

con XHCI_CTX_SIZE     : int64 := 32;    // Wenn CSZ=0 in HCCPARAMS1
con XHCI_CTX_SIZE_64  : int64 := 64;    // Wenn CSZ=1 (64-Byte-Contexts)

// Slot-Context-Felder (+0 im Slot-Context-Block)
// Bits[19:10] = Root-Hub-Port-Num, Bits[23:20] = NumContextEntries,
// Bits[29:27] = Speed (1=Full, 2=Low, 3=High, 4=SuperSpeed)
con XHCI_SPEED_FS     : int64 := 1;
con XHCI_SPEED_LS     : int64 := 2;
con XHCI_SPEED_HS     : int64 := 3;
con XHCI_SPEED_SS     : int64 := 4;

// Endpoint-Typ (EP-Context Bits[5:3])
con XHCI_EP_ISOCH_OUT : int64 := 1;
con XHCI_EP_BULK_OUT  : int64 := 2;
con XHCI_EP_INTR_OUT  : int64 := 3;
con XHCI_EP_CTRL      : int64 := 4;
con XHCI_EP_ISOCH_IN  : int64 := 5;
con XHCI_EP_BULK_IN   : int64 := 6;
con XHCI_EP_INTR_IN   : int64 := 7;


5. Initialisierung

Die Initialisierungsreihenfolge folgt dem xHCI-Spec §4.2:

1. BAR0 lesen, MMIO mappen
2. Warten bis USBSTS.CNR = 0 (Controller Ready)
3. USBCMD.RUN = 0 setzen → Controller anhalten
4. Warten bis USBSTS.HCH = 1
5. USBCMD.HCRST = 1 → Controller-Reset
6. Warten bis HCRST = 0 und CNR = 0
7. DCBAAP setzen (alloc + nullen)
8. Command-Ring initialisieren (CRCR setzen, Cycle-Bit = 1)
9. Event-Ring initialisieren (ERST alloc, ERSTBA + ERSTSZ im IR[0] setzen)
10. Interrupter-0 aktivieren: IMAN.IE = 1
11. MaxSlotsEn in CONFIG setzen
12. USBCMD.RUN = 1 → Controller starten
13. Warten bis USBSTS.HCH = 0
14. Ports scannen (→ Abschnitt 6)

Wichtig: Alle Ring-Puffer müssen physisch kontiguös und auf 64 Bytes aligned sein. Der Treiber nutzt den Kernel-PMM direkt (kein VFS-Alloc).


6. Port-Management & Geräteerkennung

Port-Register-Offsets

Jeder Port hat einen 16-Byte-Block ab OpRegs + 0x400:

con XHCI_PORT_BASE   : int64 := 0x400;   // Offset in OpRegs
con XHCI_PORT_STRIDE : int64 := 16;      // Bytes pro Port

// Innerhalb eines Port-Blocks:
con XHCI_PORT_SC     : int64 := 0;   // Port-Status-and-Control (uint32)
con XHCI_PORT_PMSC   : int64 := 4;   // Port-Power-Management-Status-and-Control
con XHCI_PORT_LI     : int64 := 8;   // Port-Link-Info
con XHCI_PORT_HLPMC  : int64 := 12;  // Port-Hardware-LPM-Control

// PORTSC-Bits
con XHCI_PORTSC_CCS  : int64 := 1;       // Current Connect Status
con XHCI_PORTSC_PED  : int64 := 2;       // Port Enabled/Disabled
con XHCI_PORTSC_PR   : int64 := 0x10;    // Port Reset
con XHCI_PORTSC_PLS  : int64 := 0x1E0;  // Port-Link-State (Bits[8:5])
con XHCI_PORTSC_PP   : int64 := 0x200;  // Port Power
con XHCI_PORTSC_SPD  : int64 := 0x3C00; // Port-Speed (Bits[13:10])
con XHCI_PORTSC_PRC  : int64 := 0x200000; // Port-Reset-Change (quittieren)

Geräte-Enumeration (pro Port)

1. PORTSC.CCS prüfen → Gerät angeschlossen?
2. PORTSC.PR = 1 setzen → Port-Reset
3. Warten auf PORTSC.PRC = 1 (Reset abgeschlossen), dann quittieren
4. PORTSC.SPD lesen → Geschwindigkeit bestimmen
5. Command: ENABLE_SLOT → Slot-ID erhalten
6. Input-Context allozieren, Slot-Context befüllen (Port, Geschwindigkeit)
7. Control-Endpoint-0-Context befüllen (MaxPacketSize nach Speed)
8. DCBAA[slot_id] = Output-Context-Adresse
9. Command: ADDRESS_DEVICE (BSR=1 = Block Set Address Request für ersten Get-Descriptor)
10. Control-Transfer: GET_DESCRIPTOR (Device, 8 Bytes) → bMaxPacketSize0 lesen
11. Command: ADDRESS_DEVICE (BSR=0) → SET_ADDRESS automatisch vom HC
12. Control-Transfer: GET_DESCRIPTOR (Device, 18 Bytes) → VID/PID, Class, Configs
13. Gerät in interner Tabelle eintragen (vid, pid, slot_id, port)


7. Transfer-Mechanismus

7.1 Transfer-Ringe

Jeder aktive Endpoint hat einen eigenen Transfer-Ring (TR). Der TR ist ein zirkulärer Puffer aus TRBs, abgeschlossen durch einen LINK-TRB der zurück auf den Ring-Anfang zeigt.

Consumer: XHCI-Hardware liest TRBs vom Dequeue-Pointer
Producer: Treiber schreibt TRBs ab Enqueue-Pointer
Cycle-Bit (C): unterscheidet alte von neuen TRBs (toggelt bei jedem Rindumlauf)

7.2 Control Transfer

Ein Control-Transfer besteht aus 3 TRBs (Setup → Data → Status):

// 1. Setup-TRB (TRB_SETUP)
//    Parameter[0..7] = 8-Byte-USB-Setup-Packet (bmReqType, bReq, wVal, wIdx, wLen)
//    Status: Bits[16:0] = 8 (Länge des Setup-Packets)
//    Control: TRB_SETUP | IDT=1 (Immediate Data) | TRT (Transfer Type: 2=OUT, 3=IN)

// 2. Data-TRB (TRB_DATA) — optional, nur wenn wLength > 0
//    Parameter = physische Adresse des Datenpuffers
//    Status: Bits[16:0] = wLength
//    Control: TRB_DATA | DIR=1 wenn IN | IOC=1

// 3. Status-TRB (TRB_STATUS)
//    Control: TRB_STATUS | DIR (umgekehrt zur Data-Phase) | IOC=1

7.3 Bulk / Interrupt Transfer

Einzelner NORMAL-TRB (oder Kette via Chain-Bit für >64 KB):

// Normal-TRB (TRB_NORMAL)
// Parameter = physische Adresse des Datenpuffers
// Status: Bits[16:0] = Byte-Count, Bits[31:22] = Interrupter-Target (0)
// Control: TRB_NORMAL | IOC=1

Nach dem Schreiben des TRB: Doorbell klingeln (poke32 auf DB[slot_id], EP-Index).

7.4 Completion-Event abwarten

1. ERDP aus IR[0] lesen (aktueller Dequeue-Pointer)
2. Auf TRB_XFER_EVENT oder TRB_CMD_COMPL warten:
   - Cycle-Bit am aktuellen ERDP muss gleich dem Producer-Cycle-Bit sein
   - Event-TRB enthält: Completion-Code (Bits[31:24] in Status), Slot-ID, EP-Index
3. ERDP um XHCI_TRB_SIZE vorwärtssetzen (mit HC-Handshake-Bit = 1)
4. IMOD-Timer zurücksetzen (IMAN.IP = 1 schreiben)

Completion-Codes:

Code Wert Bedeutung
CC_SUCCESS 1 Transfer erfolgreich
CC_DATA_BUFFER_ERROR 2 DMA-Fehler
CC_BABBLE_DETECTED 3 Gerät sendet zu viele Daten
CC_USB_TRANSACTION_ERROR 4 USB-Bus-Fehler (CRC, Timeout)
CC_TRB_ERROR 5 Ungültiger TRB
CC_STALL_ERROR 6 Endpoint STALL
CC_SHORT_PACKET 13 Weniger Daten als angefordert (kein Fehler)
CC_STOPPED 26 Transfer durch STOP_ENDPOINT-Command gestoppt

con XHCI_CC_SUCCESS   : int64 := 1;
con XHCI_CC_TRB_ERROR : int64 := 5;
con XHCI_CC_STALL     : int64 := 6;
con XHCI_CC_SHORT     : int64 := 13;


8. Ring-3-Syscall-Binding

Die USB-Syscall-Nummern sind noch nicht vergeben. Syscalls 150–155 sind belegt:

Syscall-Nr Belegt durch
150 sys_win_get_title (WP27)
151 sys_win_get_geom (WP29)
152 sys_win_get_pid (WP29)
153 sys_ramdisk_create (WP30) → RAM-Disk
154 sys_ramdisk_fmt (WP30) → RAM-Disk
155 sys_mkfs_iofs (WP01+WP09) → IOFS

Die geplante USB-Ring-3-Schnittstelle wird im XHCI-WP auf freien Nummern ab 156+ definiert:

Geplante Funktion Treiber-Funktion Bemerkung
sys_usb_enum (TBD) XhciEnumDevices(buf, max) Geräteliste in Ring-3-Puffer kopieren
sys_usb_open (TBD) XhciOpenByVidPid(vid, pid) Gerät per VID/PID öffnen → usb_fd
sys_usb_ctrl (TBD) XhciControlTransfer(slot, setup, data, len) Control Transfer mit Bounce-Buffer
sys_usb_bulk (TBD) XhciBulkTransfer(slot, ep, buf, len) Bulk Transfer via DMA
sys_usb_intr_read (TBD) XhciIntrRead(slot, ep, buf, len) Interrupt-Endpoint, blockierend
sys_usb_close (TBD) XhciClose(slot) DISABLE_SLOT + Tabellenplatz freigeben

Bounce-Buffer-Strategie: Control-Transfers nutzen einen kleinen Kernel-seitigen Puffer (max. 4096 Bytes), der sicher ins Ring-3-VA zurückkopiert wird. Bulk/Interrupt nutzen direkte DMA auf die physische Adresse des Ring-3-Puffers (Boundary: Ring-3-Seiten müssen im PMM bekannt sein).


9. Interne Gerätetabelle

Der Treiber hält eine statische Tabelle für max. 32 gleichzeitig angeschlossene Geräte:

// Eintrag: 64 Bytes
// +0  vid       (int64)
// +8  pid       (int64)
// +16 slot_id   (int64)   — XHCI-Slot (1–MaxSlots)
// +24 port      (int64)   — Root-Hub-Port-Nummer
// +32 speed     (int64)   — XHCI_SPEED_*
// +40 ep0_ring  (int64)   — physische Adresse des EP0-Transfer-Rings
// +48 state     (int64)   — 0=frei, 1=aktiv, 2=error
// +56 (reserviert)

con XHCI_DEV_ENTRY_SIZE : int64 := 64;
con XHCI_MAX_DEVICES    : int64 := 32;


10. Hinweise


11. Quelldatei

Modul Datei
XHCI-Treiber kernel/xhci.lyx

Letzte Aktualisierung: 2026-06-17