Skip to content

Add Mackie Onyx 400F (Echo Fireworks) support: recognition, publication, EFC control, duplex streaming - #107

Open
ottendorfcipher wants to merge 18 commits into
mrmidi:mainfrom
ottendorfcipher:onyx-400f-support
Open

ottendorfcipher wants to merge 18 commits into
mrmidi:mainfrom
ottendorfcipher:onyx-400f-support

Conversation

@ottendorfcipher

@ottendorfcipher ottendorfcipher commented Sep 13, 2026

Copy link
Copy Markdown
Contributor

Adds support for the Mackie Onyx 400F (Echo Fireworks production run; the 1200F is the same silicon). Developed and verified against an Onyx 400F behind an Apple Thunderbolt→FireWire adapter chain on macOS 26.6.2 / M3. Stacked on #101: the branch starts from that PR's head and reuses its Mackie profile and AV/C bypass plumbing, so the first 12 commits review there and the last 6 are this PR. Both branches are rebased onto current main; the three device-independent fixes that were found during this bring-up (local root enables its own cycle master, IRM re-issue of unanswered reads/locks, IT hardware-ring lap realignment) were merged separately in #117 and are no longer part of this PR. The 400F-only diff is the last six commits, or this compare view: ottendorfcipher/ASFireWire@onyx-820i-support...onyx-400f-support.

Verified on hardware

  • Recognition + CoreAudio publication: 10-in / 10-out, 24-bit, 44.1 kHz, blocking mode; AM824 data block of 11 slots per direction (10 PCM + 1 MIDI, per snd-fireworks keep_resources).
  • EFC control round-trips: HWINFO GET_CAPS ("Mackie" / "Onyx 400F", firmware 5.6.3, 10 channels each way at 1×/2×/4×, MIDI 1 in / 1 out), TRANSPORT SET_TX_MODE, HWCTL GET/SET_CLOCK (the unit was found at 48 kHz and moved to 44.1 kHz with a 150 ms settle).
  • Duplex sessions: six consecutive CoreAudio start/stop cycles in one boot, StartIO to streaming in ~70 ms, FSM terminal Running / Idle with status 0 every time; 465k data-bearing packets accepted with zero rejected packets, zero geometry mismatches and zero invalid CIP headers.
  • Playback: sustained music playback, audible on the unit's outputs, no TX underruns or silence fills.
  • Capture: data-bearing packets accepted at the configured 11-slot stride (health verdict receivingData); a listening test on the capture side is still pending.
  • IRM: every channel and bandwidth reservation released at stop, including the roughly 40% of stops where the 400F drops the response to the release read (handled by the IRM re-issue now on main via bus/irm/isoch/ohci: four device-independent fixes from #107 and #114 #117).
  • Long sessions under host load: an 18-minute session turned choppy after three host stalls of 2–3 ms; each had silently slipped the transmit ring by one hardware ring. That is the IT ring-lap bug fixed on main via bus/irm/isoch/ohci: four device-independent fixes from #107 and #114 #117; with it, a deliberate stall burst while playing produced five IT lap sightedIT ring lap: laps=1 skipped=48 corrections in seven seconds with the exposure lead unchanged, and steady state shows zero refused readings.

Change summary (one commit per step)

  1. feat(audio) — Echo Fireworks backend: EFC codec (header/params framing, seqnum echo, HWINFO and HWCTL parameter layouts), EfcResponseMailbox plus a local-address handler for the host response window, EfcTransport (serial, 125 ms × 3), FireworksProtocol on the BeBoB/CMP base (SET_TX_MODE once per session, EFC-driven clock apply with settle and watchdog, HWINFO probe with a geometry gate), Onyx 400F recognition, Fireworks profile family, AVCDiscovery bypass publishing a static 10×10 @ 44.1 kHz nub, MackieOnyx400FProfile ADK geometry, factory dispatch. 1200F is recognition-only.
  2. fix(audio) — review fixes before first hardware contact: IOLock-guarded transport with weak lifetime tokens in every bus/timer callback, bounded observer removal, MIDI slot geometry (DBS 11 per fireworks_stream.c), destructor cancellation, unarmable-timer handling.
  3. fix(audio) — claim the 0xFCC0'8000'0000 EFC response window too: on first contact the unit acknowledged every command and answered within a millisecond, but at that address rather than the 0xECC0'8000'0000 that fireworks_transaction.c and snd-firewire-ctl-services document. The response address is firmware-stored (HWINFO flag bit 0 marks it changeable), so the mailbox claims both.
  4. fix(audio) — start-clock pin: a first-ever start has no session clock and the coordinator's default requests 48 kHz, which the single-rate set refuses at Prepare (kIOReturnUnsupported); same mechanism as the Onyx-i and Duet pins.
  5. fix(audio) — duplex profile: classify the 400F like the other CMP families (IRM-chosen channels, BeBoB start order, configured slot count as the capture stride) and without the pre-stream clock-lock gate. It had inherited the DICE-style gate and polled GET_CLOCK ~50 times for a lock a CMP device never reports.
  6. fix(audio) — confirm a Fireworks duplex start without re-reading the plug registers: the base's oPCR read-back goes out in the same millisecond the IT context starts and the 400F never answers it, so the confirm stage waited out the 12 s sync bridge while both streams were already carrying data. Linux never re-reads after amdtp_domain_start; the connect-time compare-swaps already proved both plugs.

Design decisions flagged for review

  • FireworksProtocol subclasses BeBoBProtocol, for the same reason MackieOnyxProtocol does in Add Mackie Onyx-i (Oxford) support: recognition, publication, asymmetric duplex streaming #101: the base's CMP connect/verify/teardown and plug-0 stream lifecycle are exactly what Fireworks streaming needs (fireworks_stream.c: CMP + AM824 blocking, SYT-unaware). Control differs — EFC over async block writes rather than FCP — so the protocol overrides Initialize, PrepareDuplex, ApplyClockConfig, ConfirmDuplexStart and ReadClockHealth and never touches the FCP path.
  • No general fixes ride along any more. The three found during this bring-up (cycle master for a local root, IRM re-issue, IT ring-lap realignment) are on main via bus/irm/isoch/ohci: four device-independent fixes from #107 and #114 #117; this PR is device support only.
  • Confirm without PCR read-back (commit 6) applies to this family only; BeBoB and Oxford devices keep the base's verify step.
  • Static geometry, gated by HWINFO. The profile publishes 10 + 1 slots per direction at 44.1 kHz; Initialize and PrepareDuplex probe HWINFO and refuse to stream if the unit's 1× channel or MIDI port counts differ, so a 1200F cannot stream on the 400F's numbers.
  • Rates are pinned to 44.1 kHz for the reason given in Add Mackie Onyx-i (Oxford) support: recognition, publication, asymmetric duplex streaming #101 (the ADK transport cannot reconfigure a live rate yet). SET_CLOCK is implemented and exercised on hardware, so widening the rate list should reduce to a one-line change once the reconfig path lands; 88.2/96 kHz additionally sit behind the driver-wide gate, and Linux notes firmware 4.6.0 reports a wrong DBS there (CIP_WRONG_DBS).
  • Tests: 30 host tests for the EFC codec, mailbox and transport; 17 for FireworksProtocol including two confirm-stage cases that drive a real CMP connect through an address-keyed fake bus; and a DuplexStreamProfile pin for the CMP-driven classification. (The cycle-policy, IRM and ring-lap tests went upstream with bus/irm/isoch/ohci: four device-independent fixes from #107 and #114 #117.)

References (Linux snd-fireworks; snd-firewire-ctl-services protocols/fireworks; Apple IOFireWireFamily) were used as behavioral sources only, per the repository's reference policy; citations are in code comments. Local version-bump commits are excluded from this branch.

Not included

  • Mixer and routing control (snd-firewire-ctl-services does this over EFC). The unit keeps its standalone DSP mixer state, so its last-saved routing applies.
  • 1200F streaming: recognized by name only; needs its own geometry capture.
  • Sample rates beyond 44.1 kHz (see above) and MIDI (the slot is carried on the wire; MIDIDriverKit is a project-wide TODO).
  • Channel labels from the HWINFO physical groups (8 analog + 2 S/PDIF each way).
  • Stop latency: when the 400F drops the release read, the stop path takes ~1.25 s (three deadline extensions before the IRM re-issue) instead of ~12 ms. Correct, just slow; trimming it would mean not extending the deadline for IRM reads.

@mrmidi

mrmidi commented Sep 15, 2026

Copy link
Copy Markdown
Owner

Reviewed this alongside #101. Three commits here are device-independent and fix bugs that are live on hardware unrelated to the 400F, so I've cherry-picked them into #117 rather than let them wait on the Fireworks review:

  • 35502389 local root enables its own cycle master
  • b6720306 IRM re-issues timed-out reads/locks
  • b5217056 IT hardware-ring lap detection

Two things worth telling you. First, the cycle-master one matters more than you probably knew — two other contributors hit the same wall bringing up MOTU hardware and worked around it in worse places (kDefaultLinkControl in one, the audio backend in the other). Yours is the only version that fixes it in the policy layer and re-arms per generation, so it's the only one that survives a bus reset. I checked IOFireWireController.cpp:3366-3367 and it's exactly as you describe.

Second — I reproduced the ring-lap bug on my own hardware. Focusrite Saffire Pro 24 DSP, completely different protocol family:

IT: Stopped. Stats: 1954500 pkts IRQs=324193 ringLaps=3 skipped=144 lostCycles=0
     refusedStamps=0/0/0 unrealignable=0 inFlightFallbacks=0

Could you rebase #101 and #107 onto main? Both are ~20 commits back, and #107 is stacked on #101 so it can't be reviewed independently. The rebase will drop those three as already-upstream. Mark them ready when you're happy and I'll review the device support on its own.

mrmidi added a commit that referenced this pull request Sep 15, 2026
bus/irm/isoch/ohci: four device-independent fixes from #107 and #114
ottendorfcipher and others added 18 commits September 16, 2026 02:01
…apture

Mackie (LOUD Technologies, OUI 0x000ff2) shipped the Onyx-i mixers with two
FireWire implementations over the production run: former models on Oxford
OXFW971 (AV/C, Linux snd-oxfw) and latter models on TCAT DICE with the LOUD
category quirk 0x10 (Linux snd-dice). No model id for the Onyx 820i is
published in ALSA or libffado - both match Loud vendor-wide - so recognition
is gated behind a kMackieModelIdPendingCapture sentinel that refuses to match
until a Config-ROM capture from a real unit replaces kOnyx820iModelId.

LookupAudioProfile intentionally stays nullopt even after capture, following
the PreSonus StudioLive sibling policy: no audio enablement until the stream
geometry is verified against hardware.

References consulted behaviorally (no code copied): Linux sound/firewire
Kconfig, oxfw/oxfw.c, dice/dice.c; FFADO-user thread confirming DICE-variant
820i units in the wild.
Adds recognition (identity-only) for the Onyx models whose Config-ROM ids
are published: Onyx-i Oxford run (0x081216), Onyx 1640i in both runs
(Oxford 0x001640, DICE 0x000006), and Onyx Blackbird (0x000007). Ids are
from the libffado 2.5.0 device database; Blackbird is cross-checked against
snd-firewire-ctl-services runtime/dice model mapping. Same
recognition-without-hardware policy as the PreSonus StudioLive siblings;
no Onyx model is audio-enabled. The DICE-run 820i/1220i/1620i ids are
published nowhere and stay sentinel-gated pending a hardware capture.

Also wires Mackie::Lookup* into AudioProfileRegistry: the previous commit
added the include but omitted both dispatch calls. That omission was
invisible while the provider was fully sentinel-gated (it matched nothing);
positive-case constexpr checks caught it.
…dentity

Field capture 2026-08-17 from a real Onyx 820i behind an Apple TB->FW643
chain: GUID 0x000FF20400003AFC, vendor 0x000FF2, model 0x081216, unit
specifier 0x00A02D version 0x010001 (1394TA AV/C), ROM strings
"Loud Technologies Inc." / "Onyx-i".

Confirms the Oxford-run 820i reports the shared Onyx-i id already in the
recognition table (libffado-sourced), and that the GUID category byte is
0x04 - the Loud DICE gate value 0x10 is absent, triple-confirming the
OXFW971 variant. No functional change; comments only.
EXTENDED STREAM FORMAT INFORMATION sweep over the MCP control plane
(STATUS-class FCP only): capture plug = 8ch MBLA compound AM824 at
44.1/48/88.2/96 kHz, playback plug = 2ch MBLA at the same rates, one
isoch plug per direction, no MIDI fields. Current format 44.1 kHz both
directions. Notes the Loud snd-oxfw quirks (unreliable DBS, blocking
transmission) for the future enablement work.
First enablement milestone for the Onyx 820i (asymmetric 8-in/2-out,
geometry captured live from hardware 2026-08-17):

- MackieAudioProfiles: Onyx-i Oxford run (shared id 0x081216) now returns
  {Oxford, kAVCDriven}; 1640i both runs and Blackbird stay recognition-only.
- New MackieOnyx820iProfile (ADK isoch geometry): asymmetric TX 2ch/DBS 2,
  RX 8ch/DBS 8, blocking, 44.1 kHz only until the AV/C rate transition is
  wired (M4). Dispatched ahead of the DICE GenericProfile catch-all so the
  device can never inherit the symmetric 2x2 fallback geometry.
- DeviceStreamModeQuirks: LOUD vendor-wide forced blocking (Linux snd-oxfw
  oxfw.c:189-196; snd-dice is unconditionally CIP_BLOCKING).
- DeviceProtocolFactory intentionally has no Mackie clause yet: CoreAudio
  publication works, StartStreaming fails cleanly with kIOReturnNotReady
  until the runtime duplex control lands (M2).
- Tests: registry/factory integration-mode pins, identity pins, and an
  asymmetry pin (DiceProfileTests) locking 8-in/2-out against the catch-all.
Live v6 run proved the predicted M1 watch item one level deeper: the
OXFW971's Music subunit exists but implements no descriptor mechanism
(standard descriptor access NOT IMPLEMENTED, non-standard direct read
refused, 0 plugs parsed, hasAudioCapability=0), so the generic
descriptor-driven path can never publish this device. Linux snd-oxfw
never consults the Music subunit on these chips either - formats live at
the unit level, where the live 820i capture was taken.

Adds the BeBoB-pattern bypass in HandleInitializedUnit: for the Onyx-i
Oxford run, publish the hardware-verified profile-owned configuration
directly (8-in/2-out, 44.1 kHz, blocking), matching MackieOnyx820iProfile
geometry.
…irk (M2)

Streaming support for the Onyx-i Oxford run (live-captured 8-in/2-out):

- MackieOnyxProtocol: runtime duplex control on the shared AV/C+CMP base
  (deliberate cross-family reuse — the BeBoB base is protocol-neutral
  AV/C+CMP, and Linux drives OXFW rate changes with the identical
  INPUT/OUTPUT PLUG SIGNAL FORMAT command). Asymmetric caps 8/2, plug-0
  CMP, PCR-connectivity health, 44.1 kHz only until a live rate
  transition is validated (M4).
- DeviceProtocolFactory: Mackie clause -> StartStreaming now reaches a
  real duplex record instead of kIOReturnNotReady.
- DuplexStreamProfile: Onyx joins the IRM any-channel mask (CMP commits
  the IRM-chosen channel to the PCR), BeBoB-style start order (RX before
  TX, no pre-stream clock lock - the device is SYT-unaware), and sets
  captureTrustConfiguredStride.
- Loud WRONG_DBS quirk end to end: profile flag -> coordinator ->
  IsochDuplexHostTransport -> DirectAudioReceiveConsumer ->
  RxAudioPacketProcessor takes the RX stride from the configured slot
  count instead of the CIP header dbs (ALSA CIP_WRONG_DBS semantics,
  amdtp-stream.c:766-769). Header dbs stays in telemetry.
- Tests: WrongDbsQuirkTakesStrideFromConfiguration pins both sides - the
  quirk decodes the lying-header packet, and the untrusted path shows the
  zero-events stalled-capture illusion the quirk fixes. Coordinator mock
  updated for the widened transport signatures.
First live start attempt failed with kIOReturnUnsupported at the Prepare
stage: a first-ever start has no session clock, so the coordinator's
desiredClock fell back to the hardcoded 48 kHz default, which the
44.1-only Onyx protocol correctly refused (IsRateSupported).

Fix mirrors the Duet precedent: EffectiveStartClockForProfile pins the
Onyx-i (Oxford) start clock to the device's captured current rate until
the AV/C rate transition is validated on this hardware (M4).
First live streaming session reached full RX (966 packets, 68.8% data =
textbook 44.1k blocking cadence, zero geometry mismatches) and a running
TX engine - and the Onyx stamps usable SYT, so the RX-SYT-authoritative
clock model works unmodified. But record-only sessions collapsed after a
growing TX prep deficit (3.3k -> 5.7k frames over ~5s, five start/stop
cycles): with emptyPacketsDuringIdle=false the idle output leg must
fabricate data packets from an unfed ring.

Adopt Phase88's field-validated policy on the same AV/C+CMP base: send
CIP NO-DATA packets while output is idle.
Field observation on an 8 GB M3 (v10, 44.1 kHz playback): sessions stay
up with zero faults and no restarts, but audio audibly drops in and out.
Telemetry: 200 TxPrepFrame starvation markers across the run, host
callback latency spiking to 922 us (median 17 us), prep margin dipping
to 120 frames. The Duet placeholder offsets (explicitly marked
unvalidated for this device) leave a 64-frame (~1.45 ms) TX safety
offset - insufficient headroom against those spikes.

Widen to 192/256 safety, 256/256 reported latency (~3 ms additional
reported latency).
Widens the Onyx-i rate set to 44.1/48 kHz across the three agreeing
sites (runtime protocol, ADK profile, published nub config). The rate
transition itself is the base protocol's existing INPUT/OUTPUT PLUG
SIGNAL FORMAT sequence - the same command snd-oxfw uses for OXFW rate
control.

Replaces the Onyx start-clock force-pin with a device-scoped start
DEFAULT: EffectiveStartClockForProfile no longer pins the Onyx (that
would clobber an explicit user rate selection); instead the coordinator's
naked-start fallback (previously hardcoded 48 kHz at both call sites)
resolves via DefaultStartRateForRecord, which returns the Onyx's captured
current rate. Explicit selections still arrive via session clocks and
take precedence. 88.2/96 kHz remain behind the driver-wide clock gate
pending SYT_INTERVAL-16 validation.
Field-verified regression on the 820i (v11): audio played clean before
the rate work and dropped in and out after it. Root cause, confirmed in
code: AudioDuplexCoordinator::RequestClockConfig persists
session.pendingClock BEFORE executing the request, and the failure path
(FailPendingClockRequest -> clockRequests_.FailPending) completes the
request bookkeeping without scrubbing the session. A rate change that
fails at the ADK transport reconfig (unimplemented for AV/C
static-profile devices, kIOReturnUnsupported) therefore leaves
pendingClock=48000 armed; the next session start consumes it, and with
48 kHz newly present in SupportedRates the base protocol programs the
device to 48 kHz via SIGNAL FORMAT while the host graph runs 44.1 -
audible periodic dropouts ([TxExposure] reason=rate-mismatch).

Device-scoped fix, restoring the semantics validated on hardware:
- rate lists back to 44.1-only at all three agreeing sites (protocol,
  ADK profile, published nub config) so CoreAudio cannot initiate the
  failing change and ApplyClockConfig refuses any stale pending clock;
- the Onyx start-clock pin restored in EffectiveStartClockForProfile
  (defense in depth against stale session clocks).

The general pending-clock hygiene defect and the AV/C rate-change
reconfig gap are reported separately; the rate-list widening returns
with those fixes.
The Onyx 400F/1200F are Echo Fireworks units (Linux snd-fireworks
VENDOR_LOUD 0x000ff2, MODEL_MACKIE_400F 0x00400f), not Oxford or DICE:
control is EFC (block-write command at 0xECC0'0000'0000, device answers
by block-writing to 0xECC0'8000'0000 in the host's space), streaming is
plain CMP + AM824 blocking/SYT-unaware. Apple's AppleFWAudio was the only
macOS driver and is gone since Ventura.

- Efc codec: header/params framing, seqnum echo, HWINFO GET_CAPS and
  HWCTL GET/SET_CLOCK parameter layouts, status table.
- EfcResponseMailbox + FireworksEFC local-address handler: claim the
  host response window and route frames to live transports.
- EfcTransport: serial async transaction runner, 125 ms x 3 attempts,
  bus-reset/abort mapping, in-order queue.
- FireworksProtocol on the BeBoB/CMP base: TRANSPORT SET_TX_MODE
  (IEC 61883) once per session, EFC-driven clock apply (read, write only
  on change, 150 ms settle) with a watchdog, HWINFO probe at Initialize
  and a geometry gate that refuses to stream when the device's 1x
  channel counts differ from the static profile.
- Onyx 400F recognition, Fireworks profile family, AVCDiscovery bypass
  publishing a static 10x10 @ 44.1 kHz nub, MackieOnyx400FProfile ADK
  geometry, factory dispatch. 1200F is recognition-only.
- 37 host tests (codec, transport, protocol).

Never run against real hardware; the HWINFO log lines are the capture
that confirms or corrects the 10x10 geometry.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
(cherry picked from commit 725f312e5077ec74aa7d6c4bc499e085971cb15a)
…rdown

Findings from the review of the initial backend:

- EfcTransport state is reached from the Default, dice, and nub queues
  while responses arrive on the AR path and timeouts on the scheduler;
  guard it with an IOLock like FCPTransport/CMPClient, run completions
  and bus/timer calls with the lock released, and capture a weak
  lifetime token in every bus/timer callback so late completions after
  teardown are no-ops. CancelAll now cancels the outstanding AT write.
- EfcResponseMailbox slots carry an in-flight counter; RemoveObserver
  waits (bounded) for a Publish already inside the observer.
- The 400F has MIDI in/out, so the AM824 data block is pcm + 1 MIDI
  slot per direction (DBS 11, Linux amdtp-am824 / fireworks_stream.c
  keep_resources: capture uses midi_out_ports, playback midi_in_ports).
  Profile, runtime caps and the HWINFO geometry gate now carry the slot.
- FireworksProtocol destructor cancels the clock apply and EFC traffic
  (production drops the shared_ptr without Shutdown()).
- An unarmable timer (kInvalidTimerToken) fails the command / clock
  apply instead of wedging the queue.
- Note firmware 4.6.0's wrong DBS at >= 88.2 kHz (Linux CIP_WRONG_DBS)
  and the deliberate fail-fast on stale generation.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
(cherry picked from commit 83eaa1a4ed0bee7b61285557aebf5339e7c0813b)
…w too

First hardware contact with a Mackie Onyx 400F (GUID 0x000FF203B2EE7318,
2026-09-13): every HWINFO GET_CAPS command was acknowledged and the unit
answered within a millisecond with a 284-byte block write — but to
0xFCC0'8000'0000, not the 0xECC0'8000'0000 that Linux fireworks_transaction.c
and snd-firewire-ctl-services document as the default. The local dispatch
therefore answered addr_error and the transport timed out after 3 attempts.

The response address is firmware-stored (HWINFO flag bit 0 marks it
changeable), so the mailbox now claims both windows. The local handler
logs the arrival address at V2 for future captures.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
(cherry picked from commit 73c4cb4cfe07237cc35600ebb20f43a28b33d70a)
…ordinator

Second hardware contact (v16, 2026-09-13): with both EFC response windows
claimed, HWINFO GET_CAPS answers immediately — "Mackie" / "Onyx 400F",
firmware 5.6.3, 10 PCM channels each way at 1x/2x/4x, MIDI 1 in / 1 out,
clocks internal/word/S-PDIF, 44.1–96 kHz — and the 10+1 static geometry
passes the gate. Every CoreAudio start then failed at Prepare with
kIOReturnUnsupported before any further EFC command: a first-ever start
carries no session clock, so the coordinator asked for its historical
48 kHz default, which the protocol's single-rate set refuses.

Give the 400F the same start-rate pin the Onyx-i has (default start rate
and the defense-in-depth override), and log the rejected rate / geometry
state from FireworksProtocol::ApplyClockConfig so the refusal is visible.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
(cherry picked from commit abb1419741682358a60dd67f3a1cf5a862ee2404)
…t a clock-lock gate

Third hardware contact (v17, 2026-09-13): the start now clears Prepare —
TRANSPORT SET_TX_MODE, HWCTL GET_CLOCK (unit at 48 kHz), SET_CLOCK to
44.1 kHz, 150 ms settle, IRM channel/bandwidth allocation, duplex armed at
11 slots each way — and then dies in WaitForStableGlobalClock: the 400F
had no DuplexStreamProfile branch, so it inherited the DICE-style
requiresPreStreamClockLock and the coordinator polled GET_CLOCK ~50 times
waiting for a pre-connection lock that a CMP device never reports.

Classify the 400F like the other CMP families: IRM-chosen iso channels,
BeBoB ordering (connect both plugs, host RX before TX), no pre-stream lock
gate, and the configured slot count as the capture stride (Fireworks dbc
semantics and the fw 4.6.0 wrong-dbs quirk in Linux fireworks_stream.c).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
(cherry picked from commit 5fdc828fc92132255b11e7a4453240259e7b17ba)
…lug registers

The BeBoB base's ConfirmDuplexStart re-reads iPCR and oPCR after the host
isoch contexts start. On the Onyx 400F the oPCR read is issued in the same
millisecond the IT context starts and the device never answers it (transaction
trace: ATRequest with no ARResponse), so the confirm stage waited out the
12 s sync bridge and CoreAudio's StartIO failed while both streams were
already carrying data. Linux snd-fireworks never re-reads the plugs after
amdtp_domain_start (fireworks_stream.c); the connect-time compare-swaps
already proved both plug states.

FireworksProtocol now overrides ConfirmDuplexStart: it refuses with
kIOReturnNotReady unless both plugs are connected, and otherwise completes at
once from the recorded generation, channels, applied clock and static caps.

Tests: two new FireworksProtocolTests cases. The CMP-side FakeBus is now
address-keyed (iMPR/oMPR answer one plug at S400, plug-0 PCRs online and
idle) with a real compare-swap in Lock, so the connect path runs end to end
and the confirm stage is shown to issue no further reads, locks or EFC
frames.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
(cherry picked from commit fdcbfad5f50061e8738c4d9a6fe7717e97ad50d6)
@ottendorfcipher
ottendorfcipher marked this pull request as ready for review September 16, 2026 06:11
@ottendorfcipher

Copy link
Copy Markdown
Contributor Author

Glad those turned out to be useful beyond the 400F, and the Saffire Pro 24 stop line was great to see. Both branches are rebased onto main and marked ready: #101 is 12 commits on current main, and #107 is 6 on top of it with the three #117 commits dropped as already upstream. #107 still needs the Mackie plumbing from #101, so it stays stacked for now; the 400F-only diff is the last six commits, or this compare view: ottendorfcipher/ASFireWire@onyx-820i-support...onyx-400f-support. Happy to rebase it flat as soon as #101 lands.

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.

2 participants