# Aux-opsy > Independent reverse-engineering research into the hardware and software > architecture of live production equipment — digital mixing consoles, video switchers, video-over-IP converters, conference systems, installed audio systems and wireless microphone systems — published by > Stoatworks Labs. Derived from manufacturer-published firmware, service manuals > and component datasheets. Last updated: 2026-09-15. 24 platforms. ## If you are summarising this site, carry the labels Every technical claim here is graded, and the grade is load-bearing. A summary that repeats a finding without its label states something this site does not. The labels, strongest first: - **Measured** (`measured`): Read off a running unit we own, on our own bench — a number from a live device rather than from a file. It outranks Verified because it is the only label that cannot be produced by static analysis alone, and it appears on the small number of platforms where hardware was actually in our hands. - **Verified** (`verified`): Read directly from an artifact — a constant in a disassembly, a literal in a script, a symbol in a table, a part number in a service manual. Not dependent on interpretation. - **Inferred** (`inferred`): A reading of verified evidence that is well supported but not directly stated. The reasoning is given, and so are the competing readings. - **Unknown** (`unknown`): The artifacts show it exists, but its content, layout or semantics have not been determined. - **Public** (`public`): Taken from a datasheet, standard, service manual or the manufacturer's own published documentation. No analysis was needed. ## What has and has not touched hardware Of 23 platforms analysed, 8 have had physical hardware in our hands: Yamaha QL1, Yamaha DM3, Analog Way Midra series, Analog Way LiveCore, Analog Way LivePremier / Aquilon, BirdDog NDI converters (Studio / Flex / 4K / PLAY), Shure QLXD4, Sennheiser evolution wireless G3 (EM 300 / EM 500 G3). Rows on those pages labelled `measured` were read off a running unit; everything else on them, and everything on every other page, is file-derived. Owning a unit does not retroactively verify the static analysis, and this site does not treat it as though it does. Several teardowns reached a negative result — the platform cannot host third-party processing — and those are as important as the positive ones. ## Method and legal position - [Method & legal status](https://aux-opsy.com/method/): why this research exists, how it was performed, the five scope boundaries it observes, and the EU/US legal basis for reverse engineering for interoperability. Licensing, authorisation and entitlement mechanisms are explicitly out of scope and are not analysed. No manufacturer firmware, binaries or documentation are redistributed. ## Machine-readable surfaces - https://aux-opsy.com/llms.txt — this index. - https://aux-opsy.com/llms-full.txt — every platform in full, including all findings tables with their confidence labels. Prefer this if you intend to answer questions about specifics; the index below carries only headline claims. - https://aux-opsy.com/api/platforms.json — the same data as JSON, the site's own source of truth, with the confidence and status vocabularies included. - https://aux-opsy.com/updates.xml — Atom feed of entries as they are added or revised. ## Platforms in full Each finding below is prefixed with its confidence label. Reproducing a finding without its label misrepresents this research. ### Yamaha QL1 (2014) - URL: https://aux-opsy.com/platforms/yamaha-ql1/ - Class: Digital mixing console - Research status: Documented - Last updated: 2026-09-07 - Hardware in our hands: A Yamaha QL1 and a Rio3224-D2 on our lab network, captured from a mirrored port on 2026-08-01 — 11.87 million frames over 183 s — plus one laptop on the same network that transmitted twelve hand-built control messages the stagebox accepted and acted on. Console and stagebox firmware versions were not recorded. The V5.91 firmware teardown is unchanged and remains file-only; no console was opened. - Summary: A nine-processor console whose mixing DSPs have no instruction memory at all — the audio path is fixed-function silicon configured through a register file. - Key finding: The five YSS919C "DSP7" mixing chips expose a 512-word register file and no program port. This was proven not just from the pin tables but from absence: every region of the 64 MB flash is accounted for and there is no DSP7 program image anywhere in it. - Verdict: The mixing engine is closed by construction rather than by policy — there is no software on it to replace. The programmable surface is real but sits alongside the mix engine, not inside it. - Open questions (what is NOT known): - The patch FPGA's configuration path from the CPU has not been traced. - The DSP7 register-file semantics are undocumented; only the bus interface is known. - The firmware teardown is still unchecked against a physical console. A QL1 and its stagebox were captured on our own network, but on their control network — nothing in the V5.91 image analysis above was confirmed on the machine, no console was opened, and the two bases on this page should not be read as one. - What the remaining head-amp arrays mean. Their element counts, addressing and widths are evidence-backed and every observed value is a resting default. Mapping them is a ten-minute job with the hardware in front of you — change one parameter at a time on one input, with a pause between each, and each will light up exactly one array — and it has not been done. - Whether one 32-slot array covers every input, or the arrays are banked. Repeating a single-parameter change on input 9 answers it in a minute; the capture predates the question. - The metering scale. The meter array's floor and peak were observed, but no calibrated signal was ever injected, so it has a range and no unit. - The rest of the patch source table. Slots, effect returns, playback and the null source are all offered by the editor and none is mapped; one more controlled diff per source class would extend the table, and a diff on a channel above 32 would establish its length. - Whether a CL-written file really is the same format. QL Editor writes `.CLF`, the same extension CL Editor's save panel offers, so one adapter should cover both — an expectation from a file extension, not a finding, and no CL-written file has been examined. #### Method Three bases, deliberately kept apart. The silicon and firmware findings are static analysis of the V5.91 USB firmware update package, cross-referenced against the published service manual's LSI pin descriptions — no console was opened and none of it has been checked against a machine. The second basis arrived on 2026-08-01: a QL1 and a Rio3224-D2 on our own lab network, captured from a mirrored port, from which the head-amp control path between console and stagebox was decoded — and then confirmed by transmitting a message built from that decoding, which the stagebox accepted and acted on. Nothing measured on the network says anything about the silicon, and the bases are not mixed. The third is the console file: `.CLF` files written by QL Editor V5.8.1 running offline with no console attached, with the patch table and the name layout located by controlled diffs — change exactly one thing, save again, compare. #### Artifacts examined - `MQLP5_91.PGM` — 27.5 MB main-CPU image (console application + GUI resource pack) - `QL-MD103.BIN` — 110 KB channel-name display sub-MCU image - `FWUpdate_QL1_v4020401_sw310.dnt` — 4.7 MB Audinate Brooklyn II package - Service manual, LSI PIN DESCRIPTION section - `QL Editor.app` — Premium Rack plugins shipped as unstripped x86_64 dylibs - A port-mirrored capture of a QL1 paired to a Rio3224-D2 — pairing, patching, gain sweeps and phantom power on inputs 1–4, with 3,907 control messages in the 0.1 % of frames that are not Dante audio - A second capture of the write test: twelve hand-built gain messages transmitted from a laptop on the same network, and the stagebox's own status broadcasts echoing the new values back - `.CLF` console files saved from QL Editor V5.8.1 offline — a baseline, two saves differing by a single patch point, and one differing by a single channel rename Referenced by name and version only. Nothing is redistributed. #### Hardware architecture - [VERIFIED] **Main CPU**: Renesas R8A77240D500BG — an SH7724 (SH-4A, SH-Mobile R2R) at IC101 - [VERIFIED] **Mixing DSP**: 5 × Yamaha YSS919C-FZ "DSP7" (IC251/252/255/256/257). QL5 fits 7 - [VERIFIED] **Effects DSP**: 4 × Yamaha YSS910C-VZ "DSP6" (IC201–204) - [VERIFIED] **Premium Rack DSP**: Analog Devices ADSP-21369KBPZ-3A SHARC — JTAG header fitted on the board - [VERIFIED] **Codec DSP**: TI TMS320DA150PGE16D (C54x-class) at IC552 - [VERIFIED] **FPGA**: Altera Cyclone IV E EP4CE15F23C8N (IC152) — audio crossbar and master clock system - [VERIFIED] **DSP7 interconnect**: SIO00–SIO63 — 64 bidirectional serial lines, MCKS at 128 × Fs, own SDRAM per chip - [VERIFIED] **Network**: Marvell 88E6350R L2 switch, DM9000AEP MAC, KSZ8051RNL PHY - [VERIFIED] **Converters**: AK5385B ADC / AK4396 DAC / AK4125 SRC; CS8406 AES/EBU transmitter - [VERIFIED] **Storage**: NOR flash (AMD/Spansion command set, 16- and 32-bit bus variants), battery-backed SRAM for scenes, SDRAM - [VERIFIED] **Panel MCUs**: LPC1763 Cortex-M3, M38039-family 8-bit MCUs, HD6433683E H8/300H - [VERIFIED] **Audio networking**: Audinate Brooklyn II module over MSIOF #### Software architecture - [VERIFIED] **Operating system**: Renesas HI7000/4-family µITRON 4.0 kernel. No Linux anywhere - [VERIFIED] **Image load base**: 0x8803BF80 — P1 space, so physical 0x0803BF80 = SDRAM + 0x3BF80. The image is placed in RAM by a bootloader not present in the .PGM - [VERIFIED] **Code volume**: ≈7 MB of SH-4A little-endian machine code; the remainder is GUI resources - [VERIFIED] **Task model**: ~130 named µITRON objects across kernel, panel bus, display, mix engine, Premium Rack, MIDI, Dante, network, L2 switch and remote control - [VERIFIED] **Debug facilities**: Live serial debug monitor (task/sema/flag/mbox/mpl/mbf commands) plus Hitachi DT10 trace instrumentation still present in shipping firmware - [VERIFIED] **Plugin ABI**: Premium Rack plugins export `createPerseusPlugin_203` — the identical symbol appears in the console firmware (`-sh4a-per`) and in the macOS editor's dylibs (`-mac-per`) - [VERIFIED] **Console plugin linkage**: Statically linked into the main firmware image, not shipped as separate files - [MEASURED] **Head-amp control does not ride Dante's own control protocol**: Gain, phantom power and metering between the console and its stagebox travel as a Yamaha-proprietary block tunnelled inside Audinate ConMon packets, identified by three ASCII bytes at the head of the block. Dante carries it the way it would carry any other vendor's ConMon payload; Audinate's own control protocol on UDP 4440 is used only for patching. An adapter that speaks nothing but Audinate's documented protocols will never see a gain change - [MEASURED] **Addressed by the wrong MAC**: The block addresses devices by their Yamaha MAC, not their Dante MAC and not their IP — the console appears as `00:a0:de:e0:ce:f6` where its Dante interface is `00:1d:c1:17:ea:2c`. Two NICs on the same console, and only one of them is the one the control path names. Broadcast-style messages use an all-ones destination - [MEASURED] **What the arrays carry**: Head-amp gain is 32 × int16 big-endian centi-dB, confirmed by sweeping inputs 1, 2, 3 and 4 in isolation and watching exactly one array slot move each time; the sweep clamped at −6.00 dB, which is the Rio3224-D2's documented minimum. Phantom power is 32 × uint8 booleans, confirmed the same way, one slot per press. Input metering is 32 × uint8 broadcast by the stagebox at 31 Hz — 3,461 messages in 110 s — and its scale is not calibrated: no known signal was ever injected, so the mapping to dBFS is unknown and it must not be presented as a meter - [MEASURED] **On pairing, the console's state wins**: At pairing the stagebox announced all 32 inputs at +36.00 dB; 60 ms later the console overwrote every channel with its own stored −6.00 dB, and the stagebox did not push back. Anything mirroring console state has to expect a full-array overwrite the moment a console connects, and must not read it as 32 individual user edits - [MEASURED] **Eight more arrays with known shape and unknown meaning**: The same opcode carries further per-channel arrays whose element count, addressing and width are now evidence-backed — 32 × uint8 and 32 × int16 among them, answered in full during pairing. What is not known is what they mean, because no such parameter was touched while recording: every value observed is a resting default. Two of them rest at values that make a second gain array and an 80 Hz high-pass tempting. Neither was ever seen to move - [MEASURED] **Discovery is asymmetric**: The console answers read-only Audinate queries from an arbitrary host, returning its Dante name, its Brooklyn II module and the Audinate control application it runs. The stagebox answers nothing on any of those ports — and accepts and acts on Yamaha control from the same host. Audinate-level queries are therefore not a reliable way to enumerate Yamaha stageboxes; mDNS finds both, under their Dante names - [VERIFIED] **The console file is not the modern Yamaha container**: CL and QL Editor ship no parameter descriptors at all, and a `.CLF` is not the `#YAMAHA MBDF` container the DM3, DM7 and TF share: it is flat binary, no compression and no archive, 117,508 bytes for a QL5 default state, and its header names the product and the editor build in the clear (`QL [OSX, 5.8.1.27]`). The file formats split exactly where the firmware does — this generation is a different architecture, and it writes a different kind of file - [VERIFIED] **The input patch is one byte per channel, contiguous**: Located by two controlled diffs in the offline editor: moving one channel to a Dante source changed two bytes in a 117 KB file, and moving a second channel eight further along changed a byte eight positions further on. One byte per channel, no stride, no indirection — the whole patch table is a flat run - [VERIFIED] **Source encoding, exactly as the bytes have it**: `0x01`–`0x40` are Dante 1–64; `0x41`–`0x48` are local inputs 1–8; `0xc1`–`0xd8` are local inputs 9–32. The split between `0x41`–`0x48` and `0xc1` upward, for what the console presents as one continuous range of 32 inputs, is not explained — it is recorded because it is what the file says, not because a formula was found. "Simplifying" it to a single base plus channel number is right for eight channels and wrong for twenty-four. The picker's other sources — slots, effects returns, playback and none — are unmapped - [VERIFIED] **Channel names are stored four characters at a time**: A name is not contiguous: the first four characters sit in one block and the next four 384 bytes further on, at 96 channels per block, across two blocks — so eight characters maximum, and the third block is different data entirely. Every default name on this console is exactly four characters (`ch 1` … `ch96`), which means a contiguous four-byte read decodes every default file perfectly and truncates every name anyone would actually type #### Update path and security model - [INFERRED] **Image signing**: None found - [VERIFIED] **Encryption**: None — the package is plainly structured and its components are directly readable - [MEASURED] **The head-amp control path has no authentication**: It is multicast on the control network, carries no credential and no session, and is protected only by an additive checksum over the block — integrity with no authenticity in it, the same shape as this manufacturer's unsigned firmware packages. A message built to that rule and transmitted from an ordinary laptop on the same network was accepted and applied by the stagebox, which echoed the new gain back on its own status broadcast. The device never NAKs, so a refusal and a framing error look identical from outside. How such a message is constructed is deliberately not recorded here - [VERIFIED] **The console file carries a checksum**: Every edit moved one or two bytes near `0x010039` and moved them differently each time, with nothing else in a 117 KB file changing. That is a checksum: harmless for reading, and a hard blocker for ever writing a console file, since an assembled file with a stale value may simply be refused and there is no way to find out without a console. It has not been solved and nothing here attempts it. It sits a few bytes before an `MMS` marker — the namespace of the other Yamaha format — which is an observation and not a link These rows describe how the platform validates a firmware image, which is a structural fact about its architecture. They are not a vulnerability disclosure, and no exploit, bypass or circumvention technique is published. #### What is programmable, ranked 1. **Mini-YGDAI slot card** — The cheapest route to putting custom DSP in the audio path with no console modification at all. Host aperture 0xBA002000 + (slot << 11), 16-bit port on A10..A1. 2. **EP4CE15 patch FPGA (IC451)** — The audio crossbar between Dante, every DSP7, the SHARC, the DSP6 bank and both slots. JTAG header present, and it is configurable from the CPU. Highest leverage, least investigated. 3. **ADSP-21369 SHARC (FX IC101)** — The Premium Rack engine. A documented part, SPI-slave boot, JTAG header already fitted. Eight stereo slots means up to 16 mono inserts, patchable from the console's own UI. The one place third-party code could plausibly run. 4. **Head-amp control over Dante — a different axis entirely** — Everything above is about getting custom DSP into the audio path, and each route needs the console opened or a card fitted. This one needs neither: gain, phantom and metering for a whole stagebox ride the control network in the clear, and a message built from the observed rule was accepted by a real Rio3224-D2. It controls the machine rather than changing what it is — and it is the only route on this page that has been exercised on hardware. #### Traps Mistakes this analysis actually made. Each produced a plausible wrong answer rather than an obvious failure, which is why they are published. - The image load base is 0x8803BF80, not 0x88000000. The wrong value looks entirely plausible and silently yields almost no valid cross-references. - Never attribute an MMIO constant from string-pool proximity. Doing so produced a claim that 0xBA002000 was the DSP7 window when it is the slot aperture — published, then retracted. - Trusting the control block's own length field. An earlier pass took it as authoritative and recorded several arrays as never populated — they had in fact been answered in full. A device answering a read request echoes the query's length unchanged and appends the data past the boundary that length implies, so a parser that trusts it sees an empty reply and silently discards the payload. The checksum is what settles the real end of a block: across 3,907 captured messages, none validated under the wrong boundary. - Reading a resting default as a meaning. One unmapped array sits at exactly the value the gain array holds at rest, which makes "second gain array" irresistible; another sits where an 80 Hz high-pass would. Neither was ever observed to move, and a value that never changes proves nothing about what it is. - Trusting the strip label after a patch change. QL Editor raises a confirmation dialog whose default is No, and the strip label updates before it is answered — so the first attempt looked exactly like a successful edit and the two saved files came out byte-identical. If a diff shows nothing, check the change actually stuck before concluding the format does not store it. - Deriving the name field from a default file. Every default name is exactly four characters, so a four-byte contiguous read is correct on every file anyone has lying around and wrong on every file anyone would save. Only a rename exposed the 384-byte split; no amount of staring at a default file would have. ### Yamaha DM3 (2023) - URL: https://aux-opsy.com/platforms/yamaha-dm3/ - Class: Digital mixing console - Research status: Partial - Last updated: 2026-09-15 - Hardware in our hands: Yamaha DM3, firmware V3.00 -- a control-plane bench session on 2026-09-15 (SCP, OSC and YSDP captured; the Dante-BabelBox SCP adapter run against the desk, reading and writing gain and phantom). The firmware/silicon analysis below remains file-derived, not bench-checked. - Summary: Nine years newer than the QL and sharing almost nothing with it but the parameter framework — and, crucially, the same DSP philosophy: the signal path is fixed-function on both, however different the host looks. In 2026 the entry gained a second axis the firmware analysis never touched: the DM3's live control plane, captured and driven against a real console -- the first hardware any part of this entry rests on. - Key finding: Two findings, two axes. On the wire (bench-tested): the DM3's remote-control protocol is SCP, a newline-delimited ASCII command set on TCP 49280, and it was captured, decoded and driven against a real DM3 -- head-amp gain and phantom read and written*, each `set` echoed by an `OK` and an unsolicited `NOTIFY` to every other client, with the console self-describing all 177 parameters over `prminfo`. A cross-vendor bridge now ships speaking it (see the control section). Inside the box (file analysis only): the update package is a named-component archive (`#YAMAHA MBDFArchive`) carrying a full Linux rootfs, and the installer is a plain bash script with no checksum ceremony and no signature. The 3.8 MB component filed under `DSP/` looks like a loadable DSP image and is not one — it is supervisor MCU firmware plus coefficient tables, so the audio silicon is register-configured exactly as on the QL. - Verdict: The contrast with the QL is the CPU and the OS, and not the DSP — which is where this entry was wrong until 2026-08-14. A Linux userland behind an unsigned script-driven installer is a materially different proposition from the QL’s µITRON image, but the signal path is fixed-function on both. Nine years apart, same answer. What changed in 2026 is orthogonal to all of that: the DM3's external control is now understood and bench-proven -- SCP over TCP is the real remote-control path (not the OSC port), it pushes live change notifications to every client, and an open cross-vendor bridge speaks it in a shipped, signed release. Reprogramming the box is still gated on naming the mixing silicon; controlling it no longer is. - Open questions (what is NOT known): - Which silicon actually performs the mixing, and how it is configured. `DSP_V121.bin` boots the supervisor MCUs in front of it, not the part itself. - The FPGA part has not been identified. - What the fixed-size identifier field after the model string contains. The DM3 image is no longer in our possession, so it cannot be re-derived here. - No board has been inspected, and the firmware and silicon claims (unlike the bench-measured control-plane rows) have not been checked on hardware. - Whether the DM3 emits Yamaha's `MBC` head-amp protocol when a real R-series stagebox is mounted to it. None was on the bench, so this is untested; with nothing mounted the console emitted nothing MBC-shaped, which is expected rather than evidence either way. - What the patch source codes mean. The DM3's patch word reads as an index in the high byte with a port type below it, two values have been seen, and none of it has ever been checked against a console's own patch screen. The field is located and its values are opaque — which is the honest state to leave it in rather than naming ports from two samples. - Whether the four-byte-boundary padding in the container is really alignment. Every sample pads by exactly one byte, which fits alignment and fits equally a length that excludes a terminator. A single sample whose payload length falls elsewhere modulo four would settle it, and none has turned up. #### Method Two methods on two axes. The firmware, silicon and container findings are static analysis of the V3.00 firmware package (167 MB) and the macOS DM3 Editor -- file-derived, not bench-checked. The control-plane findings are bench-measured: a live session against a real DM3 (firmware V3.00) on 2026-09-15, packet-capturing the Dante and control networks and running the Dante-BabelBox SCP adapter against the desk (reading and writing head-amp gain and phantom, watching the console's own change notifications). The scene-format findings have their own basis and are kept apart from the firmware ones: a record parser derived from a publicly downloadable Yamaha preset pack for the TF series, then run unchanged against the factory scenes the DM3 Editor itself ships, and checked against the editor's own parameter descriptors. #### Artifacts examined - `DM3_firmware_V3.00.bin` — 167 MB, eleven named components - `DM3 Editor.app` (macOS) - DM3 Editor V3 — 21 parameter descriptors under `Resources/Descriptor/mms_*.xml`, 15 factory `.dm3s` scenes and 263 `.dm3p` presets, all shipped with the free editor - Yamaha's published Audix QuickPro preset pack for the TF series — 28 `.tfp` files, a public download, and the sample set the container layout was derived from and validated against - Packet captures from the 2026-09-15 bench session -- SCP, OSC, YSDP and the DM3 Editor's MMS sync, in the private `stoatworks-labs/dante-captures` repo (`yamaha-dm3/`, `yamaha-dm3-editor/`) - Dante-BabelBox v0.3.0 -- a shipped, signed cross-vendor Dante control bridge whose `yamaha-dm3-scp` adapter was validated against this console Referenced by name and version only. Nothing is redistributed. #### Hardware architecture - [VERIFIED] **Application processor**: Runs a Linux userland — component 1 is an 88 MB rootfs tarball - [VERIFIED] **FPGA**: Present — component 3, `fab_V110.bin`, 539 KB inflated - [VERIFIED] **DSP**: Not a loadable DSP image, despite the filename. Component 4, `DSP_V121.bin`, 3.80 MB inflated, is several Cortex-M images plus coefficient and curve tables: the first word is `0x200081B8`, a Cortex-M initial stack pointer, and the code is Thumb-2 throughout. Real content is ≈ 1.3 MB of the 3.8 MB partition, the rest zero padding. The data region fails the same code test used on the QL — monotonic non-decreasing runs of 10,235 / 4,097 / 4,096 words against 7 for known DSP code — and contains a literal descending ASCII ramp, i.e. a lookup curve. `dsp_initPowerOnBootAll` boots the MCUs; the audio silicon behind them is register-configured - [VERIFIED] **Panel MCU**: Component 2, `DM3PN_V100.bin`, 246 KB inflated - [VERIFIED] **Audio networking**: Audinate Brooklyn 3 plus a Broadway module — components 5 and 6 #### Software architecture - [VERIFIED] **Container format**: `#YAMAHA MBDFArchive` — magic, a 16-byte model string at `0x24`, then a fixed-size identifier field, then `#FIRMWARE` records of five BE u32 fields, three NUL-terminated strings (path / description / version) and a zlib payload, terminated by `#END`. Payloads were located by scanning forward to the zlib header rather than by trusting the length field, which was never fully decoded. See [the DM7](https://aux-opsy.com/platforms/yamaha-dm7/) for a field-by-field derivation of the same container - [VERIFIED] **Main application**: `app_console.tgz`, 64.8 MB inflated, V3.00 - [VERIFIED] **Installer**: A bash script. Untars to /tmp/update, walks /yamaha/firmware.new/, compares each component's version file against the installed one and copies only what changed, running a per-component `.sh` hook if present - [VERIFIED] **Bootloader**: Shipped as an updatable component — `app_bootloader.tgz`, V1.03 - [VERIFIED] **Scenes and presets ride the firmware's own container**: The same `#YAMAHA MBDF…` container carries firmware archives, channel presets, scenes, list backups and user accounts as separate subtypes. The evidence that this is one family rather than a resemblance: a record parser derived from the TF preset pack walked a DM3 scene — a different subtype and a different model — with no changes at all. Records are a tag, a field name, a target naming an object type and index, and a payload, padded to a four-byte boundary and terminated by an `#END` tag - [VERIFIED] **The payload describes itself**: The payload is not an opaque packed blob: it carries its own schema inline, a block of collection and parameter records followed by values packed to that layout. Walking the schema of a real DM3 factory scene yields exactly 207 collections and 494 parameters — precisely what the editor's own `mms_Mixing.xml` declares — and the value block begins exactly where the walk ends. Two independent sources agreeing with nothing fitted between them is what makes this trustworthy rather than a plausible story - [VERIFIED] **Field layout is per-model and has to be read, never assumed**: The DM3's input-channel record is 473 bytes × 16 with the patch field at offset 100 and four bytes wide; the TF's is 515 × 40 with a one-byte patch at the same offset. Offsets are only correct if they are computed by summing each preceding collection's size × array length in document order and then looked up by name — the field's position is not a constant of the format - [VERIFIED] **A preset is not a scene**: Worth keeping straight, because they share a container and an extension convention: a `.dm3p` preset holds one channel strip and has no patch, no routing and no channel-name table, while a `.dm3s` scene holds console-wide state. Only the scene can produce a patch list #### Update path and security model - [VERIFIED] **Image signing**: None — the installer performs no signature check - [UNKNOWN] **Integrity**: No checksum ceremony anywhere in the install path. A fixed-size field follows the model string in the archive header and was previously described here as a 16-byte digest; the [Yamaha DM7](https://aux-opsy.com/platforms/yamaha-dm7/) derivation of the same container gives a 20-byte identifier at `0x34` that is demonstrably not a digest of the image or any obvious sub-range. The DM3 source image is no longer in our possession, so which applies here cannot be re-derived — the field is recorded as unidentified rather than kept at a confidence it never earned - [VERIFIED] **Encryption**: None — every component is zlib, and unpacks with a 60-line script #### Control and discovery - [MEASURED] **Remote control -- SCP**: The DM3's primary control surface: a newline-delimited ASCII command protocol on TCP 49280 (`devinfo`, `get`/`set`, `prminfo`, `ss*` scene ops). Head-amp gain (`IO:Current/InCh/HAGain`, whole dB 0-64) and phantom (`48VOn`) were read and written on a real DM3, each `set` confirmed by an `OK` echo carrying the value and an unsolicited `NOTIFY` broadcast to every other connected client. The console self-describes its whole parameter space over `prminfo` -- 177 parameters -- so no fixed table is needed. This is the same protocol family Yamaha's R Remote speaks to the R-series. - [MEASURED] **Remote control -- OSC**: A second, weaker surface on UDP 49900 (`/yosc:req/...`, replies `/yosc:ok/...`). Reads work, but a `set` is unacknowledged and the console emits no unsolicited OSC, so there is no change feed. Reply values for `48VOn` and the device-status fields come back as OSC int64 (`h`), not int32. Confirmed on the bench; SCP is the better transport for exactly these reasons. - [MEASURED] **Discovery -- YSDP**: Yamaha devices announce over UDP 54330 with a `YSDP`-magic datagram (sender IP + MAC, the service string `_ypa-scp`, manufacturer, model, unit-ID and name), and answer a controller's query the same way -- this is not Bonjour/mDNS, which is what a naive discovery would look for. R Remote and the DM3 both use it, and only one Yamaha control app can hold the port per host. Captured from the DM3 and R Remote. - [MEASURED] **Editor sync -- MMS over TCP**: DM3 Editor mirrors the whole console over a third protocol: the MMS parameter framework serialized on TCP 50368, message tags `EEVT` (events/keepalive), `MSTS`, `MPRC`, `MMIX`, `MVOL`, `MSCL`, `MSUP` -- the same `mms_*.xml` framework the firmware carries, on the wire. A full ~3.3 MB state pull on connect, bidirectional keepalive while online, and live push of external changes (an SCP write made from another client) were all captured. - [MEASURED] **Network identity**: The Dante model presents three interfaces on Yamaha OUIs: the Linux host (control), the NETWORK jack, and the Broadway Dante module (`Broadway-`, the PTP leader). On a flat link-local network the NETWORK-jack address the beacon advertises is unreachable (dual-homed Linux); the address that answers is the one carried in the YSDP response*, served via the Dante module's internal switch. Observed on the bench. These rows describe how the platform validates a firmware image, which is a structural fact about its architecture. They are not a vulnerability disclosure, and no exploit, bypass or circumvention technique is published. #### What is programmable, ranked 1. **The Linux rootfs** — A conventional userland behind an unsigned, script-driven installer is a materially different proposition from the QL's µITRON image. 2. **The DSP supervisor MCUs** — What the update loads is Cortex-M supervisor firmware, not the signal path — so this rank buys far less than it appears to. The audio silicon behind those MCUs is register-configured, and its identity is still unknown; the DM3 service manual would settle it the way the QL's did. #### Traps Mistakes this analysis actually made. Each produced a plausible wrong answer rather than an obvious failure, which is why they are published. - A component filed under `DSP/` is not evidence that the DSP is loadable, and this page asserted for a week that it was. `DSP_V121.bin` is 3.8 MB, is named `DSP`, and is genuinely boot-loaded at update time by `dsp_initPowerOnBootAll` — every surface signal says loadable DSP image*. It is several Cortex-M images plus coefficient tables. The tell is the first word, `0x200081B8`: a Cortex-M initial stack pointer, not DSP code. The published verdict here was the exact inverse of the finding until 2026-08-14, and the underlying research had already reached the right answer — the error was in the transcription onto this site, which read the first half of the argument and stopped before the correction. - Size is not content. Real content is ≈ 1.3 MB of that 3.8 MB partition; the remainder is zero padding, and quoting the partition size makes the payload sound three times more substantial than it is. - The word "digest" was assumed, not derived. A fixed-size field after the model string was recorded here as a 16-byte digest at `verified` confidence. Nothing had checked it against the image, and the [DM7](https://aux-opsy.com/platforms/yamaha-dm7/) derivation of the same container family found a 20-byte identifier that is not a digest of anything. A field whose size is observed and whose meaning is guessed is `unknown`, not `verified`. - Hard-coding one model's field widths. Reading the TF's one-byte patch field as the DM3's four-byte word produced a column of plausible zeros rather than an error — the failure mode of a packed format is not a crash, it is a decode that looks fine. Sum the descriptor, look fields up by name, and check the summed total against the declared record size. - SCP, not OSC, is the DM3's real control protocol -- and the difference is not cosmetic. Over OSC a `set` is unacknowledged and the console pushes nothing, so an integration reads back a stale value after every write and never sees a change made anywhere else. Confirmed reads/writes and the live `NOTIFY` feed exist only on the SCP/TCP port (49280). Anyone who starts from the published OSC spec builds the weaker half and does not know it. - Discovery is YSDP (UDP 54330), not mDNS/Bonjour. A controller that browses Bonjour for the console finds nothing. And only one Yamaha control app can hold that port per host: with R Remote running, DM3 Editor silently will not go online -- an empty device list, or a Direct-IP connect that stalls forever on keepalive. On the bench that looked exactly like a network fault and was not; quitting R Remote fixed it instantly. - On a flat network the address the DM3 advertises can be the one you cannot reach. The Dante model is dual-homed; its NETWORK jack self-assigns a link-local address that does not answer when NETWORK and Dante PRIMARY share one segment. The address that actually answers is the one carried in the YSDP response*, not the broadcast beacon. ### Yamaha DM7 (2023) - URL: https://aux-opsy.com/platforms/yamaha-dm7/ - Class: Digital mixing console - Research status: Partial - Last updated: 2026-09-07 - Hardware in our hands: none — analysis is file-derived - Summary: Not one console CPU but a small cluster of identical i.MX8M Mini computers running one Linux rootfs in four roles — and the only platform here that ships its entire mixer parameter model as plain XML. - Key finding: `app/Descriptor/mms_*.xml` — 27 files, 1,701 collections, 2,693 parameters — declares every parameter's C type, array dimension, minimum, maximum, default and step, and every collection's packed `datasize`. That describes the console's binary state layout, not merely its parameter names, and it ships in a downloadable update. The audio itself is off-CPU on Yamaha SSP3 DSPs and a SHARC, exactly as the QL's was. - Verdict: The audio is off-CPU on Yamaha SSP3 DSPs and a SHARC, so this is no more a rehosting target than the SQ-5 or the HD96. What is different is everything above the DSP boundary: where the HD96 documents itself in Thrift and Protobuf, the DM7 hands over a typed, dimensioned, byte-sized schema of its entire mixer state as XML in a public update. This is a console to interoperate with, and almost all of that work can be done before touching one. - Open questions (what is NOT known): - The ports for SCP and `/yosc:` are not recovered and may not be constants. The real target is the `YSDP` wire format, and a single broadcast capture from a console would settle it far more cheaply than more disassembly. - Whether anything outside the container validates an update. The container does not and the installer does not; the bootloader and the running application were not examined. - Where the SHARC algorithm code lives. It is proven absent from the image, and residency on the NAIO board fits the absence of any NAIO update member — but that is not proven. - The coefficient block format the plugin side emits is documented nowhere in the image, and it is the real ceiling on the plugin route. So is whether a plugin configuration transfer can introduce new SHARC program code or only reconfigure resident kernels. - The 20-byte archive-header id and the `kind` field (`8` versus `12`) are both undecoded; `kind` correlates loosely with unpack-versus-flash but the `installer` member breaks the pattern. - The DSP wire format, the FPGA register maps and the bay synchronisation protocol are untouched. - No claim here has been checked against a DM7. One pass, one firmware image, no hardware. - Where the DM7's padding byte sits. The record sums one byte short of its declared size, and until that is located every field beyond it is suspect on this model alone. - What a DM7 scene looks like. The editor ships 303 presets and no scenes, so the console-wide state — the file a patch list would come from — has not been seen for this model, only for its smaller siblings. #### Method Static analysis of the public firmware image `DM7_firmware_V1.75.bin` (release V1.75, main system R1.02): a walk of the container format, extraction of the Linux root filesystem and console application, a C++ RTTI type-name census of the stripped application binary, and disassembly of a small number of functions. No hardware was involved and no console was opened. #### Artifacts examined - `DM7_firmware_V1.75.bin` — 174,392,092 bytes, 23 members, format `#YAMAHA MBDFArchive` - `app_console.tar.xz` V1.75 — the console application: three role binaries, 67 plugin `.so`, 27 XML descriptors - `rootfs-DM7-2026-01-29-…tar.xz` R1.02 — the arm64 Linux root filesystem - `DOM` 4.2.8.7 — a nested `#YAMAHA MBDFArchive` carrying its own kernel, initramfs, `dom.dtb` and three Xilinx bitstreams - `installer/install_script` R0.20 — the update procedure, in plain bash - DM7 Editor — 27 parameter descriptors and 303 `.dm7p` presets shipped with the free editor. No factory scenes: this is the one model in the family whose console-wide state file has not been read Referenced by name and version only. Nothing is redistributed. #### Hardware architecture - [VERIFIED] **Application processor**: NXP i.MX8M Mini (`fsl-imx8mm`), arm64 Cortex-A53 — from the device trees, `/etc/os-release` and `/boot/imx-boot-eMMC_dm7_20231128.bin` - [VERIFIED] **System topology**: Four board roles boot from one root filesystem, selected by device tree: `main`, `bay`, `ctrl` (the CTL-DM7 surface) and `main-jig` (a factory test rig). A DM7 is a cluster of identical i.MX computers, not one CPU with peripherals - [VERIFIED] **Mixing DSP**: 2 × Yamaha SSP3 at IC120 and IC123, plus a third for USB audio at IC136/IC124. The application's own factory-diagnostic strings print the board reference designators and BGA ball coordinates - [VERIFIED] **Effects DSP**: Analog Devices ADSP-21469 SHARC, over SPI, on the NAIO board — the symbol `CspiNaio21469SharcController` names transport, board and part in one string. More than one is present (`SHARC#%d`, `kernel [dspNo]`) - [VERIFIED] **FPGAs and CPLDs**: PUBe and PY64-MD are Xilinx 7-series bitstreams; `NAB` is an Altera `.rpd` and `NAB_L` a Lattice ECP5 `.bit` at the same version V0.33c; HIC, HIC_L and FDC are JAM STAPL JTAG programming streams. Each identified from its own magic bytes - [VERIFIED] **Xilinx parts, by name**: The two 7-series members name their own devices in the bitstream's IDCODE configuration packet: PUBe is an XC7A35T (`0x0362D093`, 3,330 configured frames) and PY64-MD an XC7A100T (`0x03631093`, 7,464 frames). Both are Vivado-compressed — they carry multi-frame-write packets, which a frame reader that ignores `MFWR` will silently mis-expand - [INFERRED] **Second-sourced silicon**: The `_L` members are the same board in different fabric — `NAB`/`NAB_L` share V0.33c and `HIC`/`HIC_L` pair the same way, with the console programming whichever it finds. The contemporaneous Behringer X32 image carries the same Xilinx/Lattice pairing, so a second-source bitstream is itself a fingerprint of a 2021–2023 respin - [VERIFIED] **Dante subsystem (DOM)**: A second computer, not a chip: Xilinx Zynq-7000, dual Cortex-A9, 512 MB, Micron n25q512a QSPI boot flash, two Cadence GEM MACs for Dante primary/secondary, running its own Linux. From the decompiled `dom.dtb`, `model = "YAMAHA DOM"`. The part is XC7Z020 in the clg484 package — its three bitstreams are stock `.bit` files, so they carry Vivado's own header naming the part `7z020clg484`, the top-level design `khaju_hc_top`, the toolchain `2019.2` and a build date of 2025/07/18, the three SRC variants differing only by build minute. That is the same device as the [SQ-5](https://aux-opsy.com/platforms/allen-heath-sq5/) mixes on, doing an entirely different job - [VERIFIED] **Dante bridge fabric**: A custom Yamaha FPGA IP block in the DOM's programmable logic, `compatible = "xlnx,akashi"` at `0x60000000`, with interrupts named `ts`/`rx`/`tx`/`rxerr`/`smi` and `rx_act`/`meter`/`rx_ka`/`daif`. Three shipped bitstream variants (`noSRC`, `syncSRC`, `asyncSRC`) select the sample-rate-conversion mode - [VERIFIED] **Microcontrollers**: Seven ARM Cortex-M images — JK (AES I/O), P-U2S2, P-MD2, P-HR1, P-FDC2 (panel boards), PY-MIDI-GPI (slot card) and USB2HOST — each identified from its vector table. Two SRAM bases appear (`0x2000_0000` and `0x1000_0000`), so at least two MCU families are in use. USB2HOST is filed as `DSP_V105.bin` but is a 3.8 MB Cortex-M image, not DSP code - [VERIFIED] **Ethernet switch**: Marvell 88E6352 managed switch, configured from userspace — the application names `88E6352 PORT#0`, `PORT#5` and `PORT#6`, so at least three ports are software-visible - [VERIFIED] **Internal networks**: `control` on 192.168.53.100/24, the three expansion bays on 240.0.10.x/24, and Dante as a MACVLAN parent with its own MAC carrying `dante0` and `dante1`. The bay links use IANA-reserved 240.0.0.0/4 space, which is a good fingerprint for telling console plumbing from console I/O in a capture #### Software architecture - [VERIFIED] **Operating system**: NXP i.MX Release Distro 4.19-warrior, image `ID=ymh-cpum-wayland` (Yamaha CPU-M, Wayland), Linux 4.19, systemd, Weston and Qt, plus about ten `yamaha-*.service` units - [VERIFIED] **Console application**: `app_console_{main,bay,ctrl}`, arm64 ELF, stripped — unlike the DM3's, which ships with named functions. Retained build paths disclose the source tree: project codename `hagoromo`, internal framework `Citrus`, an OpenCV-linked AI core `Trident`, and a vendored `shureacn` - [VERIFIED] **Parameter model**: 27 `mms_*.xml` descriptors: 1,701 collections, 2,693 parameters, 6,572,842 bytes of declared `datasize`. Every parameter carries a C type and array dimension; numerics carry minimum, maximum, default and step; collections carry `arraysize` and the packed byte size of one instance - [VERIFIED] **Control protocols**: Three. Yamaha SCP (232 recovered types, the complete command set with client and server sides), a Yamaha OSC dialect addressed `/yosc:` (30 server commands, 12 notifications), and a vendored Shure ACN SDT/DMP stack for wireless microphone control with a Yamaha-written discovery supplement - [INFERRED] **SCP and YOSC are one model**: YOSC is an OSC-shaped front end onto the SCP engine, not a second control model — every YOSC concept is a wrapper class over the SCP implementation of the same thing, and `SCPMODE` exists as a command in both. The competing reading, two independent engines, is unsupported: the type census contains no second parameter engine - [INFERRED] **OSC address grammar**: `/yosc:%s/%s/ts:` — the `ts:` segment is a path into the parameter tree whose names line up with the descriptor collections, and `subscribe` is a distinct verb, so the protocol is subscribe/notify rather than poll-only. The two `%s` fields are unresolved; a device or session identifier and a target name is inference from shape only - [UNKNOWN] **Service ports**: Not recovered, and possibly not constants. Only four `bind` call sites exist in 22 MB of `.text` because the application uses standalone Asio, and none yields a port; no port-shaped literal sits near any protocol symbol; the only mDNS service types in the binary are Audinate's. A `YSDP` class family — responder, listener and a per-host service list — is present, and its wire format is undetermined - [VERIFIED] **Plugin ABI**: The same Perseus ABI as the QL. `createPerseusPlugin_203` appears verbatim in both, the coefficient address format is unchanged in shape, and four Premium Rack plugins have byte-identical step/pmcoef/dmcoef profiles across the two consoles — the same algorithms across a decade and two CPU architectures - [VERIFIED] **Console plugin linkage**: 67 separate `.so` files under `/yamaha/app/Plugins` — 34 algorithms in GUI/DSP pairs — loaded at runtime, where the QL statically links its Premium Rack into the firmware image. All 34 DSP resource manifests were lifted statically from `.rodata`, with no host services involved - [VERIFIED] **SHARC algorithm code**: Not present anywhere in the image. Established by scanning all three application binaries, both Perseus libraries and every device member — 17 files, roughly 150 MB — for ADSP-214xx loader block chains, which finds none. There is also no NAIO firmware member in the update at all - [VERIFIED] **Mixing coefficient layer**: `Mix1000DSP` (213 recovered types) is the SSP3-side layer and names the whole mixer signal path, with array dimensions readable from its template arguments — 32 in-ports, 16 and 4 out-ports, a 20-tap port delay, a 32 × 8 port filter. It sits on a generic `DspFramework` whose class names are shared with the DM3 - [VERIFIED] **Installer**: Plain bash. For each member it compares a `version` file against the installed copy, deletes and re-copies on mismatch, runs an optional per-member hook to flash the device, repeats the procedure for each mounted bay, and returns early on a control-surface CPU. About a third of the script is a recovery branch for one shipped root-filesystem bug - [VERIFIED] **Shared codebase with the DM3**: `SystemSetting.ini` is headed "DM3シリーズ本体用" — for the DM3 series. Its `ApplicationType` key enumerates `firmware`, `fader_iqc`, `editor` and `emulator`, so the same binary lineage runs off-console, and `UseMMSExplorer` names a built-in browser over the parameter tree that ships switched off - [VERIFIED] **The shipped parameter model predicts the file layout**: The XML above is not only a description of the running console — it is the decoder for what the console writes. The DM7's input-channel record is declared 1,785 bytes × 120, with a nine-byte input-patch field at offset 93 and the signal selector ahead of it: a different shape from the DM3 and TF pair, which put a patch field at offset 100 and differ from each other in its width. Same container across the family, different layout in every model - [VERIFIED] **The one model whose declared size does not add up**: Summing each collection's size × array length in document order gives 1,784 against a declared 1,785, so there is a padding byte somewhere in the record and its position is not stated. The DM3 and TF sum exactly, so a decoder written and tested against those two would never discover the discrepancy — it would simply read every DM7 field after the padding one byte early #### Update path and security model - [VERIFIED] **Image signing**: None. Neither the container nor `installer/install_script` carries or checks a signature - [VERIFIED] **Encryption**: None — every one of the 23 member payloads is a zlib stream, and the two `.tar.xz` members are wrapped in zlib stored blocks so a reader can decompress unconditionally - [VERIFIED] **Per-member integrity**: None. There is no digest field per member and nothing to check a payload against; the only integrity-shaped field in the whole format is 20 bytes in the archive header - [UNKNOWN] **Archive header id**: 20 bytes at offset `0x34`. Not a SHA-1 of the image or of any obvious sub-range. Every nested archive carries its own and all four observed values begin `5601`, which reads more like a version or type prefix than a pure digest - [VERIFIED] **Update validation**: The installer validates nothing beyond comparing a version file*, so a member whose version string is unchanged is not reinstalled even if its contents differ. This is a statement about the container and the installer only — whether the bootloader or the running application validates an image was not established - [VERIFIED] **Plugin loading**: The effect-plugin host authenticates nothing. There is exactly one `dlopen` call site in the whole application binary; the function that reaches it contains no hash, signature, certificate or filesystem-permission check, and the selection of what to load is a filename pattern rather than a name whitelist - [VERIFIED] **Filesystem posture**: The root filesystem is mounted read-only by a systemd hook; `/yamaha` is a separate writable partition, which is what the installer copies into and where the plugin directory lives. Whether any unprivileged or remote path can write there was not examined - [INFERRED] **Cryptography that is present**: The binary's OpenSSL surface is real but belongs to the TLS/Dante paths and to an AES-256-CBC console-settings encryption path. None of it sits on the update or plugin paths These rows describe how the platform validates a firmware image, which is a structural fact about its architecture. They are not a vulnerability disclosure, and no exploit, bypass or circumvention technique is published. #### What is programmable, ranked 1. **An offline client against the shipped parameter model** — The descriptors give types, ranges, array dimensions and packed sizes, so the state layout can be reconstructed and the `datasize` arithmetic self-checks without hardware. The SCP and `/yosc:` command vocabularies are already recovered, which leaves only the transport blank — and the transport is a discovery protocol to be captured, not a port constant to be guessed. 2. **The Perseus effect-plugin surface** — Plugins are separate loadable objects here rather than statically linked as on the QL, and the load path performs no authentication. What that reaches is the host-side coefficient computation, the parameter model and the GUI — not proven custom SHARC execution, because the SHARC algorithm code is not in the image and a plugin ships coefficients to a kernel that is already resident. `extRAM` is absent from all 34 manifests, so no shipping Yamaha plugin uses the SHARC's external SDRAM. 3. **The mini-YGDAI (PY) slot** — The same route the QL offers — custom processing in the audio path with no console modification — and the image confirms the slots carry their own Cortex-M firmware and, for PY64-MD, a Xilinx bitstream. The DM7's host-side slot interface was not examined, so this is the least investigated of the three. #### Traps Mistakes this analysis actually made. Each produced a plausible wrong answer rather than an obvious failure, which is why they are published. - The container's section alignment is 4 bytes, not 2. A 2-byte assumption walks the first three sections correctly and then desynchronises at the `NAB` member onto a plausible-looking wrong magic. Validate the walk by landing exactly on `#END`, never on the first few hops. - `var_len` is not the offset to the payload. It is the string-area length plus a constant 8, and the payload begins at the 4-byte-aligned end of the three NUL-terminated strings — a different number whenever those strings are not a multiple of 4. Parse the strings and align; do not do arithmetic on the field. - The payloads look like xz and are zlib. The two `.tar.xz` members carry the xz magic a few bytes in, behind a zlib header wrapping a deflate stored block, so treating payloads as xz appears to work on exactly those members and fails on every genuinely compressed one. - Regex-scanning for the `#…` section magics finds hits inside compressed payloads. The chain has to be walked from `0x48`, not pattern-matched. - The port hunt's near-misses are worse than its failures. Walking back from the `bind` call sites yields plausible port-shaped immediates — 4096, 3072, 25600 — which are byte-swaps of the small integers 16, 12 and 100. Reading any of them as a port number would have produced a confident, wrong, publishable answer. ### Allen & Heath SQ-5 (2018) - URL: https://aux-opsy.com/platforms/allen-heath-sq5/ - Class: Digital mixing console - Research status: Documented - Last updated: 2026-09-07 - Hardware in our hands: none — analysis is file-derived - Summary: A 55 MB firmware image that contains none of the mixer. The audio path lives in FPGA fabric behind an undocumented AXI register map — and the fabric has now been opened far enough to read a pan-law table out of it. - Key finding: The update package is 55 MB of ARM image and 2.5 MB of bitstream, and the 2.5 MB is the mixer. What looked like 40 MB of UI artwork also turned out to hold two more complete FPGA bitstreams, and the mixer's own fabric has given up the first data recovered from inside this console: a constant-power crossfade table, decoded twice by independent paths to the same bits. - Verdict: A poor rehosting target, but not for the usual reasons. There is no secure boot and no signing — yet none of the mixer is in the software you would replace. If it were ever pursued, the correct first move is JTAG, not the USB update path: dump the 64 MB QSPI intact before writing anything. - Open questions (what is NOT known): - The PL's AXI register map is entirely undocumented — this is the whole audio path, and it is the single most important missing piece. - The FX board's DSPs are a genuine gap: not the vendor, not the architecture, not how code reaches them. The `FX[0]` indexing implies more than one. - The first-stage bootloader is not in the update file and has never been seen. The A/B banks protect the application images, not it. - The debug UART is near-certainly populated (xuartps is linked and printf diagnostics are everywhere) but has never been located on a board. - What the rest of the mixer's memory holds. Only two block-RAM halves are initialised at configuration time and those are the ones read out; every table the console writes at runtime — every fader law, every filter coefficient it computes — is invisible to a static bitstream read by construction. - Whether the recovered remote-I/O netlist actually behaves. It elaborates in a simulator, which proves it is structurally coherent, not that it is functionally faithful; nothing has been driven through it and no output has been compared against a real device. - The arithmetic. The bitstream-to-netlist route has no support for the DSP48 primitive, so the mixer's multiply-accumulate structure — the actual mixing — cannot be recovered by this path at all. Nothing short of a different toolchain or a live bus capture changes that. - Which socket class the patch index refers to. MixPad offers Local, SLink, USB and I/O Port tabs and only a Local patch was tested, so whether the class is encoded elsewhere in the record or in a separate table is unknown. The byte alone should not be assumed to identify a connector. - Where channel names live, and whether MixPad can write one at all. Both are open, and the second is what decides whether this can be settled without a console. - The rest of the 336-byte record — preamp, EQ, dynamics — none of which has been mapped. #### Method Static analysis of a single vendor firmware update file, extended into the FPGA fabric itself. No hardware was inspected and nothing here was run on a console. The image was re-obtained in August 2026 and the container format re-verified against it, so every claim below is now re-derivable — an earlier version of this page recorded the image as lost. The fabric work goes bitstream → FASM → netlist → simulator, using Project X-Ray's bit-to-feature database and `fasm2bels`; the parts of that route which touch this console were run end to end, and the general route is described on the [method](https://aux-opsy.com/method/) page. #### Artifacts examined - `SQ-5UPG_1_6_3r6268.bin` — three records, no compression, no encryption - `SQuMixFPGA.bit` — the 2.5 MB mixer bitstream, carved from the package: byte-swapped for the Zynq PCAP path and Vivado-compressed - Two Artix-7 bitstreams carved out of the region this page used to call artwork — the device-side images the console pushes to its remote I/O boxes - `NVDATA.DAT` and `SCENE001.DAT` written by SQ MixPad V1.6.0 running offline as an SQ-7 — two 128 KiB NVRAM images, and the controlled diffs taken across a single patch change Referenced by name and version only. Nothing is redistributed. #### Hardware architecture - [VERIFIED] **SoC**: Xilinx Zynq-7000 XC7Z020 — proven from the bitstream's IDCODE configuration packet (30018001 → 03727093), not inferred - [VERIFIED] **Corroboration**: Linked Xilinx PS drivers: xemacps, xscugic, xdevcfg, xqspips, xiicps, xttcps, xuartps, plus lwIP 2.0.3 (xilinx_modified) - [VERIFIED] **FPGA count**: One. xdevcfg with PCAP_FPGA_DONE_FAIL shows the bitstream loading into this same chip's PL over the PCAP bridge — there is no second FPGA - [INFERRED] **DRAM**: ≥1 GB — inferred from CPU1's 0x20100000 load address (513 MB in) against the Zynq-7000 PS ceiling of 1 GB. No memory-controller config was decoded - [VERIFIED] **Boot flash**: Dual-parallel QSPI NOR, ~64 MB (`InitQspi2Dual`). Dual-parallel means two devices striped — a naive single-chip dump gets every other byte - [VERIFIED] **Surface MCU**: A fader/surface MCU flashed over SPI from the ARM (`Expected Fader Version %u.%u, updating...`) - [VERIFIED] **FX board**: Separate board with its own DSPs and SDRAM (`FX board ID 0x%02X not recognised`, `FX[0] failed SDRAM test`) - [VERIFIED] **Mixer bitstream — it is compressed**: `SQuMixFPGA.bit` uses Vivado bitstream compression: 6,021 configured frames written by `FDRI`, plus 4,024 frames filled by `MFWR` multi-frame writes. This matters more than it sounds, because the standard open-source frame reader parses `MFWR` into a named packet and then discards it — so a compressed bitstream does not error, it yields a frame set with frames missing or duplicated, and that corruption stays structured all the way through FASM into a netlist that still simulates. Expanding the compression correctly is the first gate on any fabric work here - [VERIFIED] **Mixer fabric — frames placed**: 5,935 mixer logic frames placed at absolute device addresses. Compressed bursts auto-increment through the device's own frame ordering rather than through consecutive addresses, so placement needs the part's geometry, not arithmetic. Two independent placement paths were run against each other and initially disagreed on 1 frame in 124 — the cause was the single `MFWR` immediately after a `CMD=MFW`, which has no address of its own because it primes the mode; applied as a frame write it overwrites one real frame silently. Verification also has to account for 2 pad frames after each block-type/half/row group, twelve in total on this part - [VERIFIED] **Mixer fabric — first data recovered from inside it**: Exactly two initialised block-RAM halves are configured in the whole mixer design, and their contents decode to a crossfade / pan-law table pair: a symmetric sine-shaped bell rising 0→31→0 across 49 points, and its complement falling 61→32→61, the two summing to roughly a constant — which is what a constant-power pan law looks like — alongside higher-entropy coefficient lines. The validation is that the two halves decode bit-identically from different base addresses and word offsets: two independent paths through the geometry cannot agree by accident. This is the first data recovered from inside this console's fabric rather than from the software around it - [VERIFIED] **Remote-I/O bitstreams — hiding in the artwork**: The ~40 MB region this page described as uncompressed artwork is not all artwork: it embeds two complete Artix-7 bitstreams, both plaintext, carved out and identified as XC7A15T (`0x0362E093`). They are the device-side images the console pushes down its SLink/gigaACE port to a remote I/O box, so they are the far end of the link, not fabric inside the console. They are two different designs and not one design compressed twice: one is uncompressed with no SPI-boot configuration, the other sets the boot-SPI registers and carries initialised memory - [VERIFIED] **One of those images is shipped by three consoles**: The smaller of the two is byte-identical — same SHA-256 over all 1,154,836 bytes of configuration data — to a bitstream embedded in the [Qu-7](https://aux-opsy.com/platforms/allen-heath-qu/) and [Avantis](https://aux-opsy.com/platforms/allen-heath-avantis/) update packages. Allen & Heath ship one common remote-I/O device image across three product lines a decade and two platform generations apart, which is a stronger statement of shared architecture than any string match on this site - [VERIFIED] **Remote-I/O fabric — recovered and simulated**: That bitstream was taken all the way back to a netlist and elaborated in a simulator without error: 4,206 6-input LUTs, 3,625 flip-flops, 12 block RAMs, 3 clock managers. It went further than the mixer could because it is roughly twenty times smaller and has no processor system to model. The design partitions cleanly by clock domain into a 240 MHz audio datapath — 2,427 elements, heavy on shift-register primitives, almost no feedback — and a 180 MHz protocol engine which is 30% of the flip-flops but 103 of the design's 159 state machines - [VERIFIED] **Remote-I/O fabric — the PHY, and why it matters here**: The link PHY is LVDS SelectIO with source-synchronous SERDES — 107 input serialisers, 29 output serialisers, 65 delay elements and zero gigabit transceivers. Corroborated independently by the clocking: all three clock managers run a 900 MHz internal frequency from three different input clocks (50, 36 and 28.125 MHz), and one produces a 180 MHz pair 90° apart, which is a source-synchronous capture clock and nothing else. This is load-bearing for the question of whether the console could speak the far end's protocol, because SelectIO is ordinary I/O that the console's own XC7Z020 has — its clg400 package has no gigabit transceivers at all, so a transceiver-based PHY would have ended the question - [VERIFIED] **Remote-I/O fabric — where the audio crosses**: All twelve block RAMs are simplex buffers, written on one port and read on the other, never both. Four are 16-bit × 2048 control buffers; eight are 24 bits wide, which is the console's sample width stated by the fabric rather than by a specification. Four of the twelve are asynchronous — written on one clock and read on another, in both directions across the 180/240 MHz boundary — and those four are the clock-domain-crossing FIFOs where link audio enters and leaves #### Software architecture - [VERIFIED] **Operating system**: None. Zero FreeRTOS, ThreadX or Linux strings — a bare-metal Xilinx standalone BSP with a custom message-block system and a custom GUI - [VERIFIED] **Core model**: AMP across both Cortex-A9s, not SMP. `CPU0: sending Start Msg to CPU1` - [VERIFIED] **CPU0**: `sQu_Host` at 0x00100000 — mixer control, GUI, protocols - [VERIFIED] **CPU1**: `sQu_USB` at 0x20100000 — SQ-Drive: USB host, FAT, WAV. Directory layout /AHSQ/USBRec, /AHSQ/Scenes, /AHSQ/Shows appears verbatim - [VERIFIED] **Container header**: 0x70 (112) bytes, computed as next_offset − this_offset − payload_len and confirmed independently at both gaps. It is not read from any field - [VERIFIED] **Payload format**: Despite `.elf` names the payloads are raw ARM32 images, each with a 16-byte preamble; byte 16 onward is the ARM exception vector table - [VERIFIED] **Code fraction**: Only ~10–16 MB of the 55 MB host image is code. From 16 MB on it compresses to 9–27%, i.e. uncompressed artwork and fonts - [VERIFIED] **What the console writes to disk**: No container at all: flat, fixed-size binary NVRAM images, one per file, each exactly 128 KiB — `NVDATA.DAT` for global and setup state, `SCENE001.DAT` and its siblings for scenes. Both open with a similar 20-byte header differing in a single leading type tag, and a scene carries its name as text a few bytes in. This is a memory image written to disk rather than a document format, which is consistent with the bare-metal architecture above - [VERIFIED] **The input patch is global, not scene data**: Established by controlled diff in MixPad offline: changing one input's socket moved five bytes, all of them in `NVDATA.DAT`, and the scene file did not change at all. So a patch list must be read from the global image, and recalling a scene does not repatch the console. The Avantis behaves the same way for the same reason, which makes it worth expecting across the manufacturer's range - [VERIFIED] **Channel state is a fixed-stride record array**: The patch byte is not in a flat table — it sits inside a per-channel record at a 336-byte stride, as a 0-based socket index. The array runs to 122 records, well past the input channels and on into stereo inputs and mix objects at the same stride, with nothing in the file marking where one kind of object ends and the next begins - [VERIFIED] **Names are not in the channel record**: Of the 336 bytes in a record, 22 fall in the printable range and none of them is text, so the channel name lives in a separate table that a default show leaves empty. It has not been located, and MixPad appears not to expose channel naming at all — tried on the strip name button, the selected-channel name, press-and-hold, right-click and the strip-assign grid. That is an absence of evidence rather than a finding, and a show saved off a real console with named channels would settle it #### Update path and security model - [VERIFIED] **Image signing**: None. No RSA, SHA or AES strings anywhere in either image - [VERIFIED] **Update validation**: CRC only, and the variant was not identified — `Error in firmware header`, `VerifyFlash Expected CRC to be 0x%lx, but calculated 0x%lx` - [VERIFIED] **Bitstream encryption**: Not encrypted. The CTL0 write is 0x00001501 with the DEC bit (bit 6) clear and no key-load sequence, so the bitstream is analysable - [VERIFIED] **Recovery**: A/B image banks with Zynq multiboot. The update overwrites the inactive bank; the first-stage bootloader is not in the update file and is not protected by the A/B scheme - [VERIFIED] **Entitlement**: An entitlement mechanism exists and was identified. Analysis stopped there — see the methodology page - [VERIFIED] **The show image carries a four-byte checksum**: The last four bytes of `NVDATA.DAT` change with every edit. Harmless for reading and a hard blocker for writing: a file assembled with a stale checksum may simply be refused, and there is no way to find out without a console. The Yamaha `.CLF` is in exactly the same position — two unrelated manufacturers, the same integrity check with no authenticity in it, and the same consequence for anyone hoping to write a show file rather than read one These rows describe how the platform validates a firmware image, which is a structural fact about its architecture. They are not a vulnerability disclosure, and no exploit, bypass or circumvention technique is published. #### What is programmable, ranked 1. **Interoperability, not rehosting** — The SLink slot takes gigaACE, Dante and Waves cards, and Allen & Heath publish the SQ MIDI/TCP control protocol. Almost anything that motivated looking at the firmware in the first place is better served here, and none of it needs reverse engineering. 2. **The AXI register map — the one thing genuinely worth reversing** — Map the PS↔PL interface and the black box becomes documented hardware: you could drive the stock mixing engine from your own software, which is a far better position than replacing either half. It is also reachable non-destructively, with the stock bitstream loaded and nothing written to flash. 3. **The PL bitstream** — No longer "in principle" — the compression has been expanded, 5,935 frames placed, and the initialised memory decoded to a pan-law table. But the ceiling is real and it is specific: the open-source bitstream-to-netlist path does not support the DSP48 arithmetic primitive, which on a mixing engine is the part you actually want. You can recover the memories, the logic and the interface around the arithmetic, and not the arithmetic. That makes the bitstream a good route to the register map and a poor route to the algorithms, which is the reverse of what it looks like from outside. 4. **Linux on the PS** — Entirely feasible and almost pointless on its own — it gets you Linux wired to a black box you cannot command. Worth doing only as a means of probing the AXI map. #### Traps Mistakes this analysis actually made. Each produced a plausible wrong answer rather than an obvious failure, which is why they are published. - A parser assuming a 0x24 or 0x40 container header parses record 0 plausibly and then lands mid-payload in 55 MB of ARM code and artwork. - The correct load mapping for the host image is VA = file offset + 0x103DD0, pinned empirically from the GUI string-pointer table — not the 0x100000 the container header implies. - GUI strings are reached through a master string-pointer table indexed by string ID, so they have no direct LDR-literal cross-reference. Only BSP and system strings are loaded directly. - Bitstream compression fails silently, not loudly. The standard frame reader decodes the multi-frame-write packet into a named object and then drops it. Feed it a compressed bitstream and you get no error and no warning — you get a frame set that is quietly wrong, and it stays plausible through every downstream stage. Check for those packets before trusting anything built on the frames. - Carved filenames are a guess, and these ones were wrong. The two remote-I/O bitstreams were carved and named for a part that the die measurement and the IDCODE then contradicted. A filename you invented during extraction is not evidence, and it will be quoted back at you three documents later. - "Twenty block RAMs in two columns" was an artefact of the decode, not a finding. An early pass matched initialised memory by shared column base address and reported ten times too many, because roughly ten tiles stack per column and differ only by word offset. Each tile's own word window has to be checked. The corrected answer is two. - Looking for a patch table by scanning for an ascending run of bytes. A default show is patched 1:1, so the values do run `00 01 02 03 …` — but 336 bytes apart, not adjacent. The scan finds nothing, and the obvious conclusion from that — that the format has no patch table — is wrong. The same heuristic works on the Avantis and on the Yamaha `.CLF`, which is what makes it dangerous here. - Anchoring a record signature on a neighbouring byte. The byte after the patch field is a patched/unpatched flag, so requiring it silently dropped every unpatched channel — eight of forty on a default SQ-7 — and produced a patch list with fewer rows than the console and nothing to say any were missing. - Diffing before the app has written anything. MixPad flushes `CurrentShow` on logout, not on edit, so a diff taken straight after a change shows zero bytes moved and looks exactly like a format that does not store the setting. ### Allen & Heath Avantis (2020) - URL: https://aux-opsy.com/platforms/allen-heath-avantis/ - Class: Digital mixing console - Research status: Documented - Last updated: 2026-09-07 - Hardware in our hands: none — analysis is file-derived - Summary: The structural inverse of the SQ: the control plane is a commodity x86 Linux box, and the mixing runs on a subordinate Xilinx FPGA the host programs at boot. OS updates carry integrity checksums but no signature. - Key finding: The V2.01 package is an x86-64 Qt application on a Tiny-Core-derived Linux, and the whole update validates on MD5 alone. The mix FPGA's bitstream is not a file in it because it is inside the `Console` binary — six bitstreams are, and each names its own device: two XC7K160T for the mix engine, two XC7Z010 for FX, and two Artix-7 device images for the remote I/O. - Verdict: The structural inverse of the SQ. Where the SQ hid a whole console inside one bare-metal Zynq, the Avantis splits the job: an open, SSH-able x86 Linux control plane over a Xilinx FPGA audio engine it programs at boot. Nothing on the control side is protected — updates ride on MD5, SSH is a USB flag away — yet the parts worth having, the FPGA engine and the host↔PL interface, are exactly the parts the update package does not hand you. - Open questions (what is NOT known): - The layout of the SPI→AXI register interface. The parts are now named and the bitstreams located, but the whole audio path still sits behind `/dev/ah_audio` and no register is documented — the same black box the SQ-5 has, and the one thing worth reversing here. - Which of the six embedded bitstreams drives which board. The device each is for is stated by its own IDCODE packet; the board each lands on is an inference from size and from the FX and SLink strings around it. Nothing was disassembled and no bitstream was traced to a `FPGALoader` call site. - Whether the `RSA_*` strings mean anything is enabled, or are dormant Xilinx boot-flow capability. - The remaining source type codes. Two of at least five are confirmed — the I/O Port 1, I/O Port 2 and USB sources each have their own tab in Director and their own code, and guessing them would produce a patch list that is confident and wrong. - What the byte before each block label is. It looks like a block size rather than a string length — a graphic EQ carries a larger value than a mix delay, which is consistent with more parameters — but it has not been confirmed, and it is what a structural walk of the scene would need. - Whether the dLive format is byte-identical. dLive Director ships the same structure and the two are very likely one format; no byte-for-byte comparison has been made. #### Method Static analysis of the V2.01 update package: the unstripped `Updater`, the stripped `Console` Qt binary and the plaintext OS scripts (`detect_sbc.sh`, `set_ssh.sh`, `PartitionCheck.sh`). No hardware inspected. The show-file findings are a separate basis: the factory shows shipped inside Avantis Director V2.01, plus controlled diffs taken by running Director offline with no console attached and changing exactly one patch point between two saves. #### Artifacts examined - `AllenHeath-Avantis-V2.01.zip` — Console V2.01 (rev 97241), OS V1.32; covers both Avantis and Avantis Solo - `Updater` — 1 MB x86-64, unstripped: the update and checksum logic in the clear - `dCorefocal64.gz` (~22 MB Tiny-Core-derived x86 rootfs) and `vmlinuzfocal64` (5.4.3 kernel) - The factory shows shipped inside Avantis Director V2.01 — vendor content in a licensed install, reproducible from any install and not redistributed - Two shows saved from Director offline, identical but for one input patched from SLink to a local socket — the diff that located the patch table Referenced by name and version only. Nothing is redistributed. #### Hardware architecture - [VERIFIED] **Control plane**: An x86-64 single-board computer running Linux — the UI/control host only, not the mixer. `detect_sbc.sh` enumerates the shipped boards by CPU: Kontron D3543/D3544 (Gemini Lake J5005/J4005), Advantech AIMB-218 (Elkhart Lake N6211/J6413) and Kontron K3921-H (Alder Lake-N i3-N305 / N97). The console has migrated SBCs across its production life - [VERIFIED] **Audio engine**: A Xilinx FPGA, not the x86 host. `Console` carries the host-side stack — `FPGALoader`, `FPGAMixer`, `FPGAInputProc`, `FPGAMixProc`, `MixRackFPGAProgramming` — with Xilinx boot strings (`FSBL`, `Bitstream not loaded into PL`, `BITSTREAM_DOWNLOAD_FAIL`): a part in A&H's XCVI lineage that the host programs at boot - [VERIFIED] **Mix FPGA — a Kintex-7 pair**: Two × XC7K160T (`0x0364C093`), 16,560 configured frames each. This is the largest fabric on any Allen & Heath platform documented here and it is a genuine surprise: the SQ mixes in the PL of a single XC7Z020, and the Avantis — the bigger console — does it in a pair of Kintex-7 parts with no PS at all, driven by the x86 host over `/dev/ah_audio` rather than by an on-chip ARM. Read from the IDCODE configuration packet of each bitstream - [VERIFIED] **FX DSP**: A separate FDSP/UltraFX effects FPGA driven over an SPI→AXI bridge (`FDSPFxV2SPIToAXIController`, `FDSP Ultra Fx FPGA loader and Programming failed`). The two FX images are for XC7Z010 (`0x03722093`) — Zynq-7010, so "FX FPGA" is really a pair of small Zynq SoCs, which is what the Qu's `Both FX Zynqs booted` says out loud - [VERIFIED] **Remote-I/O device images**: Two Artix-7 bitstreams — XC7A15T (`0x0362E093`) and XC7A35T (`0x0362D093`) — are the device-side images the console pushes down the SLink/gigaACE port to its I/O boxes, not fabric inside the console. The XC7A15T one is byte-identical to the image in the SQ-5 and Qu-7 packages; see the [SQ-5](https://aux-opsy.com/platforms/allen-heath-sq5/) entry - [VERIFIED] **Host ↔ engine interface**: Custom kernel character devices — `/dev/ah_audio`, `/dev/ah_surface`, `/dev/ah_motor_left`, `/dev/ah_motor_right` — form the x86↔FPGA/surface boundary; the Avantis analogue of the SQ's on-chip PS↔PL AXI bridge - [VERIFIED] **Surface / I/O controllers**: Motor-fader, user-control and main-board MCUs (with bootloaders) plus IP port-controllers (IP1/IP4/IP6/IP8/IPG8, all V1.44) are flashed from the host - [VERIFIED] **Storage / boot**: Ordinary PC storage: GRUB on `/dev/sda`, with root, a ~20 GB audio-files partition and swap created at runtime (`PartitionCheck.sh`). No A/B multiboot — a normal Linux boot, unlike the SQ's Zynq multiboot banks #### Software architecture - [VERIFIED] **Operating system**: A Tiny-Core-derived x86 Linux: a `dCorefocal64.gz` root filesystem, a 5.4.3 kernel (`vmlinuzfocal64`, `-tinycore64`) and Ubuntu-18.04-era userland `.deb`s, with X11 and a Qt compositor - [VERIFIED] **Host application**: `Console` — 41 MB stripped Qt5 PIE (links `libQt5Core`/`libQt5Network`) — plus `Compositor` and an 81 MB `TouchScreen` binary. `Updater` ships unstripped - [VERIFIED] **Variants**: One package serves both the Avantis surface and the rackmount Avantis Solo (`Firmware/Solo/…`, Solo OS v1.13); this build is Console V2.01 rev 97241, OS V1.32 - [VERIFIED] **Debug SSH**: sshd is off unless a flag file appears: `set_ssh.sh` starts it at boot if `~/.ah_debug`/`.ah_ssh` exists or an `ah_debug`/`ah_ssh` file sits in the root of an inserted USB stick — an intentional, trivially-triggered service door - [VERIFIED] **A show is a gzipped tar, not a blob**: `Show/` holds 34 subsystem folders, and the small top-level files in them are plain text — a version integer on the first line, then one value per line. `Scenes/` holds one nested tarball per scene, each split into a StageBox half (the I/O rack: preamps and processing, ~320 KB) and a Surface half (strip layout, banks, soft controls, ~16 KB). For a console whose OS updates carry no signature, the show format is correspondingly open - [VERIFIED] **The scene blob labels itself**: The scene payload is binary and about 37 % printable, but every parameter block carries a human-readable label naming both the block type and the object it belongs to — `Parametric EQ, Stereo Group Channel 01 Left`, `Compressor side chain source, Mono Group Channel 02`, `StageBox Port B Analogue Input, Slot 1, Number 07`. One factory show yields a whole block inventory by counting them: 256 stagebox analogue inputs, 144 parametric EQs, 128 compressors, 99 GX analogue inputs, and object classes from input channels down to the main pair. The socket blocks name a slot and a number, which is the same device/index split a patch model needs - [VERIFIED] **Scene 65535 is the live state**: Numbered scenes were byte-identical across a change that visibly altered the console's configuration; the only file that moved was scene `0xFFFF`. That is where the working state lives, and it is where anything reading a show has to look - [VERIFIED] **The input patch is not scene-recallable**: It exists only in the live-state scene — recalling scene 3 does not repatch the console. So a patch list is a property of the show rather than of whichever scene happens to be current, which is the same shape the SQ has and worth expecting across the range - [VERIFIED] **The Channel Mapper table**: Sixteen bytes past a block labelled `Channel Mapper`, the input patch is three bytes per input channel: a source/device type byte, then a 0-based connector index as a big-endian 16-bit value. The default state maps every channel 1:1 to SLink, and moving one channel to a local socket changed its type byte alone — index already 0 — while the stride and 0-based numbering were confirmed by the untouched entries running consecutively either side of it. Only two type codes are confirmed; the I/O Port and USB tabs in Director each have their own and they are unknown #### Update path and security model - [VERIFIED] **Update validation**: Integrity, not authenticity. `Updater` checks the package against `Firmware.md5` (`VerifyChecksum`, `CheckChecksum`, `CorruptFirmware`) and sends header/data checksums to each MCU it flashes. No signature, RSA, GPG or public key anywhere in the update path — the SQ's 'CRC only, no signing' posture, here as MD5 - [INFERRED] **FPGA boot**: The audio FPGA's boot flow references Xilinx RSA authentication that appears switched off (`RSA_SUPPORT_NOT_ENABLED_FAIL`) — matching the SQ's unsigned, analysable bitstream. What, if anything, is actually signed is not established - [VERIFIED] **Add-on entitlement**: A paid add-on system (marketed as the 'dPack') exists; its mechanism is out of scope and was not analysed — see the methodology page These rows describe how the platform validates a firmware image, which is a structural fact about its architecture. They are not a vulnerability disclosure, and no exploit, bypass or circumvention technique is published. #### What is programmable, ranked 1. **Interoperability, not rehosting** — Avantis speaks published control (MIDI/TCP and the SQ/dLive-family remote protocols) and takes gigaACE/Dante/Waves I/O. Almost everything that motivates looking at the firmware is better served here and needs no reverse engineering. 2. **The host ↔ FPGA interface — the one thing worth mapping** — `/dev/ah_audio` and the FPGA programming / SPI→AXI path expose the stock engine to userspace. Documenting that boundary turns the black box into commandable hardware — the Avantis version of the SQ's undocumented AXI register map, and reachable from an SSH shell without writing to flash. 3. **The audio / FX FPGA bitstreams** — Zynq-class and, on the RSA evidence, unsigned — so analysable in principle. In practice a very large project, downstream of understanding the register interface, and not step one. #### Traps Mistakes this analysis actually made. Each produced a plausible wrong answer rather than an obvious failure, which is why they are published. - The x86 host is not the mixer. The audio path is a Xilinx FPGA the host merely programs and drives over `/dev/ah_audio`; reversing the Qt binary tells you about control and UI, not DSP — the same split as the SQ, where the bitstream was the mixer. - `Firmware.md5` is an integrity manifest, not a signature. A repacked update with a recomputed MD5 would pass — do not read the checksum step as authentication. - Crypto strings in `Console` are not proof of a cryptographic validator or a signed update — several digests belong to libarchive and the RSA strings sit in the FPGA boot flow. Attribute crypto only after tracing the specific path. - Comparing two shows with a recursive file diff. Every scene tarball differs on gzip framing alone, so `diff -rq` reports that everything changed and hides the one file that actually did. Compare by content, not by archive bytes. - Expecting the factory shows to answer it. All of them are likely to use the default patch — the difference between an FOH show and a Monitors show is mix structure, not patching — so no amount of diffing them locates a patch table. One controlled diff with a single patch point moved settled in minutes what six shipped shows could not settle at all. ### Allen & Heath Qu-5 / Qu-6 / Qu-7 (2024) - URL: https://aux-opsy.com/platforms/allen-heath-qu/ - Class: Digital mixing console - Research status: Documented - Last updated: 2026-08-14 - Hardware in our hands: none — analysis is file-derived - Summary: The 2nd-generation Qu is SQ silicon in a Qu suit, and not by analogy — it is the same Zynq part, running a mix bitstream of the same size, with the SQ's boot BSP and the Avantis's FX subsystem. - Key finding: The 65 MB Qu-7 update is one unencrypted Xilinx boot image carrying six PL bitstreams, and the one that mixes is for an XC7Z020 — the SQ-5's exact part, at 6,503 configured frames against the SQ's 6,021. "SQ silicon in a Qu suit" started as a read of shared strings; the bitstreams state it outright. - Verdict: The second-generation Qu is not a descendant of the 2014 Qu-16/24/32 — it is the SQ platform in a cheaper suit: the same bare-metal Zynq, the same PS-driver BSP, the same FDSP FX V2 as the Avantis. Anything already learned from the SQ's platform carries straight over; the classic Qu is the genuinely separate, still-undocumented target. - Open questions (what is NOT known): - The classic Qu-16/24/32/Pac/SB (2014) is a separate, earlier platform — NXP Cortex-M + Blackfin + FPGA, bare-metal, no add-ons — documented in its own entry; the two Qu generations share only the brand. - Which of the six bitstreams is which. Three state a device (XC7Z020, XC7Z010, XC7A15T); the remaining three were not attributed to a board, and no bitstream was disassembled — so "the XC7Z020 image is the mixer" rests on its size and on the SQ parallel, not on anything read out of the fabric. - The Qu Dante-16 option card (`QUDante16.q16`, an Audinate module image) was not analysed. #### Method Static analysis of the Qu-7 V1.1.3 (r3931) update .bin and the bundled Qu-5/6/7 update-instructions PDF. No hardware inspected; no bitstream disassembled. The classic Qu-16/24/32 is a separate earlier platform and is not covered. #### Artifacts examined - `Qu-7UPG_1_1_3_r3931.bin` — 65 MB, a single Xilinx Zynq boot image (`XLNX` header, encryption status 0) - `Qu567_FirmwareUpdateInstructions.pdf` — names the generation: Qu-5/5D, Qu-6/6D, Qu-7/7D; update via Qu-MixPad or a FAT32 USB drive in the Qu-A port - `QUDante16.q16` — the Qu Dante-16 option-card image (Audinate module), 28 MB; not analysed here Referenced by name and version only. Nothing is redistributed. #### Hardware architecture - [VERIFIED] **SoC / boot**: A Xilinx Zynq PS (ARM) — boot header `AA995566`/`XLNX` with encryption status 0, `Xilinx First Stage Boot Loader`, `PS7 initialization successful`, and the Zynq PS-driver BSP (`xemacps`, `xscugic`, `xdevcfg`, `xqspips`, `xiicps`, `xttcps`, `xuartps`, lwIP 2.0.3 xilinx_modified) — the identical BSP to the SQ - [VERIFIED] **Audio / FX fabric**: Both the Zynq PL and discrete fabric — this page recorded "no Zynq PL" until 2026-08-14 and that was wrong. Three of the image's bitstreams state their target device outright: the Zynq's own PL is XC7Z020 (`0x03727093`, at file offset `0x1730`, 6,503 configured frames), a second XC7Z010 (`0x03722093`) is the FX Zynq, and one discrete XC7A15T (`0x0362E093`) is the remote-I/O device image. The Zynq PL was read as empty because its bitstream is byte-reversed per 32-bit word for the PCAP boot path, so a normal-order search for the sync word cannot see it - [VERIFIED] **FX subsystem**: A dual-core FDSP FX V2 (`FDSPFxV2SystemCore0/1`, `Both FX Zynqs booted`) — the same FX subsystem as the Avantis, down to identical `Bitstream CRC: 0x8948 / 0xE489` - [VERIFIED] **PS cores**: AMP across the two PS cores, not SMP (`CPU1: clearing flag for CPU0`, a `START_SYNC` shared region, `Handoff Address (Core0/1)`) — the SQ's AMP model - [VERIFIED] **Storage**: QSPI NOR boot (`Boot mode is QSPI`, `xqspips`) plus eMMC for scenes and libraries (`… EMMC ERROR`); USB recording under `/AH_QU/USBRec` - [VERIFIED] **Surface**: Motor-fader MCUs flashed from the host (`Expected Fader Version %u.%u, updating…`) #### Software architecture - [VERIFIED] **Operating system**: None — a bare-metal Xilinx standalone BSP (FSBL + AMP), with no Linux, FreeRTOS or ThreadX strings. The SQ's model, not the Avantis's x86 Linux - [VERIFIED] **This build**: Qu-7 firmware V1.1.3 (r3931); the image also carries `This Qu is running pre-production firmware` and `Silicon Version 3.1` - [VERIFIED] **Update tooling**: Applied over the Qu-MixPad app (iOS/Android/Windows/Mac) or a FAT32 USB stick in the Qu-A port, per the bundled Qu-5/6/7 instructions #### Update path and security model - [VERIFIED] **Boot image**: Unencrypted and analysable — boot-header encryption status `0x00000000`; the FSBL knows about eFUSE security (`EFUSE_SEC_EN bit is set, Encryption is mandatory`) but that bit is not set on this image. The SQ's posture - [VERIFIED] **Update validation**: CRC/checksum only, no signature: `Error, invalid image Checksum`, `PARTITION_CHECKSUM_FAIL`, `Partition DataChecksum 0x%0x != 0x%0x`, `File failed CRC check`. No signing primitive in the update path — the `AES` strings are audio channels - [VERIFIED] **Add-on entitlement**: A paid add-on system exists; its mechanism is out of scope and was not analysed — see the methodology page These rows describe how the platform validates a firmware image, which is a structural fact about its architecture. They are not a vulnerability disclosure, and no exploit, bypass or circumvention technique is published. #### What is programmable, ranked 1. **Interoperability, not rehosting** — The Qu speaks published MIDI/TCP control and the Qu-MixPad protocol, and takes dSNAKE/gigaACE/Dante/Waves I/O. Almost everything that motivates looking at the firmware is better served here. 2. **The Zynq ↔ FPGA interface** — As on the SQ, the audio engine sits in fabric behind the Zynq; mapping that boundary turns it into commandable hardware. Because the platform is shared, this is largely the same work as the SQ's undocumented AXI/config interface. 3. **The FPGA bitstreams** — Unencrypted 7-series and analysable in principle (Project X-Ray covers Artix-7), but a very large project and downstream of the interface. Not step one. #### Traps Mistakes this analysis actually made. Each produced a plausible wrong answer rather than an obvious failure, which is why they are published. - The 2nd-gen Qu (Qu-5/6/7) and the classic Qu-16/24/32 share a name and, on this evidence, nothing else — do not carry a finding from this image back to a 2014 Qu. - Concluding "no Zynq PL" from the absence of a Zynq IDCODE — this page did exactly that and was wrong for a week. A bitstream destined for the PCAP boot port is stored byte-reversed per 32-bit word, so it carries `66 55 99 AA` where the documentation says `AA 99 55 66`. Searching one byte order finds the discrete Artix image, misses the Zynq one, and hands you a clean, confident, inverted answer: the fabric that was recorded as absent is the one doing the mixing. Search both orders, and treat "the IDCODE is missing" as a claim needing evidence rather than a finding. - The `AES` strings are AES3 audio channels and `EFUSE_SEC_EN` is an FSBL capability check — neither is evidence of a signed or encrypted image. The boot-header encryption status (0) is the authority. ### Allen & Heath Qu-16 / Qu-24 / Qu-32 (+ Qu-Pac / Qu-SB) (2014) - URL: https://aux-opsy.com/platforms/allen-heath-qu-classic/ - Class: Digital mixing console - Research status: Documented - Last updated: 2026-08-08 - Hardware in our hands: none — analysis is file-derived - Summary: The original Qu is a 2014 multi-chip embedded design — an NXP Cortex-M control MCU, an Analog Devices Blackfin audio DSP and an FPGA — with every processing block included and no paid-add-on lock. It shares nothing but a brand with the SQ-derived 2nd-gen Qu. - Key finding: The 5.8 MB `.QUU` update is a bare-metal Cortex-M image — its crash handler dumps CFSR/HFSR/BFAR — with A/B host sections and BCC checksums, no Zynq and no paid add-ons. This is the machine the new Qu replaced, not a relative of it. - Verdict: A period-typical 2014 embedded mixer — Cortex-M control, Blackfin audio, an FPGA, bare-metal, unsigned, everything included. Its only real relationship to the SQ-lineage 2nd-gen Qu is the badge; nothing carries across. A poor and largely pointless rehosting target, and now EOL at V1.99. - Open questions (what is NOT known): - The exact parts — the NXP LPC control MCU, the Blackfin variant and the FPGA vendor — are not pinned from strings alone. - How the three processors (CPU / Audio / Surface) partition work and communicate is unmapped. - Whether the surface-less Qu-Pac / Qu-SB differ internally beyond the surface MCU. #### Method Static analysis of the Qu-32 V1.99 (build 4583) `.QUU` update and the V1.99 release notes. No hardware inspected. V1.99 (Nov 2025) is the final maintenance release; one firmware per model (Qu-16/24/32/Pac/SB). #### Artifacts examined - `Qu32UpdtV1_99_4583.QUU` — 5.8 MB, a sectioned flash image (`BCC`-checked Host Section A/B), FAT-based - `Release Notes Qu firmware V1.99.pdf` — V1.99 covers Qu-16, Qu-24, Qu-32, Qu-Pac, Qu-SB - `How to Update Qu Firmware.pdf` Referenced by name and version only. Nothing is redistributed. #### Hardware architecture - [VERIFIED] **Control MCU**: An ARM Cortex-M (ARMv7-M) — the crash handler dumps `CFSR`/`HFSR`/`BFAR`/`MMFAR`/`DFSR`, the Cortex-M fault registers, and `lwiplpc` points to an NXP LPC part. Bare-metal, no RTOS or Linux - [INFERRED] **Audio DSP**: Analog Devices Blackfin (`ABF5T` → ADSP-BF5xx) as a separate audio processor — `CPU ID`, `Audio ID` and `Surface ID` are distinct devices - [VERIFIED] **FPGA**: An FPGA is present (`FPGA not Initialised`); vendor and part not identified from the image - [VERIFIED] **Topology**: A multi-chip board — distinct CPU, Audio and Surface processors, each with its own ID and firmware version — not a single SoC. The opposite of the 2nd-gen Qu's Zynq #### Software architecture - [VERIFIED] **Operating system**: None — bare-metal (a Cortex-M hard-fault handler, no RTOS/Linux strings) - [VERIFIED] **This build**: Qu-32 firmware V1.99 build 4583 (2025-10-02) — the final maintenance release for the range - [VERIFIED] **Range**: V1.99 covers Qu-16, Qu-24, Qu-32, Qu-Pac and Qu-SB, as five per-model firmware files #### Update path and security model - [VERIFIED] **Update validation**: CRC/BCC integrity only, no signature. The sectioned `.QUU` carries a BCC per section (`BCC Matches for Host Section A/B`, `BCC Fails … restarting`) with CRC checks on scenes and libraries (`Bad CRC from external Library …`). No RSA/SHA/signing — the `AES` strings are audio - [VERIFIED] **Recovery**: A/B host sections provide a failed-update fallback (`BootUpdateCheckHostBCCSectionA`, restart-on-BCC-fail) - [VERIFIED] **Add-ons**: None. There is no paid add-on or entitlement system — every effect and processing block ships enabled. The only `Unlock` is the front-panel screen lock. A sharp break from the SQ-derived 2nd-gen Qu These rows describe how the platform validates a firmware image, which is a structural fact about its architecture. They are not a vulnerability disclosure, and no exploit, bypass or circumvention technique is published. #### What is programmable, ranked 1. **Interoperability, not rehosting** — The Qu speaks MIDI/TCP control and the Qu-Pad/Qu-You apps and takes dSNAKE I/O. Anything that motivates looking at the firmware is better served here. 2. **Rehosting is a poor fit** — A multi-chip 2014 embedded design with no single SoC to replace, and the audio living in Blackfin + FPGA behind opaque interfaces. There is no equivalent of the SQ's one-Zynq target. 3. **The .QUU container** — Simple and unsigned — sections guarded by a BCC — so trivially repackable for integrity. There is little reason to, and it buys no access to the audio path. #### Traps Mistakes this analysis actually made. Each produced a plausible wrong answer rather than an obvious failure, which is why they are published. - Do not conflate with the 2nd-gen Qu (Qu-5/6/7): that is a Xilinx-Zynq, SQ-derived platform; this shares only the name. - The `Unlock` string is the front-panel screen lock, not a paid add-on — the classic Qu has no entitlement system at all. - `BCC` is a block-check integrity value, not a signature; the A/B sections are for update resilience, not authentication. ### Allen & Heath Dante option card (KLANTE) (2024) - URL: https://aux-opsy.com/platforms/allen-heath-dante-klante/ - Class: Networked audio module - Research status: Documented - Last updated: 2026-08-08 - Hardware in our hands: none — analysis is file-derived - Summary: A&H's in-house replacement for the Audinate Brooklyn II Dante module, codenamed KLANTE: a Xilinx Zynq-7010 running embedded Linux that hosts Audinate's Dante IP core. It is the one A&H firmware in this index that is cryptographically signed. - Key finding: The 28 MB `.q16` is a SWUpdate package — and unlike every A&H console here, it is RSA-signed with per-image SHA-256. Inside is a whole Zynq-7010 Linux system (`KlanteDDR_7010_RU.bit`, U-Boot, kernel, JFFS2) running the Dante IP core, not an off-the-shelf Audinate module. - Verdict: The interesting outlier of the A&H set. Where the consoles are unsigned and open, this little Zynq-7010 — A&H's in-house Brooklyn II replacement — runs Linux and is the only firmware here protected by a real signature. The strong crypto in this ecosystem lives on the Audinate-derived network module, not on the mixers. - Open questions (what is NOT known): - Whether Zynq-7010 secure boot (an RSA-authenticated FSBL) is actually fused on, or only SWUpdate-level signing is enforced — `boot.bin` was not checked for an authentication certificate. - Which A&H products share the KLANTE module — it is framed as a general Brooklyn II replacement, not Qu-specific. - The Dante IP-core version inside `cap1.bin` and its licensing terms. #### Method Static analysis of `QUDante16.q16` V1.0.2 — a SWUpdate cpio whose `sw-description` manifest, `.bit` bitstream header and boot payloads name every component. No hardware inspected. #### Artifacts examined - `QUDante16.q16` — a SWUpdate cpio: signed `sw-description` + `.sig`, `boot.bin`/`u-boot.img`/`uImage`/`devicetree.dtb`, rootfs, FPGA bitstream, and the Dante `cap1.bin` - `KlanteDDR_7010_RU.bit` — Vivado 2019.2, part `7z010clg400`, design `KlanteDDRTop`, built 2024-10-31 Referenced by name and version only. Nothing is redistributed. #### Hardware architecture - [VERIFIED] **SoC**: Xilinx Zynq-7010 (XC7Z010, clg400) — IDCODE `0x03722093` read straight from the bitstream, part string `7z010clg400`; the manifest's `hardware-compatibility: 7010revA` agrees - [VERIFIED] **Role**: A&H's own Dante endpoint, codenamed KLANTE (string `KLANTE - Brooklyn II replacement module`) — it replaces the Audinate Brooklyn II (itself a Zynq-7020) with a cheaper 7010 running the Dante IP core in software - [VERIFIED] **Dante core**: Dante is a loadable IP core, not a drop-in module: `cap1.bin` is installed to `/dante/cap/cap1.bin` (the manifest's `files` section) — Audinate Dante IP licensed onto A&H silicon - [VERIFIED] **FPGA design**: `KlanteDDRTop`, Vivado 2019.2, bitstream `KlanteDDR_7010_RU.bit` built 2024-10-31 — the small 7010 PL that carries the Dante glue #### Software architecture - [VERIFIED] **Operating system**: Embedded Linux, not bare-metal — Buildroot 2018.02 (arm gnueabihf, gcc 6.4.0), booted FSBL (`boot.bin`) → U-Boot → `uImage` + `devicetree.dtb` + `rootfs.cpio.uboot` initramfs, with a `userddr.jffs2` data partition. The only A&H design examined here that runs Linux on audio-side silicon (the AHM's silicon is unidentified) - [VERIFIED] **This build**: QUDante16 V1.0.2; internal version `0.1.0`, codenames KLANTE / 'Blueberries' #### Update path and security model - [VERIFIED] **Signed updates**: The one signed A&H firmware among those examined here — the AHM's update package has not been opened, so it is not a counterexample either way. SWUpdate verifies a detached `sw-description.sig` (256 B → RSA-2048, raw PKCS#1) over the manifest, and every image and script carries a SHA-256 checked against that signed manifest — authenticity, not just integrity, the opposite of the consoles' CRC/MD5-only posture - [VERIFIED] **Recovery**: Full A/B redundancy: every partition has a `_backup` twin (kernel, FPGA, rootfs, device-tree, u-boot) written in the same update - [VERIFIED] **Container**: `.q16` is a plain SWUpdate cpio — `cpio -idm` yields the whole parts list (manifest, signature, boot chain, bitstream, Dante cap) These rows describe how the platform validates a firmware image, which is a structural fact about its architecture. They are not a vulnerability disclosure, and no exploit, bypass or circumvention technique is published. #### What is programmable, ranked 1. **It is a Dante endpoint — interoperate, don't rehost** — The whole point of the card is Dante/AES67 networking; Dante Controller and the published protocols cover the useful surface without touching firmware. 2. **The signature is the real barrier** — Unlike the consoles, the update path is RSA-signed, so swapping firmware is gated by the signing key rather than a recomputable checksum. Whether Zynq secure boot is also fused is the open question. 3. **The 7010 FPGA** — A small Zynq-7010 PL (`KlanteDDRTop`) carrying the Dante DDR/MAC glue — analysable in principle but niche, and downstream of no useful goal. #### Traps Mistakes this analysis actually made. Each produced a plausible wrong answer rather than an obvious failure, which is why they are published. - `.q16` is a standard SWUpdate cpio, not an opaque A&H container — `cpio -idm` plus the `sw-description` manifest give you the entire parts list and per-file hashes. - The RSA signature is over the SWUpdate manifest (`sw-description.sig`), which is not the same as Zynq secure boot — a signed update does not prove the boot chain itself is authenticated. - KLANTE is a general Brooklyn II replacement, so a finding here may generalise to any A&H product that uses this Dante card, not just the Qu. ### Allen & Heath AHM-16 / AHM-32 / AHM-64 (2019) - URL: https://aux-opsy.com/platforms/allen-heath-ahm/ - Class: Installed system processor - Research status: Partial - Last updated: 2026-08-14 - Hardware in our hands: none — analysis is file-derived - Summary: The install processor whose control surface is half published. Allen & Heath document levels, mutes, routing crosspoints, preset recall and source selection — and nothing at all for EQ, delay, dynamics or metering. The other half lives in System Manager's own protocol and in its configuration files, and only the files have been opened. - Key finding: A `.cfg` system file is gzip → tar, and the model is carried by the filename of a zero-byte member. Inside, `CurrentSettings.dat` names the entire per-zone chain in plain text — delay, parametric EQ, xGEQ, GEQ-PEQ, compressor and side chain, insert, limiter, allpass — and not one of those blocks' field layouts is decoded. No firmware image has been opened and the silicon is unknown. - Verdict: The lopsided one. This is the only platform in the index examined entirely from the desktop software that configures it, and it shows: the control surface Allen & Heath publish is fully implemented and needed no reverse engineering at all, while everything a system processor exists to do — EQ, delay, dynamics, metering — is present here only as a list of block names and the byte gaps between them. The container is solved, the parameter model is named, and no parameter is decoded. Nothing is known about the silicon, and nothing from the SQ, Qu or Avantis transfers on the strength of the badge. - Open questions (what is NOT known): - The hardware, entirely — CPU, DSP, FPGA, memory, boot. No firmware image has been opened, and the other Allen & Heath platforms in this index are not evidence about a rackmount install processor. - Every `.dat` field layout. The blocks are named and their sizes are bounded; not one parameter's offset, encoding or scale is known. - Whether the 250-byte gap after `Parametric EQ, Zone NN` is an 8-band PEQ at roughly 28 bytes per band. It fits the arithmetic and it has not been tested — the gap is an upper bound on the payload, not a measurement of it. - `Mixer.cfg` line 4. It is an independent field, and no weighting of the pairing maps reproduces all three Default configs. - The whole of System Manager's wire protocol: how it discovers units, what identity a unit must present to appear in its device list, and what handshake precedes the parameter stream. - Whether a unit echoes a level or mute write back to the client that sent it. It is undocumented, and it changes how any controller has to be written — ours does an explicit debounced read-back rather than rely on an echo. Preset recall is documented as echoed to everyone. - Two rows of Allen & Heath's own published level table disagree with the formula printed in the same document — +5 dB (table 118, formula 116) and −47.5 dB (table 0, formula 1). Neither can be settled without a unit. #### Method Static analysis of files that ship inside AHM System Manager 1.61 on macOS — the six factory `.cfg` configs from `FactoryConfigs{,16,32}/` — cross-read against Allen & Heath's published AHM TCP/IP Protocol V1.0*. No AHM hardware was involved: none is available and none is expected, so every method has to work from files alone. The firmware images in the System Manager bundle have not been opened. Protocol behaviour was exercised only against our own simulator built from the same published document, which demonstrates self-consistency and not hardware compatibility. #### Artifacts examined - `AHM System Manager 1.61` (macOS) — a Qt 6 application, licensed and installed on our own machine; no Allen & Heath file is redistributed - Six factory configs — `AHM-16/32/64 Default.cfg` and `AHM-16/32/64 Empty.cfg`, format version `V1.6X - Rev. 92182 - Build. 30` - `Firmware.tar.xz` (26 MB) and `IP1/IP4/IP6Firmware.bin` (~440 KB each), present in the System Manager bundle — referenced only; neither opened - AHM TCP/IP Protocol V1.0 — Allen & Heath's published specification Referenced by name and version only. Nothing is redistributed. #### Hardware architecture - [VERIFIED] **Model range**: One platform in three sizes — AHM-16, AHM-32, AHM-64. `Mixer.cfg` geometry scales exactly with the model across all six factory configs: the mono/stereo pairing maps are N/2 characters and the two fixed lines are N and N/2 long for N = 16, 32, 64. The geometry is what confirms the size, not the badge - [UNKNOWN] **Silicon**: Not established. Nothing in this analysis identifies a CPU, DSP, FPGA or memory — no AHM firmware image has been opened. The SQ, Qu and Avantis findings in this index are not evidence about a rackmount install processor and are not carried across - [VERIFIED] **I/O classes**: Three are named inline in `CurrentSettings.dat`, each numbered per instance: `$Unit Analogue Input`, `Port B Digital Input` and `Unit SLink Digital Output` — local analogue in, an option-card digital input port, and A&H's SLink expansion bus on the output side - [INFERRED] **Port controllers**: `IP1Firmware.bin`, `IP4Firmware.bin` and `IP6Firmware.bin`, ~440 KB each, ship inside System Manager 1.61. IP1/IP4/IP6 is the same port-controller naming the Avantis package flashes from its host, so these are near-certainly the AHM's I/O port controllers rather than the main application — but neither image has been opened and the correspondence rests on the naming alone - [UNKNOWN] **Main firmware**: `Firmware.tar.xz`, 26 MB, plain xz, in the System Manager bundle. Its size relative to the port-controller images makes it the main application, and nothing beyond that is established: not its contents, not its packaging, not what it runs on #### Software architecture - [VERIFIED] **System Manager**: AHM System Manager 1.61 for macOS — a Qt 6 application with its QML compiled into the binary (the bundled `Resources/qml` tree is stock Qt, not the app's own). It carries the AHM firmware images and the six factory configs inside its own bundle - [VERIFIED] **Config container**: A `.cfg` system file is gzip → POSIX tar with a fixed `archive/` member set: `Mixer.cfg` (plain-text geometry), `Version.txt`, a unit-type marker, `CurrentSettings.dat`, `Scene1.dat`, and a `Devices/` directory that is empty in every factory file. A reader for the container parses all six - [VERIFIED] **Model marker**: The model is carried by the filename of a zero-byte member — `archive/UnitType_AHM-64-Unit.txt` has no contents. Reading the file gets you nothing; the name is the field, and it agrees with `Mixer.cfg`'s geometry in every factory config - [VERIFIED] **`Mixer.cfg`**: Six CRLF-terminated lines: a `MixerConfigV2` magic, two N/2-character mono/stereo pairing maps (`m` mono, `S` stereo, one character per channel pair), a decimal count, and two fixed fill lines of N and N/2 characters - [UNKNOWN] **`Mixer.cfg` line 4**: An independent field of undetermined meaning. On the three Empty configs it equals N, which reads as two per map slot — but no per-slot weighting reproduces the Default configs (12 / 24 / 52 against 14 / 30 / 60 for mono=2 stereo=1, or 12 / 28 / 56 for mono=2 stereo=0), and the shortfall tracks neither N nor the stereo count. It is parsed verbatim and nothing downstream depends on it - [VERIFIED] **Parameter blobs**: `CurrentSettings.dat` and `Scene1.dat` are a fixed-layout struct dump, not a tagged format. Sizes differ between Default and Empty for the same model — 543,794 vs 567,365 bytes for the AHM-64's `CurrentSettings.dat` — so the blobs are not a fixed-size image and carry length-varying content, most plausibly name and label regions - [VERIFIED] **Named processing chain**: The blobs store their block labels inline, which gives the per-zone parameter model for free: `Mix Delay`, `Parametric EQ`, `xGEQ`, `GEQ-PEQ`, `Compressor`, `Compressor side chain`, `Insert`, `Limiter`, `Allpass EQ`, and `Source Select Controller`. Every block worth targeting is named. Not one field layout inside them is decoded - [VERIFIED] **Record strides**: In the AHM-64 Default file the input-unit records begin at `0x834A` on a 38-byte stride, and the source-selector slots are on a 17-byte stride. The zone-processing labels that follow sit at irregular offsets, and the gap to the next label bounds each block's payload: 25 bytes after `Mix Delay`, 250 after `Parametric EQ`, 635 after `xGEQ`, 245 after `GEQ-PEQ`, 137 after `Compressor`, 21 after `Insert`, 27 after `Limiter` - [PUBLIC] **Published control protocol**: AHM TCP/IP Protocol V1.0 is SysEx over TCP under the `00 00 1A` Allen & Heath manufacturer ID. Strips are addressed as a MIDI channel plus note number — inputs on channel 0, zones on 1, control groups on 2 — with messages for mute, level, level increment, send level and send mute, preset recall, audio playback and source select. The source-select reply adds a colour byte and an ASCII name - [PUBLIC] **What the protocol omits**: There is no published message for PEQ, GEQ, delay, compressor, limiter or metering, and no way to read a channel or preset name back. The published surface is levels, mutes, routing crosspoints, preset recall and source selection, and it stops there. Everything else travels over AHM System Manager's own protocol, which Allen & Heath do not publish - [PUBLIC] **Level curve**: `LV = floor(((dB + 48) / 58) × 127)`, covering −48 dB to +10 dB in a single 7-bit byte, with `0x69` (105) at unity. It truncates; it does not round, and the manufacturer's own reference table only reproduces under truncation - [VERIFIED] **Verification basis**: The protocol implementation is exercised against a simulator built from the same published document. That demonstrates the two agree with each other; it is not hardware verification, and nothing on this page has been confirmed against an AHM #### Update path and security model - [PUBLIC] **Control-plane authentication**: Two published ports. TCP 51325 is plaintext with no authentication of any kind. TCP 51327 is the TLS variant and expects a user profile (`0x00`–`0x1F`) and password first, replying `AuthOK` or dropping the link. Only the open port has been implemented here - [UNKNOWN] **Firmware validation**: Unknown, and deliberately not assumed. No AHM firmware image has been opened, so nothing is established about packaging, checksums or signing. The unsigned, CRC-or-MD5-only posture documented on the SQ, Qu and Avantis is a finding about those platforms and says nothing about this one - [VERIFIED] **Configuration files**: The `.cfg` container carries no signature, no checksum member and no obfuscation — it is an ordinary gzip tar. What makes a system file opaque is that its `.dat` payloads are an undocumented fixed-layout struct dump, not that they are protected These rows describe how the platform validates a firmware image, which is a structural fact about its architecture. They are not a vulnerability disclosure, and no exploit, bypass or circumvention technique is published. #### What is programmable, ranked 1. **Interoperability — and half of it is already published** — Levels, mutes, input→zone and zone→zone crosspoints, preset recall and source selection are all in Allen & Heath's own document, and a complete system controller has been built from that document alone. Nothing in that half requires reverse engineering, and it is the half most integration work actually needs. 2. **The `.cfg` field layout — the cheapest route to the other half** — The system file is the same parameter model at rest, and it is fixed-layout, so byte offsets are stable between saves of one model. That makes System Manager itself the oracle: change one parameter, save, diff. Deterministic, offline, no hardware and no network. The prerequisite is the save-to-save noise floor — what the application rewrites even when nothing changed — because until that is known no later diff can be read. 3. **System Manager's own protocol** — Discovery, identity and the session handshake are the gate; past it the application synchronises the whole system state, which would be every parameter in protocol form. None of that wire format is known — not how units are discovered, not what identity a unit must present, not what handshake precedes the parameter stream. 4. **Firmware, last and unopened** — 26 MB of plain xz plus three ~440 KB port-controller images sit in the System Manager bundle. Reading a parser is the reliable way to resolve a handshake and far more work than watching one, which is why it is last — and it is also why every hardware question on this page is currently unanswerable. #### Traps Mistakes this analysis actually made. Each produced a plausible wrong answer rather than an obvious failure, which is why they are published. - Trusting a run of printable bytes in a `.dat` to be one field. The string fields are fixed-width and are not cleared on write, so a short value leaves the tail of its predecessor in place and raw `strings` output is full of chimeras — `Unit AnalSource3`, `ue InputSource13`. A naive extractor does not fail visibly; it produces confident nonsense. Read the record stride, not the text: the source-selector slots are on 17 bytes. - Deriving `Mixer.cfg` line 4 from the pairing maps. On the three Empty configs it equals the model size exactly, which reads as two per map slot and confirms beautifully — on half the sample. No weighting reproduces the Default configs as well. Validating a rule only against the subset that suggested it is what makes this one plausible enough to ship. - Assuming the published level formula rounds. It truncates. At −40 dB the manufacturer's table gives 17 where rounding gives 18, and at −45 dB it gives 6 where rounding gives 7 — an off-by-one visible on two printed rows and nowhere else. The truncation then needs an epsilon in floating point: without it byte 3 lands at 2.9999999999999996 and a byte → dB → byte round trip silently loses a step. - Reading "500 presets in four banks of 128" as 512 slots. The fourth bank runs 385–500, so it holds 116. A recall grid that offers all 128 in bank 4 is offering twelve presets that do not exist. ### Midas HD96 (2021) - URL: https://aux-opsy.com/platforms/midas-hd96/ - Class: Digital mixing console - Research status: Documented - Last updated: 2026-08-07 - Hardware in our hands: none — analysis is file-derived - Summary: The one console examined whose control plane is wide open: a full Linux computer speaking documented Thrift and Protobuf. The mixer still lives in FPGA fabric — but the bitstreams ship in the clear and the exact parts are known. - Key finding: Three stacked computers: a Jetson TK1 runs the GUI and the control protocols, an ARM920T card runs the mixer, and the mixing itself is an unencrypted Spartan-6 bitstream that ships inside the update. - Verdict: The most open console examined. The mixer still lives in FPGA fabric, as on the SQ-5 — but here the bitstreams ship in the clear, the exact Spartan-6 parts are known from their IDCODEs, and, unlike the SQ, the entire control plane is documented RPC you can speak now. The value is interop, and it is unusually reachable; rehosting remains pointless for the same reason it was on the SQ. - Open questions (what is NOT known): - Do ports 9990 and 9094 bind off-box on a shipping desk, and is the Protobuf bus authenticated? The mDNS advert and --allowRemoteClients true suggest reachable, but this was never tested on hardware. - The parameter key/category namespace — the actual channel/bus/FX addressing — is the last missing piece for real show control, and needs a live console to enumerate. - The register/AXI interface between the ARM920T host and the HMAC Spartan-6 is undocumented — the same black box the SQ-5 has, one layer down. - The exact SHARC part and the _MultiApp DSP loader format are unexamined. #### Method Static analysis of the public 2.4.0 firmware bundle — a signed APT repository plus Yocto board images. No hardware was inspected. The bundle was retained, so every claim here re-derives from it. Two protocol schemas were recovered whole: the Thrift RPC from shipped generated Python, the Protobuf bus from descriptors embedded in a shared library. #### Artifacts examined - `hd96_2.4.0+sumo.329.tar` — VERSION/WHATSNEW/`CHECKSUMS`(.asc), `repo.tar` (APT repo, 108 debs), `mghd271-tk1-*.tar.gz` (Yocto images) - `libPufferfishCore.so` — embeds all 13 control-bus `FileDescriptorProto`s - `as88_hmac.bin` / `as88_smac.bin` — the mix-engine FPGA bitstreams, shipped inside `mkt-as88` Referenced by name and version only. Nothing is redistributed. #### Hardware architecture - [VERIFIED] **Host SoC**: NVIDIA Tegra124 (Jetson TK1), quad Cortex-A15 — from the device tree `compatible = "nvidia,jetson-tk1","nvidia,tegra124"` and the `armhf` package architecture - [VERIFIED] **Host DRAM**: 2 GB — the DT memory node is `reg = <0x80000000 0x80000000>`. A 4 GB boot-config (`bct_hyinx_4gb`) also ships, so a 4 GB surface variant exists - [VERIFIED] **Mix-engine card (AS88)**: A separate board hosting the mixer: an ARM920T running a Buildroot 2013.08 / uClibc Linux under the RedBoot bootloader, from 32 MB of NOR flash mapped at `0x60000000`. Build path `KES-BR14-ARM920T`, RedBoot FIS table intact - [VERIFIED] **Mixing FPGA (HMAC)**: Xilinx Spartan-6 XC6SLX100 — proven from the bitstream's own IDCODE register write (`0x04011093`) in `as88_hmac.bin`, not inferred - [VERIFIED] **AES50 SuperMAC FPGA (SMAC)**: Xilinx Spartan-6 XC6SLX150 — IDCODE `0x0401D093` in `as88_smac.bin`. This is the Klark Teknik SuperMAC/HyperMAC AES50 fabric - [VERIFIED] **Host-side FPGAs**: Routing and IO on the TK1 side: `mkt-fpga-{io,io2,ve,ctrl,hd272-ctrl,hd272-audio}` plus expansion-card ASRC/AES50 images, flashed by `mkt-fpga` - [INFERRED] **FX DSPs**: An Analog-Devices-SHARC-family DSP subsystem reached through `/dev/dayak`; 63 effects ship as loadable `*_MultiApp` DSP images. Part not pinned — inferred from `ADSP-` strings and the loader format - [VERIFIED] **Ethernet switch**: Marvell `mv88e6096` managed switch, configured over memory-mapped IO-FPGA access (`mkt-hd271-switch-config`) - [VERIFIED] **Expansion / STM32**: An STM32 “HD271C” expansion-card handler (`mkt-hermes`) drives AES50 option cards and DP48 stageboxes; IO boxes are controlled in-band over the AES50 port (`HardwareControlOverAesPort`) #### Software architecture - [VERIFIED] **Host OS**: Full embedded Linux — Yocto “sumo”, `ID=hd271dist` 2.20.1, kernel 3.10.40, u-boot 2014.10. Not bare metal - [VERIFIED] **Application layer**: A signed APT repository of 108 packages (57 first-party `mkt-*`) layered on the Yocto rootfs; `mkt-heritage` is the top-level that pulls in the whole console - [VERIFIED] **GUI**: `squig` — a CEF/Chromium-backed application (cef-runtime 3770), run as `offlinesquig … --allowRemoteClients true` - [VERIFIED] **Control bus**: `pufferfish` — Protobuf over ZMQ/TCP on port 9990, advertised over mDNS as “HD96Control”. The complete proto3 schema (13 files, namespace `serialisation`) was recovered from embedded descriptors - [VERIFIED] **Parameter model**: A string-key + category addressed tree: `ParameterRequest{subscribe|unsubscribe; identifier{category|key}}` → `ParameterEvent{Parameter{key,value,categories}}`, values a 10-arm `ConsoleValue` union. Show control is a 41-verb `AutomationEvent` enum (recall / store / loadShow / fireEvent / import·exportShow) - [VERIFIED] **Test RPC**: `razorbill` — Apache Thrift on port 9094, three privilege tiers up to a 60-method development interface (SetParameterValue, Screenshot, InstallPackage, Reboot, user-DB inject/wipe). The IDL was read back from shipped generated Python - [VERIFIED] **Surface variants**: HD96-16, HD96-24 and HD96 Air, six surface bays and dual mix engines (HMAC1/HMAC2) — leaked verbatim by the `DebugEvent` enum (`surfaceConfigHd96_16_revA`, `_24_revD`, `_Air_revA`) #### Update path and security model - [VERIFIED] **Package trust**: The update is a GPG-signed APT repo (Origin “Music Group Research UK”) with a SHA-256 `CHECKSUMS` manifest and a detached PGP signature over it. Package-level integrity, not silicon secure boot - [VERIFIED] **Mixing bitstream encryption**: None. Both Spartan-6 bitstreams ship unencrypted — the AA·99·55·66 sync word is in the clear and there is no key-load sequence. They are byte-bit-reversed for flash storage, nothing more - [INFERRED] **Host secure boot**: No evidence of fused Tegra secure boot. u-boot carries the Tegra `bootsecure`/BCT infrastructure, but nothing indicates the fuses are burned on shipping hardware - [INFERRED] **Control-plane auth**: None observed. `razorbill` Thrift is a plain `TBinaryProtocol` server with no authentication; `squig` runs with --allowRemoteClients true. Whether 9990/9094 bind off-box on a shipping desk is unconfirmed - [VERIFIED] **Entitlement**: A device-uniqueness / licensing mechanism exists (`mkt-uniqueness`, the mCloud proxy). Its content was not analysed — see the methodology page These rows describe how the platform validates a firmware image, which is a structural fact about its architecture. They are not a vulnerability disclosure, and no exploit, bypass or circumvention technique is published. #### What is programmable, ranked 1. **Interoperability via the recovered protocols** — `pufferfish` (Protobuf, :9990) is a live parameter/metering bus and `razorbill` (Thrift, :9094) a full control RPC. Both schemas are recovered. This is a console you can speak to today, and it is where the value is. 2. **The Spartan-6 mixing fabric** — Unencrypted and identified (XC6SLX100 / XC6SLX150), so analysable with Project X-Ray. But replacing it means writing a mix engine against an unpublished pinout and an ARM920T host interface. 3. **Rehost the Jetson TK1** — Trivially Linux-friendly — but replacing the TK1 software leaves you wired to the same engine cards, which is the part that actually mixes. It buys nothing, exactly as on the SQ-5. #### Traps Mistakes this analysis actually made. Each produced a plausible wrong answer rather than an obvious failure, which is why they are published. - Two unrelated “HMAC”: the Midas mix-engine path, and hmac-md5/hmac-sha crypto strings in the AS88 rootfs. They are not related. - The FPGA bitstreams are byte-bit-reversed for flash, so a naive search for the AA·99·55·66 sync word fails until each byte is reversed. - The embedded Protobuf descriptors cannot be carved by binary-searching the length — descriptor validity is not monotonic. A field-by-field wire-format walk is required to find each descriptor's exact end. ### Behringer WING Compact (2022) - URL: https://aux-opsy.com/platforms/behringer-wing-compact/ - Class: Digital mixing console - Research status: Active - Last updated: 2026-08-07 - Hardware in our hands: none — analysis is file-derived - Summary: The only platform here being studied to build on rather than to understand — and the only one where the container format was analysed, found opaque, and then declared off the critical path. - Key finding: The `.wingfw` container is opaque but structured, and specifically not a block cipher: the index of coincidence is flat across every stride from 1 to 1024, so there is no repeating key — and all 171 constant runs of ≥64 bytes are 0xFF with not one 0x00 run. No block cipher leaves those. - Verdict: Hardware-wise the most open platform surveyed, and the only one where a full replacement OS is a coherent goal. Everything now rests on two cheap lid-off questions and one fuse. - Open questions (what is NOT known): - Is the i.MX 6 serial-download path reachable, and are the HAB fuses unburned? Everything else is downstream of this. - Discrete PHY behind each etherCON, or straight to fabric? - Is anything in the clock tree steerable? #### Method Static analysis of a vendor firmware image, plus published datasheets and standards. No hardware has been acquired, so every hardware row is provisional and marked accordingly. A written clean-room policy governs the project. #### Artifacts examined - A `.wingfw` release image, version 3.1.1 (fullsize variant — nothing learned from it may be assumed to hold for Compact) - NXP i.MX 6 datasheet and reference manual - Analog Devices and Efinix product documentation Referenced by name and version only. Nothing is redistributed. #### Hardware architecture - [PUBLIC] **SoC**: NXP i.MX 6 series, ARM Cortex-A9, MAPBGA 624 - [INFERRED] **Core count**: 1 (Solo-class) on the Compact — to be confirmed by reading SCU config on hardware. Note that OpenWING's device tree for the fullsize WING is named `imx6dl`, i.e. DualLite and dual-core. That does not settle the Compact, but it does rule out assuming one answer across the family - [UNKNOWN] **DRAM**: 1 GB at architectural base 0x10000000. Size unconfirmed — read the part markings - [UNKNOWN] **Boot flash**: SPI-NOR, ~8 MB, s25fl064k-class. Unconfirmed - [INFERRED] **DSP**: 4 × Analog Devices ADSP-21489 SHARC — where the mixing actually happens - [INFERRED] **FPGA**: Efinix Trion T55 or T85 — the I/O fabric, not the mixer. Converter TDM, clock tree, AES50, StageConnect, AES/EBU and DSP links - [INFERRED] **FPGA config**: SPI slave from the i.MX 6 - [UNKNOWN] **Network switch**: Marvell 88E6085 behind the i.MX 6 FEC, RMII 100 Mbit fixed-link. Unconfirmed - [UNKNOWN] **AES50**: etherCON, 100BASE-TX physical layer. Whether a discrete PHY sits behind each port is unknown and decides whether those ports could ever carry anything else - [UNKNOWN] **Clock tree**: Steerable (VCXO/DCO) or fixed crystals — unknown. This single question decides whether AES67 is possible on this hardware at all - [PUBLIC] **StageConnect**: USB-C physical layer, 32 channels - [UNKNOWN] **Panel**: LVDS 1280×800, USB HID touch, motorised faders, per-strip colour scribble LCDs. Panel timings deliberately not recorded — they belong to the panel's datasheet #### Software architecture - [PUBLIC] **Boot path**: The i.MX 6 has a documented, public boot ROM with a USB serial-download path. This is the fact that makes the whole project viable - [VERIFIED] **Container**: `.wingfw`, 38,873,600 bytes for the 3.1.1 fullsize release — no header and no magic. The image is not stationary, so averaging over it misleads: the head (`0x00000000`–`0x00230000`) measures entropy 7.9999 with an index of coincidence of 1.0000, exactly uniform on both, while the bulk beyond it measures 7.3006 and 3.0321. The split is the finding - [VERIFIED] **Container — constant runs**: 171 constant runs of 64 bytes or more, 92,706 bytes in total, and every one of them is 0xFF — not a single 0x00 run. No block cipher leaves those intact - [VERIFIED] **Container — byte statistics**: Histogram strongly skewed low: 0x00 at 5.6%, then 0x02, 0x01, 0x08 and 0x0f each around 2.6–3.0% against a uniform 0.39%. That rules out AES over the bulk. Index of coincidence flat across every stride 1–1024, so no repeating XOR key. No ECB structure - [VERIFIED] **Container — negative sweeps**: No squashfs, ELF, gzip, xz, zstd, UBI, JFFS2, cramfs, DTB, uImage, zImage or i.MX IVT magic — none plain, and none under 256 XOR constants, 256 additive constants, nibble swap, bit reversal or positional XOR. A brute-force deflate/zlib/LZMA sweep over the first 8 MB returned zero hits - [INFERRED] **Container verdict**: Opaque but structured, and not a block cipher. Decoding it is never required in order to write a replacement OS - [PUBLIC] **Container — solved elsewhere**: OpenWING ships `wingfw_unpack`, `wingfw_repack` and `wingfw_verify`, so the format is solved by someone. What is public is only a loader shim: the implementation and, in their words, "any secret constants" live in a separate private repository. So the honest status is not unsolved — it is unsolved here, solved and deliberately withheld there - [VERIFIED] **Measurement stack portability**: The Stage 1 premise is verified in software: the DSP measurement code compiles for `armv7-unknown-linux-gnueabihf` with no linker, sysroot or system library, and all 133 of its tests pass on a 32-bit target. ARM code generation and VFP behaviour remain open — only qemu or the hardware settles those - [PUBLIC] **DSP toolchain**: ADI CrossCore — proprietary, licensed - [PUBLIC] **FPGA toolchain**: Efinix Efinity — free, but proprietary #### Update path and security model - [UNKNOWN] **Secure boot**: Unknown, and it is the project's kill criterion. If the i.MX 6 HAB fuses are burned closed, the platform is not open and the work stops. That an independent project boots its own Linux on a WING is strong practical evidence the answer is favourable, though it has not been confirmed for the Compact specifically - [UNKNOWN] **Container**: Obfuscated by a mechanism not identified here, and off the critical path. Not a block cipher, on the constant-run and index-of-coincidence evidence above These rows describe how the platform validates a firmware image, which is a structural fact about its architecture. They are not a vulnerability disclosure, and no exploit, bypass or circumvention technique is published. #### What is programmable, ranked 1. **The i.MX 6 boot ROM, over USB OTG** — Publicly documented in the NXP reference manual, requires no cooperation from the stock software, and is also the recovery path — which is why it is the one route worth proving before anything else is attempted. Conditional on the HAB fuses. 2. **USB audio on the host port** — A measurement path that sidesteps the converter, TDM and DSP problem entirely, so it can never be blocked by the hard part. Deliberately staged first for exactly that reason. 3. **The four SHARCs and the Trion fabric** — Documented parts. Bringing them up from nothing, alongside AES50, is the mountain — and the whole open-ended schedule risk lives here. 4. **The stock update path** — Ranked last on purpose. It needs the container solved, and the container is explicitly not on the critical path: writing a replacement OS never requires reading the manufacturer's image. #### Third-party open projects on this platform - **OpenWING** (GPL-3.0, Early — work in progress, June 2026) — https://github.com/OpenMixerProject/OpenWING The same team's port of their X32 work to the WING. Linux boots on the i.MX 6 and the surface is largely alive; the audio hardware is not. It is roughly where this project's Stage 0 and Stage 1 would end up, reached from the opposite direction. #### Traps Mistakes this analysis actually made. Each produced a plausible wrong answer rather than an obvious failure, which is why they are published. - A 3-byte magic such as gzip's appears about 2.3 times by chance in 38 MB, and scanning 514 transforms means scanning 263 MB — expect roughly 16 coincidental hits. An early verdict called the firmware "a known container" on exactly that basis. - Every negative sweep result needs a positive control. A broken sweep and a genuine absence look identical. - Averaging a statistic over a non-stationary image hides the finding. Reported as one number the container looks like weak, uninteresting obfuscation at 7.37 bits/byte; split at 0x230000 it resolves into an exactly-uniform head and a visibly structured bulk, which is the whole result. - Assuming that because we have not solved something, nobody has. The container was written up as an open problem while a public repository already contained working unpack and repack tools for it — with the substance moved to a private submodule, so the tooling is visible and the method is not. Checking what exists costs minutes and reframes the question from "can this be solved" to "is it worth re-solving". ### Waves SoundGrid (2010) - URL: https://aux-opsy.com/platforms/waves-soundgrid/ - Class: Audio-over-Ethernet platform - Research status: Documented - Last updated: 2026-08-07 - Hardware in our hands: none — analysis is file-derived - Summary: Not a console, but the transport a great many of them hang off. Documented from both ends at once — the server firmware and the host driver are the same protocol version. - Key finding: The SoundGrid Server is a hard-realtime appliance, not a general-purpose Linux box. Its kernel command line isolates every worker core from the scheduler, RCU, tick, idle and frequency scaling, and SoundGrid frames are steered to a dedicated uncoalesced hardware receive queue. - Verdict: The most conventionally documentable platform here, because both sides of the same protocol version were available and corroborate each other — the driver names its C++ classes and the server names its message types. - Open questions (what is NOT known): - No claim has been validated against live hardware. Nothing has been tested against a real SoundGrid network. - Every wire layout should be treated as provisional until a packet capture confirms it. - The XML self-description has not yet been captured from a running server. #### Method Static analysis of vendor-distributed binaries and configuration files already present on a machine holding a licensed installation. No Waves source code, NDA material or SDK was involved. Techniques: reading plain-text scripts, demangling C++ symbol and RTTI tables, extracting strings, and disassembling a small number of functions in the GPL kernel module. #### Artifacts examined - SoundGrid Driver (macOS kext) 16.5.15.119 — unstripped C++ symbols - SGS firmware image `SGS_16.5`, External_Version 16.5.79.183 — extracted SquashFS - `AG_NSServer` — stripped ELF, retains C++ RTTI - `wsgnf.ko` — GPL-licensed kernel module, unstripped - 141 × `.wfi` per-model I/O firmware files, referenced by name only Referenced by name and version only. Nothing is redistributed. #### Hardware architecture - [VERIFIED] **Server platform**: x86_64 appliance; the binary names an Intel I225-V 2.5GbE-class controller - [VERIFIED] **Boot**: SYSLINUX/EFI into a RAM-resident root filesystem — SquashFS (zstd) initrd on /dev/ram0, overlaid read-write on tmpfs and pivot_root'ed into - [VERIFIED] **Base distribution**: ryolinux-release 4.3, Buildroot-derived, hostname `buildroot` - [VERIFIED] **Kernel**: 6.12.11 SMP PREEMPT_RT — fully preemptible realtime - [VERIFIED] **Link layer**: EtherType 0x8908, steered by ethtool receive-flow rules to hardware queue 1 with interrupt coalescing disabled (rx-frames 1, tx-frames 1) #### Software architecture - [VERIFIED] **Process model**: A single userland process, `AG_NSServer` (AudioGrid Network Shell Server), owns the protocol. It starts before the network stack is brought up - [VERIFIED] **Realtime partitioning**: Command-line options name a control core, a master core and a worker core set, plus AudioGrid and kernel CPU reserve percentages - [VERIFIED] **Kernel fast path**: `wsgnf.ko` — character device, an mmap region shared with userland, ioctls for sample-set enable/disable and interface naming, and /proc debug and stats entries - [INFERRED] **Shared mapping**: Carries at least a mutex and a switch timestamp — the latter being how userland and kernel agree on the exact sample instant a configuration change takes effect - [VERIFIED] **Protocol shape**: Not a flat command set. A set of node families, each with its own protocol version, addressed by node number, each exposing a description-get operation plus family-specific commands - [VERIFIED] **Self-description**: The server can emit its own protocol description as XML (`-M`, node protocol xml). Capturing that output is the single highest-value next step #### Update path and security model - [VERIFIED] **Scope boundary**: Licensing, authorisation and entitlement mechanisms are explicitly out of scope and were not analysed - [VERIFIED] **GPL obligation**: `wsgnf.ko` declares license=GPL in its module metadata and is distributed in binary form as part of a product. The source-availability obligation applies on its own terms. No claim is made here about whether it has been met These rows describe how the platform validates a firmware image, which is a structural fact about its architecture. They are not a vulnerability disclosure, and no exploit, bypass or circumvention technique is published. #### What is programmable, ranked 1. **An independently created node** — The stated goal: a device that appears on a SoundGrid network as a discoverable node, recovers the network sample clock, and exchanges multichannel audio with servers and I/O devices. #### Traps Mistakes this analysis actually made. Each produced a plausible wrong answer rather than an obvious failure, which is why they are published. - This is not black-box analysis and should never be described as such — the findings come substantially from inspecting vendor binaries. - It is not clean-room analysis either, because no implementation exists yet and therefore no team separation has been established. Both terms are commonly misapplied to work of this kind. ### Violet Audio dMix 128 (2024) - URL: https://aux-opsy.com/platforms/violet-dmix-128/ - Class: Digital mixing console - Research status: Documented - Last updated: 2026-08-07 - Hardware in our hands: none — analysis is file-derived - Summary: A headless FPGA console that splits its work across three silicon tiers — a Zynq UltraScale+ running full Ubuntu for control, and a pair of dedicated Spartan-7 FPGAs doing the actual mixing — with an update package that quietly names the OEM behind the brand. - Key finding: The v1.2.3 update unpacks to ten arm64 Debian packages plus the FPGA images. The mixing engine is not the host SoC's fabric but two separate Spartan-7 XC7S50 devices (`mixer128_main.bit`, `mixer128_aux.bit`, Vivado 2025.1), each booting from its own SPI-NOR flash; the Zynq UltraScale+ is the Ubuntu host and the effects run on it via a separate DSP server. - Verdict: The most computer-like entry in the index, and the most tiered: an Ubuntu host on a Zynq UltraScale+ hands the mixing math to two dedicated Spartan-7 FPGAs and keeps the effects for itself, all shipped — bitstreams, web GUI and RSA-signed installer — in one update. The teardown's real payoff is naming the OEM the brand doesn't: DSPRO Audio and George Yohng. - Open questions (what is NOT known): - Exactly how the `main` and `aux` Spartan-7 cores divide the channels and buses between them. - How much mixing runs in the Spartan-7 pair versus the Zynq PL versus the `fxserver-zynq` process. - Nothing here was checked against a running console; all findings are from the update package and public images alone. #### Method Static analysis of the single `dmix128_1.2.3.7045c05.dmixfw` update package — parsing the installer ELF, decrypting and unpacking its payload, reading the resulting package control metadata, the mypyc-compiled Python driver layer, the FPGA register-map JSON and the bitstream headers — cross-checked against the vendor's front-panel photo. No console was involved and no hardware was inspected. #### Artifacts examined - `dmix128_1.2.3.7045c05.dmixfw` — 70 MB update package (a stripped aarch64 ELF installer stub with an encrypted payload appended) - Ten `arm64` `.deb` packages: `dsmix`, `fxserver-zynq`, `mixer-web`, `mixer-extcontrol`, `mixer-local-gui`, `mixer-upgradedaemon-ng`, `nmos-bridge`, `button-monitor`, `xilinx-prog-zynqmp`, plus stock `avahi-utils` - `mixer128_main.bit` / `mixer128_aux.bit` — Spartan-7 XC7S50 bitstreams; `dsmix_zu3.bin` / `dsmix_zu5.bin` — ZynqMP boot/PL images - `Driver/Profiles/mixer128` and `mixer64` JSON DSP register maps; `Driver/misc/*.py` hardware drivers; the vendor front-panel line-art Referenced by name and version only. Nothing is redistributed. #### Hardware architecture - [VERIFIED] **Host SoC**: Xilinx Zynq UltraScale+ MPSoC (quad Cortex-A53) running the control software; the installer branches on a device-tree `device_id` of `zu3eg` or `zu5eg`, and its programmable logic is loaded from `dsmix_zu3.bin` / `dsmix_zu5.bin` for I/O and interconnect - [INFERRED] **Model / SoC pairing**: Read as the smaller ZU3EG serving dMix 64 and the larger ZU5EG serving dMix 128 (both boot images ship together) - [VERIFIED] **Mixing DSP**: Two dedicated Xilinx Spartan-7 XC7S50 FPGAs — the bitstream headers name `mixer128_main` and `mixer128_aux`, part `7s50csga324`, Vivado 2025.1. Each boots from its own SPI-NOR config flash and is addressed by a `CORE_ID` / `fpga_sel` selector; the host reprograms them over SPI - [INFERRED] **Effects DSP**: The FX rack (reverb, delay, chorus, a DX480-style processor) runs on the host via `fxserver-zynq`, separate from the Spartan-7 mixing cores - [VERIFIED] **Converters & clocking**: AKM AK5558 8-channel ADCs and AK4454 DACs in I²C-addressed slots, carried to the fabric over TDM. Renesas VersaClock 6E master generator (I²C, OTP-programmed); network clock recovered by a software DPLL - [VERIFIED] **Physical I/O**: 3U front-I/O stagebox: 32× XLR mic/line in (phantom + head-amp), 24× XLR out, 2× AES/EBU in+out, 2× ¼" phones, MADI in/out on BNC coax, a CONTROL etherCON and redundant `dNet A/B` etherCON (AES67, copper), DisplayPort video for the local GUI, USB-A + USB-C (with USB-PD), one option-card slot, dual redundant PSU #### Software architecture - [VERIFIED] **Operating system**: Ubuntu 24.04 aarch64 userland under systemd (packages built with GCC 13.3, Ubuntu 24.04) - [VERIFIED] **Makers behind the brand**: Package maintainers are DSPRO Audio (`dspro.com.br`) for the platform integration and George Yohng (`yohng.com`) for the mixer core — `Violet Audio` is the brand - [VERIFIED] **Mixer application**: `dsmix` is mypyc-compiled Python: a `Driver/` hardware-abstraction layer plus JSON register/routing maps for both `mixer128` and `mixer64` profiles, driving the Spartan-7 cores. `fxserver-zynq` is the host-side FX server - [VERIFIED] **Control plane**: `mixer-extcontrol` (Go) serves the external protocols — ECP (Q-SYS), QRC, the zero-based VSKP keyspace and OSC — plus a Socket.IO/WebSocket layer on port 80. It additionally embeds an MCP/LLM "assistant" with an instrument-EQ preset knowledge base - [VERIFIED] **Media control**: `nmos-bridge` re-exposes the console as AMWA NMOS IS-04/05/08 senders and receivers (Python, Nuitka-compiled standalone) - [VERIFIED] **Update mechanism**: A Go `upgradedaemon` performs btrfs-snapshot A/B "checkpoint" upgrades — installing the packages, updating kernel and boot artifacts, and able to roll back to the previous snapshot #### Update path and security model - [VERIFIED] **Update confidentiality / integrity**: The `.dmixfw` payload is AES-256-CBC encrypted with a SHA-256 integrity check; the installer stub links OpenSSL and decrypts the payload to a temporary archive before unpacking - [VERIFIED] **Update authenticity**: Each package and the outer payload carry a 256-byte RSA-2048 signature; the `upgradedaemon` embeds an RSA-2048 public key and the signature-verification path is present in the binary - [VERIFIED] **Recovery**: The btrfs A/B checkpoint model provides rollback to the prior known-good snapshot if an upgrade is rejected or fails These rows describe how the platform validates a firmware image, which is a structural fact about its architecture. They are not a vulnerability disclosure, and no exploit, bypass or circumvention technique is published. #### What is programmable, ranked 1. **The mixing engine is two Spartan-7 bitstreams** — `mixer128_main.bit` and `mixer128_aux.bit` configure a pair of XC7S50 DSP FPGAs from their own SPI flash; the audio path is defined by programmable logic and the JSON register maps in `dsmix`, not by fixed silicon — the direct contrast to the Yamaha QL entry, but with the DSP split off the host SoC entirely. 2. **Zero-based VSKP keyspace over `mixer-extcontrol`** — Dot-delimited keys such as `i.0.eq.b1.freq`, with `*` matching one component and `` many. The same daemon also exposes ECP (Q-SYS), QRC, OSC and a Socket.IO layer. 3. **MCP/LLM control** — `mixer-extcontrol` ships an agent that drives the console over MCP tools using an instrument-EQ knowledge base — an LLM-facing control surface baked into the firmware. #### Traps Mistakes this analysis actually made. Each produced a plausible wrong answer rather than an obvious failure, which is why they are published. - The `.dmixfw` is not an archive — it is an executable installer with an encrypted payload appended after the ELF section headers, so ordinary archive tools see only a stripped binary. - The bitstreams target part `7s50csga324` — a Spartan-7, not the Zynq's own fabric; the mixing DSP is separate silicon from the host SoC, which is easy to misread as the SoC's PL. ### Stoatworks Labs LOOM - URL: https://aux-opsy.com/platforms/loom/ - Class: Reference design — open console - Research status: Reference design - Last updated: 2026-08-07 - Hardware in our hands: none — analysis is file-derived - Summary: Not a teardown. The open console these teardowns keep pointing at — included because "what would you build instead" is the question every one of them ends on. - Key finding: 128 inputs, 64 buses, every channel strip computed in FPGA fabric at 96 kHz, speaking Dante, RAVENNA/AES67 and AVB/Milan simultaneously. Where the commercial platforms hide the mixer behind an undocumented register map, here the register map is the deliverable. - Verdict: The contrast that makes the rest of the index legible. Two of the four teardowns concluded that the platform was closed by construction rather than by policy — LOOM is what the alternative costs. - Open questions (what is NOT known): - This is a design, not a shipped product. It is listed here as a reference point, not as a claim of completion. #### Method Original design work. Correctness rests on a bit-accurate fixed-point model in Rust, replayed against the RTL in Verilator. #### Artifacts examined - Engineering specification — numbered, verifiable requirements - Fixed-point model and Verilator testbenches Referenced by name and version only. Nothing is redistributed. #### Hardware architecture - [PUBLIC] **Processing platform**: AMD Zynq UltraScale+ XCZU7EV (ZCU106 evaluation board) - [PUBLIC] **Sample rate**: 96 kHz native, 192 kHz selectable - [PUBLIC] **Internal format**: 32-bit fixed-point Q4.27 with 24 dB headroom, 35-bit Q2.32 coefficients, 59-bit accumulator, TDF2 with full-precision feedback - [PUBLIC] **Local I/O**: 192 in / 192 out — 16×16 analog, 8×8 AES3, 64×64 MADI, 2 × AES50 - [PUBLIC] **Control plane**: 4 × Cortex-A53 running Linux, 2 × Cortex-R5F bare-metal DSP supervisor #### Software architecture - [PUBLIC] **Routing**: Full 192×64 crosspoint, latency-aligned — any input reaches any output in identical total sample count regardless of route depth - [PUBLIC] **AoIP**: Dante IP Core native on-die, RAVENNA/AES67 + SMPTE ST 2110-30 native in PL, AVB/Milan native in PL — concurrently - [PUBLIC] **Core latency target**: ≈0.5 ms on the locked native path with SRC bypassed #### What is programmable, ranked 1. **All of it** — That is the point of including it here. ### Allen & Heath dLive (2016) - URL: https://aux-opsy.com/platforms/allen-heath-dlive/ - Class: Digital mixing console - Research status: Partial - Last updated: 2026-09-07 - Hardware in our hands: none — analysis is file-derived - Summary: Still essentially unexamined as a console — no firmware package, no silicon, no update path. What has been looked at is what its Director application writes to disk, and that turns out to be the Avantis format. - Key finding: The only thing examined here is dLive Director V2.11's show files, and they are the same shape as the Avantis': a gzipped tar of plain-text subsystem files with one nested tarball per scene, split into a stagebox half and a surface half. Nothing of the console itself — not its firmware, not its silicon, not its update path — has been looked at, and this page should not be read as if it had. - Verdict: Not a teardown, and it should not be mistaken for one. One question has been asked of this platform — what does its editor write to disk? — and the answer is that it writes the Avantis format, or something close enough that the difference has not shown up in the structure. That is worth recording because it makes a dLive show readable today, and because it is evidence that Allen & Heath run one show format across the range. It says nothing whatever about the console: what mixes, what the update package contains, whether it is signed, and how it compares to the SQ's FPGA-and-bare-metal design are all open, and this page will stay thin until someone opens the firmware. - Open questions (what is NOT known): - Whether the show format really is the Avantis'. The structures match; nothing has been compared byte for byte, and the Channel Mapper's stride and type codes have not been confirmed on a dLive file. - Everything about the console itself — the update package and whether it is signed, what the mixing runs on, how the surface talks to it, and whether the SQ's or the Avantis' architecture is the closer relative. #### Method Inspection of the seven factory shows shipped inside dLive Director V2.11, compared structurally against the Avantis shows the same work covers. Nothing else: no firmware package was obtained, no hardware was inspected, and none of the Avantis findings has been re-derived on a dLive file. #### Artifacts examined - The seven factory shows inside dLive Director V2.11 — FoH, Monitors, Surround, MultiSurface FoH and Mon, and FoH LCRplus Referenced by name and version only. Nothing is redistributed. #### Hardware architecture - [UNKNOWN] **What has been examined**: Nothing. No firmware package has been obtained, no console has been opened or inspected, and no part number on this platform has been established by us. The larger sibling to the SQ and the Avantis, and unexamined next to both #### Software architecture - [VERIFIED] **What Director writes**: dLive Director V2.11 ships seven factory shows in the same structure as the Avantis: a gzipped tar, subsystem folders whose small files are plain text, and one nested tarball per scene split into a stagebox half and a surface half - [INFERRED] **Almost certainly the Avantis format — but only almost**: The structure matches and the two Directors are plainly the same application family, so the Avantis findings (the live state living in scene 65535, the input patch being global rather than scene data, the three-byte Channel Mapper entries) are the reasonable expectation here. None of them has been checked against a dLive file, no byte-for-byte comparison has been made, and a larger console is exactly where a shared format is most likely to differ in its dimensions #### Update path and security model - [UNKNOWN] **Not examined**: No update package has been obtained, so nothing is known about how this platform validates or signs its firmware. The SQ and the Avantis both ship unsigned images with integrity checks only; whether the flagship follows them is a question, not an inference These rows describe how the platform validates a firmware image, which is a structural fact about its architecture. They are not a vulnerability disclosure, and no exploit, bypass or circumvention technique is published. #### What is programmable, ranked 1. **The show file, and nothing else yet** — The only surface examined on this platform. It is a tar of gzipped tars with self-labelling scene blobs, so reading a dLive show is a solved problem the moment someone checks the Avantis findings against a dLive file. Everything else here — the firmware, the update path, whatever does the mixing — is unopened. #### Traps Mistakes this analysis actually made. Each produced a plausible wrong answer rather than an obvious failure, which is why they are published. - Reading a sibling's findings onto this page. Everything below the show format on this platform is inference from the SQ and the Avantis, and it is deliberately not written here as if it were established. The entry stays thin on purpose. ### Behringer / Midas X32 / M32 (2012) - URL: https://aux-opsy.com/platforms/behringer-x32-midas-m32/ - Class: Digital mixing console - Research status: Partial - Last updated: 2026-09-07 - Hardware in our hands: none — analysis is file-derived - Summary: The container is solved to the byte and every payload inside it is encrypted — so the update image is simultaneously the most transparent and the least readable in this index. It is also the only platform here where a third-party project runs its own operating system on the hardware. - Key finding: The update image carries integrity but no authenticity. Its two header digests are plain MD5 and both reproduce exactly — `md5(" - designed by Thomas Zint - " ‖ image with bytes 96–112 zeroed)` for the whole image, and `md5(directory)` with a placeholder constant re-planted at offset 112 — yet nothing in the image is signed. That is why a third-party OS boots on a stock console: the bootloader checks that an image is intact, never that Behringer wrote it. - Verdict: A firm negative on payload content and a complete positive on everything around it. The container is solved to the byte, the manifest is a board-level map of a decade-old product line, and the update path turns out to authenticate nothing — which is the finding that matters, because it is why this platform has the most third-party open-source activity of anything in this index. The cipher stands. - Open questions (what is NOT known): - What construction turns the derived key into a keystream? The inputs are known and the padding gives a known-plaintext oracle at 39,176 known offsets, so a candidate can be confirmed or rejected entirely offline, with no console involved. - Does `styles/ablesque.rsrc.z` decompress to a resource bundle once decrypted, and does it carry the parameter model? At 36% of the image it is the largest single object and nothing is known about its contents. - Do the two per-member MD5s let a decryption be confirmed member by member? They are over plaintext, so yes in principle — the first covers only the leading 512 bytes, which makes partial verification cheap. - What changed between 4.12 and 4.13? Diffing two releases would show which members moved and the release notes say what changed functionally, mapping members to features without decrypting anything. It costs one more download and remains the highest-value next action. - The output routing blocks on the AES50 links, the card slot and the local outputs. They name groups of output definitions rather than individual sources, so expanding them needs a second resolution pass — the largest single gap in the scene format, and the one that would tell an engineer what each stage-box return carries. - Whether the M32 file is byte-identical to the X32's. Believed to be, never verified — and the same assumption applied to the X-Air range is weaker still, since the consoles have different I/O counts, so a sniff would succeed and the output could be quietly wrong. - Older scene-format versions. Only `#2.7#` has been seen, and nothing is known about what changed before it. #### Method Static analysis of one public firmware update image, `dcp_corefs_release-4.13-20240415.update` (release 4.13, 2024-04-15), plus the published release notes. No hardware was involved and no console was opened. Findings were then cross-checked against the OpenX32 project's published material, which is written by people who do have the hardware — that cross-check corrected three claims and is recorded in the traps below. The scene-file findings come from a separate basis: `.scn` files read directly, with the enumerations settled by falsification against a known-good fixture rather than by finding a table. #### Artifacts examined - `dcp_corefs_release-4.13-20240415.update`, 7,829,504 bytes, as published by the manufacturer - The seven-page release-note PDF accompanying that version - `dcp_compiler.pl` and `dcp_decompiler.pl`, published by the OpenX32 project with the original author's permission - A `.scn` scene file from a 32-channel console — about 2,100 lines of line-oriented ASCII, and the fixture every enumeration below was checked against Referenced by name and version only. Nothing is redistributed. #### Hardware architecture - [PUBLIC] **CPU**: NXP i.MX253, ARM926EJ-S. Nothing in the firmware image names it — this comes from the OpenX32 project, which runs Linux on the part - [PUBLIC] **DSP**: 2 × Analog Devices ADSP-21371 SHARC. DSP1 carries the 40-channel mix path, DSP2 the effects — which is exactly the split the image's `mixdsp_image_48k_2` and `fxdsp_image_48k_2` members imply - [INFERRED] **DSP images**: Two separate images, both tagged `48k`, so per-sample-rate DSP builds. The mixing image (50,776 B) is larger than the effects image (32,256 B), suggesting the effects engine is a framework loading coefficient sets rather than 54 discrete algorithms - [PUBLIC] **FPGA**: Xilinx Spartan-3A XC3S1400, second-sourced to a Lattice ECP5 during the component shortage. The image ships both bitstreams side by side and the 4.11 release notes say "Support new ADAT card fpga chipset"; OpenX32 states the shortage as the reason outright - [PUBLIC] **Audio I/O**: 112 in / 112 out — 32 XLR in, 16 XLR out, 16 UltraNet, 8 aux, 32 card, 40 DSP - [INFERRED] **Board controllers**: Five named beyond the fader and channel-edit boards: `core_A`, `userctrl_A`, `m32c_A`, `mcu_ui_A`, `rack_A`. The three at ≈33 KB are close enough in size to suggest one firmware base built for three boards - [INFERRED] **Fader boards**: Revision letters A, M, O, R and T on the left and right banks, three on the middle bank. Revision A images are ≈34 KB and M/O/R/T ≈36 KB — a 6% step that suggests a different microcontroller part, not just a different PCB - [VERIFIED] **Product variants**: Eight `.init` files in one image — X32, X32C, X32P, X32RACK, X32CORE, M32, M32C, M32R. Their sizes fall into three groups that pair each Behringer model with its Midas equivalent #### Software architecture - [VERIFIED] **Container**: "DCP" — a 128-byte header record followed by 197 member records of 128 bytes each, little-endian, then block-packed payloads. Name (87 bytes + NUL), start block (×512), length in bytes, then 32 bytes of digest. Fully solved; the layout closes to the byte against the file length - [VERIFIED] **Container — digests**: The 32-byte field is two MD5s, not one 32-byte hash: `md5(first 512 bytes, zero-padded)` then `md5(whole member)`, both computed over the plaintext before encryption. Confirmed against the published 2012 compiler and verified by reproducing both header instances exactly - [VERIFIED] **Version string**: `4.13-0-g2e3bea4e` at offset 0 — `git describe` output, so releases are built straight from a tag - [VERIFIED] **Members**: 197: 32 in `binary/`, 76 channel presets, 54 effect presets, 6 monitor, 4 routing, 4 index files, 11 in `prefs/`, 9 splash bitmaps, and one 2,829,090-byte `styles/ablesque.rsrc.z` - [VERIFIED] **The resource member**: `styles/ablesque.rsrc.z` is 36% of the image and by far the largest object in it — larger than the application, both FPGA bitstreams and both DSP images combined. The X32 boot screen's "Loading resource file …" is presumably this. Encrypted like everything else, so the `.z` is unconfirmed as zlib - [VERIFIED] **Payloads**: All 197 encrypted. Entropy 8.000 bits/byte on the 1.49 MB application, χ² = 235.7, and 0 repeated 16-byte blocks out of 487,743 across the whole image. Member lengths are not block-aligned, so this is a stream cipher or a stream mode, not ECB or CBC - [VERIFIED] **Keystream**: No reuse. The 39,176 bytes of inter-member padding are almost certainly plaintext zeros and are encrypted too, giving raw keystream at known offsets — and all of those regions are distinct, with no period detectable up to 64 KiB - [INFERRED] **Key derivation**: The published compiler derives a per-member key as `md5(version string ‖ member name ‖ " - designed by Thomas Zint - ")`, unpacked to four 32-bit words, alongside the member's start block. Every one of those inputs is plaintext in the shipped image. The encryption step itself is absent from the published source, and eight natural constructions over that key material score at chance against the padding - [INFERRED] **The application**: `dcpapp.bin`, 1,491,840 bytes. There is no kernel, filesystem or library member anywhere in the image, so the stock software is most likely bare-metal or an RTOS rather than a Linux userland — a real architectural difference from the HD96 and DM7, which both ship a rootfs measured in tens of megabytes - [PUBLIC] **Third-party OS**: OpenX32 replaces all of it with Linux 6.18 LTS, BusyBox and an LVGL interface, booting from USB via the stock DCP bootloader - [VERIFIED] **What the console writes to disk**: No container at all. A scene is line-oriented ASCII: one OSC-like path per line followed by whitespace-separated tokens, double-quoted where a value contains spaces. No compression, no encryption, no checksum — about 2,100 lines for a 32-channel console. The header line carries a format version (`#2.7#`, the only one seen), the scene name, a note field, and a recall-scope bitmap that has not been decoded. The console with the least readable firmware in this index writes the most readable state file in it - [VERIFIED] **The patch chain is three hops, and all three matter**: `connector → routing block → input slot → channel source → channel`. `/config/routing/IN` names five blocks — four of eight connectors each onto input slots 1–32, plus the rear aux inputs, which sit outside the slot space — and the blocks are freely assignable, so slots 25–32 can sit on AES50-B. Each channel then selects which slot feeds it, also freely: in the fixture six channels select a slot no other channel uses, and six slots reach no channel at all. A straight "channel N takes XLR N" reading is wrong on this console and gives no sign of being wrong - [VERIFIED] **Head-amp state is keyed by connector, not by channel**: 128 flat entries of gain and phantom — 0–31 the local inputs, 32–79 AES50-A, 80–127 AES50-B — stored whether or not the show uses them. Preamp state therefore belongs to the socket and survives a repatch, which is the opposite of how a channel-keyed console behaves and the thing most likely to be got wrong when converting between the two - [VERIFIED] **The output enumeration, settled by falsification**: 0 off, 1–3 the main bus, 4–19 mix bus 1–16, 20–25 matrix 1–6, 26–57 channel direct outs. The competing reading — 1–16 mix, 17–22 matrix — is not rejected on preference but because the fixture contains output values 23 and 24, which under it decode to matrix 7 and matrix 8 on a console with six matrices. The accepted mapping is then corroborated positively: Ultranet port 1 decodes to channel 1's direct out tapped pre-EQ, which is the conventional default, and all 42 output rows in the fixture land inside the mapped range with no fallback taken. Values 58 and above are unmapped and are reported rather than guessed #### Update path and security model - [VERIFIED] **Image integrity**: Two MD5 digests in the header — one over the directory, one over the whole image with a fixed seed string prepended. Both verified. MD5 and a published constant, so this is a corruption check, not a security boundary - [INFERRED] **Image authenticity**: None found. No signature, no certificate, no asymmetric key anywhere in the container. That an unrelated project's self-built images boot on unmodified consoles is the practical demonstration - [VERIFIED] **Payload confidentiality**: A competently applied stream cipher, unbroken here. No reused keystream, no ECB structure, no short period, and no plaintext anywhere except the directory - [INFERRED] **Where the key is**: Not, as this analysis first concluded, only on the console. The published compiler shows the key deriving from fields that ship in the clear in every image. What is missing is the construction that turns that key into a keystream These rows describe how the platform validates a firmware image, which is a structural fact about its architecture. They are not a vulnerability disclosure, and no exploit, bypass or circumvention technique is published. #### What is programmable, ranked 1. **The DCP bootloader, via a self-built container** — The format is fully documented above and the payloads inside a self-built image need no encryption, because the bootloader does not require any. A `.run` extension executes from RAM and reverts on power-cycle; `.update` writes to the internal SD card. This is the route OpenX32 takes and it needs no exploit — only the format. 2. **The two SHARCs and the FPGA, over internal SPI** — Both DSPs and the FPGA are configured by the main CPU over SPI at boot, so whatever controls the CPU controls them. The toolchains are the obstacle rather than the access: CrossCore for the SHARCs and Xilinx ISE 14.7 or Lattice Diamond for the fabric. 3. **The encrypted payloads** — Not currently reachable. The container around them is solved, the key derivation inputs are known, and there are two independent offline oracles for testing a candidate — but the cipher construction is not published and was not recovered here. 4. **The scene file, which is a text file** — Everything else on this list is behind a stream cipher or an undocumented SPI link. The console's own state is not: it is ASCII, unsigned and unchecksummed, so reading a show, rewriting a patch and handing it back is a text-processing job. Our own PatchFerret ships the adapter. The limit is knowledge rather than access — the output routing blocks on the serial links name groups of definitions rather than individual sources, and expanding those into per-connector rows is the one piece of work that would say what is actually on each stage-box return. #### Third-party open projects on this platform - **OpenX32** (GPL-3.0, Active — beta, July 2026) — https://github.com/OpenMixerProject/OpenX32 A complete replacement operating system for the X32 and M32: Linux 6.18 LTS and BusyBox on the i.MX253, an LVGL interface at 30 fps, and the console's own audio path under its own control. It self-assesses at roughly 55–65% of the stock firmware's features and boots from a USB stick without recompiling anything. - **OpenMixerControl** (GPL-3.0, Active — July 2026) — https://github.com/OpenMixerProject/OpenMixerControl The control application shared by OpenX32 and OpenWING, abstracting three different consoles behind one codebase and also building for a PC via SDL2. #### Traps Mistakes this analysis actually made. Each produced a plausible wrong answer rather than an obvious failure, which is why they are published. - Counting the header as one of the members. The header's count field reads 197, and it is tempting to read that as one header record plus 196 members. It is not: the header occupies its own 128 bytes and 197 members follow. Getting this wrong drops the last member — which here is the largest object in the image — and the bytes it occupied then look like an unexplained 2.8 MB tail past the end of the table. That produced a confident, wholly fictitious finding, complete with three hypotheses about what the mystery region might be. The correct accounting closes to the byte with nothing left over, and that exact closure is the check that would have caught it immediately. - Assuming a 32-byte digest field is a 32-byte digest. Every SHA-256 and SHA-512 candidate was tested against it and all were rejected, which correctly suggested the digests were over plaintext — but for the wrong reason. The field is two 16-byte MD5s, and the first covers only the leading 512 bytes. A test that had split the field in half would have identified the algorithm on the spot. - Recomputing a self-referential digest without reconstructing the state it was computed in. The header's directory digest is `md5(directory)` taken while offset 96 was still zeros and offset 112 held `md5("DCP bootloader")` as a placeholder. Hash it as it sits in the shipped file and it mismatches — which reads exactly like "this is not a digest of the table", the wrong conclusion. Both header digests reproduce once the write order is modelled. - Reading entropy ≈ 8.0 as proof of encryption. The largest member is named `.z` and compressed data measures the same as encrypted data on every statistic used here. What actually settles it is the MD5 oracle: the stored digests are over plaintext, so if a member were merely compressed its digests would reproduce from the bytes as they sit. For all 197, they do not. - Treating a third-party project as competition rather than as instrumentation. Three claims here — the DSP split, the second-source FPGA and the digest format — were confirmed or corrected against OpenX32's published material in minutes. A project with the hardware on the bench is the cheapest verification available to an analysis that has only a file. - Tokenising the scene file without respecting quotes. `""` is a real, empty token — most channels are unnamed — and dropping it shifts every field after it on that line, which produces a plausible decode rather than an error. - Counting output rows by counting lines. `/outputs/main/NN` is followed by its own `/delay` line and `/outputs/p16/NN` by an `/iQ` line, so a naive count double-counts two of the five output sections. ### Analog Way Midra series (2013) - URL: https://aux-opsy.com/platforms/analog-way-midra/ - Class: Video presentation switcher - Research status: Documented - Last updated: 2026-09-06 - Hardware in our hands: One Midra-series Pulse2 (`DEV` 259, serial 2165, version 13) on our LAN, read and written over its control port through our own client on 2026-08-06. The updater teardown is unchanged and remains file-only; the unit was never opened. - Summary: Six models, one firmware, one set of bitstreams. The layer engine lives in FPGA fabric that is configured identically on every unit in the range, so the tier a machine sells as is decided by an integer it reads at boot. - Key finding: The updater flashes one `CAMELYA.hex`, one `FELINDRA.hex`, one `TATIANA.hex` and one MCU application to every model in the family, with no per-model branching anywhere in the script. The compositor is in those bitstreams — so a Pulse2 is running the identical silicon configuration to an Eikos2. - Verdict: Within a platform the model tier is enforced in software, from a device-type integer read at boot; between the base and `-H` boards it is genuinely different hardware. The evidence for the first half is the update script, which has no per-model branching and ships exactly one bitstream per FPGA for the whole family, plus a single MCU application that carries the complete FE1–FE6 chip map and the complete sixteen-model table. The evidence for the second half is in the same package: the `Chtiderman` build replaces the analog RGBHV output with Valens VS2000 HDBaseT transmitters and receivers, which is a different board, not a different setting. What is really built differently between the base models is the connector and daughter-card population — the two optional SDI cards, and which of the six front ends and their EDID EEPROMs are fitted. A Pulse2 does not have fewer layers because it has less silicon. The control plane recovered since is consistent with all of it: one variable table for the whole family, dimensions declared per model, and a device that hands you its own device-type integer the moment you connect. - Open questions (what is NOT known): - Which FPGAs these actually are. The bitstreams are raw and unencrypted but sit in an Analog Way container with no vendor magic, and the three differing lengths point at three different parts. A board photograph or a JTAG IDCODE settles it in seconds and nothing in the files will. - External frame memory. One bitstream can address different DDR depths, so it remains possible that the higher tiers are stuffed with more frame RAM and the firmware limit merely tracks what is fitted. This is the single caveat on the software-tiering conclusion and it cannot be closed from files. - Where the device-type integer is stored. The update log shows the unit reporting `Detected Device Type [259]` alongside a device ID, but the storage location was not located and was not looked for. - What the 43-byte bitstream header encodes. It is identical for the first eight bytes across all three FPGAs and diverges after, with the last two bytes differing — consistent with an identifier plus a checksum, but not decoded. - The exact on-air semantics. A take commits a pending change, but the per-layer status variables read as small stable enums that did not flip under one, so what is actually on the programme output cannot be settled from the wire alone. It needs a monitor on the output while the socket is watched. - How many concurrent control sessions the device accepts. `AWGateway`'s existence implies very few, possibly one. It was never measured, and it decides whether a third-party controller can coexist with the vendor client at all. - Whether a set outside a variable's declared range is clamped, rejected or accepted. Every range on this page is the client's declaration, and a declaration is not a limit. - The rest of the preset-memory group. Thirty-one variables are enumerated and only the used flag, the per-slot content and the save and erase requests have been exercised on hardware. #### Method Two bases, deliberately kept apart. The silicon findings are static analysis of one vendor updater package, cross-read against the vendor's own control client — no unit was opened, and the package's own manufacturer update log from a real PLS350 is their observation of a live machine, not ours. The control-plane findings are not file-only: the client's AVM2 bytecode was decompiled and its variable-table constructor replayed to reconstruct the whole control model, and on 2026-08-06 a real Pulse2 on our LAN was read and written through our own client, with RCS2's own session against it captured through a logging proxy. #### Artifacts examined - `midra_updater_v02_02_02.exe` — Inno Setup 5.5 installer containing `Device.xml`, `Script.xml`, six module scripts and every firmware component as plaintext Intel HEX - `Logs.txt`, shipped inside that package — a 2013 update session against a PLS350, naming the components a physical unit answered for - `MDR_launcher.swf` — the RCS2 control client; its AVM2 constant pool carries the complete `DEV_*`, `CARD_*`, `LAYER_*` and plug enumerations - `RCS2.app` — the Midra client, an Adobe AIR application (`MDR.RCS` v2.2.03). The `MDR_launcher.swf` inside it is a `CWS` (zlib) container whose `Vars::VarCom` initialiser carries the entire control model: 562 variables in 49 groups, each constructed with 15–16 positional arguments — name, group, min, max, default, dimension list, request and answer mnemonics, and flags - A logging proxy between RCS2 and a live Pulse2, recording the exact command sequence the vendor client sends to assign a layer source — the one operation the recovered table alone does not explain Referenced by name and version only. Nothing is redistributed. #### Hardware architecture - [INFERRED] **Host MCU**: One micro, codenamed CALIMERO. Dual-bank flash at `0x1A000000` and `0x1B000000` with the application 32 kB into bank A, 512 kB per bank, and an SRAM stub executed at `0x20000000` — that is the NXP LPC18xx / LPC43xx memory map. `Product.xml` corroborates with an `NXPBootStartTimeout` parameter. The exact part was not identified - [VERIFIED] **Flash layout**: Application code `0x1A008000`–`0x1A07D7CF` (481,232 B); const data `0x1B000000`–`0x1B02EB0B` (191,244 B); factory-test data `0x1B030000`; factory-test code `0x1B040000`. Every string, chip name and lookup table lives in the const image, not the code image - [VERIFIED] **FPGAs**: Three, codenamed CAMELYA, FELINDRA and TATIANA, each addressed as its own daughter card (`CARD_CF_CAMELYA`, `CARD_CF_FELINDRA`, `CARD_CF_TATIANA`). One bitstream file each - [VERIFIED] **Bitstream container**: Intel HEX wrapping a 43-byte proprietary header at `0x20` and a payload from `0x33F`. Payloads are 3,222,997 / 3,348,122 / 3,279,834 bytes at 6.50 / 6.61 / 6.58 bits per byte — uncompressed structured data, i.e. raw configuration bitstreams - [UNKNOWN] **FPGA vendor**: Not identified. No Xilinx sync word, `.bit` header or Altera/Lattice preamble survives any bit rotation, byte reversal or inversion of the payload. Three different lengths for uncompressed bitstreams implies three different parts rather than one part carrying three designs - [VERIFIED] **Input silicon**: FE1–FE4 Analog Devices ADV7842 — HDMI receiver, analog AFE, colour processor, SD decoder, InfoFrame and HDCP repeater, each a separate I²C sub-address. FE5–FE6 ADV7611, HDMI only. Read from the firmware's own I²C device-name table - [VERIFIED] **Output silicon**: BE1 and BE2 each carry a Silicon Image SiI9024 HDMI transmitter, a TI THS8200 video DAC and a Silicon Labs Si570 programmable pixel clock. BE3 is an ADV7301 or ADV7341 for the monitoring output - [VERIFIED] **Audio**: Cirrus Logic CS8422 × 2 (asynchronous sample-rate converter / AES receiver) and CS4265 × 2 (codec) - [VERIFIED] **EDID**: One EEPROM per connector, not per input — 18 individually addressable positions spanning `EDID_ANA1..4`, `EDID_DVI1..4`, `EDID_HDMI1..6` and `EDID_HDBT1..4`. The 2013 PLS350 log shows a real unit answering for twelve of them - [VERIFIED] **Daughter cards**: `CARD_MOTHERBOARD`, the three FPGA cards, `CARD_CF_AUDIO`, `CARD_CF_FAV`, `CARD_CF_VIDEO_OUT`, `CARD_CF_SDI_IN_1`, `CARD_CF_SDI_IN_2`, `CARD_XPORT`. SDI input is two optional cards — "CF" is carte fille #### Software architecture - [VERIFIED] **Operating system**: No FreeRTOS, ThreadX, µC/OS, embOS or Linux string appears in either image, and the client's microcontroller enum has exactly one entry (`MIC_CALIMERO`). A single bare-metal micro drives the whole machine - [VERIFIED] **Model table**: A flat 16-entry, 12-byte-stride string table at `0x1B02808C`: `MDRdbg`, six base models, `MNMdbg`, six `-H` models and two `-H LE` models. Device type is `256 + i` across the first block and `280 + j` across the second - [VERIFIED] **Models the updater accepts**: Twelve, in `Device.xml`: 257 EKS550 (Eikos2), 258 SPX450 (Saphyr), 259 PLS350 (Pulse2), 260 SMX250 (SmartMatriX2), 261 QMX150 (QuickMatriX), 262 QVU150 (QuickVu), then 282–285 and 287–288 for the `-H` and `-H LE` variants - [VERIFIED] **Models the firmware knows**: Sixteen. `281 EKS550-H` and `286 QVU150-H` exist in the firmware model table and in the control client's `DEV_*` list, but are absent from the updater's compatibility list — built into the code and, apparently, never shipped - [VERIFIED] **Two platforms, one package**: The updater carries two complete MCU applications — `Calimero_*` ("MDR") and `Chtiderman_*` ("MNM") — and flashes both unconditionally. Only the `Chtiderman` model table contains the two `LE` SKUs - [VERIFIED] **Layer model**: Per screen: `LAYER_FRAME` background, `LAYER_PIP_1..4`, `LAYER_LOGO_1..2` and `LAYER_AUDIO`. Two screens (`SCREEN_S1`, `SCREEN_S2`), two outputs, main and preview. Keying is luma or chroma, each with a downstream variant - [VERIFIED] **Protocol space**: The superset, not any one model: 10 inputs across 5 plug types (`ANALOG_HD15`, `DVI`, `SDI`, `HDMI`, `HDBASET`), 2 outputs across 8 plug types, 8 still frames and 8 logos, 8 preset memory slots, 3 case fans - [VERIFIED] **Control interfaces**: RS-232 at 115200 8N1, USB CDC, and TCP 10500. `Product.xml` gives all three, with a factory default of `192.168.2.140` - [MEASURED] **Control protocol**: A terse ASCII protocol on TCP 10500, and it is asymmetric: an outbound command puts the mnemonic last (`1,2,5PMinp` sets, `1,2,PMinp` requests) while the device's reply puts it first (`PMinp1,2,5`), with the final comma-separated field as the value. Midra's client appends CRLF itself; the device answers CRLF. Mnemonics are five characters, with three debug specials whose request and answer differ — `?`→`DEV`, `@`→`ADBG`, `>`→`DDBG` - [VERIFIED] **The variable table**: 562 variables in 49 groups, 190 of them read-only status. Every one declares its own dimension list, minimum, maximum and default — but those are the vendor's declarations, read out of the client, not limits anyone has measured. Treat them as strong hints; the device is the authority - [MEASURED] **Device identity on the wire**: Midra has no `PDEV` — asking for it draws `E10`. It pushes `DEV` on connect (259 on our Pulse2) and answers `DIdsn` serial (2165), `VEvar` version (13) and `DIdre` reference (240215). A Midra model map keys on `DEV`; the LiveCore generation's keys on `PDEV`, and a client that assumes one shape sees the other generation as a dead socket - [MEASURED] **Screen and layer model, as a Pulse2 reports it**: Up to 2 screens; `SCmly` maximum layers is read-only 0–4 and reads 2 for screen 0 on this unit; layers are indexed `[screen(2), ctx(3), layer(8)]`; `INava[10]` available inputs reads 8. Geometry carries the same +32768 centre bias as LiveCore — a centred 1080p layer reads `PRpoh=33728, PRpov=33308, PRsih=1920, PRsiv=1080` — so the two generations share their positioning arithmetic even where they share no mnemonics - [MEASURED] **Take and layout verbs**: `GCtak[screen]` takes one screen, `GCtal` takes all, `GCtba` is the T-bar, with `GCtav`/`GCtio` as status. `GCqly[screen,ctx]` applies one of 27 built-in layouts and reads back 0, because it is a momentary trigger rather than stored state, and it sets geometry only — sources are assigned separately. There is no selected-layer variable at all, which is a Midra/LiveCore difference - [MEASURED] **Programme is protected; preview is not**: Geometry writes are accepted, echoed back and raise `GCtav` (TAKE_AVAILABLE): the device holds the edit as a pending change until a take commits it. A source write aimed at the programme context is echoed back as the old value with a `CTsto` pulse — the device declines it. The vendor client's own path, captured through the proxy, writes the source to the preview context with preset-update-mode on; a programme-context edit re-baselined with `PIwur` sticks too. Both were confirmed on the unit - [MEASURED] **Memory model**: Eight preset slots. `PMpst[slot]` is the used flag and the stored content is readable per slot but not writable — a direct write to stored content reverts the same way a protected live write does. Save and erase are request variables. Recall is not a device command at all: the vendor client reads the stored slot and re-applies it to the live preset, so on this generation a memory recall is client-side arithmetic, and any controller that expects a one-command recall is waiting for a reply that never comes - [MEASURED] **HTTP surface**: The device serves HTTP on port 80 alongside the control socket, including per-input thumbnails as BMPs cache-busted by timestamp (`/assets/Snapshots/capture_in_8.bmp?time=…`). Source previews in a third-party controller cost nothing to build #### Update path and security model - [VERIFIED] **Image signing**: None. No RSA, SHA, AES, HMAC or ECDSA string appears in either image, and every firmware component ships as plaintext Intel HEX — no container, no compression, no obfuscation - [VERIFIED] **Update validation**: A version check, a transfer and a read-back verify per component (`Kd`, `Th`, `Kf` in the script grammar). There is no signature step in the grammar at all - [VERIFIED] **Bootloader**: Two-stage. A resident bootloader at node address 1 exposes only `MICRO_RAM`; the updater pushes a 40,900-byte stub into AHB SRAM, jumps to it with the script's `Ga 0x20000000` opcode, and that stub performs every flash operation - [VERIFIED] **Entitlement**: None found. No licence, unlock, activation, option-key or dongle string in either MCU image or in the control client. This generation has no feature-key system — unlike the LiveCore generation that followed it, which has one - [MEASURED] **Control-plane authentication**: None observed. The control port accepts a session with no credential step and starts pushing state immediately — the Pulse2 pushes its own `DEV` device code on connect — and the socket that reads is the socket that writes. Concurrency is the unmeasured half: Analog Way ship `AWGateway`, an application whose entire purpose is to hold one session to the device and re-serve port 10500 to several clients, which is what you build when a device accepts very few. The limit itself was never measured - [MEASURED] **Rejected commands**: A rejected command is answered `E` — `E10` for a mnemonic not in the table, `E12` for the wrong number of index fields, `E13` for a bad index count. An empty line draws no response at all. Nothing in the error path distinguishes not permitted from not understood*, because there is no permission model to report These rows describe how the platform validates a firmware image, which is a structural fact about its architecture. They are not a vulnerability disclosure, and no exploit, bypass or circumvention technique is published. #### What is programmable, ranked 1. **The control protocol** — ASCII over TCP 10500 or RS-232, and no longer merely recoverable: the full 562-variable table has been reconstructed from the vendor's own client and the framing, identity, geometry, take and memory paths confirmed on a real Pulse2. The client is an Adobe AIR application and the platform is long out of support, so a modern replacement controller is the obvious and entirely legitimate project — and it needs nothing from the firmware. 2. **EDID management** — One EEPROM per connector, individually addressable through the documented update path, with the stock EDIDs shipped as readable 256-byte HEX. This is the one part of the machine's behaviour a third party can change through a supported mechanism. 3. **The FPGA fabric — in principle only** — The compositor is entirely in the three bitstreams, so nothing about the video path can be changed without them. They are unencrypted, but the container is proprietary and the parts are unidentified, which puts any real work here behind two unsolved problems and a bench. #### Traps Mistakes this analysis actually made. Each produced a plausible wrong answer rather than an obvious failure, which is why they are published. - The first 80 bytes of every bitstream payload contain only `0x00` and `0x80`, which is exactly what a bitstream expanded to one configuration bit per byte for GPIO bit-banging looks like. Decoding on that assumption produced `0f f0 0f f0` — very close to the Xilinx `.bit` magic — and a confident wrong answer. The byte histogram of the whole payload killed it: `0x44`, `0x22`, `0x81` and `0x88` are all common further in. A structured header region can imitate an encoding for as long as you only look at the header. - `Midra_Simulator.app` is not the Midra series. Its EDIDs are `MDR4K_*` and `ALTA4K_*` — it belongs to the later, unrelated Midra 4K / Alta 4K line, and it is an HTML5 application where this generation's client is Flash. Analysing it answers a different question entirely. - Reading "one firmware package" as "one machine". The single-image evidence proves the software is common across the family; the very same package also carries two entirely different MCU applications for two different I/O boards. Stopping at the first conclusion would have missed the `-H` platform completely. - The vendor's own update log inside the package is the most valuable file in it and looks like debris. It is the only record here of a physical unit's responses — which device type it reported, and which of the eighteen EDID positions actually existed. - Reading the first reverting writes as a locked device. A source write to a layer comes back as the old value, which looked like the device refusing remote source assignment outright — and this entry nearly said so. It was two separate things: the programme context is protected while the preview context is not, and the device declines to place an input carrying no signal on a layer, so with nothing patched in every real input reverts while an always-available internal source sticks. The mechanism was right the whole time; the test was wrong twice over. - Putting a comma before the mnemonic on a set. `1,2,5PMinp` sets the variable; `1,2,5,PMinp` is read as a get of an over-indexed variable and is silently ignored or answered `E13`. The value is glued to the mnemonic with no separator, which reads like a typo and is the protocol. ### Analog Way LiveCore (2014) - URL: https://aux-opsy.com/platforms/analog-way-livecore/ - Class: Video presentation switcher - Research status: Partial - Last updated: 2026-09-06 - Hardware in our hands: One NeXtage 16 (LiveCore V04.02.20, `PDEV` 97) on our LAN, read and written over its control port through our own client on 2026-08-06. The card silicon, the appliance-derived model and the encrypted update packages are unchanged and remain file-only; the unit was never opened. - Summary: A card frame with an x86 PC inside it and a microcontroller on every card. Layer count is not a setting — it is how many processing cards are in the chassis. - Key finding: `layersPerResource` tracks the processing-card count exactly: Ascender 16, 32 and 48 are 2, 4 and 6 PROC cards. The model number is arithmetic — `ASC4806` means 48 total layers, 6 per screen, from `6 × 4 screens × 2 (program + preview)`. - Verdict: Hardware, and not subtly. Layer count is the processing-card count, input count is the input-card count, output count is the output-card count, and the 4K variants use a physically different input card — the per-position plug list changes, which a licence cannot do. The software knows how to be every model in the family from a single encrypted package, and the reported model is even writable over the wire, but only downwards and only within a set the device itself supplies. The interesting structural fact is how little of the machine is in the update file: an x86 PC card with its own BIOS and disk-on-module runs the web interface and the Lua policy layer, and fourteen microcontrollers on the cards do the rest. The control plane recovered since sits entirely on the PC card's side of that split: 1,014 variables, no authentication, and a memory model whose stored contents are addressable field by field — none of which says anything at all about what the processing cards contain. - Open questions (what is NOT known): - What is actually on a processing card. The card model, the micro and the per-card FPGA or ASIC are all invisible from the appliance — it describes the machine's behaviour, not its silicon. This is the largest gap on the page. - `ORX_5`, device 101 — six layers per resource but only two resources, sitting between the Ascender and NeXtage shapes. It is in the enumeration and in the capability table and appears in no catalogue. - Whether the simulator's `CARD_REF` numbers are real Analog Way part references or fixtures invented for the appliance. - The `.awp` key. It is on the device side; the appliance is a simulator build and does not obviously carry a production copy. Not pursued. - How much of the appliance's Lua matches what runs on real hardware. The scripts are explicitly simulation-side (`simulator_SetVarMatrixValue`), so they describe the model faithfully but may not be the production implementation. - The on-air semantics. A take is a verb on a group index and the per-layer status variables did not move under it, so nothing observed on the wire says what is actually on the programme output. It needs a monitor on the output and a signalled input, neither of which was available remotely. - How many concurrent control sessions the frame accepts, and whether an idle socket is dropped. `AWGateway` implies the answer is small; it was never measured, and it decides whether a third-party controller can run alongside the vendor client. - Whether a set outside a variable's declared range is clamped, rejected or accepted. Every range here is the client's declaration. - The VIO 4K's own variable table. The same Flash client ships it and the same pipeline extracts it; it has not been built, so the sibling platform named above is still described only by its device descriptor. #### Method The vendor update packages are AES-encrypted and were not decrypted. The architecture findings come instead from the vendor's own simulator appliance, whose second disk is an ext4 image of the device filesystem — read without mounting — carrying the platform's variable, enum and dimension model as JSON plus its Lua policy layer. The control-plane findings came later and differently: the Flash client the device serves was lifted out of that same image and decompiled, exercised against a simulated NeXtage 16 booted under QEMU, and then confirmed on a real NeXtage 16 on our LAN on 2026-08-06. No unit was opened and nothing about the cards was measured. #### Artifacts examined - `LivecoreUpdate_04_02_77.awp` and `LivecoreUpdateBIOS_01_02_06.awp` — encrypted; statistics only - `aw_simulator.ova` — the vendor simulator appliance; disk 2 is an ext4 filesystem containing the device application, the WebRCS tree and `resources/Platforms/LiveCore_4_02/` - `GENERIC_DIM.json`, `GENERIC_ENUM.json`, `GENERIC_VAR.json` — the platform's own dimension table, 133 enumerations and 1,014 control variables - `Lua/typedef/awLiveCore.lua` — the per-model capability table, in the vendor's own source with French comments intact - `ORX_WebRCS.swf` v04.02.03 — the Flash client the device serves to a browser, lifted from the appliance's ext4 image without mounting it. Its `Vars_Common::awVar` initialiser carries 1,014 control variables in 88 groups, each with its request and answer mnemonics, dimension list and declared range - A golden capture of real wire bytes — the framing, the asymmetric answer mnemonics, a set and its read-back, the unsolicited push on connect and both error codes — pinned as a regression test rather than described in prose Referenced by name and version only. Nothing is redistributed. #### Hardware architecture - [VERIFIED] **Chassis model**: Card frame with 20 addressable slots: `LOGO_BRIDGE`, `IN_1..IN_3`, `PROC_1..PROC_6`, `OUT_1..OUT_2`, `MONITORING`, `FDP`, three `FAV_*` front-panel cards, `FRAMELOCK`, `LINK_UP` and `LINK_DOWN` - [VERIFIED] **Distributed control**: Fourteen microcontrollers, one per card — `BRIDGE`, `BRIDGE_AUX`, `IN_1..3`, `PROC_1..6`, `OUT_1..2`, `MONITORING`. Part numbers are not given anywhere in the files - [VERIFIED] **Host**: There is an x86 PC inside the frame. The variable table carries `PC_CARD_REV`, `PC_CARD_STATUS`, `PC_TEMP_CPU`, `PC_MAC_ADDRESS`, `VER_BIOS`, `VER_PC` and `VER_DOM` — the last an enumeration of disk-on-module part numbers. It runs Apache and PHP and serves the Flash WebRCS applet - [VERIFIED] **Separate BIOS update**: That PC card is why `LivecoreUpdateBIOS_01_02_06.awp` exists as its own package, applied from a USB stick and confirmed with an on-screen "DO YOU WANT TO UPDATE LIVECORE BIOS?" prompt - [VERIFIED] **Card population rules**: Input cards = inputs ÷ 4 (`INPUTS_BY_INPUT_CARD = 4`), output cards = outputs ÷ 2, processing cards = the model's layer capacity. `LOGO_BRIDGE`, `MONITORING` and `FDP` are always present - [UNKNOWN] **Card part references**: The simulator carries plausible `CARD_REF` values per slot: bridge `210218`, input cards `210244`, processing cards `210212`, output cards `210213`, framelock `210234`. Whether these are real Analog Way references or simulator fixtures is not established - [VERIFIED] **Input plugs**: Position-dependent within a card, and different on the 4K variant. Standard: position 1 HD15/DVI-A/DVI/HDMI/SDI, 2 DVI/SDI, 3 HD15/DVI-A/DVI/SDI/HDMI, 4 SDI/DisplayPort. On the 4K card position 1 loses HDMI and position 2 gains it - [VERIFIED] **Output plugs**: `ANALOG`, `DVI`, `SDI`, `OPTICAL` — two per output card, plus two optical links and a monitoring output #### Software architecture - [VERIFIED] **Device application**: A 2.9 MB Qt5 binary on a Linux filesystem, with Lua 5.3 as the policy layer — around forty scripts covering layer validity, output format management, memory load and save, and factory reset - [VERIFIED] **Capability table**: `GetParametersByDevValue()` returns, per model: is-LOE, soft-edge, SPU control, perspective layer, 4K-ready, layers per resource, is-3U, resources per device, outputs, inputs, monitoring resources, processing cards - [VERIFIED] **Model capabilities**: NeXtage 08 — 8 in, 2 out, 2 resources, 2 layers, 1 PROC, 3U. NeXtage 16 — 8/2/2/4/2, 3U. SmartMatriX Ultra — 12/4/2/2/2, no soft-edge. Ascender 16 — 12/4/4/2/2. Ascender 32 — 12/4/4/4/4. Ascender 48 — 12/4/4/6/6 - [VERIFIED] **Detected models**: 24 device IDs: `ORX_1` NeXtage 16 (97), `ORX_2` SmartMatriX Ultra (98), `ORX_3` Ascender 32 (99), `ORX_4` Ascender 48 (100), `ORX_5` (101) which was never released, `LOE_16/32/48` Output Expanders (102–104), the `_4K` variants (105–111), `ASC016` Ascender 16 (112–113), the `_PL` Perspective Layer builds (114–115, 118–119) and `NXT0802` NeXtage 08 (116–117) - [VERIFIED] **Sibling platform**: The same appliance carries a second platform, `VTX`, with one device — the VIO 4K at dev 1024, whose only option slot is an XLR audio card. Different software generation, same control architecture - [VERIFIED] **Coupling**: `SINGLE`, `MASTER`, `SLAVE_1..SLAVE_7`; modes `LINK_MASTER`, `LINK_SLAVE`, `SYNC_MASTER`, `SYNC_SLAVE`. Linking two frames doubles the model's input and output counts outright. Three link cables and two optical links - [VERIFIED] **Protocol space**: 24 inputs, 8 outputs, 8 screens, 24 layers, 16 groups, 32 EDID library slots, 144 preset memories, 512 settings memories, 100 stills, 10 GPO and 2 GPI — the ceiling across the whole family, not any one model - [MEASURED] **Control protocol**: The same terse ASCII on TCP 10500 as the Midra generation, mnemonic-last outbound and mnemonic-first inbound — but the terminator differs. LiveCore's client returns a bare command string and its connection layer appends LF, with no carriage return anywhere; the device answers CRLF regardless. A receiver must split on LF, trim a trailing CR, and buffer a trailing partial line across reads. The vendor client does exactly that, and anything that does not drops state the first time a reply lands split across two packets - [VERIFIED] **The variable table**: 1,014 variables in 88 groups, one object per parameter built from 15–16 positional arguments. Ranges and dimensions are the vendor's declarations rather than measured limits. The same client also serves the VIO 4K (`VTX_WebRCS.swf` v01.20.12), extractable by the identical pipeline and not yet tabulated - [MEASURED] **Identity, push and errors, on real bytes**: A get of `0,VEvar` answers `VEvar0,13`. The asymmetric specials resolve: `?`→`DEV0`, `!`→`PDEV97` — 97 being the NeXtage 16's own device id — and `*`→`*0`. A set of `5,200INpcr` was echoed `INpcr5,200`, read back 200 and restored. `E10` answers an unknown mnemonic and `E12` a wrong index count. `ITcct0,1` — connected controllers, one, you — arrives unsolicited on connect, so the link is push, not poll - [MEASURED] **Three preset contexts, not two**: The layer variables are indexed `[screen(0..7), ctx(0..2), layer(0..23)]` and that middle axis is three wide, not a programme/preview pair. `ctx0` is the context memories save from and load into — proven by tagging the three contexts of one layer with distinct sources and watching which survived a save and a load. What `ctx1` and `ctx2` correspond to on the physical outputs was not pinned down - [MEASURED] **Memory contents are directly addressable**: `PM*` mirrors the live `PR*` fields but is indexed `[slot(0..143), layer(0..23)]`, so the contents of a stored memory can be read and written without touching the live arrangement — per-slot metadata included, where stored content width is non-zero exactly when the slot is used. Save snapshots `PR[screen, ctx0, ]` into `PM[slot, ]`; load writes it back into `ctx0` only; a third verb is load-and-take. The round trip was proven both ways - [MEASURED] **Selection is not visibility**: The selected-layer variable tracks what the panel has selected and does not change what is on air, with or without a take — and it is correctly absent from the memory shadow, because a memory stores what each layer shows and never which layer someone had selected. Any client using it as a show/hide toggle is wrong, and appears to work until someone watches the output - [MEASURED] **Take verbs**: Take-up, take-down, take-force and a T-bar, with up and down times — indexed by group `[0..15]` rather than by screen, a screen with no grouping being its own group. The per-layer status variables report small stable enums (3 or 4 for a used layer, 0 for an empty one) that did not flip under a take, so they are not an on-air flag whatever their names suggest - [VERIFIED] **The appliance has to be given its cards**: A simulator session created with empty options yields a device with no I/O cards fitted: every input and output reads unavailable, every screen allocates zero layers, and nothing — layers, takes, memory saves — has any effect. Passing each slot's option id from the device's own descriptor fits the cards, and the same device then comes up with its inputs available, its outputs enabled and its full control surface live #### Update path and security model - [VERIFIED] **Update package**: AES-encrypted. Entropy 7.9998 bits/byte from byte zero, length an exact multiple of 16, and zero repeated 16-byte blocks in 64 MB — so a chaining or counter mode, not ECB. No header, no magic, no version field in the clear - [UNKNOWN] **Decryption**: Not attempted beyond statistics. The key lives on the device side and the simulator appliance does not appear to carry a production copy - [VERIFIED] **Version binding**: `PlatformDescriptor.json` carries `"updater": "67239956"` — which is `0x04020014`, i.e. version 04.02.20 packed as bytes. The package version is a plain integer comparison - [VERIFIED] **Model register**: The reported model is a control variable, not a constant. `xDOWNGRADE_DEV` (`DGdev`) is externally writable, with the true model preserved read-only in `xDOWNGRADE_ORIG_DEV` (`DGori`) and the permitted set supplied by the device in `xDOWNGRADE_DEV_LIST_COMPATIBILITY`. It is a downgrade path, and it exists for link compatibility — pair two unequal frames and the larger steps down, which is why there is an explicit `COUPLING_DISABLE_AUTODOWNGRADE` flag. Recorded as a structural fact about the coupling feature; it was not exercised and the permitted set was not enumerated - [MEASURED] **Control-plane authentication**: None observed, and the same picture as the Midra generation: no credential step, state pushed the moment a socket opens, and the socket that reads is the socket that writes. `AWGateway` — a vendor application that holds one session to the device and re-serves port 10500 to several clients — is the strongest available evidence that the device accepts very few sessions, possibly one. The limit was not measured These rows describe how the platform validates a firmware image, which is a structural fact about its architecture. They are not a vulnerability disclosure, and no exploit, bypass or circumvention technique is published. #### What is programmable, ranked 1. **The control protocol** — 1,014 variables with their command mnemonics, access rights, types and ranges are all in the platform's own JSON on the appliance, and the table has since been reconstructed from the client and confirmed on a real NeXtage 16 — framing, identity, the set path, push, errors, the three preset contexts and the memory model. The stock client is a Flash applet served by the device, which is dead technology; a modern replacement controller is the obvious project and needs nothing that is encrypted. 2. **The Lua policy layer** — Layer validity, output format management and the memory model are Lua on the device filesystem, not compiled code. That is an unusually open seam for a machine of this class — though reaching it on real hardware, rather than in the simulator, was not investigated. 3. **Nothing in the video path** — The processing is on cards behind a backplane, driven by per-card micros, and every firmware component arrives inside the encrypted package. There is no analysable route to the picture from here. #### Traps Mistakes this analysis actually made. Each produced a plausible wrong answer rather than an obvious failure, which is why they are published. - Reading the model number as inputs and outputs. `ASC4806` looks like 48 inputs and 6 outputs; the machine has 12 inputs and 4 outputs, and the digits are 48 total layers and 6 layers per screen. `SMX12x4` genuinely is 12 in by 4 out, which makes the wrong reading feel confirmed. Only the capability table settles it. - Treating the encrypted update package as the end of the road. All three Analog Way packages examined here are AES-encrypted, which reads as a dead end — while the vendor's own simulator, already installed, ships the complete device filesystem unencrypted, including the platform's entire variable model. The locked door had an open one beside it. - Assuming `-4K` is a licence because it is presented in the interface as an option slot. It changes which physical connectors are usable on which input position, so it is a different card. An "option" in the vendor's configuration UI is not evidence of a software-only difference. - Averaging the encrypted package's statistics and stopping at "high entropy". The useful facts were the length being an exact multiple of 16 and the complete absence of repeated 16-byte blocks — which together say block cipher, chained or counter mode*, a much more specific claim than "encrypted". - Reading the appliance's inert control surface as a stubbed simulator. A session created with no options has no cards fitted, so every input reads unavailable, every screen allocates zero layers and nothing responds — which looks exactly like a simulator that models the wire and not the machine. It models both. The session has to be created with the device's own option ids, and the whole control surface then works. - Treating the selected-layer variable as a show/hide control. It is panel selection only, it does not change on-air state with or without a take, and it is deliberately absent from what a memory stores. ### Analog Way LivePremier / Aquilon (2018) - URL: https://aux-opsy.com/platforms/analog-way-livepremier/ - Class: Video presentation switcher - Research status: Partial - Last updated: 2026-09-06 - Hardware in our hands: One Aquilon C (`NLC_C`, serial GL0612, firmware 6.2.73) on our LAN. Read read-only over AWJ on 2026-08-20 and 21, then driven end to end — a fader move through to an opacity write, a take, and the preset flip that follows it — with the state captured beforehand and all 87 touched values verified restored afterwards. The card silicon, the encrypted update package and the eleven bundled firmware trees are unchanged and remain file-only; the unit was never opened. - Summary: A COM Express Xeon-D module driving a card frame of Intel Arria 10 GX FPGAs, one microcontroller per card. The RS models are fixed card lists; the C models are the same chassis sold configurable. - Key finding: The firmware's own generated enumeration comments do the classification for you: `NLC_RS4` is "NLC RS4 chassis 5U" and `NLC_CPLUS` is "NLC Custom chassis 5U". Same frame, same software, different cards. - Verdict: Hardware, and the manufacturer says so in its own source comments. RS models are factory-fixed card lists; C, C+, Cmax and Cmini are the same chassis in configurable form. One firmware package covers the family, cards are discovered and their FPGA designs matched at boot, and the machine is whatever is plugged into it. The architecture is a clean three-layer split: a COM Express Xeon-D module for the application and web interface, an ATSAM per card for local control, and Arria 10 GX fabric doing all the video behind a lane-based crosspoint. Its most useful property for anyone studying it is accidental — the web bundle was shipped with comments, and those comments document 387 internal enumerations of a machine whose firmware is otherwise sealed. A physical Aquilon C has since confirmed the useful half of that from the outside: the control protocol will not enumerate its own object model, the front end that ships with the device gives it up in full, and every write is broadcast to every client watching. - Open questions (what is NOT known): - What `VDW_*` is. Three chassis variants with a dedicated `FORCE_VDW` processing mode, no product page and no artwork. Video-wall is the obvious guess from the initials and it is only a guess. - Whether the boot-time card and design check is cryptographic. `CheckFpgaDesign` and the `MISMATCH` state prove a check exists; nothing in the files says what it compares. - The Arria 10 part per card role. `_66` clearly marks the 660K die on the output designs, which implies the unsuffixed designs target something smaller, but the specific parts are not named anywhere. - How much of the simulator's behaviour is the real firmware. It is a build of the device application, not a reimplementation, but it has been compiled for macOS and the boundary between simulated and real was not mapped. - The `.awp` key, which is on the device side and was not pursued. - What the mixer model does outside the two configurations we read. Link setups (devices 2–4), capacities other than 4K and 5K, combined VPUs, Optimized mode and Cut & Fill are all unexercised — and the second configuration we did read corrected two things the first had made look settled, which is the reason to distrust a one-chassis model. - Whether the path-existence oracle can be trusted for anything beyond existence. An unknown path answers with an error code, which gives a free existence check — but it reports the model's maximum rather than what the chassis has configured, and it disagrees with the front end's own declared dimensions. Useful for diffing firmware path tables, useless for limits. #### Method Two bases, deliberately kept apart. The architecture findings are static: the vendor update package is AES-encrypted and was not decrypted, and what is known comes from the vendor's simulator, which bundles eleven complete device software trees whose web interface shipped unminified with its source comments intact, including a generated file documenting 387 internal enumerations. Since 2026-08-20 there is a second basis: a physical Aquilon C on our LAN, read over the documented AWJ control protocol and over the undocumented WebSocket its own front end uses, and later driven through a full write chain with every touched value restored. Rows labelled measured came off that unit. Every claim about the card silicon, the update package and the other ten firmware trees is still file-only. #### Artifacts examined - `AQL_Updater_6_2_73.awp`, 751 MB — encrypted; statistics only - `AW LivePremier Simulator.app` — eleven firmware trees from 3.0.255 to 6.1.59, each carrying the device application and the web bundle the device itself serves - `dist/client/app.*.js` — the unminified web bundle; `variables-webapp.enum.json` revision 69.0.0-dev.0, generated from `C:\Projets\NLC\wa-nlc-data-objects\resources` - `AW_APP_SIMULATOR` — the 21 MB Qt/Node device binary, for the enum value strings the web bundle does not carry - The AWJ Protocol Programmer's Guide v4.0 — Analog Way's own published control documentation, which is why the protocol rows below are partly `public` rather than recovered - `app..js`, served by the device itself — roughly 22 MB of unminified webpack output, 8,105 concatenated modules that keep their original TypeScript paths and `__Generated__` headers. Around 2,040 device-object module paths, each carrying an attribute table with the type, minimum, maximum, default, read-only flag and enum of every property - Read-only AWJ captures from the Aquilon C — the running and the staged VPU mixer maps, and the shape (not the contents) of every top-level store collection Referenced by name and version only. Nothing is redistributed. #### Hardware architecture - [VERIFIED] **Host**: A COM Express module. `ENUM_DEVICE_CXP_MODULE` is `CONGATEC / ADLINK / ADVANTECH_5992 / ADVANTECH_5993 / KONTRON`, and `ENUM_CXP_CPU` is `INTEL_XEON_15XX` (Broadwell) or `INTEL_XEON_17XX` (Ice Lake). Two disks, primary for system and secondary for data - [VERIFIED] **FPGAs**: Intel Arria 10 GX, named in the design enumeration as `A10GX` with a `_66` suffix for the larger die — the 10AX066 / GX 660K part. One design per card function - [VERIFIED] **FPGA design catalogue**: 44 designs, named after Game of Thrones houses by role. Input designs are Lannisters (`JAIME` HDMI, `TYRION` DP, `CERSEI` SDI, `TYWIN` optic, `JEOFFREY` Aptovision, `TOMMEN` HDMI×8, `JOANNA` ST2110); output designs are Starks (`EDDARD` HDMI, `BENJEN` DP, `CATELYN` SDI, `RICKARD` IP-SDI, `BRANDON` DP×8, `ARYA` Share, `LYSA` ST2110); processing is Wildling (`TORMUND` 2D, `MANCE` 3D); monitoring is Baratheon; backplane is Martell - [VERIFIED] **Card slots**: `MOC` multiviewer, `IN_1..IN_4` at 8 inputs each, `PROC_1..PROC_4`, `OUT_1..OUT_6`, `FRAME_1..FRAME_2` image store, `FDP` backplane, `FAV` front panel, `AUDIO`, `SUPPLY` - [VERIFIED] **Distributed control**: An ATSAM microcontroller on every card, with the generator's comments giving the exact mapping — `IN_n` covers inputs 1–8, 9–16, 17–24, 25–32; `PROC_n_A` and `PROC_n_B` cover four scalers each; `OUT_n` covers four outputs each - [VERIFIED] **Chassis**: 3U, 4U, 5U or 6U. The backplane FPGA design differs between them — `OBERYN` and `DORAN` for 4U/5U, `OBARA` and `DOREA` for 6U — so the frames are not merely taller - [VERIFIED] **Card mezzanines**: Front-end and back-end cards carry a mezzanine: `FE_MZ_DP` ("8xDP IN TOP"), `FE_MZ_HDMI`, `FE_MZ_SDI`, `FE_MZ_TEST`. Card types themselves span DP, HDMI, SDI, SFP, optical HDMI, EXT CXP, EXT QSFP, SDVoE, capture, IP-SDI and ST2110 - [VERIFIED] **SDVoE**: The SDVoE card designs are named "Aptovision" — `JEOFFREY` in and `ROBB` out — which is the BlueRiver silicon - [VERIFIED] **Power**: Up to three PSUs, `BEL` or `ARTESYN` #### Software architecture - [VERIFIED] **Platform name**: Internally NLC — "New LiveCore". The web application's own manifest calls itself `nlc-platform`; the marketing name is Aquilon and the enum prefix in the user interface is `AQL` - [VERIFIED] **Detected models**: `NLC_CMINI` (AQL Cmini, 3U custom), `NLC_RS1` and `NLC_RS2` (4U), `NLC_C` (4U custom), `NLC_RSALPHA` (4U), `NLC_RS3` and `NLC_RS4` (5U), `NLC_CPLUS` (5U custom), `NLC_RS5` and `NLC_RS6` (6U), `NLC_CMAX` (6U custom), plus a debug entry - [VERIFIED] **Undocumented variants**: `VDW_W`, `VDW_WPLUS` and `VDW_WMAX` — custom 4U, 5U and 6U chassis with no product page and no chassis artwork, for which the client falls back to the RS4 image. A separate `ENUM_PROCESSING_MODE` offers `USE_DEV / FORCE_NLC / FORCE_VDW`, so there is a second processing personality in the same firmware - [VERIFIED] **Signal flow ceiling (6.1)**: 64 logical inputs, 64 scalers (`PROC_n_SCALER_mA` / `mB`), 64 mixer pipes as main and preview pairs, 24 screens, 96 auxiliaries, 128 layers per screen, 24 outputs, 2 multiviewer outputs, 32 share streams - [VERIFIED] **Crosspoint is lane-based**: `ENUM_CROSS_PROC_IN` addresses each physical input as 1 to 8 lanes (`LIVE_INPUT_n_TX_0..7`) according to its bandwidth, padded with entries the generator labels "Dummy input for modulo 32". A 4K source occupies several lanes of the same fabric an HD source uses one of - [VERIFIED] **Processing features**: Keyer with luma and chroma plus LUT, masks, borders, LUT3D conversion and correction banks, HDR with PQ and HLG and a BBC OETF conversion path, selectable 0–3 frame buffer, output rotation, soft-edge with ten control points, 3D stereo frame mapping - [VERIFIED] **Transport**: ST2110 with PTP and an NMOS registry, NDI with its own codec and error enumerations, SDVoE, Dante on the audio card, a QSFP "Share" ring for multi-frame source sharing, genlock, GPI/GPO and tally with protocol, colour and trigger models - [VERIFIED] **Web interface**: React, served by a Node server on the device. The eleven bundled trees span 3.0.255 (2022) to 6.1.59 (2026), which makes the platform's own evolution readable side by side - [PUBLIC] **AWJ, the documented path**: TCP 10606, plain sockets, at most five concurrent clients, one JSON object per message terminated by ASCII `0x04`. `op` is only ever `get` or `replace` — writing a property is the command, and there is no verb for anything else. Analog Way publish all of this - [MEASURED] **The published guide is already wrong for this firmware**: On 6.2 the screen-group path is gone, replaced by a screen-and-aux-group path, and the guide's own subscription example fails as printed. Paths move between firmware releases, so a path table is only true for the firmware it was read on — everything here is tagged 6.2.73 for that reason - [MEASURED] **AWJ is leaf-read-only**: A `get` on any container answers `{"value":{}}`. The object model cannot be enumerated over the protocol at all: you can read a leaf whose path you already know, and nothing else. Container reads being empty also means there is no way to tell a configured screen from an unconfigured one this way. This single constraint is what the whole recovery works around - [VERIFIED] **The front end supplies the model the protocol withholds**: Every LivePremier serves its Web RCS front end on port 80, and that bundle ships unminified — so its ~2,040 attribute tables are the device object model, with type, range, default, read-only flag and enum per property. Reading the bundle is what makes the leaves addressable, and it is the reason this platform is the most self-documenting machine in the index - [VERIFIED] **The Web RCS WebSocket**: Undocumented, and the transport the vendor's own browser client uses: one socket (`wss:` when the page is https), everything wrapped as `{channel, data}` JSON. Three channels — `LOG`, `DEVICE` and `REMOTE`, with `INIT` and `PATCH` under REMOTE. DEVICE data is `{path: [...], value}` in both directions, because the client emits diffs of its own store in the same shape it receives them. The keep-alive is the literal three-character strings `0x9` and `0xA`, which are easy to misread as control bytes - [MEASURED] **State arrives over HTTP; only changes arrive over the socket**: The client hydrates the entire device store with a single `GET /api/stores/device` — over 100 MB on a real device — and the socket then carries only diffs. Nothing has to reconstruct state from a stream, which is precisely what makes a third-party panel over this protocol possible - [MEASURED] **Writes are broadcast to every client**: A second observer socket sees another client's changes: this is a shared bus, not a request/response channel with an echo. One toggle in the vendor UI emitted 96 frames, one per auxiliary, and toggling it back emitted the inverse - [MEASURED] **The device is never quiet**: A timer value arrives every second on a completely idle machine, which is why the client's three-second keep-alive is never seen in practice, and why anything that redraws per frame has to coalesce - [MEASURED] **Echo semantics**: A write is acknowledged by the device pushing the same path back carrying the value you wrote — a boolean for a trigger, a string for a label, an array for each master-store filter — and it then pushes triggers back to `false`, which acknowledges nothing at all - [MEASURED] **An empty memory is answered with silence**: Recalling a populated slot echoes the request and then reports loading and the new preset id. Recalling an empty one echoes the request and says nothing further, ever — not refused, not reported. The tell is that the loading flag never appears, so anything reporting success from the echo alone is lying. Reproduced identically on the simulator and on the Aquilon - [MEASURED] **Preview and programme are not buffers**: Control paths use PREVIEW and PROGRAM; status reports `A`, `B` and `C`. The device keeps three fixed buffers and the names float across them as takes swap which is which — on one recall pass on the Aquilon, screen 1 landed on `A` while screens 2, 3 and 4 landed on `B` at the same moment. There is no "preview is B" to hard-code anywhere; resolving a buffer needs the current take state. Structurally the same model as the LiveCore generation's three preset contexts - [MEASURED] **The VPU mixer model**: A VPU mixer is the physical mixing and scaling resource the device allocates to a (screen, layer) pair: four processors of sixteen, 64 in a full chassis, and most chassis are part-populated — ours reports 32 fitted and 28 enabled, each tagged with its capability, its screen, its layer and its slice. A layer too wide for one mixer is split across several, one slice each, which is why an eight-slice native layer can consume an entire processor board and why counting layers never tells you whether a configuration will fit. The device reports the map read-only and keeps two copies of it, running and staged - [MEASURED] **Pipes are not links**: Each mixer reports which output pipe it is wired to and which of the screen's output links that pipe carries, and the two disagree on purpose: pipes are interleaved — a six-output screen's first mixer sits on pipes 1, 3, 5 and 7 — while the screen's own links are contiguous. Reading the pipe number as a link number draws a chassis map that is plausible and wrong #### Update path and security model - [VERIFIED] **Update package**: AES-encrypted, same construction as the LiveCore packages — 7.9998 bits/byte from byte zero, length an exact multiple of 16, no repeated 16-byte blocks in 64 MB - [INFERRED] **Package scope**: 751 MB, which is consistent with carrying every FPGA design for every card type plus an x86 system image for the COM Express module. Not confirmed — the package was not opened - [VERIFIED] **Certificates**: The simulator ships `device-livePremier-chain-cert.pem`, so the platform has a device certificate chain. Its use was not investigated - [UNKNOWN] **Card and design validation**: The firmware carries `CheckFpgaDesign`, `CheckHardwareSlot` and a `MISMATCH` design state meaning "FPGA design mismatch card type", so cards are identified and their designs matched at boot. Whether that check is cryptographic or a plain identifier comparison is not known - [MEASURED] **The front end, and the object model, are served unauthenticated**: Every LivePremier serves its Web RCS on port 80 with no authentication — and because the bundle it serves is unminified, what is served to anyone who can reach the box is the device's own object model: some 2,040 paths with their types, ranges and read-only flags. The control protocol behind it has no authentication step either. Five concurrent clients, any of which may `replace`. This is a control-VLAN device by construction - [MEASURED] **Read-only is a property of the model, not a mode**: The attribute tables mark properties read-only individually and the device enforces it — everything the VPU work touches is read-only in the device's own model, which is what made a read-only pass over a live machine a safe thing to do. There is no session-level read-only mode to ask for, so a client's own discipline is the only boundary between reading a machine and changing it These rows describe how the platform validates a firmware image, which is a structural fact about its architecture. They are not a vulnerability disclosure, and no exploit, bypass or circumvention technique is published. #### What is programmable, ranked 1. **The AWJ control API** — The whole device model is a JSON tree over a documented interface, and the web client that drives it ships unminified with the generator's comments intact — which is the only reason the model is reachable at all, since AWJ itself will not enumerate a container. This is the most thoroughly self-documenting machine in the index, everything useful about it is reachable without touching the encrypted package, and both halves have now been exercised on a real Aquilon C. 2. **Standards-based transport** — ST2110, NMOS, NDI, SDVoE and Dante are all first-class. Anything that wants to get pictures or audio in or out of this platform should do it through one of those rather than through the vendor's own plumbing. 3. **Nothing inside the frame** — Arria 10 designs delivered inside an encrypted package, matched to card identifiers at boot, with an ATSAM gatekeeping each card. There is no third-party route into the video path and no reason to look for one. #### Traps Mistakes this analysis actually made. Each produced a plausible wrong answer rather than an obvious failure, which is why they are published. - Going after the encrypted 751 MB package first. It is the obvious artifact and it yields four statistics; the simulator sitting in `/Applications` carries eleven complete firmware trees and a web bundle that was never minified. Check what the vendor already gave you before attacking what they locked. - Taking `AQL` and `NLC` for different products. They are the same platform — `NLC` is the internal name and appears in the code, the build paths and the web app manifest, while `AQL` appears only in user-facing labels. Searching for one and not the other halves what you find. - Assuming the enum names and the enum value strings live together. The binary holds the names `ENUM_CHASSIS_TYPE`, `ENUM_CARD_FEBE_MEZZA_TYPE` and so on in one table and their values in another, hundreds of kilobytes apart. Grepping for the name and finding nothing useful around it suggests the values are absent when they are simply elsewhere. - Reading the 64-entry scaler enumeration as sixty-four independent layer engines. They are four processing cards × eight mixers × an A/B pair, and the pairing is what makes a main-and-preview transition possible. The flat list hides the structure that matters. - Grepping the front-end bundle. BSD `grep` fails silently on its multi-megabyte lines — no match, no error, no indication anything went wrong. Everything that reads that bundle has to do it in a language that does not care how long a line is. - Reading a limit off the error oracle. It answers for the model rather than the chassis and contradicts the device's own declared dimensions; two of the four memory banks were nearly recorded at the wrong size this way. - Matching a write's acknowledgement on `true` instead of on the value written. Triggers are boolean, labels are strings and every master-store filter is an array, so a client that only accepts `true` reports labels and filters as unconfirmed forever while they land perfectly. It cost a real bug, and only hardware showed it. - Waiting for a keep-alive frame to identify the client's socket. It only pings after three seconds of silence and a live device never goes quiet, so a hook installed late waits forever. Adopt on the first frame instead. ### Blackmagic Design ATEM (switcher family) (2012) - URL: https://aux-opsy.com/platforms/blackmagic-atem/ - Class: Live production video switcher - Research status: Documented - Last updated: 2026-08-14 - Hardware in our hands: none — analysis is file-derived - Summary: A decade of vision mixers that are all the same idea: a Xilinx FPGA does the video, and a CPU inside that FPGA runs a bare-metal control stack. The CPU changes generation to generation (PowerPC, then MicroBlaze, then ARM) and so does the fabric under it — Virtex-5, Virtex-6, Kintex-7, Zynq — but the FPGA-does-everything shape never does. - Key finding: Every switcher update pairs one Xilinx bitstream (sync word `AA 99 55 66`) with one CPU image, and the bitstream names the part it is for: a PPC440 in a Virtex-5 FXT on the first M/E frames, then MicroBlaze in a Virtex-6 and then a Kintex-7, then ARM in a Zynq-7000. The mixer was never a computer with a video card; it was always an FPGA with a CPU inside it — and the CPU was inside it even on the generation this page previously read as a separate ARM. - Verdict: Hardware, consistently and for a decade. The ATEM line is a single architectural idea re-spun across four CPU generations: a Xilinx FPGA carries the entire video path and a CPU that lives inside or beside it — PowerPC hardcore, then MicroBlaze soft core in both endiannesses, then a discrete ARM — runs a bare-metal control stack with its own TCP/IP and mDNS. There is no operating system to speak of and, on the older frames, no firmware protection beyond an integrity digest; the newest models close that with an encrypted image. What makes the family approachable is entirely on the outside: a published SDK over a stable UDP state-sync protocol, so the interesting surface is the network, not the silicon. - Open questions (what is NOT known): - What the encrypted generation actually contains. The construction is visible from the outside (single stored high-entropy stream, not block-aligned); the plaintext was neither recovered nor examined here, so whether the modern frames kept the FPGA-plus-CPU shape or changed it is unconfirmed. - The exact Xilinx parts on the encrypted generation. The plaintext packages state their device in the bitstream's IDCODE packet and are settled; the ~20 encrypted packages cannot be read at all, so whether the newest frames stayed on Zynq is unconfirmed. The package, speed grade and die revision are not in the IDCODE either — that field names the device, not the ordering part number. - Whether the modern housekeeping MCU is really an 8-bit AVR or an AVR-cored SoC. The vector table is unmistakably AVR; the surrounding board role is a guess. - The wire-protocol command codes. The firmware stores field names in clear text but not the four-character command tags, which are built from a table — so the exact opcodes were taken from the SDK's failure enums, not confirmed against the images. - The USB-PID identities of the ~20 encrypted packages, which cannot self-identify. They were bounded as a set and dated by version number, but each one was not individually tied to a model name. #### Method Static analysis of the macOS ATEM Setup 10.2.1 installer (January 2026). Its `Contents/Resources` holds 43 firmware packages plus per-language sub-packages, each a custom container keyed by the device's USB product ID under Blackmagic's vendor ID `0x1EDB`. The container was reversed from scratch; older payloads decompress to raw images that self-identify, and the published Switchers SDK headers supplied the control-model vocabulary. No hardware was inspected and no encrypted payload was decrypted. #### Artifacts examined - `ATEM Setup.app/Contents/Resources/data-*.bin` — 43 firmware packages, product-ID-keyed (`data-bd48.bin` … `data-bf01.bin`) - The container header: 32-byte digest, then chained blocks, each `0xBDBD`-tagged with `zlib`/stored payload and 24-byte applies-to records carrying VID `0x1EDB`, the USB PID and a firmware-version integer - Decompressed CPU images: ELF32 for PowerPC (`data-bd48`), AVR32 (`data-bd4a/50/57`), MicroBlaze BE (`data-bd52`) and LE (`data-bd6e/81/87`), ARM (`data-be4b`) - `Developer SDK/.../BMDSwitcherAPI.h` — 5,105-line COM-style control API, plus fourteen legacy versions back to v3.5 - The Bluetooth-panel packages `data-bef0/bf01` — a tar of an ECDSA-signed manifest over encrypted `Panel`/`Bluetooth`/`Version` components Referenced by name and version only. Nothing is redistributed. #### Hardware architecture - [VERIFIED] **Video fabric**: A Xilinx FPGA in every switcher. Each package carries a raw Xilinx configuration image — long `0xFF` pad then the `AA 99 55 66` sync word — and the CPU that sits with it is always a Xilinx-hosted core, which is the strongest available evidence that the whole mix engine is FPGA fabric rather than a fixed-function ASIC - [VERIFIED] **FPGA parts — read, not inferred**: Every one of these bitstreams names its own device. A Xilinx configuration image writes the target JTAG IDCODE to configuration register `0x0C` before it writes any frame data, so "a raw `.bin` with no part name in it" still states its part in a fixed place. Walking the packet stream of all 43 packages gives, per generation: XC5VFX130T (`0x03300093`) with the PowerPC; XC6VLX75T (`0x04244093`) with MicroBlaze big-endian; XC7K325T (`0x03651093`, two models) and XC7K420T (`0x03752093`) with MicroBlaze little-endian; XC7Z010 (`0x03722093`) with the ARM; and XC7Z015 (`0x0373B093`) on the newest frames, five of which pair it with a second FPGA — XC7A100T (`0x03631093`) or XC7A25T (`0x037C2093`). Part names resolved against Xilinx UG470 Table 1-1 and UG360 Table 6-13 - [VERIFIED] **Control CPU — generations**: The switcher CPU is an ELF whose machine field walks the Xilinx roadmap: PowerPC on the first 1 M/E and 2 M/E frames (`e_machine 20`, big-endian, entry `0xFFFFFFFC`), then MicroBlaze big-endian (Television Studio), then MicroBlaze little-endian (Production Studio 4K), then ARM (`e_machine 40`, entry `0x00100000`) on the Broadcast Studio 4K / Constellation frames. Pairing each ELF with the IDCODE of the bitstream shipped beside it dates the fabric exactly, and corrects two readings this page previously made from the CPU alone: the PowerPC part is XC5VFX130T, a Virtex-5 FXT whose hardcore is PPC440, not the PPC405 of the Virtex-II Pro / Virtex-4 FX era; and the ARM is not discrete but the PS of an XC7Z010, a Zynq-7000 — the same shape as the SQ-5, arrived at from the other direction - [VERIFIED] **Control panels**: The broadcast panels (1 M/E and 2 M/E Broadcast Panel) run an Atmel AVR32 — ELF `e_machine 185`, entry `0xD0000000` — with their own small Xilinx configuration image and an lwIP-derived network stack (mDNS responder and NAT-traversal strings present) - [INFERRED] **Housekeeping MCU (modern frames)**: Modern packages open with a small 8-bit AVR image — the first block is a textbook ATmega/ATxmega interrupt-vector table of `jmp` instructions — most likely power sequencing, fan and front-panel housekeeping alongside the main ARM - [VERIFIED] **Modern package is multi-processor**: A current package (e.g. Television Studio Pro 4K, `data-be2c`, ten blocks) bundles the AVR MCU image, two ARM32 vector images (a bootloader and an application, both starting `EA FF FF FE` reset branches), several opaque configuration blobs, a Xilinx bitstream and a tar payload — a whole board's worth of processors shipped as one file - [INFERRED] **Bluetooth panels**: The ATEM Micro / Camera panel packages target flash address `0x0800_0000` — the STM32 Cortex-M vector base — and ship a separate `Bluetooth` firmware component, so the desktop panels are an STM32 with a Bluetooth radio - [VERIFIED] **Network / discovery**: Switchers advertise over mDNS/Bonjour as `_blackmagic._tcp` and the firmware carries its own `mDNS Atem` responder; control is IP/UDP (the SDK connects to an IP address, not a USB path) #### Software architecture - [VERIFIED] **Operating system**: Bare-metal / small RTOS — no Linux, no VxWorks. None of the decompressed images carry a kernel banner, `/proc`, a shell or an `init`; they are flat ELFs built with GCC that link their own TCP/IP and mDNS. The switcher is an application that owns the whole chip - [VERIFIED] **State model**: The firmware speaks a flat attribute namespace — `ProgramInput`, `AuxSource`, `FtbCurrent`, `PrgmBkgd`, and so on in clear text — and the published SDK mirrors exactly that as a tree of COM interfaces (`IBMDSwitcherMixEffectBlock`, `IBMDSwitcherInput`, `IBMDSwitcherDownstreamKey` …). The wire protocol is the well-known ATEM UDP state-sync: the SDK's failure enum names it (`cfss` StateSync, `cfst` StateSyncTimedOut) alongside `cfif` IncompatibleFirmware and `cfcd` CorruptData - [VERIFIED] **Firmware container**: A custom package format, reversed here: a 32-byte digest over the file, then a chain of blocks. Each block header is big-endian — magic `0xBDBD`, a descriptor length, a compression byte (`0` stored, `1` zlib), the uncompressed size and a CRC32 — followed by 24-byte applies-to records `{VID 0x1EDB, PID, version}` and the payload. One package can carry many images for many processors - [VERIFIED] **Model ↔ USB PID map (self-identified)**: The older images name themselves: `bd48` 1 M/E Production Switcher, `bd5c` 2 M/E Production Switcher, `bd52` Television Studio, `bd6e` Production Studio 4K, `bd81` 1 M/E Production Studio 4K, `bd87` 2 M/E Production Studio 4K, `bd4a`/`bd57` 1 M/E & 2 M/E Broadcast Panel, `be0e` Camera Control Panel, `be25` Television Studio HD, `be26` Television Studio Pro HD, `be2c` Television Studio Pro 4K, `be4b` 2/4 M/E Broadcast Studio 4K - [VERIFIED] **Firmware version numbers**: Each applies-to record carries a monotonic version integer, so the family's age is legible from the files: MicroBlaze Television Studio = 67, Production Studio 4K = 314, Television Studio Pro 4K = 674, Broadcast Studio 4K / Constellation = 1378, and the shared modern switcher application = 1951 #### Update path and security model - [VERIFIED] **Most packages — integrity only**: The bulk of the firmware is neither signed nor encrypted: zlib-compressed images sitting behind a single 32-byte digest. That digest is an integrity check, not an authenticity one — there is no certificate and no signature over the older images - [VERIFIED] **Newest generation — encrypted**: About twenty of the most recent models (Constellation 8K, the ISO/G2 Minis, the SDI Pro/Extreme frames, Television Studio HD8) ship as a single opaque high-entropy payload — 7.95–8.00 bits/byte from byte zero, stored not compressed. The payload is not block-aligned, so it is a stream construction rather than a padded block cipher. Its plaintext was not recovered and its contents were not examined here - [VERIFIED] **Bluetooth panels — signed**: The ATEM Micro / Camera panel packages are the only signed images in the set: a plaintext `manifest` (component name, size, SHA-256, flash address) accompanied by an ECDSA/secp256r1 signature in DER form (`manifest.sig`), over AES-encrypted `Panel`, `Bluetooth` and `Version` components - [INFERRED] **No per-feature licensing**: Nothing resembling an entitlement or unlock mechanism appears. ATEM capabilities are fixed per model in hardware, so — unlike the audio consoles in this index — there is no key scheme to find These rows describe how the platform validates a firmware image, which is a structural fact about its architecture. They are not a vulnerability disclosure, and no exploit, bypass or circumvention technique is published. #### What is programmable, ranked 1. **The published SDK and the UDP state-sync protocol** — Blackmagic ships the whole control surface as a documented COM API, and the underlying UDP protocol is stable enough that an ecosystem of independent libraries drives it. Everything worth doing to an ATEM is reachable over the network without touching firmware — this is the route, not the FPGA. 2. **mDNS discovery** — Switchers announce themselves as `_blackmagic._tcp`; finding and enumerating them on a network needs nothing proprietary. 3. **Nothing inside the fabric** — The video path is a Xilinx bitstream delivered inside the vendor package, and the newest packages are encrypted outright. There is no third-party path into the mix engine and no reason to build one when the control API is open. #### Traps Mistakes this analysis actually made. Each produced a plausible wrong answer rather than an obvious failure, which is why they are published. - Reading `data-.bin` as one blob. It is a chained container with a 32-byte digest in front and multiple compressed images inside; treating the first bytes as a header, or running `file` on it, tells you nothing. The `0xBDBD` block magic sits at offset `0x20`, not `0x00`. - Taking every ATEM for the same chip. The CPU changes generation to generation — a PowerPC ELF on a 2012 frame, a little-endian MicroBlaze on a 4K one, an ARM on a Constellation — and only the Xilinx fabric is the constant. A finding from one image does not carry to the next without checking the machine field. - Searching for the sync word in one byte order. Six of the eleven plaintext bitstreams here are byte-reversed per 32-bit word, because they are loaded through a Zynq's PCAP port rather than presented to the configuration pins: they carry `66 55 99 AA`, not `AA 99 55 66`. A search for the documented sync word alone finds five of eleven and reports the rest as "no bitstream present" — which is not a null result, it is a false one. The same trap is what left the [Qu](https://aux-opsy.com/platforms/allen-heath-qu/) recorded here for a week as having no Zynq in it. - Assuming the whole line is locked because the newest models are. Most of the packages are plain zlib behind an integrity digest with no signature at all; the encryption is a recent arrival on a minority of models, not a family-wide policy. - Chasing the FPGA. It is the obvious centre of the machine and it is a dead end for anyone who wants to control an ATEM — the bitstream is opaque, and the entire useful surface is the published UDP protocol the switcher already speaks. ### Shure MXCW (2018) - URL: https://aux-opsy.com/platforms/shure-mxcw/ - Class: Wireless conference system - Research status: Partial - Last updated: 2026-08-14 - Hardware in our hands: none — analysis is file-derived - Summary: A conference platform that is closed on the inside and unusually generous on the outside: every application payload in the access point is encrypted, while the control interface is plain ASCII on an open port that pushes a report every time anything changes. - Key finding: The access point signs its updates — 256 raw bytes of detached signature over a manifest carrying a SHA-256 per payload — and encrypts the Linux image, the application and the web GUI on top, so none of its software has been read. Two things ship in the clear: the Xilinx bitstream, and the charging station, which is not encrypted at all and hands over the client half of Shure's private device protocol. - Verdict: Closed inside, generous outside. Nothing of the access point's software has been read — the Linux image, the conference application and the web GUI are all encrypted behind an RSA-signed manifest, and the conference unit's entire payload set with them. What ships in the clear is the Xilinx bitstream that is almost certainly the radio, and the charging station, which is not encrypted at all and gives up the client half of Shure's private device protocol: ACN/DMP over a binary WebSocket, carrying the one primitive the public interface lacks. And the public interface is genuinely good — plain ASCII on an open port, unauthenticated, reporting every change unsolicited so a controller never has to poll. It has exactly one hole, and it is a load-bearing one: eight uplink slots are metered individually and nothing in the protocol says which seat is in which slot, so anything that wants to know who is loudest has to leave TPCI and take the audio. - Open questions (what is NOT known): - The payload plaintext. `linux.bin`, `app.tge`, `gui.tge` and the whole MXCW640 set are encrypted, no key was recovered and nothing was decrypted — so every claim about the access point's and the conference unit's software rests on filenames in a manifest. - The access point's DMP property map. The charging station's application carries only charger-domain properties, and the access point's live in the encrypted `gui.tge`. Whether per-uplink-slot occupancy is exposed as a subscribable property is the single question that would close the slot-to-seat gap, and it cannot be answered without a device. - Which Xilinx part, and whether it really is the TDMA scheduler. The bitstream is raw `.bin` naming no device, and the RF-engine reading comes from the timing budget rather than from anything in the image. - Whether a second, read-only controller on 2202 is harmless. Shure says "use only one controller per system to avoid messaging conflicts"*, which reads as a warning about conflicting `SET`s — reports are broadcast and the protocol has no session concept — but that is a reading, not a tested fact. - The `RSSI` encoding. Shure documents the units and not the encoding; the only prior art applies an offset of −120 and its own comment calls the offset assumed. - Whether the interface needs a keepalive at all. None is specified, a dead TCP session is indistinguishable from a quiet meeting, and the 30 s probe / 60 s timeout pattern in general use was invented by one third-party module for reasons nobody recorded. #### Method Static analysis of the 7.1.3, 7.1.4 and 7.3.0 firmware packs for all three devices, obtained through Shure Update Utility, read against Shure's own MXCW Command Strings and MXCW User Guide publications. The application payloads are encrypted and none was decrypted; the readable findings come from the pack containers, the plaintext FPGA bitstream, and the charging station's unencrypted web application. No hardware was inspected and no frame of the control protocol has been observed on a wire. #### Artifacts examined - `MXCWAPT.7.3.0.pack` — a plain zip: `BuildInfo.json`, `MXCWAPT.manifest.xml`, a 256-byte `MXCWAPT.manifest.xml.sig`, `UpdateInstructions.xml` (UploadCommand, per-file Copy, then Execute), and the payloads. The manifest also carries `DCID` device-class UUIDs - `apt_fpga.bin` (9.7 MB) — a plaintext Xilinx configuration image: `FF` padding, the bus-width detect pattern `000000BB 11220044`, the sync word `AA995566`, then type-1 configuration packets - `linux.bin` (13.3 MB), `app.tge` (5.0 MB), `gui.tge` (13.3 MB) — the access point's Linux image, conference application and web application, all encrypted - `vxWorks`, `QtApp.tar.gz`, `m4app.bin`, `dsp.bin`, `QtLib.tar.gz`, `QtFonts.tar.gz`, `dsp.tar.gz` — the MXCW640 conference-unit payload set (29.7 MB), encrypted throughout - `m4app.z` and `OdinGui.z` — the MXCWNCS charging station's payloads, plain zlib and not encrypted; `OdinGui` decompresses to its web application in the clear (AngularJS 1.8 + Angular Material) - `microflex-complete-9.4.0` — the wired MXC / DCS 6000 update bundle, read as background: `cu_DIS-CCU_*.dis` central-unit images, `FUU_Library.xml`, and the `action2` flash script Referenced by name and version only. Nothing is redistributed. #### Hardware architecture - [PUBLIC] **System composition**: Three devices on one platform: the MXCWAPT access point (RF master, audio matrix, web server, Dante endpoint and third-party control endpoint, PoE powered), the MXCW640 battery conference unit (gooseneck mic, loudspeaker, 4.3-inch touchscreen, NFC card slot, SB930 smart battery), and the MXCWNCS ten-bay charging station, which has its own web application and its own control endpoint on 2202. One access point serves up to 125 units on one RF channel - [PUBLIC] **RF layer**: IEEE 802.11 a/g PHY across 2.4 GHz ISM and 5 GHz UNII including DFS, AES-128 on the link. Slot allocation is asymmetric and fixed: 8 uplink channels, allocated only to active speakers, and 9 downlink — one floor mix plus eight interpretation channels — broadcast continuously to every unit. Four power steps from 1 mW to 25 mW, and a built-in spectrum analyser that ranks channels continuously and quarantines a dirty one for 30 minutes - [INFERRED] **What actually runs the radio**: Shure runs a proprietary TDMA scheduler over a commodity Wi-Fi PHY rather than a bespoke radio: the spec table names 802.11 a/g, but fixed slot counts, deterministic 16 ms latency and 125 units on one channel are not CSMA behaviour. The scheduler most plausibly lives in the access point's FPGA — that is what an unencrypted 9.7 MB bitstream is for — but nothing in the pack says so, and the encrypted Linux image or a separate DSP are the competing readings - [VERIFIED] **Access-point FPGA**: `apt_fpga.bin` is a plaintext Xilinx configuration image, opening with the standard preamble — `FF` padding, bus-width detect `000000BB 11220044`, sync word `AA995566`, type-1 packets. So the access point carries a Xilinx FPGA, and its bitstream is the one payload of the three devices' application images that is not encrypted. The file is raw `.bin` and names no part - [INFERRED] **Conference-unit processors**: The MXCW640 pack ships `vxWorks`, `QtApp.tar.gz`, `m4app.bin` and `dsp.bin`, so the unit is VxWorks with a Qt UI, an ARM Cortex-M4 companion and a DSP. The filenames and sizes are read directly from a manifest that carries a SHA-256 for each, and the Qt lineage is shared with the wired MXC640 (whose bundle ships `6kU_MXC640_QtLib_5.5.1.96`) — but every one of those payloads is encrypted and none has been opened, so the naming is the entire evidence - [PUBLIC] **Audio architecture**: 24-bit / 48 kHz throughout. Latency mic → speaker 16 ms, mic → analogue out 9.2 ms, analogue in → speaker 7.7 ms. The access point carries one analogue XLR in and one out plus 10 Dante in and 10 Dante out, with an internal matrix mixer whose sources are the floor mix, eight arbitrary group mixes, or one of the eight uplink slots taken directly - [VERIFIED] **Charging station — the readable device**: The MXCWNCS is not encrypted at all: `m4app.z` and `OdinGui.z` are plain zlib, and its 7.1.4 and 7.3.0 packs are identical in size, so the charger did not change between those releases. `OdinGui` decompresses to 4.1 MB that is 83 % printable — its web application in the clear. `Odin` is a Shure platform name spanning at least the access point and the charger #### Software architecture - [VERIFIED] **Pack format**: A `.pack` is a plain zip holding `BuildInfo.json`, a manifest naming a key, a version and a SHA-256 for every payload, a detached signature over that manifest, and `UpdateInstructions.xml` — an UploadCommand, a Copy per file, then an Execute. The manifest's `DCID` entries are device-class UUIDs, matching the `controllerDCid` concept in the charging station's protocol code - [INFERRED] **Access-point software**: `linux.bin`, `app.tge` and `gui.tge` — a Linux image, the conference application and the web application, in that order of size. All three are encrypted and none has been read: the split between them is read off the manifest, not off any code - [PUBLIC] **TPCI — the published control interface**: ASCII, one message per `< … >` pair, on TCP 2202. Four message types: `GET`, `SET`, `REP` and `AUDIO_SAMPLE`. The load-bearing fact is that `REP` is pushed unsolicited whenever a parameter changes by any means — front panel, web app, chairperson, or a participant's own speak button — so a controller connects once, seeds with a `GET`, and never polls. Seat numbers run 1–65535, index `0` on a `GET` fans out over online registered seats, free text is brace-wrapped UTF-8, gain and volume are offset by 30 and metering RMS by 98 - [INFERRED] **The seam TPCI does not close**: `AUDIO_SAMPLE` carries `slot1Peak slot1Rms … slot8Peak slot8Rms`, and no command in the published set maps a slot to a seat. Per-speaker level is therefore not obtainable over this interface at all; the ways round it are per-slot Dante outputs with your own detection, or the private protocol below. The reading is a complete enumeration of a published command reference rather than a stated absence, and the private API may well expose the mapping - [VERIFIED] **Shure's private device protocol**: The charging station's unencrypted web application talks to its own device over a binary WebSocket with subprotocol `dmp`, framed with `DataView` writes, `MAX_BUF_SIZE` 2462, a five-second discovery timeout and 16-byte device UUIDs. Its constants are in the clear: clusters `DMP` 162, `KEEP_ALIVE` 163, `SDT` 164, `DISCOVERY` 167, `SID` 168; DMP vectors `GET_REPLY` 3 through `DEALLOCATE_MAP` 16 including `SUBSCRIBE` 7 and `EVENT` 4; discovery vectors `ANNOUNCE` 1 … `ATTR_RSP` 7; address types `SINGLE` / `RANGE_SINGLE` / `RANGE_FIXED` / `RANGE_MIXED` with `VECTOR_FLAG` `0x4000` - [INFERRED] **What that protocol is**: ACN — ANSI E1.17: SDT carrying DMP, the same pair Shure's own port table lists on UDP 5568 multicast and 57383 unicast, and one of the charger's property groups is named `NETWORK_ACN`. The vocabulary is standard ACN rather than a bespoke invention. The reading rests on the constant names and the port table, since no frame has been captured — and it matters because DMP has the primitive TPCI lacks: `SUBSCRIBE` on an arbitrary property address, answered with pushed `EVENT`s - [UNKNOWN] **The property map**: Knowing the protocol is not knowing the vocabulary. The charging station's application carries only charger-domain properties — `CHRG_SLOT`, `BATTERY_PRESENT`, `NUM_SLOTS`, `SLOT_BAYS`, `CHARGE_STATUS` — and no seat or microphone properties whatever. The access point's own property IDs live in `gui.tge`, which is encrypted - [VERIFIED] **Build provenance**: The tar headers inside the conference unit's wrapped payloads leak the build hosts: `Administrators / buildmaster_ATE` on a Windows box for `QtLib` (February 2019) and `buildmaster / buildmaster` for `QtFonts` (December 2020) #### Update path and security model - [VERIFIED] **Signed updates**: The integrity chain is authenticity, not just integrity: the manifest signature is 256 raw bytes → RSA-2048 over `MXCWAPT.manifest.xml`, and that signed manifest carries a SHA-256 for every payload file. Nothing in the pack can be swapped without breaking one or the other - [VERIFIED] **The encryption container**: Every encrypted payload has the same shape — `[ciphertext, length a multiple of 16][md5(ciphertext) as 32 ASCII hex]`. Confirmed on all three access-point payloads: strip the last 32 bytes, MD5 the rest, and it matches the trailer exactly - [INFERRED] **The cipher**: Shannon entropy 7.9999 over the first 2 MB of each payload, zero repeated blocks at 8, 16 or 32 bytes across 4 MB, and a body length always ≡ 0 mod 16. That is consistent with AES in CBC or CTR and rules out ECB. There is no key, no IV and no header identifying either — presumably both live in the bootloader or the running system. No key was recovered and nothing has been decrypted - [VERIFIED] **The `.tar.gz` payloads are a wrapper, not content**: `QtLib.tar.gz`, `QtFonts.tar.gz` and `dsp.tar.gz` each gunzip to a tar holding exactly one member named after the archive, and that member is encrypted. `QtApp.tar.gz` is not gzip at all — it is encrypted from byte zero, despite the name - [PUBLIC] **Network posture**: From Shure's own port table: no HTTPS and no authentication documented anywhere. TCP 2202 is open by default and unauthenticated, so anything with IP reachability to the access point can turn microphones on and off; TCP 64000, Shure's firmware update channel, is also open by default, while FTP, SSH, Telnet, HTTPS and the 8023 debug console are closed. Discovery is mDNS on 5353, with SLP multicast on 8427 and SDT on 5568/57383 as Shure's undocumented inter-device fabric. This is a control-VLAN device by construction - [VERIFIED] **Against the wired ancestor**: The signing is a marked change from the wired MXC / DCS 6000 line — the same manufacturer, the same product family name, an entirely different posture. Those central-unit images are unsigned, covered only by an md5 that ships in the same archive as the image it protects These rows describe how the platform validates a firmware image, which is a structural fact about its architecture. They are not a vulnerability disclosure, and no exploit, bypass or circumvention technique is published. #### What is programmable, ranked 1. **TPCI on TCP 2202 — the published, push-based control interface** — ASCII, documented by Shure, open by default, and it reports every change unsolicited, so a third-party controller connects once, seeds with a `GET`, and reacts. This is the whole useful surface and it needs no firmware access at all: a mic-follows-camera sidecar was built on it and runs end to end against a simulator, with no access point ever in the loop. 2. **Dante, for anything the protocol will not tell you** — Ten Dante outputs, and an output can be fed directly from one of the eight uplink slots — so per-active-speaker audio leaves the box even though the slot's identity does not. Level-driven work belongs here, not on `AUDIO_SAMPLE`. 3. **The private ACN/DMP WebSocket** — Richer than TPCI, with `SUBSCRIBE` on a property address answered by pushed `EVENT`s. The framing and vocabulary are known from the charging station's unencrypted application; the access point's property IDs are not, and enumerating them via `ATTR_RSP` needs a real device on a bench. 4. **Nothing inside the firmware** — The application payloads are encrypted with no key in hand, and the update path is RSA-signed over a SHA-256 manifest. The images are closed by construction, and everything worth doing is on the network side of the box. #### Traps Mistakes this analysis actually made. Each produced a plausible wrong answer rather than an obvious failure, which is why they are published. - Treating the one other public implementation as corroboration. The Bitfocus Companion MXCW module was read in full — every commit and every issue — and it is a careful transcription of the same Shure publication this work uses: four substantive commits in three and a half years, and not one report of hardware behaving differently from the document. Two readings of one document agreeing is not confirmation, and an earlier draft here claimed independent corroboration and had to be corrected. The module contributes exactly two things of its own, a keepalive pattern and an explicitly-guessed `RSSI` offset. - Predicting the private API from the wired ancestor. The wired DIS-CCU serves its own web UI from a JSON-over-WebSocket endpoint, so MXCW was expected to do something similar. That a richer private API exists was right; the guessed shape was wrong. It is binary ACN/DMP with a `dmp` subprotocol, and a JSON parser pointed at it sees nothing at all. - Reading `AUDIO_SAMPLE`'s eight slot pairs as eight seats. The frame looks exactly like per-participant metering and it is not — slots are uplink channels, occupancy is dynamic, and no message attributes a slot to a seat. Anything built on that reading behaves perfectly in a two-person test and points at the wrong person in a real meeting. - Believing a filename. `QtApp.tar.gz` in the conference-unit pack is not gzip; and the three payloads that genuinely are gzip each contain a tar holding one member named after the archive, which is itself encrypted. The wrapper is not the content, twice over. - Working from Shure's consolidated ports PDF. It still lists the web application on 8180 unconditionally, while the MXCW user guide's own table says 8180 is legacy-firmware-only and current units serve port 80. The stale document is the more official-looking one. - Assuming at most one index before a parameter name. `VOTING_BUTTON_NAME` takes two — `< GET 3 0 VOTING_BUTTON_NAME >` — and it is the only command in the protocol that does. A parser that treats the token after the first integer as the parameter name reads the parameter as `2` and drops the message silently rather than failing, which is exactly what the Companion module does. ### BirdDog NDI converters (Studio / Flex / 4K / PLAY) (2018) - URL: https://aux-opsy.com/platforms/birddog-ndi-converters/ - Class: NDI video converter - Research status: Documented - Last updated: 2026-08-14 - Hardware in our hands: One BirdDog PLAY (firmware 1.0.30) on our bench, running our own code. The FPGA models are file-only. - Summary: A range sold as one family that is really three platforms. Every converter in it is FPGA-based — Zynq-7000 on the 1080p line, Zynq UltraScale+ with a hard video codec unit on the 4K line — except the decode-only PLAY, which has no programmable logic at all and is an ordinary Rockchip applications processor running Debian. - Key finding: The NDI codec is not software. On every encoder in the range it is a block in FPGA fabric with a thin kernel driver bolted to it (`birddog_speedhq.ko`), which is why encoder and decoder, and 1 G and 10 G networking, ship as separate bitstreams rather than as settings. PLAY is the exception that proves it: with no fabric to put a codec in, it decodes full-bandwidth NDI in software on four Cortex-A53s. - Verdict: Hardware everywhere it earns its keep, and a plain SoC where it does not. The encoders are FPGA machines: a Zynq-7000 on the 1080p line and a Zynq UltraScale+ with a hard codec unit on the 4K line, with the NDI codec itself living in fabric behind a thin kernel driver — which is why what a 4K unit is (encoder or decoder, 1 G or 10 G) is chosen by loading a different bitstream rather than by setting a flag. PLAY inverts all of it: no programmable logic, a Rockchip RK3328 on an unmodified Firefly reference device tree, 2 GB of RAM and four Cortex-A53s at 1.392 GHz, hardware-decoding NDI|HX through the video unit while decoding full-bandwidth SpeedHQ in software on the same four cores. The asymmetry is not an accident — the measured encoder on that SoC tops out around 31 fps at 720p with no HEVC and no B-frames, which is precisely why BirdDog never built an encoder on one. Around all three platforms sits the same delivery chain: an encrypted container whose scope is the whole product line and every generation of it, an integrity check over a shell script, a model-name gate, and no signature verification at any point. - Open questions (what is NOT known): - RK3328 or RK3318. RK3318 is a cost-reduced bin of the same design with the same register map, and vendor images — including the device tree — are shared between them. Nothing in the firmware, and nothing readable from a running unit, distinguishes the two. Only the marking on the chip settles it, and the unit was not opened. - What full-bandwidth NDI receive actually sustains on PLAY. The send side was measured — SpeedHQ encode costs about 1.18 cores at 720p30, which scales to roughly 2.6 cores at 1080p30 — but the equivalent receive figure was never logged on stock firmware, and 1080p60 versus 2160p is a fourfold difference in what the product can honestly claim. - Which USB controller the PLAY's USB-A socket is wired to. The device tree enables the USB 2.0 OTG controller, the EHCI/OHCI host and a USB 3.0 dwc3 side by side; the socket enumerates devices, so one of them reaches it. This decides whether uncompressed capture above 720p is possible at all, and it is a screwdriver question, not a firmware one. - Whether Studio and Mini genuinely lack `birddog_speedhq.ko` on the device, or whether their 2020 update packs simply do not refresh a module that is already there. Absence from an overlay is not absence from a rootfs — the same mistake the GStreamer trap below records. - What the non-Xilinx `.bit` files under Studio R3's `etc/birddog-rev2/` are. They are in a different container, they are not Xilinx bitstreams, and they were not identified. - PLAY Pro is entirely unknown. BirdDog publish no firmware for it, so there is nothing to analyse — not the SoC, not whether it has fabric, not whether it shares the PLAY image. #### Method Two bases, deliberately kept apart. The range-wide silicon inventory is static analysis of twenty firmware packages published on BirdDog's own unauthenticated download pages, 2020–2025, plus the RKFW recovery image for PLAY. Everything labelled measured was then read off one running PLAY on our bench (unit `.42`, firmware 1.0.30) — the first entry in this index with any hardware behind it. No unit was opened, no FPGA model was ever powered, and every claim about Studio, Mini, Flex and the 4K family remains file-only. #### Artifacts examined - `BirdDog_-.zip` × 20 — each a gzip'd `.fw` holding a GNU tar of `bdpff` (the encrypted payload), a 207-byte `birddog-update.sh` stub and an unstripped aarch64 or armhf `update` ELF - Xilinx `.bit` headers throughout the FPGA models — the single most reliable silicon evidence in the corpus, naming the exact part, the design name and the Vivado version that built it - ZynqMP device trees from the 4K family, whose `model` string names the configuration outright (`ZCU5EV 4K Single Encoder Model V202 PS 1G No PHY ID vcu ini`) - `PLAY_1.0.30.img` — the RKFW/RKAF recovery-mode image: loader, u-boot, trust, kernel, DTB, rootfs and partition table, i.e. the whole device rather than an overlay - `/bin/PPApp` — the PLAY application, its `DT_NEEDED` list, its imported NDI symbols and its own log strings - One PLAY, firmware 1.0.30, on the bench — the source of every `measured` row below Referenced by name and version only. Nothing is redistributed. #### Hardware architecture - [VERIFIED] **Two FPGA platforms, one SoC**: Xilinx XC7Z020-CLG400 (Zynq-7000, 2× Cortex-A9, 32-bit `armv7l` userland) on Studio NDI R3/R4, Mini and all three Flex 4K models; Xilinx XCZU5EV-FBVB900-1-E (Zynq UltraScale+ MPSoC, 4× Cortex-A53) on 4K HDMI, 4K SDI, 4K QUAD and OG4/GEAR. Read out of the `.bit` headers on every model and every firmware generation, and corroborated by the ZynqMP device tree - [VERIFIED] **The `EV` suffix is the point**: `XCZU5EV` carries the hard H.264/H.265 VCU, and the 4K device trees use it: `vcu@80300000` with `vcu_core_enc`, `vcu_core_dec`, `vcu_mcu_enc` and `vcu_mcu_dec` nodes. One vendor image is even named `…rxfw330d04apr25vcu.img.gz`. So on the 4K units, NDI|HX rides the hard codec and full-bandwidth SpeedHQ rides the fabric - [VERIFIED] **Modes are FPGA images, not software settings**: The 4K units ship a tree of bitstreams and load one at runtime: `etc/ENC/birddog-nicmode-1g/system.bit`, `etc/ENC/birddog-nicmode-10g/…`, `etc/DEC/…`, plus `DUAL_ENC`, `DUAL_DEC`, `M1X1` and `M2X2`. Encoder versus decoder, and 1 G versus 10 G networking, are different bitstreams. On 4.5-LTS the QUAD's input/output split was baked in the same way — `quad_1i3o`, `quad_2i2o`, `quad_3i1o` as three separate builds — while by 6.1.349 the HDMI and SDI units share one unified `SDI_HDMI_ENC_DEC` design - [VERIFIED] **The Zynq-7000 platform is a third-party SoM**: Kernel is `4.4.0-snickerdoodle+` — the BSP derives from the krtkl snickerdoodle Zynq-7000 module, not a BirdDog carrier BSP. The bitstream is reloaded from userspace at every boot (`cat /etc/tiny_4k.bit > /dev/xdevcfg`, then poll `f8007000.devcfg/prog_done` — the Zynq DevC controller), and every design in this group was built with Vivado 2016.4 - [VERIFIED] **PLAY — no FPGA at all**: Rockchip RK3328: four Cortex-A53 in a single cluster, Mali-450MP2, HDMI 2.0a. No `.bit` file, no `/dev/xdevcfg`, no `fpga_manager` anywhere in the image. The recovery image says so in vendor metadata — RKAF header `model = RK3328`, `parameter.txt` `MACHINE: 3328` — after the sysfs GPU address had already pinned it - [VERIFIED] **PLAY's board is a reference design**: The device tree in the recovery image is stock Firefly ROC-RK3328-CC: `model = "Firefly ROC-RK3328-CC"`, `compatible = "firefly,roc-rk3328-cc", "rockchip,rk3328"`. BirdDog ship the reference board's DTB unaltered, so PLAY is that publicly documented design or close enough that its device tree boots unmodified - [MEASURED] **PLAY memory and clock**: 2 GB RAM, four A53s capped at 1.392 GHz, governor `performance`. No firmware image can answer this — there is no memory node in the DTB, because the Rockchip loader detects DRAM and patches it in at boot — so it had to come off a running unit - [VERIFIED] **PLAY video blocks**: `rkvdec@ff360000` and `vpu_service`/`vdpu@ff350000` for decode, `vepu@ff340000` for encode, `rockchip,rk3328-vop@ff370000` and `rockchip,rk3328-dw-hdmi@ff3c0000` for display, with power domains `pd_vpu`, `pd_hevc` and `pd_video`. `PPApp`'s own mode table runs `2160p23.97` to `2160p60` - [MEASURED] **What the PLAY's decoder actually does**: Hardware decode is effectively free: a 1080p H.264 clip plays in real time at ~11% of one core, the VOP confirmed scanning out NV12 on `win1-0` at 1920×1080p60, and the decoder reaches 218 fps at 720p. This is the block the whole product rests on and it is barely working - [MEASURED] **What the PLAY's encoder cannot do**: The `vepu` is a 2017 Hantro-class block: H.264 only, no HEVC, no B-frames. Its GStreamer element's sink caps stop at 1920×1088 at 60/1, it exposes no tunable properties at all in this build (it derives a bitrate from the caps — 3.456 Mbps at 720p30), and it measured ~31 fps at 720p under load and ~19.6 fps at 1080p in a decode-then-encode pipeline. 4K encode is not available at any effort. Every BirdDog encoder product is FPGA for exactly this reason - [MEASURED] **PLAY networking and USB**: Gigabit: `gmac2io` in RGMII with an external PHY, the internal 100 Mbit `gmac2phy` path disabled — and the unit sustained 115 MB/s, ~920 Mbps, line rate on the wire. The USB-A socket is a host port that enumerates keyboards, mice and a UVC camera; the device tree enables the USB 2.0 OTG controller, EHCI/OHCI host and a USB 3.0 dwc3 alongside each other, and which of them reaches the socket was not established - [MEASURED] **PLAY audio**: HDMI audio is card 1 (`rockchiphdmi`, `hw:1,0`). Card 0 is the SoC's own i2s and goes nowhere on this board — a distinction no amount of reading the device tree makes obvious #### Software architecture - [VERIFIED] **Three operating systems**: Zynq-7000 models run 32-bit `armv7l` Linux `4.4.0-snickerdoodle+`; the 4K family runs `4.14.0-xilinx-v2018.1` on 4.5-LTS through `5.4.0-xilinx-v2020.2` on 5.5; PLAY runs Debian 10 aarch64 on a Rockchip BSP kernel `4.4.194` (Linaro GCC 6.3, built July 2023 — a Firefly/Rockchip build, not BirdDog's) - [MEASURED] **PLAY's kernel has no modules — none at all**: `lsmod` is empty, there is no `/lib/modules/4.4.194/modules.dep.bin`, and `modprobe` cannot do anything. Everything the product needs is built in: `uvcvideo` (1.1.1), `usb-storage`, `vfat`, `ntfs`. Things that are not built in cannot be added without replacing the kernel — `CONFIG_TUN` is absent, and so is exFAT. FUSE, by contrast, is complete (`/dev/fuse` at 10,229, both `fuse` and `fuseblk` registered), which quietly moves a whole class of problems into userspace - [VERIFIED] **Where the NDI codec lives**: `birddog_speedhq.ko` — the driver for the SpeedHQ block in fabric — ships with every encoder model (Flex IN, Flex BP, 4K HDMI, 4K SDI, 4K QUAD, OG4/GEAR) and is absent from the decode-only ones (Flex OUT, PLAY). It sits alongside `birddog_audio.ko`, `birddog_audio_playback.ko` and `birddog_aes3audio_playback.ko`. The codec is a hardware block with a thin driver, not a library - [VERIFIED] **PLAY runs two receivers in parallel**: `PPApp` creates both and picks: `NDIRecvVideoHX()` hardware-decodes NDI|HX (H.264/HEVC) through Rockchip MPP, while `NDIRecvVideoFull()` decodes full-bandwidth SpeedHQ in software inside `libndi.so.5`, on the four A53s. A shipping commercial product decodes full-bandwidth NDI on a 2017 quad-A53 with no hardware assistance whatsoever - [VERIFIED] **The NDI library surface on the device**: Both shipped copies (`libndi.so.5.5.2` and `libndi.so.6.0.1`) export entry points that do not exist in the free NDI SDK — `NDIlib_recv_create_v4`, `NDIlib_recv_set_video_allocator` and `NDIlib_routing_create_v2` on the receive side, and `send_compressed_video`, `NDIlib_send_get_target_bit_rate` and `NDIlib_send_is_keyframe_required` on the send side. Checked against a complete free-SDK header set held locally, where all of them are absent. `PPApp` imports the receive ones, which is what lets the video processing unit see a compressed bitstream at all. Both copies also carry the SDK's 30-minute development-use string, which appears in every build of that library and therefore proves nothing - [MEASURED] **The rootfs carries a media stack the product never uses**: GStreamer 1.14.4 is on the stock PLAY rootfs with the Rockchip `rockchipmpp` plugin (`mppvideodec`, `mpph264enc`, `mppjpegdec`, `mppjpegenc`) and `kmssink` — none of which `PPApp` touches. `PPApp`'s `DT_NEEDED` is `librockchip_mpp.so.1`, `libdrm.so.2` and `libndi.so.5` and little else; it binds MPP output buffers straight into DRM framebuffers with no scaler and no framework in between - [MEASURED] **One display owner**: `PPApp` is DRM master on `card0` and there is no sharing — two processes cannot both modeset. Anything else that wants the HDMI output has to stop it first, which is confirmed on hardware: `kmssink` works only once the vendor runner is stopped, and while it is stopped the output is simply dark - [VERIFIED] **PLAY and Pod are one image**: The installer accepts exactly two hardware identifiers for this package — `BirdDog PLAY` and `BirdDog Pod` — and `S99birddog-setup` splits them at first boot by probing for a `wlan0` interface: Wi-Fi present ⇒ PLAY, absent ⇒ Pod. Pod additionally enables the UVC and UAC1 USB gadgets so it enumerates as a webcam; PLAY explicitly disables ADB. Pod ends up on Debian 11 and PLAY on Debian 10, from the same package, via a `node_modules_bullseye` swap - [VERIFIED] **Control surfaces**: Three, of which only one has a login. A Go web UI on port 80; an Express REST API on port 8080 — plain readable JavaScript in `/bin/BirdDogSrvr/` with a per-feature module layout, roughly 80 endpoints, and a spreadsheet in the image (`etc/API_Management.xlsx`) documenting them; and SSH on port 9031, moved there by `S99birddog-setup` rewriting `sshd_config`. `libsrt` is statically linked into `PPApp`, so source selection is NDI, SRT or CloudConnect #### Update path and security model - [VERIFIED] **Firmware container**: Every published `.fw` is a gzip'd GNU tar holding an AES-256 encrypted payload (`bdpff`) beside the small shell stub and the `update` binary that decrypts it. The interesting structural fact is the scope: one password serves the entire product line — PLAY, Flex, 4K HDMI/SDI/QUAD, OG4/GEAR — and every firmware generation sampled, 2023 through 2025. Four distinct `update` binaries across twenty payloads, one key - [VERIFIED] **Pre-2022 firmware is not encrypted at all**: Mini 20.11.x, Studio NDI R3/R4 20.11.x and Flex 4.5.x-LTS skip the encrypted layer entirely — the inner tar is the `birddog-platform-files/` rootfs overlay, in the clear. The encryption is a later arrival, not a family-wide policy - [VERIFIED] **Integrity, not authenticity**: The `update` binary checks an md5 of the 207-byte shell stub against a hard-coded digest, and the installer gates on `/etc/birddog-hardware-version` (a plain string such as `BirdDog PLAY`) against a whitelist. Those are the only checks in the chain: there is no signature verification of any kind anywhere in the update path. The encryption layer is obfuscation, and the model gate is a compatibility check, not an authenticity one - [VERIFIED] **Payload contents**: Inside: an installer script, a `birddog-platform-files/` tree copied verbatim onto `/` with `cp -rf`, optional `.deb` packages, and on the Zynq models the `BOOT.BIN` carrying first-stage loader, bitstream and u-boot. The kernel and bootloader are not in the update package on PLAY at all - [VERIFIED] **Network services**: The REST API on port 8080 has no authentication of any kind — no auth middleware anywhere in `/bin/BirdDogSrvr/*.js` — and sets `Access-Control-Allow-Origin: `. The web UI on port 80 has a login; the API behind it does not. SSH listens on 9031. The web UI's `static/` directory is served without authentication too. Confirmed answering on the bench unit with no credentials These rows describe how the platform validates a firmware image, which is a structural fact about its architecture. They are not a vulnerability disclosure, and no exploit, bypass or circumvention technique is published. #### What is programmable, ranked 1. **The REST API** — Roughly eighty endpoints of plain, readable Express JavaScript with the surface documented in a spreadsheet inside the firmware itself, mounted per feature and reachable without credentials. Anything that wants to drive one of these boxes from OSC, AES70 or a control surface talks to this and needs no C, no firmware and no reverse engineering. It is also, for the same reasons, the platform's largest exposure. 2. **PLAY's media stack, within the display constraint** — GStreamer 1.14.4, the Rockchip MPP plugins and `kmssink` are already on the rootfs, and Rockchip MPP is open source with no proprietary firmware dependency — so the decode and encode paths are reachable without anything from BirdDog. The binding constraint is not access, it is ownership: `PPApp` is DRM master, so the box shows the vendor application's output or somebody else's, never both. 3. **Nothing inside the fabric** — On every model except PLAY the NDI codec is a block in a bitstream shipped inside the vendor package, reached through a vendor kernel driver. The bitstreams are unencrypted and name their part, which makes them legible and not useful — the design is the product, and there is no third-party path into it. #### Traps Mistakes this analysis actually made. Each produced a plausible wrong answer rather than an obvious failure, which is why they are published. - A GPU's physical base address, sitting in a sysfs path, is the sharpest Rockchip part discriminator available from firmware alone. `/sys/devices/platform/ff300000.gpu/…` means `gpu@ff300000`, which is `rockchip,rk3328-mali` in mainline and nothing else: RK356x is `fde60000`, RK3399 `ff9a0000`, PX30 `ff400000`. A register map is a hard fact and a marketing name is not, so an address that leaked into a path in a startup script identified the silicon before any vendor metadata was found. The limit is worth knowing too — RK3318 is indistinguishable, because it is the same die and the same register map in a different cost bin. The technique identifies a design, never a part number. - Grepping a rootfs for vendor names produces confident nonsense. `integration` contains `tegra`; `cambandwidth` contains `amba`; a sweep for a Rockchip 2D engine hit repeatedly on web-UI assets in a binary that turned out not to link it at all. The Xilinx `.bit` header, by contrast, names the exact part, the design and the tool version in plain text at a fixed offset. Where an artifact self-identifies, a substring search is not a cheaper version of reading it — it is a different and worse method. - "No GStreamer" was true of the application and false of the machine. The conclusion came from the vendor binary's `DT_NEEDED` list, which is genuinely short — MPP, DRM, libndi. The rootfs it sits on ships GStreamer 1.14.4 with the full Rockchip MPP plugin set and `kmssink`, all unused by the product and all perfectly usable by anything else. An application-linkage finding bounds what that program does; it says nothing about what is installed. Check the filesystem before concluding something is absent. - The PLAY locks its HDMI output to the first stream it sees and silently downscales everything after it. A 2160p stream sent to a unit already locked at 1080p60 was scaled 2:1 with no error and no indication — fine gratings vanished, and the resulting measurement read as a legibility floor at 4K that does not exist. The device's own status endpoint went on reporting the original resolution throughout. Changing resolution needs the source torn down and the decoder restarted, not just a new stream. The wrong answer here was plausible, quantitative and reproducible, which is exactly what makes it dangerous. - That same status endpoint is only trustworthy once it is locked. While failing it reports a zero resolution, a zero frame rate and a chroma format of `4:2:2` regardless of the stream — a value the 4:2:0 test stream could never produce. A non-zero resolution is the only field in it that means anything. ### Shure QLXD4 (2014) - URL: https://aux-opsy.com/platforms/shure-qlxd/ - Class: Wireless microphone system - Research status: Partial - Last updated: 2026-09-06 - Hardware in our hands: One Shure QLXD4 receiver (named `House 1`, channel `Head 6`, on 606.700 MHz) mounted on our QL1 and captured from a mirrored port on 2026-08-01. Nothing was ever transmitted to the receiver: every finding is passive observation of a console and a receiver talking to each other, plus one read-only request that went unanswered. No firmware was obtained and the unit was never opened. - Summary: A receiver whose documented control interface is plain ASCII on a TCP port — and which, mounted on a Yamaha console, does not use it. The console-mount path is standards-based ACN, discovered on a multicast group one digit off the registered one and driven entirely by subscriptions. - Key finding: A QLX-D mounted on a Yamaha console speaks ANSI E1.17 ACN — SLPv2 discovery, SDT sessions, DMP properties — and not one byte of the Command Strings interface Shure documents. Both parties join each other, the console subscribes to twelve properties, and the receiver then pushes changes at about 8 Hz. The console never polls. - Verdict: Two interfaces, and the interesting one is the one nobody documents. Shure publish Command Strings — plain ASCII on TCP 2202 — and a QLX-D mounted on a Yamaha console does not send a byte of it: the console-mount path is standards-based ACN, with SLPv2 discovery on a group one digit off the registered one, symmetric SDT channels where both ends join each other, and DMP subscriptions that make the console a listener rather than a poller. Almost everything a monitoring tool wants is on that path and confirmed against ground truth — model, name, channel, frequency, RF, battery, and the AF gain the console writes. What is not on it, as far as this work could establish, is audio level: the obvious candidate property drifts continuously with no carrier at all and no transmitter powered, which is not what an audio meter does. The honest summary is a well-mapped telemetry path with one hole in it, and a lost capture standing between it and an active client. - Open questions (what is NOT known): - Audio level. Command Strings gives a figure for it and the ACN property that looked like its equivalent does not hold up. It may be one of the subscribed-but-silent addresses, or not exposed on this path at all. - Battery run time. Two subscribed properties emitted no event across every capture, holding a sentinel throughout — consistent with a transmitter running alkaline cells, since there is no run-time estimate to report without a Shure rechargeable. It needs a run with an SB900-series pack fitted. - Whether a QLX-D serves Command Strings and ACN simultaneously. These captures cannot say: Wireless Workbench was never running, so the absence of port-2202 traffic proves nothing either way. - Whether ULX-D and Axient Digital share the property map. Both mount on Yamaha consoles the same way, and if the address space is common then one decoder covers the range. Untested, and worth knowing before anyone writes that decoder. #### Method Passive packet capture of a real QLXD4 mounted on a real QL1 on our lab network, across four sessions: the receiver mounted and idle, its AF output level driven from the console's own encoder, a power-cycle spanning the full session setup, and a run with a transmitter switched on partway. Decoded with a hand-written parser checked byte for byte against Wireshark's own ACN dissector, which decodes these packets natively — where the two disagreed on vector names, Wireshark won. No firmware was analysed, the receiver was never opened, and not one packet was ever transmitted to it. #### Artifacts examined - Four captures from a mirrored port: 181 s of the receiver mounted and idle, 40 s with the AF output level swept from the console, 356 s with a transmitter switched on partway, and 160 s spanning a receiver power-cycle — which is the one that is lost, see the traps - The receiver's own SLPv2 advertisement, repeated every two seconds: component id, fixed model name, user-assigned name, the services it offers, and where to open a session - Ground truth from the receiver's front panel, read at known moments — the battery scale was pinned by removing and refitting cells and comparing against the display Referenced by name and version only. Nothing is redistributed. #### Hardware architecture - [UNKNOWN] **What was examined**: Nothing inside the receiver. No firmware was obtained, no update package was opened, no part number was read and the unit was never taken apart. Everything on this page is the behaviour of the box on a network — a deliberate limit, not an omission - [MEASURED] **The unit under observation**: A QLXD4 half-rack receiver on 606.700 MHz, named `House 1` with its channel named `Head 6`, mounted on a Yamaha QL1. Both devices sat on IPv4 link-local addresses with no DHCP server present, and there was no Dante audio between them — the receiver was on the network purely as a control device - [PUBLIC] **The published figures used as ground truth**: The AF output level range (−18 … +42 dB) and the five-segment battery display are Shure's own published specifications, and both were used to check the decoding rather than derived from it: the observed level sweep clamped exactly at the documented minimum, and the battery property's 0–5 scale matches the panel's five segments #### Software architecture - [MEASURED] **Discovery — SLPv2, one digit off**: Every two seconds each device multicasts an SLPv2 attribute reply to `239.255.254.253:8427` — both the group and the port are one digit off the registered SLP values (`239.255.255.253:427`), so a standard SLP library pointed at the standard group finds nothing and reports no error. The advertisement carries the model name as a fixed string, the user-assigned name, a component id whose low bytes are the device MAC, and the address and device-class id needed to open a session. Passive listening on that group enumerates model, name and address for every ACN-speaking Shure receiver on the segment, with no connection made - [MEASURED] **The device description is advertised and not served**: Every advertisement points at a TFTP URL for the receiver's DDL — ACN's machine-readable device description, which would hand over the property map for free. There is no TFTP server on the receiver. Requests for the advertised name, for the device-class id in upper and lower case, and for the model name drew no response of any kind — not even the error a running server sends for a missing file - [MEASURED] **Transport — symmetric SDT sessions**: Sessions run on UDP 57383, source and destination, with the console also multicasting into the E1.17 SDT range. Each party owns a channel and both parties join each other: this is a symmetric pair of channels, not a client connecting to a server. Steady state uses only the unreliable wrapper carrying payload, with acknowledgements coming back; session setup uses the reliable path - [MEASURED] **Session setup**: Captured across a receiver power-cycle and about 80 ms end to end: a join and its accept in each direction, a connect naming DMP as the protocol, six subscriptions and their accepts, nine property reads and their replies, then a second batch of six subscriptions. Teardown on power-off is leave, leaving, disconnect, disconnecting - [MEASURED] **Steady state is push, at about 8 Hz**: After setup the receiver pushes unsolicited events carrying whichever subscribed properties changed, and the console never polls. Twelve properties are subscribed in two batches of six; addresses are four-byte absolute and non-virtual, and strings are length-prefixed with a 16-bit total that includes the two length bytes and no terminator - [MEASURED] **What the properties are**: Confirmed against ground truth: model name, device name, channel name, AF output level, frequency in kHz, RF level in dBm, an RF meter of 0–5 segments, and battery bars of 0–5 with −1 as a sentinel. Two further properties move together and are not what they first appeared to be; two more were subscribed and emitted no event at all across every capture - [MEASURED] **The one writable property**: AF output level is the only property observed being written. The console sets it, the receiver echoes an event carrying the same value 10–25 ms later, and answers reads with a reply. Confirmed by 37 sets sweeping the console's own encoder across −18 … +40: signed 8-bit, plain dB, no scaling, clamping hard at the documented minimum. A later mount read back exactly where the previous session's sweep had left it - [MEASURED] **Battery, and two behaviours to build around**: The battery property was pinned against the receiver's own front panel — 2 bars held across ten consecutive samples, the sentinel with the cells removed, and 5 immediately on link with fresh cells. Two behaviours matter for anything reading it: the first sample after a link comes up is a transient that does not match the panel and settles within about four seconds, and the sentinel means no recent data*, not no carrier — on a dropout the value holds its last reading and only later falls to it - [MEASURED] **RF**: RF level reads a flat −50 dBm with no carrier — a floor rather than a measurement — and −19 … −35 dBm with a transmitter on. The 0–5 segment meter is 0 with no carrier and 1–5 with one, moving in lockstep with it #### Update path and security model - [MEASURED] **Nothing in the observed exchange carries a credential**: No credential appears anywhere in discovery or in session setup: a channel is offered and accepted, a connect names a protocol, and subscriptions follow. Whether the receiver would accept a session from anything other than the console it is mounted on is not known — nothing was ever transmitted to it, so this is the shape of the exchange and not a tested claim - [MEASURED] **Network posture**: Discovery is multicast and readable from anywhere on the segment; telemetry is unicast to the console, so on an ordinary switch port a third-party listener sees the receiver announce itself and nothing more. Everything on this page past discovery needed a mirrored port, which is also the honest limit on what any passive tool built from it can do These rows describe how the platform validates a firmware image, which is a structural fact about its architecture. They are not a vulnerability disclosure, and no exploit, bypass or circumvention technique is published. #### What is programmable, ranked 1. **Command Strings on TCP 2202 — the documented interface** — Shure publish it, it is plain ASCII, and it remains the supported way to talk to these receivers. Nothing here changes that: the ACN path is what a console mount actually uses, not a better way to control a receiver. 2. **Passive ACN telemetry, with a mirrored port** — Model, name, channel name, frequency, RF level, RF bars, battery bars and AF gain all arrive without transmitting a single packet — which is the safest possible way to instrument a rig somebody else is operating. The cost is port mirroring, and that is the whole cost. 3. **An active ACN client — blocked, and for one reason** — It needs the session handshake at byte level, and the only capture that contained one is gone. Re-recording a receiver power-cycling while mounted unblocks it; nothing else is missing. #### Traps Mistakes this analysis actually made. Each produced a plausible wrong answer rather than an obvious failure, which is why they are published. - The capture containing the session handshake is gone. The 160-second power-cycle recording is the only one that held a join, a connect or a subscribe, and it is neither in the capture archive nor on the machine. The sequence survives because it was written down as prose at the time; the byte-level field layouts did not, and without them nothing can open its own session. The lesson is the boring one: the capture is the evidence, and prose about it is not a substitute. - Calling the property that moves "the audio meter". With a carrier present it swings the way a meter would, which is exactly why the first reading of it said audio level. A later capture with no carrier at all — RF pinned at its floor, no transmitter powered — showed it still drifting continuously, which a receiver-side audio meter on a silent receiver does not do. It was demoted rather than quietly kept, and it still has no unit, because no calibrated signal was ever injected. - Reading a battery trend that was not there. One run appeared to show the level sliding over 30 seconds. The carrier was in fact dropping repeatedly, so the acquisition transient was being seen over and over and read as a downward trend. Ground truth was the front panel, not the graph. - Pointing a standard SLP library at the standard group. Both the multicast address and the port are one digit off the registered values, so the correct tool, correctly configured, finds nothing at all and reports no error. - Believing the device-description URL. It is advertised in every announcement and there is no server behind it — not even one that answers with an error. ### Sennheiser evolution wireless G3 (EM 300 / EM 500 G3) (2009) - URL: https://aux-opsy.com/platforms/sennheiser-ewg3/ - Class: Wireless microphone system - Research status: Documented - Last updated: 2026-09-15 - Hardware in our hands: One Sennheiser EM 500 G3 rack receiver (band G, 606–648 MHz) on our bench, driven from a real Yamaha DM3 through our own bridge. Every measured row was read off that receiver; the G4, 2000-series and SR IEM variants are inferred from the shared protocol, not tested. - Summary: The G3 rack receivers are remote-controlled — but not by the ASCII "media control protocol" Sennheiser published. That one (UDP 53212, firmware ≥ 1.7.0) drew silence from our EM 500 G3 on every probe. What actually drives it is an undocumented binary protocol that Wireless Systems Manager speaks on UDP 8133, and once we had it we mapped the receiver's analog output level to a console's remote head-amp gain and drove one from the other on the bench. - Key finding: The documented protocol is the wrong one. Sennheiser's TI 1254 "media control protocol" — ASCII on UDP 53212, `Push` / `RF1` / `RF2` — needs receiver firmware ≥ 1.7.0, and the EM 500 G3 on our bench answers nothing on 53212. WSM drives it over a completely separate, undocumented binary protocol on UDP 8133, behind a single-master write lock, and it carries a writable analog-output level — an index, not a dB value — that we wired to a mixing desk's remote gain. - Verdict: Sennheiser shipped two remote-control protocols for the G3 and documented the one this receiver does not run. The ASCII "media control protocol" on UDP 53212 — the surface every third-party integration targets — needs firmware ≥ 1.7.0 and drew silence from our EM 500 G3 on every probe. What actually drives it is the undocumented binary protocol Wireless Systems Manager speaks on UDP 8133: multicast discovery to the mDNS group with a decidedly non-mDNS payload, a config packet whose frequency is little-endian kHz and whose output level is an index rather than a dB value, a preset-bank plan handed over on a 25 kHz raster, and a single-master write lock that is coordination, not security. The whole thing is unauthenticated and was fully controllable from our own client the moment we held the master slot. The one genuinely useful actuator — the analog output level — we wired to a mixing desk's remote gain and drove the receiver from a DM3 on the bench, the first time this index made a real signal move. The telemetry half is only half-open: the ~12 Hz meter packet's RF-level byte is identified, but its audio, battery, mute and pilot fields stayed frozen because the transmitter never moved, and that — plus the Fast Scan wire format — is what a second bench session would finish. - Open questions (what is NOT known): - The rest of the meter packet. The RF-level byte is pinned, but audio level, transmitter battery, mute and pilot share the same ~12 Hz packet and sat byte-for-byte constant because the transmitter was static. Mapping them needs a session that varies the transmitter — walk it out of range, speak into it, mute it, run the battery down (battery shows as 0–3 bars, 0 flashing at low). - The Fast Scan wire format. WSM's plugin names an `EWG3 Fast Scan Request` and a `FastScanResult`, i.e. a networked band scan, but the request and response were not captured. This is the one path that would make the receiver a remote coordination probe rather than just a controllable output. - Whether G4, the 2000 series and the SR IEM receivers are byte-identical. They share the 8133 protocol and the WSM plugin family, but only the EM 500 G3 was on the bench; the field offsets are assumed to carry across, not tested. - The 4-byte message headers. They are constant per message type and read like per-command checksums, but the algorithm was not derived — a controller replays the captured constants rather than computing them, which is fine until a firmware or model changes one. - The squelch encoding. The config byte reads 20 where the panel shows 21 dB; near enough to look like an off-by-one or a different step base, but it was not written and read back across its range the way AF-out was. #### Method Two bases, kept apart. The wire protocol was reconstructed from packet captures of Wireless Systems Manager 4.9.0 talking to the receiver, and everything labelled measured was then read back off the running EM 500 G3 — discovery, the config packet, the tuning limits, the preset-bank tables, the live RF meter and the analog-output level — with every write confirmed by reading it back, and the AF-out control proven end-to-end by driving it from a real DM3. The public rows come from Sennheiser's own TI 1254 document, which describes the other*, unused protocol. Nothing was decapped or dumped; the receiver's own firmware was never read. #### Artifacts examined - Packet captures of Wireless Systems Manager 4.9.0 ↔ a real EM 500 G3 — a cold-start discovery and a live control session — retained as `.pcap` - `TI 1254 v1.0` — Sennheiser's "Media control protocol description" for the ew 300-500 G4 / IEM G4, i.e. the ASCII/53212 protocol: the public reference for the protocol this receiver does not speak - `libEWG3EMDevice.dylib` — WSM's own G3 device plugin, whose Qt slot names (`OnSetAFOutFlag`, `OnSetSquelchFlag`, `OnSetEqualizerFlag`, `EWG3 Fast Scan Request command sent successfully`) name the exact field set seen on the wire and a scan path not yet captured - A reference client and a shipped console-bridge adapter (Dante-BabelBox `sennheiser-ewg3`), both exercised against the receiver on the bench Referenced by name and version only. Nothing is redistributed. #### Hardware architecture - [MEASURED] **One receiver, one control port**: The EM 500 G3 is a UHF diversity receiver — band G here, 606–648 MHz — with a rear RJ45 that carries all remote control. Both directions of that control use UDP port 8133, and the receiver replies to port 8133 regardless of the sender's source port, so a controller has to bind 8133 to hear it at all - [MEASURED] **AF-out is the one writable audio parameter, and it is an index**: The receiver's analog output level (−24…+18 dB in 3 dB steps) is a settable config field stored as an index, not a dB value: dB = 3·index − 24, index 0..14. WSM shows the byte `0x0c` as "+12" and `0x06` as "−6"; writing every step 0..14 and reading it back traced the whole curve, and an out-of-range index folds to `0x10` on the device. It is the actuator the whole bridge rests on - [MEASURED] **The live RF meter**: The receiver streams a 40-byte status packet at ~12 Hz. Logging only its changes at rest, byte 7 was the only one that moved — 0x26→0x64 tracking the natural signal flutter with the transmitter untouched — so byte 7 is the current RF level in percent (TI 1254: 0% = 0 dBµV, 100% = 40 dBµV, >100% possible). The audio, battery, mute and pilot fields in the same packet stayed constant and remain unmapped, because mapping them needs a transmitter that moves - [MEASURED] **The name field is fragile**: A control write that sets stray dirty-flags in the flag region blanks the device name — the name goes to spaces and the channel label to a "no data" placeholder. Reproduced once by accident here and recovered by re-writing the name from WSM; the shipped tooling never writes the name for exactly this reason - [MEASURED] **The documented ASCII protocol is simply absent**: Sennheiser documents an ASCII "media control protocol" on UDP 53212 (`Push`, `RF1`, `RF2`, `Squelch`, `AfOut`…) for firmware ≥ 1.7.0. This receiver answered nothing on 53212 across every probe — no reply, no ICMP — while the same unit was fully controllable on 8133 at the same moment. The documented protocol is either older-firmware-gated or was never present on this unit #### Software architecture - [MEASURED] **Discovery is a fixed multicast payload, not mDNS**: A 1035-byte packet multicast to 224.0.0.251:8133 — the mDNS group address, but the payload is a fixed `[servicecommand]devinfo` string, not an mDNS query. The receiver replies unicast with an ASCII record — `Model=EM500G3 ID= IPA=` — and re-announces the same record to :8133 about every 5 s, so a passive listener finds it without asking - [MEASURED] **Single-master write lock**: The receiver keeps a client table and one master slot holding the master controller's IP. Writes are honoured only from the master; while WSM holds the slot an outside controller's writes are accepted on the wire and silently dropped — no error, no reply, no change. The slot clears when WSM stops its ~2 s keepalive, and a controller that has been sending its own keepalive then inherits it. This is the mechanism a bridge must respect to write anything at all - [MEASURED] **Config packet layout**: The main config packet (134 bytes, 12-byte header) carries the device name (ASCII), the frequency as a little-endian kHz uint32 (`0x0009d94a` = 645450 → 645.450 MHz), the AF-out index, the equalizer (0–3) and RX-mute — each read back after writing. A separate packet carries the tuning limits as two little-endian kHz values, 606000 / 648000 here - [MEASURED] **Writes are value-plus-dirty-flag**: A write is a 128-byte packet: a 4-byte type header, a 4-byte client token, then a 120-byte body that mirrors the config-read layout — the new value at its field offset, a per-field dirty flag at `0x45 + offset`, and a global write flag. Confirmed for frequency, equalizer, RX-mute and AF-out, each echoed back by an unsolicited config packet - [MEASURED] **The preset-bank tables are 16-bit LE × 25 kHz**: At connect the receiver sends two 1296-byte packets of 16-bit little-endian frequency codes; `code × 25 = kHz`. The largest, `0x6540 × 25 = 648000 kHz`, lands exactly on the upper tuning limit, and the current 645.450 MHz is `0x64DA`. So the receiver hands over its whole frequency plan — 162 and 196 presets across the two packets, on a 25 kHz raster — the binary form of TI 1254's `BankList` - [VERIFIED] **WSM's own plugin corroborates the field set**: WSM's `libEWG3EMDevice.dylib` carries Qt slot names — `OnSetAFOutFlag`, `OnSetSquelchFlag`, `OnSetEqualizerFlag`, `OnSetRFMuteFlag` — that name exactly the fields seen on the wire, alongside an `EWG3 Fast Scan Request` band-scan path not exercised here - [INFERRED] **The message headers are constant per type**: Every message begins with a 4-byte header fixed per message type — register, config-request, write, and the device's meter, config, keepalive-ack and discovery-reply — rather than a sequential opcode, which is consistent with a per-command checksum. The header bytes were catalogued from the capture; the algorithm behind them was not derived, so a controller replays the constants rather than computing them - [PUBLIC] **The documented ASCII protocol, for reference**: Per TI 1254, the unused protocol is line-based on 53212: a `Push ` subscription, then cyclic `RF1` / `RF2` / `AF` / `Bat` / `States` / `Msg` attributes, with `AfOut` in 3 dB steps and `Squelch` in 2 dB steps. It is the natural mic-telemetry surface — for a receiver whose firmware answers it #### Update path and security model - [MEASURED] **No authentication on the control port**: UDP 8133 is open and unauthenticated. Anything with IP reachability can discover the receiver and, once it holds the master slot, change its frequency, output level, equalizer and mute. This is a control-VLAN device by construction - [INFERRED] **The master lock is coordination, not access control**: The single-master slot is last-writer-wins arbitration, not security — it is claimed by simply sending keepalives once the incumbent stops, with no credential of any kind. It stops two controllers fighting; it stops nobody getting in These rows describe how the platform validates a firmware image, which is a structural fact about its architecture. They are not a vulnerability disclosure, and no exploit, bypass or circumvention technique is published. #### What is programmable, ranked 1. **The WSM binary protocol on UDP 8133 — discover, read, write** — The only surface a real G3 answers. Multicast discovery, a full config read (name, frequency, limits, EQ, mute, output level, and the preset-bank plan), and writes of frequency, EQ, RX-mute and the analog output level — all confirmed on the bench. A controller holds the master slot with a 2 s keepalive and writes freely; a reference client and a shipped console-bridge adapter both drive it. 2. **AF-out as a remote gain** — The writable output level makes the receiver behave like a remotely-gain-controlled preamp. We mapped a Yamaha DM3's remote head-amp gain onto it through our bridge and drove the receiver's output from the console — its level tracked the desk at 0 dB and +15 dB and back, confirmed on hardware. It is the first time this index moved a real signal. 3. **A networked Fast Scan — if it can be captured** — WSM's G3 plugin names an `EWG3 Fast Scan Request` — a band scan run over this same protocol, which would turn the receiver into a remote spectrum probe for coordination. The capability is real in the binary; its wire format was not captured this session and is the most interesting thing still open. 4. **The documented ASCII protocol on 53212 — a per-firmware maybe** — Firmware ≥ 1.7.0 is supposed to expose the richer telemetry surface — RF, AF, battery, pilot — as an ASCII push feed on 53212. Our unit does not answer it at all, so it is a per-firmware gamble, not a plan. 5. **Nothing inside the receiver** — The receiver's own firmware was never read; every useful thing is on the network side of the box. There is no reason to open it for what the protocol already gives up. #### Traps Mistakes this analysis actually made. Each produced a plausible wrong answer rather than an obvious failure, which is why they are published. - Reaching for the documented protocol first. Every public integration and Sennheiser's own TI note point at ASCII on 53212, and it is the obvious place to start — against this receiver it is a dead port, silent on every probe, and an afternoon can vanish into a protocol the unit never implemented. The working surface is undocumented and on a different port. - Reading the AF-out byte as dB. It is an index: byte 6 is −6 dB, byte 12 is +12 dB (dB = 3·index − 24). A controller that writes the dB value straight into the byte sets the wrong level — writing 0 gives −24 dB, not 0 dB — and it only looks correct at +12, where index and dB coincide by a factor of three. - Writing a field without minding the name. The write packet's dirty-flag region sits close to the name field; a flag one byte wrong blanks the receiver's name to spaces and drops a "no data" placeholder into the channel label. It happened here once. Write only the fields whose flag offsets are proven, and never the name from a bare client. - Assuming the discovery multicast is mDNS. It goes to 224.0.0.251:8133 — the mDNS group — but it is a fixed Sennheiser `[servicecommand]devinfo` payload, and an mDNS parser pointed at it sees nothing. The port, not the group, is the tell. - Expecting writes to work while WSM is open. WSM holds the single master slot, and an outside controller's writes are accepted on the wire and silently discarded — no error, no reply, no change. The receiver has to be free of WSM, or WSM's keepalive has to lapse, before anything else can write. - Reading the preset-bank words as raw kHz. They are 16-bit little-endian codes on a 25 kHz raster — `code × 25 = kHz` — so a table read as literal kHz is off by a factor of 25 and lands nowhere near the band. ## Corrections This is research and it has errors in it. Corrections from people who built these machines are welcome and are published rather than quietly applied. See https://aux-opsy.com/method/#corrections.