USB Implementation Plan¶
Living document — tracks design decisions, architecture, progress, and open questions for Asuro's USB subsystem.
Table of Contents¶
- 1. Overview
- 2. Design Decisions
- 3. Architecture
- 4. File Layout
- 5. Shared Types (
usbtypes.pas) - 6. USB Core (
usbcore.pas) - 7. Host Controller Drivers
- 8. Hub Driver (
usbhub.pas) - 9. USB Class Drivers
- 10. Integration with drivermanagement
- 11. Memory & DMA Strategy
- 12. Transfer Scheduling
- 13. Implementation Phases
- 14. Progress Tracker
1. Overview¶
Implement a full USB subsystem for Asuro, supporting all four host controller types (UHCI → OHCI → EHCI → xHCI), all four transfer types (Control, Interrupt, Bulk, Isochronous), full hub support, and a class-driver model integrated with drivermanagement. Initial class drivers: USB Keyboard and USB Mouse.
Target environment: VirtualBox (PIIX3 = UHCI, ICH9 = OHCI + EHCI, optional xHCI), bare-metal i386, ring 0, Standard Pascal (FPC cross-compiler), no runtime library.
2. Design Decisions¶
| Decision | Choice | Rationale |
|---|---|---|
| HC implementation order | UHCI → OHCI → EHCI → xHCI | UHCI first (VirtualBox PIIX3 default), then OHCI, then EHCI for USB 2.0, xHCI last |
| Transfer types | All four (Control, Interrupt, Bulk, Isochronous) | Full spec compliance over time |
| Hub support | Full from the start | Needed for real hardware; root hub + external hubs |
| USB device ↔ class driver matching | drivermanagement with biUSB bus type |
Reuse existing driver lifecycle, no parallel systems |
| Completion model | Interrupt-driven (ISR on PCI IRQ, PollBusy guard) | Inline polling fallback in usb_control_msg; ISR fires completion hooks for HID drivers |
| DMA memory | Existing pmemorymanager / vmemorymanager (force_alloc_block / map_page) |
Already proven in OHCI stub; identity-mapped pages for DMA |
| File layout | src/driver/bus/usb/ subdirectory |
Keep HC logic together; class drivers in src/driver/hid/ and src/driver/storage/ |
| Data structures | lists.pas linked lists + manual kalloc/kfree records |
Avoid static arrays, use existing infrastructure; class drivers (keyboard, mouse, storage) all use dynamic lists for unlimited device tracking |
3. Architecture¶
┌──────────────────────────────────────────────────────────────────┐
│ drivermanagement │
│ register_driver(biUSB, class/sub/proto) ←→ register_device() │
├──────────────────────────────────────────────────────────────────┤
│ USB Class Drivers │
│ ┌──────────────┐ ┌──────────────┐ ┌────────────────────┐ │
│ │ USB Keyboard │ │ USB Mouse │ │ USB Mass Storage │ │
│ │ (src/hid/) │ │ (src/hid/) │ │ (src/storage/) │ │
│ └──────┬───────┘ └──────┬───────┘ └────────┬───────────┘ │
│ │ │ │ │
├──────────┴──────────────────┴────────────────────┴───────────────┤
│ usbcore.pas │
│ Device enumeration, configuration, pipe management, │
│ control transfers, descriptor parsing, address assignment │
├──────────────────────────────────────────────────────────────────┤
│ usbhub.pas │
│ Hub driver, port status/change, reset sequencing, │
│ topology tracking, split transaction TT info │
├──────────────────────────────────────────────────────────────────┤
│ Host Controller Interface (HCI) Abstraction │
│ usbtypes.pas │
│ TUSBHCDriver record with function pointers: │
│ init, reset, start, stop, submit_transfer, poll │
├─────────┬──────────┬──────────┬──────────┬───────────────────────┤
│ UHCI │ OHCI │ EHCI │ xHCI │ Host Controller │
│ .pas │ .pas │ .pas │ .pas │ Drivers │
├─────────┴──────────┴──────────┴──────────┴───────────────────────┤
│ PCI Bus │
│ (PCI.pas) │
└──────────────────────────────────────────────────────────────────┘
* = future, not in initial implementation
4. File Layout¶
src/driver/bus/usb/
├── usbtypes.pas # Shared types, constants, HC abstraction record
├── usbcore.pas # Enumeration, configuration, control transfers, pipe API
├── usbhub.pas # Hub driver (root + external)
├── UHCI.pas # UHCI host controller driver (moved from bus/)
├── OHCI.pas # OHCI host controller driver (moved from bus/)
├── EHCI.pas # EHCI host controller driver (moved from bus/)
├── XHCI.pas # xHCI host controller driver (moved from bus/)
└── USB.pas # Init entry point, PCI registration (moved from bus/)
src/driver/hid/
├── usb_keyboard.pas # USB HID keyboard class driver
└── usb_mouse.pas # USB HID mouse class driver
src/driver/storage/
└── usb_storage.pas # USB Mass Storage class driver (BOT + SCSI)
Migration: Existing
UHCI.pas,OHCI.pas,EHCI.pas,XHCI.pas,USB.pasmove fromsrc/driver/bus/intosrc/driver/bus/usb/. Build scripts will need path updates.
5. Shared Types (usbtypes.pas)¶
5.1 USB Descriptor Types¶
{ Standard descriptor type constants }
const
USB_DESC_DEVICE = $01;
USB_DESC_CONFIGURATION = $02;
USB_DESC_STRING = $03;
USB_DESC_INTERFACE = $04;
USB_DESC_ENDPOINT = $05;
USB_DESC_HUB = $29;
{ USB Request Types }
USB_REQ_GET_STATUS = $00;
USB_REQ_CLEAR_FEATURE = $01;
USB_REQ_SET_FEATURE = $03;
USB_REQ_SET_ADDRESS = $05;
USB_REQ_GET_DESCRIPTOR = $06;
USB_REQ_SET_CONFIG = $09;
{ Endpoint direction }
USB_DIR_OUT = $00;
USB_DIR_IN = $80;
{ Transfer types }
USB_TRANSFER_CONTROL = 0;
USB_TRANSFER_ISOCHRONOUS = 1;
USB_TRANSFER_BULK = 2;
USB_TRANSFER_INTERRUPT = 3;
{ USB speeds }
USB_SPEED_LOW = 0; { 1.5 Mbps }
USB_SPEED_FULL = 1; { 12 Mbps }
USB_SPEED_HIGH = 2; { 480 Mbps }
USB_SPEED_SUPER = 3; { 5 Gbps }
{ Max devices per controller }
USB_MAX_DEVICES = 127;
USB_MAX_ENDPOINTS = 32;
5.2 Standard Descriptors (packed records)¶
type
PUSBDeviceDescriptor = ^TUSBDeviceDescriptor;
TUSBDeviceDescriptor = packed record
bLength : uint8;
bDescriptorType : uint8;
bcdUSB : uint16;
bDeviceClass : uint8;
bDeviceSubClass : uint8;
bDeviceProtocol : uint8;
bMaxPacketSize0 : uint8;
idVendor : uint16;
idProduct : uint16;
bcdDevice : uint16;
iManufacturer : uint8;
iProduct : uint8;
iSerialNumber : uint8;
bNumConfigurations : uint8;
end;
PUSBConfigDescriptor = ^TUSBConfigDescriptor;
TUSBConfigDescriptor = packed record
bLength : uint8;
bDescriptorType : uint8;
wTotalLength : uint16;
bNumInterfaces : uint8;
bConfigurationValue : uint8;
iConfiguration : uint8;
bmAttributes : uint8;
bMaxPower : uint8;
end;
PUSBInterfaceDescriptor = ^TUSBInterfaceDescriptor;
TUSBInterfaceDescriptor = packed record
bLength : uint8;
bDescriptorType : uint8;
bInterfaceNumber : uint8;
bAlternateSetting : uint8;
bNumEndpoints : uint8;
bInterfaceClass : uint8;
bInterfaceSubClass : uint8;
bInterfaceProtocol : uint8;
iInterface : uint8;
end;
PUSBEndpointDescriptor = ^TUSBEndpointDescriptor;
TUSBEndpointDescriptor = packed record
bLength : uint8;
bDescriptorType : uint8;
bEndpointAddress : uint8;
bmAttributes : uint8;
wMaxPacketSize : uint16;
bInterval : uint8;
end;
PUSBHubDescriptor = ^TUSBHubDescriptor;
TUSBHubDescriptor = packed record
bDescLength : uint8;
bDescriptorType : uint8;
bNbrPorts : uint8;
wHubCharacteristics : uint16;
bPwrOn2PwrGood : uint8;
bHubContrCurrent : uint8;
{ Variable-length DeviceRemovable + PortPwrCtrlMask follow }
end;
5.3 Setup Packet¶
PUSBSetupPacket = ^TUSBSetupPacket;
TUSBSetupPacket = packed record
bmRequestType : uint8;
bRequest : uint8;
wValue : uint16;
wIndex : uint16;
wLength : uint16;
end;
5.4 USB Device / Endpoint / Pipe State¶
{ Transfer completion status }
TUSBTransferStatus = (
tsSuccess,
tsStall,
tsNAK,
tsBabble,
tsDataBufferError,
tsCRCError,
tsBitStuffError,
tsTimeout,
tsNotStarted,
tsInProgress
);
{ Transfer direction }
TUSBDirection = (dirOut, dirIn, dirSetup);
{ Pipe type matches transfer type }
TUSBPipeType = (ptControl, ptIsochronous, ptBulk, ptInterrupt);
PUSBEndpoint = ^TUSBEndpoint;
TUSBEndpoint = record
Address : uint8; { Endpoint number (0-15) }
Direction : TUSBDirection;
PipeType : TUSBPipeType;
MaxPacket : uint16;
Interval : uint8; { Polling interval (interrupt/iso) }
Toggle : uint8; { Data toggle state: 0 or 1 }
end;
PUSBDevice = ^TUSBDevice;
{ Forward declaration for HC driver }
PUSBHCDriver = ^TUSBHCDriver;
PUSBTransfer = ^TUSBTransfer;
TUSBTransfer = record
Device : PUSBDevice;
Endpoint : PUSBEndpoint;
Direction : TUSBDirection;
PipeType : TUSBPipeType;
Buffer : Pointer;
BufferLen : uint32;
ActualLen : uint32; { Bytes actually transferred }
Status : TUSBTransferStatus;
Setup : TUSBSetupPacket; { For control transfers }
HCPriv : Pointer; { HC-specific data (TD chain, etc.) }
end;
TUSBDevice = record
Address : uint8; { Assigned USB address (1-127) }
Speed : uint8; { USB_SPEED_* }
MaxPacket0 : uint8; { EP0 max packet size }
HC : PUSBHCDriver;
HCPort : uint8; { Root hub port or hub port }
ParentHub : PUSBDevice; { nil for root-hub-attached devices }
ParentPort : uint8; { Port on parent hub }
DevDesc : TUSBDeviceDescriptor;
Endpoints : array[0..USB_MAX_ENDPOINTS-1] of TUSBEndpoint;
NumEndpoints: uint8;
Configured : boolean;
end;
5.5 Host Controller Abstraction¶
{ HC operations - each HC driver fills in these function pointers }
TUSBHCInit = function(dev : PPCI_Device) : boolean;
TUSBHCReset = function(hc : PUSBHCDriver) : boolean;
TUSBHCStart = function(hc : PUSBHCDriver) : boolean;
TUSBHCStop = procedure(hc : PUSBHCDriver);
TUSBHCSubmit = function(hc : PUSBHCDriver; transfer : PUSBTransfer) : boolean;
TUSBHCPoll = procedure(hc : PUSBHCDriver);
TUSBHCPortReset = function(hc : PUSBHCDriver; port : uint8) : boolean;
TUSBHCPortStatus = function(hc : PUSBHCDriver; port : uint8) : uint32;
TUSBHCDriver = record
Name : PChar;
HCType : uint8; { 0=UHCI, 1=OHCI, 2=EHCI, 3=xHCI }
NumPorts : uint8;
PCIDev : TPCI_Device; { Owning PCI device }
BaseAddr : uint32; { I/O base (UHCI) or MMIO base (OHCI/EHCI/xHCI) }
PrivData : Pointer; { HC-specific state (frame list, HCCA, etc.) }
Devices : PLinkedListBase; { List of TUSBDevice attached to this HC }
NextAddress : uint8; { Next available USB address }
{ Operations }
fnReset : TUSBHCReset;
fnStart : TUSBHCStart;
fnStop : TUSBHCStop;
fnSubmit : TUSBHCSubmit;
fnPoll : TUSBHCPoll;
fnPortReset : TUSBHCPortReset;
fnPortStatus : TUSBHCPortStatus;
end;
6. USB Core (usbcore.pas)¶
The core module orchestrates enumeration and provides the public transfer API used by class drivers.
6.1 Responsibilities¶
- Maintain a global list of HC drivers (
HCList : PLinkedListBase) - Register/unregister HC drivers — called by each HC's
loadfunction - Device enumeration sequence (called when hub reports a new device):
- Port reset (via HC abstraction)
- Create
TUSBDevicewith address 0, EP0 at 8 bytes - GET_DESCRIPTOR(Device, 8 bytes) → read
bMaxPacketSize0 - SET_ADDRESS(N) → assign unique address
- GET_DESCRIPTOR(Device, 18 bytes) → full device descriptor
- GET_DESCRIPTOR(Configuration) → all configs, interfaces, endpoints
- Parse interface descriptors → populate
Endpoints[] - SET_CONFIGURATION(1)
- Call
drivermanagement.register_device()withbiUSBbus identifier - Control transfer helpers:
usb_control_msg(dev, requestType, request, value, index, buf, len) : TUSBTransferStatususb_get_descriptor(dev, descType, descIdx, buf, len) : TUSBTransferStatususb_set_address(dev, addr) : TUSBTransferStatususb_set_configuration(dev, config) : TUSBTransferStatus- Interrupt/Bulk transfer helpers:
usb_interrupt_transfer(dev, endpoint, buf, len) : TUSBTransferStatususb_bulk_transfer(dev, endpoint, buf, len) : TUSBTransferStatus- Polling loop — called from timer/scheduler tick:
- Iterate all HC drivers → call
fnPoll - HC poll checks for completed transfers and port status changes
- Port status changes trigger hub logic → enumeration
6.2 Device Identifier Encoding for drivermanagement¶
When a USB device is enumerated, we register it via:
DevID.Bus := biUSB;
DevID.id0 := (idVendor SHL 16) OR idProduct; { Vendor:Product }
DevID.id1 := bDeviceClass;
DevID.id2 := bInterfaceClass; { From first/matching interface }
DevID.id3 := bInterfaceSubClass;
DevID.id4 := bInterfaceProtocol;
DevID.ex := nil; { Could chain additional interface descriptors }
Class drivers register with wildcards where appropriate:
{ HID Keyboard driver registers: }
DevID.Bus := biUSB;
DevID.id0 := idANY; { Any vendor/product }
DevID.id1 := idANY; { Any device class }
DevID.id2 := $03; { HID class }
DevID.id3 := $01; { Boot interface subclass }
DevID.id4 := $01; { Keyboard protocol }
The TDriverLoadCallback receives a pointer to a PUSBDevice, giving the class driver full access to endpoints and the HC transfer API.
7. Host Controller Drivers¶
7.1 UHCI (Universal Host Controller Interface)¶
Priority: First
Spec: Intel UHCI Design Guide, Rev 1.1
I/O model: Port I/O (not MMIO)
PCI prog_if: $00
Registers (I/O port offsets from BAR4)¶
| Offset | Name | Size | Description |
|---|---|---|---|
| $00 | USBCMD | 16-bit | Command register |
| $02 | USBSTS | 16-bit | Status register |
| $04 | USBINTR | 16-bit | Interrupt enable |
| $06 | FRNUM | 16-bit | Frame number |
| $08 | FRBASEADD | 32-bit | Frame list base address |
| $0C | SOFMOD | 8-bit | Start of frame modify |
| $10 | PORTSC1 | 16-bit | Port 1 status/control |
| $12 | PORTSC2 | 16-bit | Port 2 status/control |
Key Data Structures (all physically contiguous, 16-byte aligned)¶
- Frame List: 1024 × 32-bit pointers (4KB page-aligned)
- Queue Head (QH): 8 bytes —
QH_Link_Pointer,QH_Element_Pointer - Transfer Descriptor (TD): 16 bytes —
TD_Link_Pointer,TD_Control_Status,TD_Token,TD_Buffer_Pointer
Implementation Steps¶
- Read BAR4 from PCI config → I/O base address
- Stop controller (clear USBCMD.RS)
- Global reset (USBCMD.GRESET), wait 10ms, clear
- Allocate frame list (4KB, page-aligned, identity-mapped)
- Initialize all 1024 frame list entries to terminate (T-bit set)
- Write frame list physical address to FRBASEADD
- Set FRNUM = 0
- Create skeleton QH schedule:
- Control QH → Bulk QH → Interrupt QHs (at frame list intervals)
- Start controller (USBCMD.RS = 1)
- For each port: check PORTSC, detect device, trigger enumeration
- Poll: check USBSTS for completion, walk TD chains for completed transfers
UHCI Private Data¶
PUHCIData = ^TUHCIData;
TUHCIData = record
IOBase : uint16;
FrameList : Pointer; { 4KB-aligned physical address }
ControlQH : Pointer; { Control transfer queue head }
BulkQH : Pointer; { Bulk transfer queue head }
IntQH : array[0..7] of Pointer; { Interrupt QHs at different intervals }
end;
7.2 OHCI (Open Host Controller Interface)¶
Priority: Second
Spec: OHCI Rev 1.0a
I/O model: MMIO (BAR0)
PCI prog_if: $10
Key Registers (already partially in existing TOHCI_MMR)¶
Already have the MMIO register record. Need to extend with:
- Operational registers (HcControl, HcCommandStatus, etc.)
- HcHCCA → points to 256-byte Host Controller Communications Area
- Port status registers (HcRhPortStatus[1..N])
Key Data Structures¶
- HCCA: 256 bytes, 256-byte aligned — contains interrupt table (32 × ED pointers) and frame counter
- Endpoint Descriptor (ED): 16 bytes — links to TD list
- Transfer Descriptor (TD): 16 bytes (General) or 32 bytes (Isochronous)
Implementation Steps¶
- Map BAR0 MMIO region (existing code does this)
- Reset: write OHCI_RESET to HcCommandStatus, wait for completion
- Allocate HCCA (256-byte aligned)
- Set HcHCCA register
- Build ED skeleton schedule (Control, Bulk, Interrupt, Isochronous lists)
- Set HcControl to OPERATIONAL state
- Enable relevant interrupts in HcIntEnable
- Set HcPeriodicStart
- Power on root hub ports via HcRhPortStatus
- Detect devices, trigger enumeration
- Poll: read HcDoneHead for completed TDs
7.3 EHCI (Enhanced Host Controller Interface)¶
Priority: Third
Spec: EHCI Rev 1.0
I/O model: MMIO (BAR0)
PCI prog_if: $20
Key Concepts¶
- Capability Registers (read-only, offset 0): CAPLENGTH, HCSPARAMS, HCCPARAMS
- Operational Registers (offset CAPLENGTH): USBCMD, USBSTS, USBINTR, FRINDEX, etc.
- Periodic Frame List: 1024 × 32-bit frame list entries (4KB aligned)
- Async Schedule: Circular linked list of Queue Heads (QHs)
- Queue Head (QH): 48 bytes — contains overlay area for current TD
- Queue Transfer Descriptor (qTD): 32 bytes, 32-byte aligned
Companion Controller Handling¶
EHCI routes full/low-speed devices to companion controllers (UHCI/OHCI) via port routing logic. We must: - Read HCSPARAMS for N_PORTS and N_CC (companion controllers) - Handle port owner change (CONFIGFLAG, PORTSC[n].PortOwner)
Implementation Steps¶
- Map BAR0, read capability registers
- Stop controller (USBCMD.RS = 0), wait for halt
- Reset (USBCMD.HCRESET), wait for clear
- Allocate periodic frame list + async QH
- Set PERIODICLISTBASE, ASYNCLISTADDR
- Set CONFIGFLAG = 1 (claim all ports from companions)
- Enable async + periodic schedules
- Start controller
- Port detection, reset (50ms PORTRESET), enumeration
- Poll: check USBSTS for async/periodic completion
7.4 xHCI (eXtensible Host Controller Interface)¶
Priority: Fourth (last)
Spec: xHCI Rev 1.2
I/O model: MMIO (BAR0, 64-bit capable)
PCI prog_if: $30
Key Concepts (significantly different from UHCI/OHCI/EHCI)¶
- No frame list — uses ring-based command/transfer/event model
- Device Context: per-device state maintained by hardware
- Command Ring: kernel submits commands (enable slot, address device, etc.)
- Transfer Ring: per-endpoint ring of TRBs (Transfer Request Blocks)
- Event Ring: hardware posts completions
- Doorbell Registers: notify controller of new TRBs
- Scratchpad Buffers: required by some controllers
Implementation Steps¶
- Map BAR0 (may need 64-bit BAR handling)
- Read capability registers (CAPLENGTH, HCSPARAMS1/2/3, HCCPARAMS1)
- Stop controller, wait for halt
- Reset (USBCMD.HCRST)
- Program MaxSlots, MaxIntrs
- Allocate DCBAA (Device Context Base Address Array)
- Allocate Command Ring, Event Ring(s), Scratchpad
- Set DCBAAP, CRCR registers
- Define Interrupter 0 event ring
- Start controller
- For each port: check PORTSC, issue Port Reset, Enable Slot, Address Device
- Poll: read Event Ring for completion TRBs, advance dequeue pointer
8. Hub Driver (usbhub.pas)¶
8.1 Hub Detection¶
Hubs are detected during enumeration when bDeviceClass = $09 (Hub) or bInterfaceClass = $09. The hub driver is registered as a USB class driver via drivermanagement:
DevID.Bus := biUSB;
DevID.id0 := idANY;
DevID.id1 := $09; { Hub device class }
DevID.id2 := $09; { Hub interface class }
DevID.id3 := idANY; { Subclass }
DevID.id4 := idANY; { Protocol }
8.2 Hub Responsibilities¶
- GET_HUB_DESCRIPTOR → determine number of downstream ports
- SET_PORT_POWER for each port (if not ganged-power)
- Poll hub status endpoint (interrupt IN endpoint) for port status change bitmap
- On port change:
- GET_PORT_STATUS → check
PORT_CONNECTION,PORT_ENABLE,PORT_RESET - If new device: SET_PORT_FEATURE(PORT_RESET), wait, CLEAR_PORT_FEATURE(C_PORT_RESET)
- Call
usbcoreenumeration for the new device (withParentHubset) - If device removed: tear down device, notify class driver, free resources
- Topology tracking — maintain parent/child relationships for split transactions (EHCI TT)
8.3 Root Hub Emulation¶
Each HC driver emulates root hub ports using its native port status registers. The HC abstraction (fnPortReset, fnPortStatus) provides this. usbcore treats root hub ports uniformly by calling these functions during initial scan.
9. USB Class Drivers¶
9.1 USB Keyboard (usb_keyboard.pas)¶
USB HID Boot Protocol Keyboard
- Registers with
drivermanagementforbiUSB, class=$03, subclass=$01, protocol=$01 loadcallback receivesPUSBDevice- SET_PROTOCOL(0) → Boot protocol (simpler 8-byte reports)
- SET_IDLE(0) → Only report on change
- Find interrupt IN endpoint from device's endpoint list
- Poll endpoint periodically (using interrupt transfer API)
- Parse 8-byte boot report:
- Byte 0: modifier keys (Ctrl, Shift, Alt, GUI)
- Byte 1: reserved
- Bytes 2-7: keycodes (up to 6 simultaneous)
- Translate keycodes to existing
TKeyInfoformat - Feed into existing
keyboard.hookmechanism
9.2 USB Mouse (usb_mouse.pas)¶
USB HID Boot Protocol Mouse
- Registers for
biUSB, class=$03, subclass=$01, protocol=$02 - SET_PROTOCOL(0) → Boot protocol
- Find interrupt IN endpoint
- Poll for 3-byte (or 4-byte with scroll) boot reports:
- Byte 0: buttons (bit0=left, bit1=right, bit2=middle)
- Byte 1: X displacement (signed)
- Byte 2: Y displacement (signed)
- Byte 3: wheel (optional)
- Feed into existing
mousestate/mousedriver system
10. Integration with drivermanagement¶
10.1 PCI → HC Driver Loading (existing pattern, refined)¶
The existing USB.init procedure already registers four PCI device identifiers with drivermanagement. When PCI scan finds a USB controller, drivermanagement.register_device triggers the matching HC driver's load callback. This stays the same.
10.2 HC Driver → USB Core¶
Each HC driver's load function:
1. Initializes hardware
2. Fills in a TUSBHCDriver record (kalloc'd)
3. Calls usbcore.register_hc(@hcdriver) to add to the global HC list
4. Calls usbcore.scan_ports(@hcdriver) to enumerate root hub ports
10.3 USB Core → Class Driver Loading¶
When usbcore completes device enumeration:
1. For each interface on the device, create a TDeviceIdentifier with biUSB and class/subclass/protocol
2. Call drivermanagement.register_device('USB Device', @DevID, @usbdev)
3. drivermanagement matches against registered class drivers and calls their load callback
4. Class driver receives PUSBDevice via the ptr parameter, begins operation
10.4 Lifecycle Flow¶
Boot
→ kernel.init
→ drivermanagement.init
→ PCI.init (registers PCI driver, force_load)
→ PCI.load (scans all buses)
→ For each PCI device: drivermanagement.register_device(biPCI, ...)
→ Matches USB-UHCI/OHCI/EHCI/XHCI drivers registered by USB.init
→ UHCI.load / OHCI.load / EHCI.load / XHCI.load
→ HC hardware init
→ usbcore.register_hc(hc)
→ usbcore.scan_ports(hc)
→ For each port with a device:
→ port reset
→ enumeration (GET_DESCRIPTOR, SET_ADDRESS, SET_CONFIG)
→ drivermanagement.register_device(biUSB, class/sub/proto, @usbdev)
→ Matches usb_keyboard / usb_mouse / usbhub drivers
→ Class driver load callback
→ Begin polling / operation
11. Memory & DMA Strategy¶
11.1 Requirements¶
USB host controllers require physically-contiguous, properly-aligned memory that must be accessible by the hardware (DMA). In Asuro's identity-mapped kernel space, virtual = physical (for pages set up via force_alloc_block + map_page).
11.2 Allocation Patterns¶
| Structure | Size | Alignment | Allocator |
|---|---|---|---|
| UHCI Frame List | 4096 bytes | 4KB (page) | force_alloc_block + map_page (full page) |
| UHCI TD | 32 bytes | 16-byte | kalloc(32) — ensure returned address is 16-byte aligned (may need alignment wrapper) |
| UHCI QH | 16 bytes | 16-byte | kalloc(16) + alignment |
| OHCI HCCA | 256 bytes | 256-byte | Need aligned alloc helper |
| OHCI ED | 16 bytes | 16-byte | kalloc(16) + alignment |
| OHCI TD | 16 bytes | 16-byte | kalloc(16) + alignment |
| EHCI Periodic Frame List | 4096 bytes | 4KB | force_alloc_block + map_page |
| EHCI QH | 64 bytes | 32-byte | kalloc(64) + alignment |
| EHCI qTD | 32 bytes | 32-byte | kalloc(32) + alignment |
| xHCI rings | 4096+ bytes | 64-byte (TRBs are 16B) | Page allocation |
| xHCI DCBAA | N × 8 bytes | 64-byte | Page allocation |
| Transfer buffers | Variable | 4KB for bulk/iso | kalloc or page alloc |
11.3 Alignment Helper¶
{ Allocate size bytes with specified alignment }
function kalloc_aligned(size : uint32; alignment : uint32) : Pointer;
var
raw : Pointer;
aligned : uint32;
overhead : uint32;
begin
overhead := alignment + sizeof(uint32);
raw := Pointer(kalloc(size + overhead));
aligned := (uint32(raw) + overhead + alignment - 1) AND (NOT (alignment - 1));
{ Store original pointer just before aligned address for kfree }
PUint32(aligned - 4)^ := uint32(raw);
kalloc_aligned := Pointer(aligned);
end;
procedure kfree_aligned(p : Pointer);
var
raw : Pointer;
begin
raw := Pointer(PUint32(uint32(p) - 4)^);
kfree(void(raw));
end;
This utility should go in usbtypes.pas or a dedicated usbmem.pas.
12. Transfer Scheduling¶
12.1 Polled Model¶
All four HC types use a polled completion model initially:
- A timer callback (registered with the existing timer/scheduler system) fires periodically
- Calls
usbcore.poll_all()which iterates all registered HCs and callshc^.fnPoll(hc) - Each HC's poll function:
- Checks hardware status register for completion events
- Walks completed TD/TRB chains
- Updates
TUSBTransfer.StatusandTUSBTransfer.ActualLen - Frees completed TDs/TRBs
- For hub interrupt endpoints: checks for port status changes, triggers enumeration/removal
12.2 Transfer Submission Flow¶
Class Driver usbcore HC Driver
│ │ │
├─ usb_interrupt_transfer() ──►│ │
│ ├─ build TUSBTransfer ──────►│
│ │ hc^.fnSubmit(transfer) │
│ │ ├─ Build TD/QH/TRB chain
│ │ ├─ Link into schedule
│ │ ├─ Ring doorbell (xHCI)
│ │◄──────────── true ─────────┤
│◄──── tsInProgress ───────────┤ │
│ │ │
│ ... time passes ... │ │
│ │ │
│ ├─ poll_all() ──────────────►│
│ │ ├─ Check status register
│ │ ├─ Walk done list
│ │ ├─ Update transfer status
│ │◄──────────────────────────┤
│ │ │
│ (Class driver checks │ │
│ transfer.Status on next │ │
│ poll cycle) │ │
12.3 Interrupt Transfer Resubmission¶
HID devices need periodic interrupt transfers. After a successful completion, the class driver (or a helper in usbcore) automatically resubmits the interrupt transfer to maintain continuous polling. The poll interval is derived from bInterval in the endpoint descriptor.
13. Unit Testing Strategy¶
All phases must include unit tests where appropriate, following the pattern established in strings.pas:
- Each unit that has testable logic exposes a
procedure UnitTest;in its interface - The
UnitTestprocedure uses a localAssert(condition, testName)nested procedure - Tests log via
syslogwith the unit name as tag - A
PrintSummarynested procedure reports passed/failed counts - Tests are called from
kernel.pasafter init (alongsidestrings.UnitTest) - Tests must properly
kfreeany allocated memory to avoid leaks - Since this is bare-metal, tests run at boot — focus on logic that can be validated without hardware (descriptor parsing, data structure manipulation, encoding/decoding, identifier matching)
What to test per phase:
| Phase | Testable Items |
|-------|---------------|
| 0 (Foundation) | usbtypes: kalloc_aligned/kfree_aligned, setup packet building, descriptor size constants |
| 1 (Core) | usbcore: descriptor parsing, address assignment, identifier encoding, endpoint extraction |
| 2-3 (UHCI/OHCI) | TD/QH/ED construction helpers, frame list setup validation, register bitmask helpers |
| 4 (Hub) | Port status bitmap parsing, topology depth calculation |
| 5 (HID) | Boot report parsing (keyboard modifier decode, mouse delta extraction) |
| 6-7 (EHCI/xHCI) | QH/qTD/TRB construction, capability register parsing |
14. Implementation Phases¶
Phase 0: Foundation (Prerequisites)¶
- [x] Create
src/driver/bus/usb/directory structure - [x] Move existing USB/UHCI/OHCI/EHCI/XHCI .pas files to new location
- [x] Update build scripts (
compile_sources.sh) for new paths - [x] Implement
usbtypes.pas— all shared types, constants, HC abstraction record - [x] Implement alignment helper (
kalloc_aligned/kfree_aligned) - [x] Implement
usbtypes.UnitTest— test alignment helpers, setup packet building, constants - [x] Create
usbcore.passkeleton — HC registration, device list, poll stubs - [x] Verify build still succeeds with moved files
Phase 1: USB Core Skeleton¶
- [x] Implement
usb_control_msg()— builds setup packet, submits via HC abstraction - [x] Implement descriptor parsing helpers (device, config, interface, endpoint)
- [x] Implement address assignment logic
- [x] Implement full enumeration sequence (reset → get desc → set addr → get full desc → set config → register device)
- [x] ~~Register a timer/scheduler callback for
poll_all()~~ — DEFERRED: hookingpoll_allinto TMR_0_ISR caused severe UI lag (runs inside IRQ0 with interrupts disabled). Removed for now.usb_control_msgpolls inline. A non-ISR polling approach (e.g. main loop or scheduler task) is needed later. - [x] Implement
usbcore.UnitTest— descriptor parsing, identifier encoding, endpoint extraction (47 tests)
Phase 2: UHCI Host Controller¶
- [x] UHCI register definitions and private data structure
- [x] UHCI init: read BAR4, global reset, allocate frame list
- [x] UHCI skeleton schedule: create QH chain (control, bulk, interrupt)
- [x] UHCI start: write FRBASEADD, set RS bit
- [x] UHCI port detection: read PORTSC, detect connected devices
- [x] UHCI port reset sequence
- [x] UHCI submit_transfer: build TD chain, link to appropriate QH
- [x] UHCI poll: check USBSTS, walk TDs for completion, update transfer status
- [x] UHCI control transfer: implemented in
uhci_submit_control(live test needs real UHCI hardware) - [x] UHCI interrupt transfer: implemented via
uhci_submit_async(live test needs real UHCI hardware) - [x] UHCI bulk transfer: implemented via
uhci_submit_async(live test needs real UHCI hardware) - [ ] UHCI isochronous transfer support (low priority — no current use case)
- [x] UHCI unit tests: TD/QH construction helpers, bitmask encoding (47 tests)
- [ ] Test: enumerate a USB device on real UHCI hardware — VirtualBox never emulates UHCI (always uses OHCI for USB 1.1)
Phase 3: OHCI Host Controller¶
- [x] Extend existing
TOHCI_MMRwith full register set - [x] OHCI init: map MMIO, reset, allocate HCCA (256-byte aligned)
- [x] OHCI ED/TD management: create, link, free
- [x] OHCI skeleton schedule: control/bulk/interrupt/isochronous ED lists
- [x] OHCI start: set operational, enable port power
- [x] OHCI port detection and reset
- [x] OHCI submit_transfer: build TD chain, attach to ED, place in appropriate list
- [x] OHCI poll: read HcDoneHead, process completed TDs
- [x] OHCI control, interrupt, bulk, isochronous transfers
- [x] OHCI unit tests: ED/TD construction, register bitmask helpers (78/78 pass)
- [x] Test: enumerate device on VirtualBox (ICH9 chipset) — VID=80EE PID=0021, address 1
- [x] Fix: DMA virtual→physical address conversion via
vtop()for all HC-visible pointers
Phase 4: Hub Support¶
- [x] Implement
usbhub.pas— hub class driver - [x] Hub detection during enumeration (class $09) via drivermanagement (biUSB, id1=$09)
- [x] GET_HUB_DESCRIPTOR, port power on (SET_FEATURE PORT_POWER per port)
- [x] Hub interrupt IN endpoint polling (status change bitmap)
- [x] Port status change handling (connect, disconnect, reset, overcurrent, enable, suspend)
- [x] Recursive enumeration for devices behind hubs (depth-limited to 5)
- [x] Device removal / cleanup path —
usb_remove_device()withfnDisconnectcallback (implemented in Phase 8) - [x] Topology tracking (parent hub, port, depth via TUSBDevice.ParentHub/ParentPort)
- [x] Root hub emulation consistency — enumerate_device skips HC port_reset when parentHub<>nil
- [x] Hub unit tests: port status bitmap parsing, speed detection, constants (44/44 pass)
Phase 5: USB HID Class Drivers¶
- [x]
usb_keyboard.pas— register with drivermanagement (biUSB, $03/$01/$01) - [x] Keyboard: SET_PROTOCOL(boot), SET_IDLE(0)
- [x] Keyboard: find interrupt IN endpoint, begin polling
- [x] Keyboard: parse 8-byte boot report, translate to
TKeyInfovia HID usage tables - [x] Keyboard: integrate with existing keyboard hook system (
captin_hook, modifier globals) - [x]
usb_mouse.pas— register with drivermanagement (biUSB, $03/$01/$02) - [x] Mouse: SET_PROTOCOL(boot), find interrupt IN endpoint
- [x] Mouse: parse 3/4-byte boot report (buttons, X, Y, wheel) with signed delta extraction
- [x] Mouse: integrate with existing mousestate system (setMousePos, fireMouseEvent, addScroll)
- [x] HID unit tests: boot report parsing — keyboard 62 tests, mouse 33 tests (modifier keys, usage-to-ASCII, deltas, buttons, scroll)
- [x] Test: USB keyboard in VirtualBox (xHCI) — boot protocol keyboard on slot 1, EP1 interrupt transfers, input working
Phase 6: EHCI Host Controller¶
- [x] EHCI capability + operational register mapping
- [x] EHCI init: reset, halt, allocate periodic frame list + async QH
- [x] EHCI QH/qTD management
- [x] EHCI async schedule (control + bulk)
- [x] EHCI periodic schedule (interrupt + isochronous)
- [x] EHCI port routing: CONFIGFLAG, companion controller handoff
- [x] EHCI split transactions for full/low-speed devices behind hubs (TT)
- [x] EHCI start, port detection, reset, enumeration
- [x] EHCI poll: USBSTS, async/periodic doorbell, qTD completion
- [x] EHCI unit tests: QH/qTD construction, capability parsing (90 tests pass)
- [x] Test: USB 2.0 device on VirtualBox (ICH9) — Intel 8086:265C detected, companion routing works
Phase 7: xHCI Host Controller¶
- [x] xHCI capability register parsing (CAPLENGTH, HCSPARAMS1/2, HCCPARAMS1, DBOFF, RTSOFF)
- [x] xHCI init: reset, halt, MaxSlots, DCBAA, scratchpad buffers
- [x] xHCI Command Ring, Event Ring, Transfer Ring allocation (page-aligned, Link TRBs)
- [x] xHCI Interrupter 0 setup (ERST, ERDP, polling mode — no hardware interrupts)
- [x] xHCI start controller (RS, HSEE, no INTE)
- [x] xHCI BIOS/OS handoff (USBLEGSUP extended capability)
- [x] xHCI port detection: PORTSC scan, speed mapping
- [x] xHCI Enable Slot → Address Device commands (Input Context, Slot+EP0 contexts)
- [x] xHCI Configure Endpoint: lazy per-endpoint ring allocation + command for non-EP0 endpoints
- [x] xHCI transfer submission: control (Setup/Data/Status TRBs, SET_ADDRESS interception), bulk/interrupt (Normal TRBs)
- [x] xHCI poll: dequeue Event Ring, process Transfer Events + Command Completion + Port Status Change
- [x] xHCI unit tests: TRB construction, DCI calculation, speed mapping, EP type mapping (119 tests pass)
- [x] Test: USB device on VirtualBox (xHCI enabled) — Intel 8086:1E31 detected, keyboard enumerated + input working
Phase 8: Hardening & Extras¶
- [x] Error recovery: stall handling (CLEAR_FEATURE ENDPOINT_HALT) —
usb_clear_halt()in usbcore - [x] Transfer timeouts —
usb_control_msgusesbios_data_area.Counters.c32(1024 Hz) with 5-second real-time timeout instead of spin-loop - [x] Device disconnect cleanup (free TDs, endpoints, device record) —
usb_remove_device()in usbcore - [x]
USBterminal command for USB device information - [x] Interrupt-driven completion (ISR registered for each HC on PCI interrupt line)
- [x] USB Mass Storage (Bulk-Only Transport) class driver —
usb_storage.paswith CBW/CSW, SCSI (INQUIRY, TEST_UNIT_READY, READ_CAPACITY, READ_10, WRITE_10), storagemanagement integration - [x] Hotplug: ISR-driven detection (OHCI RHSC, EHCI PCD, xHCI Port Status Change TRB) → deferred
usb_check_hotplugin main loop;HotplugArmedguard prevents spurious boot-time events - [x] Disconnect callbacks:
TUSBDisconnectCallbackonTUSBDevice.fnDisconnect, called byusb_remove_devicebefore freeing — keyboard/mouse/storage drivers clean up transfers and deactivate - [x] Dynamic device lists: keyboard, mouse, storage drivers use
lists.paslinked lists (no static array limits); devices freely plug/unplug without slot exhaustion - [x] HID fail-count guard: 3 consecutive transfer failures → self-deactivate (handles race between unplug and deferred hotplug processing)
- [x]
usb_bulk_transfer_wait— blocking bulk helper with timer-based timeout, used by mass storage BOT - [ ] Code audit: verify all kalloc/kfree pairs, no leaks
14. Progress Tracker¶
| Phase | Status | Notes |
|---|---|---|
| Phase 0: Foundation | Complete | Files moved to src/driver/bus/usb/, usbtypes.pas + usbcore.pas created, build scripts updated, 71 unit tests passing (51 usbtypes + 20 usbcore) |
| Phase 1: USB Core | Complete | Descriptor parsing helpers, control/bulk/interrupt transfer API, inline polling |
| Phase 2: UHCI | Complete | Full UHCI driver with QH/TD, registers, schedule, control+bulk+interrupt submit, poll — 47 unit tests pass. Live testing requires real UHCI hardware (VirtualBox always uses OHCI for USB 1.1, never UHCI). Isochronous not yet implemented (low priority). |
| Phase 3: OHCI | Complete | Full OHCI driver: HCCA+ED+TD, MMIO, reset/start/stop, control+async submit, poll with TD chain cleanup, vtop() DMA fix — 78 unit tests pass, VirtualBox ICH9 enumerates VID=80EE PID=0021 at address 1 |
| Phase 4: Hub Support | Complete | usbhub.pas hub class driver: hub descriptor, port power, port reset, status change polling, recursive enumeration, depth limit — 44 unit tests pass. Device removal via usb_remove_device() + fnDisconnect (Phase 8). |
| Phase 5: HID Drivers | Complete | usb_keyboard.pas (62 tests) + usb_mouse.pas (33 tests): HID boot protocol, usage-to-ASCII tables, signed delta extraction, mousestate/keyboard hook integration. VBox live test pending (no HID keyboard/mouse hardware attached). |
| Phase 6: EHCI | Complete | Full EHCI driver: capability/operational registers, QH+qTD 32-byte aligned, async+periodic schedule, BIOS legacy handoff (EECP), port reset with companion controller routing (low-speed/not-enabled → OHCI), control+bulk+interrupt submit, poll with qTD chain completion — 90 unit tests pass. VBox ICH9 live: Intel 8086:265C detected, CONFIGFLAG routes ports, full-speed devices correctly handed off to companion OHCI. |
| Phase 7: xHCI | Complete | Full xHCI driver: capability parsing, ring-based model (Cmd/Evt/Transfer rings), BIOS handoff, DCBAA, scratchpad, polling mode (no IRQ), Enable Slot + Address Device + Configure Endpoint, control+bulk+interrupt submit, event ring poll — 119 unit tests pass. VBox xHCI live: Intel 8086:1E31, keyboard on slot 1 with lazy EP configuration, input working. |
| Phase 8: Hardening | Complete | usb_clear_halt() stall recovery, usb_remove_device() with fnDisconnect callback, USB terminal command, interrupt-driven completion for all 4 HCs, completion hooks for HID drivers, polling removed from kernel main loop. Timer-based transfer timeouts (5s via bios_data_area.Counters.c32). USB Mass Storage BOT driver (usb_storage.pas) with SCSI over bulk endpoints + storagemanagement registration. Hotplug: ISR sets PortChangePending (guarded by HotplugArmed), deferred usb_check_hotplug in main loop. Disconnect callbacks clean up class driver state. Dynamic linked lists replace static arrays — unlimited plug/unplug cycles. HID fail-count self-deactivation (3 strikes). Remaining: kalloc/kfree audit. |
Appendix A: USB Class Codes (Reference)¶
| Class | Description | Subclass examples |
|---|---|---|
| $00 | Use interface descriptors | — |
| $03 | HID | $01 Boot, Protocol: $01 Keyboard, $02 Mouse |
| $08 | Mass Storage | $06 SCSI, $50 Bulk-Only |
| $09 | Hub | $00 Full-speed, $01 Hi-speed single TT, $02 Hi-speed multi TT |
Appendix B: VirtualBox USB Testing Notes¶
- PIIX3 chipset → emulates OHCI + EHCI (VirtualBox never emulates UHCI)
- ICH9 chipset → emulates OHCI + EHCI companion pair
- xHCI → optional, enable in VM settings (USB 3.0 controller)
- USB device passthrough or VirtualBox's emulated USB tablet/keyboard for testing
- Serial output via
Get-Content "C:\Temp\asuro.log"for debug logging - VirtualBox USB filters can attach host USB devices to the VM
Appendix C: Known Issues & Future Work¶
- UHCI live-hardware testing: VirtualBox never emulates UHCI (always uses OHCI for USB 1.1 regardless of PIIX3/ICH9 chipset). UHCI driver code is complete with 47 unit tests but needs real Intel ICH southbridge hardware (Pentium 4 era) for end-to-end testing.