Skip to content

OHCI: fix ALi M5237 (10b9:5237) init — enumeration now works on real hardware - #3

Draft
LXXero wants to merge 21 commits into
netrunner01:masterfrom
LXXero:fix/ali-m5237-fminterval-write
Draft

LXXero wants to merge 21 commits into
netrunner01:masterfrom
LXXero:fix/ali-m5237-fminterval-write

Conversation

@LXXero

@LXXero LXXero commented Sep 14, 2026

Copy link
Copy Markdown

Summary

On the ALi/ULi M5237 OHCI controller (PCI 10b9:5237 — the USB block in the ALi M1543C southbridge) the driver reported "no USB device found": the root hub never enumerated anything. This fixes the three OHCI init bugs behind that. A USB HID mouse now enumerates fully on real M5237 hardware.

Test hardware

ASUS P5A-B (Super Socket 7), ALi M1543C, VIA C3, Windows 98 SE, real-mode DOS. Diagnostics captured over COM1 (DEBUG build).

Root cause & fixes (all USBDDOS/HCD/ohci.c)

The existing "Gap 6" quirk for this chip skipped writing HcFmInterval, not just reading it, at three sites (InitController, its INITRESET retry, OHCI_ISR recovery), and its substitute default 0x2EDF had no FSMPS. After HCR the register stayed 0x00002EDF, the FSMPS verify failed, and the retry couldn't help because it skipped the write too. Real silicon shows the post-reset read is fine (Linux's ohci_run/periodic_reinit read+write it here); only the pre-reset handoff read is worth avoiding.

  • Gap 6 — keep the pre-reset read-skip, but always write a full HcFmInterval after HCR: FIT | FSMPS(FI)<<16 | FI = 0xA7782EDF (FSMPS = (6*(FI-210))/7, OHCI 1.0a 7.3.1 / Linux FSMP()).
  • Gap 6b — mirror Linux's unconditional OHCI_QUIRK_INITRESET for this chip: reprogram HcFmInterval (toggling FIT, as periodic_reinit) + HcPeriodicStart after the controller reaches USBOPERATIONAL.
  • Gap 6c (all chips) — always assert SetGlobalPower before the per-port SetPortPower loop. The M5237 reports PowerSwitchingMode=1 but PortPowerControlMask=0 (HcRhDescriptorB=0), so its ports are gang-controlled and the per-port writes were no-ops; PPS stayed 0 and no bus-powered device could be seen. Linux ohci_run() writes RH_HS_LPSC unconditionally.

Evidence (COM1, real M5237)

  • alpha.1.1: FmInterval=00002edf ... didn't latch; INITRESET retry / still won't latch, no device
  • Gap 6: latch errors gone
  • Gap 6b: HcControl=0xBC, FmInterval=0x27782EDF, frame timer advancing — but Port1=Port2=0 (PPS=0)
  • Gap 6c: Port1=0x00000100 (PPS=1), Port2=0x00010301 (CCS=1, low-speed), then full enumeration — set address, descriptors, config 1, 2× class-3 endpoints, HID boot protocol, driver started.

DEBUG builds now also log a pre-enumeration register snapshot.

Scope / not addressed here

This fixes OHCI enumeration. It does not touch the PS/2 8042 bridge (USB_HID_Mouse_GenerateSample): on this box, after enumeration, delivering samples via 8042 cmd 0xD3 hard-hangs the machine — the keyboard port is left disabled (0xAD) and the while(inp(0x64)&…) waits are unbounded, so it spins forever if nothing drains port 0x60. That appears to be a separate, pre-existing issue and is left for a follow-up.

Developed with AI assistance (noted in the commit trailer).

netrunner01 and others added 18 commits May 23, 2026 19:11
For full-speed and low-speed interrupt/iso endpoints, bInterval is a
LINEAR polling period in milliseconds per USB 2.0 section 9.6.6
(range 1..255), not a log-scale exponent the way it is for high-speed
endpoints.  EHCI's periodic-schedule InterruptTail[] is indexed by
log2 of the polling interval -- slot i polls at 2^i frames = 2^i ms.

Pre-patch, the full-speed branch treated bInterval as a direct slot
index after just clamping to EHCI_INTR_COUNT (= 8).  A typical HID
keyboard with bInterval=10 (10ms requested) was placed on slot 7
(128ms polling) -- 12.8x slower than the device requested.  Keys
weren't necessarily silent, just laggy / under-sampled.

This was uncovered during the Gap-J investigation (HID interrupt-in
via EHCI high-speed hub doesn't deliver keystrokes).  H5 is a real
bug regardless of whether it's the primary cause of the Gap-J
keyboard-non-functional symptom -- a device polled at 128ms instead
of 10ms is degraded UX even when transfers otherwise succeed.

Fix: compute floor(log2(bInterval)) for the FS/LS path and use
that+1 as the clamped value.  Examples after fix:
  bInterval=10  -> slot 3 (8ms)
  bInterval=8   -> slot 3 (8ms)
  bInterval=1   -> slot 0 (1ms)
  bInterval=255 -> slot 7 (128ms, clamped)

Closest-power-of-2-not-exceeding the request, which is the
conventional EHCI scheduler placement for non-aligned intervals.

High-speed bInterval handling is unchanged.  It has its own quirk
(2x over-polling vs USB spec due to a -4 vs -3 exponent offset) but
that's harmless (extra bandwidth, no functional issue) and out of
scope for this commit.

Also: tighten the stale assertion at EHCI_RemoveEndpoint's INTR/ISOC
branch from '<= 11' (correct when EHCI_INTR_COUNT was 11) to
'>= 1 && <= EHCI_INTR_COUNT' (currently 8).  Adjacent dead-code
cleanup since the array bounds and the assertion got out of sync
during an earlier refactor.

Refs: USB 2.0 spec section 9.6.6 (endpoint descriptors / bInterval
semantics by speed)
Refs: EHCI 1.0 spec section 4.2 (Periodic Schedule)
Refs: USBDDOS Gap-J diagnostic investigation (May 2026)

Signed-off-by: Arthur Clark <tekguru@tekguru.us>
F-RECAP-1: USB_MSC_InitDevice computed a /1024 MaxLBA for capacity display and clobbered the same field, breaking later bounds checks for small media; introduce MaxLBA_K local.
F-AUDIT-1: DPMI_DMAMalloc fixed at source (no offset arithmetic on NULL malloc return).  Ten callsites in hub.c, ohci.c, uhci.c, ehci.c, usb.c converted from assert(p) (no-op in RELEASE) or unchecked deref to graceful if-null return + _LOG.
F-AUDIT-3: five printf sites in MSC install/mount/teardown paired with _LOG so error/success info appears in COM1 regardless of CTTY state.  Includes brace fix preventing if/else regression.
F-AUDIT-2 (visibility half): when USB_InitDevice rejects a device because the HC or global capacity is exhausted, emit a _LOG line identifying which limit was hit and on which port. Helps users diagnose silent enumeration failures in hub configurations.

The original F-AUDIT-2 also raised USB_MAX_DEVICE_COUNT from 4 to 8 on Watcom/Borland builds; that change was found to break RELEASE-watcom on no-device tests (any increase above 4 triggers a silent hang in usb.c init flow). Deferred to a separate investigation branch pending root-cause analysis. Binary is byte-identical to stock alpha.3 in RELEASE; new strings appear only in DEBUG builds.
F-MEMMGR-DOC: documents the DPMI host requirement difference between USBDDOS.EXE (Watcom, embedded PM) and USBDDOSP.EXE (DJGPP, needs external DPMI host); CWSDPMI tight-XMS failure mode and resolutions; per-DOS table for FreeDOS 1.3/1.4, MS-DOS 6.22/7.x, DR-DOS 7.x, PC DOS 2000.
EHCI: fix periodic schedule slot index for full-speed/low-speed bInterval
MSC: preserve un-scaled MaxLBA for sector bounds checks
OOM hardening: DMAMalloc returns NULL cleanly, callsites check
MSC: pair install-path printfs with _LOG for COM1 capture
usb: log USB device-capacity rejects to COM1
README: add Memory managers and XMS section
… correct wrong-constant guard

The free-slot loop in USB_InitDevice had no bounds check: with all slots
valid it indexed one past Devices[] and memset an adjacent function-pointer
table, GP-faulting on the 5th concurrent device (docking station). Guard
the scan so a full pool refuses enumeration with a log line instead of
corrupting memory. Raise USB_MAX_DEVICE_COUNT to 8 for the 16-bit build
(~576 bytes resident, measured) to cover dock topologies. Remove the dead
DeviceCount field whose guard could never fire, and correct the remaining
guard to HCD_MAX_DEVICE_COUNT. Field-validated on IBM 300GL dock (Issue crazii#2).
Bounded control-transfer wait + UHCI abort (fixes enumeration hang on NAK-forever devices)
Device pool: bounds-guard the free-slot scan, raise 16-bit pool 4->8, correct wrong-constant guard
DPMI: defer nested hardware IRQs to avoid reentering the non-reentrant V86 mode switch (fixes GP fault)
@LXXero
LXXero marked this pull request as draft September 14, 2026 05:22
netrunner01 and others added 3 commits September 13, 2026 23:30
…der, port power

The Gap 6 quirk for the ALi M5237 skipped not just reading HcFmInterval but
also writing it, at three sites (InitController, its INITRESET retry, and the
OHCI_ISR unrecoverable-error recovery), and its substitute default 0x2EDF had
no FSMPS. After HCR the register stayed 0x00002EDF, the FSMPS verify failed,
the retry could not help because it skipped the write too, and the root hub
never enumerated anything ("no USB device found"). Real M5237 silicon shows
the post-reset read is safe (Linux's ohci_run/periodic_reinit reads and writes
it on this chip); only the pre-reset handoff read is worth avoiding.

Three changes, confined to OHCI_HasFmIntervalLockup() chips except where noted:

- Gap 6: keep the read-skip but always write a full HcFmInterval after HCR:
  FIT | FSMPS(FI)<<16 | FI = 0xA7782EDF (FSMPS = (6*(FI-210))/7 per OHCI
  1.0a 7.3.1 / Linux FSMP()). FIT is a guaranteed toggle since HCR clears it.
- Gap 6b: mirror Linux's unconditional OHCI_QUIRK_INITRESET for this chip -
  reprogram HcFmInterval (FIT toggled, as periodic_reinit) and HcPeriodicStart
  right after the controller goes USBOPERATIONAL.
- Gap 6c (all chips): always assert SetGlobalPower with a plain W1S write
  before the per-port SetPortPower loop. With PowerSwitchingMode set, per-port
  writes only affect ports whose PortPowerControlMask bit is set (OHCI 7.4.3);
  the M5237 reports PSM=1 with PPCM=0 (HcRhDescriptorB=0), so its ports are
  gang-controlled, PPS stayed 0 and a bus-powered device could never be seen.
  Linux ohci_run() writes RH_HS_LPSC unconditionally.

Debug builds also log a pre-enumeration snapshot (HcControl, HcCommandStatus,
HcFmInterval, HcPeriodicStart, HcFmNumber sampled twice 2 ms apart, both root
hub descriptors, HcRhStatus and both HcRhPortStatus registers).

Verified on an ASUS P5A-B (ALi M1543C, Win98 SE real-mode DOS) with logs
captured over COM1. Original alpha: "FmInterval=00002edf ... didn't latch;
INITRESET retry / still won't latch", no device. Gap 6 alone: latch errors
gone, still no device. Gap 6b: HcControl=0xBC, FmInterval=0x27782EDF,
FmNumber advancing, but Port1=Port2=0x00000000 (PPS=0). With Gap 6c:
RhDescB=0x00000000, Port1=0x00000100 (PPS=1), Port2=0x00010301 (CCS=1,
PPS=1, low-speed), and a USB HID boot-protocol mouse enumerates fully
(address set, descriptors read, config 1, two class-3 endpoints, boot
protocol, HID driver started, driver goes resident).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012vCAmSU6DrcjpXWvjyXcPd
…the packet instead of hanging the machine

The keyboard/mouse bridge injects bytes into the 8042 with cmd D2/D3 and then
spins in unbounded while loops until the resident PS/2 driver's IRQ handler
reads port 60h. The mouse finalizer does this with every IRQ except 12 masked
and the keyboard port disabled, so if the byte is never drained (no PS/2 mouse
driver resident, its aux-IRQ enable cleared by a driver re-probe, a KBC that
doesn't raise IRQ12 for D3, ...) the box hard-hangs: no keyboard, no
Ctrl-Alt-Del. Seen on real hardware (ASUS P5A-B, ALi M1543C KBC) right after
the first successful OHCI enumeration on that chipset.

Bound all three waits (~256K port reads, >=250ms on any PC). On timeout the
mouse path drains the stuck byte, aborts the rest of the packet, re-enables the
keyboard port and restores the IRQ mask; the DEBUG build logs the drop count to
COM1 so a non-draining bridge is diagnosable instead of fatal.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012vCAmSU6DrcjpXWvjyXcPd
@LXXero
LXXero force-pushed the fix/ali-m5237-fminterval-write branch from 5ad896f to 8b27d2e Compare September 14, 2026 05:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants