diskOS installer: initial public beta

Flashes the diskOS custom UI onto the FiiO Snowsky Disc over Ingenic mask-ROM USB,
building the image from your own stock firmware. Runs from source via install.sh.
This commit is contained in:
b0hemia
2026-08-26 15:26:14 +10:00
commit e0bc4785e9
109 changed files with 12625 additions and 0 deletions
+298
View File
@@ -0,0 +1,298 @@
# Snowsky Disc - Hardware Capability Map
Live-probed from the device root shell (serial `/dev/ttyACM0`) on 2026-06-25, while
running **stock firmware** (V2.09 family, kernel built 2026-06-03). This documents what
the *hardware* can do, independent of what stock software chooses to use - to scope
diskOS enhancements and the "write our own mq_player/mq_ui" question.
Probing was read-only (`/proc`, `/sys`, `aplay --dump-hw-params`, `i2cdetect`-equivalent
via sysfs names, `dmesg`). Nothing was written to the device during this survey.
---
## 1. SoC & Compute
| Item | Value | Source |
|---|---|---|
| SoC | Ingenic **X2000** (xburst2), board `ingenic,x2000_halley5_module_base` | `/proc/cpuinfo` |
| Cores | **2× XBurst II V2**, SMP | `/proc/cpuinfo` (processor 0,1) |
| Clock | ~1.2 GHz (BogoMIPS ≈ 2390) | `/proc/cpuinfo` |
| FPU | Yes (per-core) | `/proc/cpuinfo` |
| SIMD | **MSA** (MIPS SIMD Architecture, 128-bit) - `ASEs implemented: msa` | `/proc/cpuinfo` |
| ISA | mips1 / mips2 / mips32r2 + MSA | `/proc/cpuinfo` |
| TLB | 288 entries; 1 HW watchpoint; 6 kscratch regs | `/proc/cpuinfo` |
| DVFS | **None exposed** - no `cpufreq/scaling_available_frequencies` | `/sys/.../cpufreq` |
| Thermal | **No thermal zones** - `/sys/class/thermal` empty | sysfs |
| GPU | **None** (X2000 has no GPU) | `/dev` has no gpu node |
| VPU / video decode | **None** - no `vpu`/`video`/`mem2mem` dev nodes | `/dev` |
| Hardware JPEG | **None** as a dev node (stock `jpg_to_png.c` is software) | `/dev`, RE |
**Implication:** all audio DSP and all UI rendering are CPU-bound. The single biggest
untapped compute lever is **MSA SIMD** - diskOS's LVGL is almost certainly built without
`-mmsa`, so blends/scales/rotations run scalar. Rebuilding LVGL (and any DSP/resampler)
with MSA is the highest-leverage perf win available.
---
## 2. Memory
| Item | Value |
|---|---|
| RAM total | **120 MB** (`MemTotal: 120308 kB`) |
| Free / available (stock running) | ~19 MB free / ~64 MB available |
Tight but workable. diskOS already runs in this budget. Big in-RAM buffers (e.g. a
full-res rotating album-art layer) must be sized carefully.
---
## 3. Storage
NAND (MTD) with **A/B dual-boot** for OTA, plus the user microSD:
| mtd | size | name | notes |
|---|---|---|---|
| mtd0 | 2 MB | uboot | bootloader |
| mtd1 | 8 MB | kernel | slot A |
| mtd2 | 128 MB | rootfs | slot A - **squashfs, read-only** (why `fiio_init.sh` can't be edited in place) |
| mtd3 | 8 MB | kernel2 | slot B |
| mtd4 | 25 MB | rootfs2 | slot B (smaller - recovery/fallback) |
| mtd5 | 1 MB | ota | OTA state |
| mtd6 | 1 MB | mac | MAC/calibration |
| mtd7 | 83 MB | **userdata** | writable - mounted as `/usr/data`, where **diskOS lives** |
| mmcblk0 | ~238 GB | microSD | single partition `mmcblk0p1` - the music card |
**Implication:** the **boot hook** is a small edit patched into the read-only rootfs's
`usr/project/fiio_init.sh` (which is why enabling it requires rewriting the rootfs partition - the
flash). The **payload it launches** - the `mq_ui` binary and diskOS's runtime state - lives in the
writable `/usr/data` (mtd7), the safe persistent target. So: the hook is baked into the RO rootfs;
only the UI/state are on `/usr/data`.
---
## 4. Audio - the headline subsystem
### 4.1 DACs - quad CS43131, fully balanced
Four Cirrus **CS43131** chips on I²C bus 3:
| I²C addr | sysfs name | /dev node (major) |
|---|---|---|
| 3-0030 | cs43131 | `/dev/cs43131` (248) |
| 3-0031 | cs43131b | `/dev/cs43131b` (247) |
| 3-0032 | cs43131c | `/dev/cs43131c` (246) |
| 3-0033 | cs43131d | `/dev/cs43131d` (245) |
dmesg tags include `cs43131_left_negetive` and `cs43131b_open` → the four DACs are wired
as **L+/L−/R+/R− (fully differential / balanced)**, two CS43131 per channel. This is an
unusually serious analog design for the form factor. *(Whether the physical jack exposes
balanced (4.4 mm) or sums to single-ended (3.5 mm) is a board question - confirm against
the unit's connectors.)*
Each CS43131 is a stereo DAC + integrated headphone amp; the family supports PCM to
384 kHz and **DSD64/128/256**. Control is via a **custom FiiO `cs43131` kernel driver**
exposing the four char devices above; stock `mq_player` drives them with **ioctl** (not
ALSA controls). Raw `/dev/i2c-3` is also present as a fallback.
### 4.2 SoC I²S/DMA path - the streaming ceiling
The Ingenic audio controller (ALSA card 0 `x2000`) exposes **5 playback + 5 capture DMA
channels** (`hw:0,0`-`hw:0,4` playback). `aplay --dump-hw-params` on an idle channel:
```
FORMAT: ALL
SAMPLE_BITS: [3 64]
CHANNELS: [1 8]
RATE: [8000 768000]
```
So the **kernel/I²S link can stream up to 768 kHz / 64-bit / 8-channel** - far beyond the
current track (S32_LE / 48 kHz / 2ch on DMA3). The real output ceiling is set by the
CS43131 (~384 kHz PCM, DSD256), not the SoC.
### 4.3 ALSA mixer surface
`amixer controls` shows only the **SoC internal codec** (`ICODEC HPOUTL/MIC GAIN`,
`MICBIAS`), digital mic (`DMIC ...`), line-out muxes (`LO0_MUX`…`LO11_MUX`), and the five
audio-interface formatters (`baic0_fmt`…`baic4_fmt`). There is **no CS43131 control, no
DSD switch, and no digital-filter control in ALSA** - all of that is the kernel driver +
mq_player ioctl path. `BAIC: baic start/stop` in dmesg marks I²S on/off per track.
### 4.4 Decode & format support (from mq_player RE)
- Decoder backend: **libavcodec.so.58 (ffmpeg)** - string `decoder_ffmpeg`.
- DSD: `DSD_MODE_NONE` / `DSD_MODE_NATIVE` / `DSD_MODE_DOP` - native DSD **and** DoP.
- MQA: `is_mqa` flag (detection/passthrough).
- Containers: FLAC, APE, DSF/DFF, **SACD ISO**, CUE sheets; ffmpeg covers ALAC/OPUS/
WavPack/etc.
- Param validation: `check_sample_param`, `reset hw params`, `AudioCodecOpen dsd`,
`no support sample! dsd_mode/out_dev/...` - the player gates rate/format per DAC mode.
**Tools present:** `aplay`, `amixer`, `tinyplay`, `tinymix` - raw PCM playback is possible
today (the DAC just has to already be configured for the rate).
---
## 5. Display
| Item | Value |
|---|---|
| Driver | `ingenicfb` |
| Visible | 360×360, 32 bpp (XRGB8888/BGRA, panel mounted 180°-rotated) |
| Framebuffer virtual | 360×**1080** = triple-buffered 360×360 |
| **Overlay layers** | `fb0`-`fb3` = **4 hardware LCDC planes** (`/dev/fb0..fb3`) |
| Backlight | standard `backlight` class, **41 levels (0-40)** |
**Layer control interface (confirmed via sysfs):** `ingenicfb` exposes `layer0`-`layer3`
under `/sys/class/graphics/fb0/device/`, each with `enable`, `src_fmt`, `src_size`,
`target_pos`, and **`target_size`**. `target_size` ≠ `src_size` ⇒ the LCDC has a
**per-layer hardware scaler** (notable, since there's otherwise no GPU/VPU). layer0 is the
active UI plane (`enable: 1`, src 360×360). Layers position + scale in hardware but **do
not rotate**.
**Implication:** static scaled art/backdrops *could* be HW-composited on fb1-3 (e.g. a
scaled cover or a dim backdrop under the LVGL UI) with no CPU blend. BUT:
- A **spinning** cover still needs software rotation (layers don't rotate) - LVGL already
does this fine, so the overlay is an optimization, not a requirement.
- **Alpha/blend mode is NOT in sysfs** (no alpha/zorder/colorkey node) - likely an FBIO
ioctl in the ingenicfb driver; blending behavior is **unverified**.
- **Visual confirmation needs a camera:** `fbshot` reads fb0's memory, but overlays
composite at *scanout*, so an fb1 test pattern won't appear in an fb0 capture. Defer the
live overlay test until we're ready to pursue HW-layer art and can eyeball the panel.
---
## 6. Input
| Device | node | notes |
|---|---|---|
| Touch | `/dev/input/event1` - **cst816t** | single-finger panel, but speaks **MT type-B** protocol (slot/tracking-id/ABS_MT_POSITION_X/Y/TOUCH_MAJOR/PRESSURE; no plain ABS_X/Y). Caps `EV=0xb`, `ABS=0x6618000` (high word). |
| Keys | `/dev/input/event0` - **x2000_key** | **physical buttons** (GPIO keys) |
We can synthesize input by writing the 32-bit-ABI `input_event` (16-byte) MT-B sequence to
`/dev/input/event1` - verified working (used to drive stock's UI pages over serial during RE). diskOS can also read the hardware keys via event0.
---
## 7. Power
| IC | I²C | role |
|---|---|---|
| **SGM41513** | 2-001a | battery charger (Li-ion, ~3A class) |
| **CW221X** (Cellwise) | 2-0064 | battery **fuel gauge** → `/sys/class/power_supply/cw221X-bat` |
| **AW35615** | 2-0022 | **USB-C PD / CC** controller (Type-C orientation + power delivery) |
Live read: capacity 100%, 4.32 V, source "Mains". The fuel gauge gives real %/voltage;
`/dev/usbcc_ioctl` (from RE) is the PD/CC control path. **PD negotiation hardware exists**,
so faster charging / power-role awareness is at least theoretically addressable.
---
## 8. Connectivity (wireless)
| Item | Value | Source |
|---|---|---|
| Module | **AP6212** = Broadcom **BCM43438 / 4343A1** | dmesg: `chip:0xa9a6`, `fw_bcm43438a1.bin`, `nvram_ap6212a.txt` |
| WiFi | **2.4 GHz only**, 802.11 b/g/n (single-band) | BCM43438 spec |
| WiFi attach | SDIO (`[dhd]` driver v101.10.591.91.40, 512 KB dongle RAM) | dmesg |
| Bluetooth | Broadcom BT over **UART `/dev/ttyS0`** @3Mbaud (`hci0`, BD address (device-specific, redacted)) | `hciconfig` |
| BT firmware | `BCM4343A1_001.002.009.1026.1055.hcd` (the `BCM4345C5/C0` `.hcd` files are leftovers for other FiiO models) | `/lib/firmware/bt_bcm` |
| BT bring-up | **hci0 is NOT attached at boot** - `bcmdhd.ko` only loads the driver/GPIOs. The HCI iface is created on-demand by `brcm_patchram_plus --enable_lpm --enable_hci --baudrate 3000000 --patchram <.hcd> /dev/ttyS0`, then `/usr/project/bluetoothd` (a2dp,avrcp,source) + `bluealsa --device=hci0` (SBC/LDAC). **Stock = bluez; `bsa_server`/`bt_enable_bsa*.sh` = dead code for the wrong chip.** Decoded from `mq_player` strings. | mq_player RE |
> **Corrects prior note:** earlier memory said "BCM4345C5". The actual silicon is
> **BCM43438 (AP6212)** - single-band 2.4 GHz + BT 4.x.
**Implication:** no 5 GHz → WiFi music transfer / streaming is capped at 2.4 GHz real
throughput (tens of Mbps, congestion-sensitive). BT codec quality (LDAC/aptX) is a
userspace-stack question, not a chip blocker for A2DP.
---
## 9. USB
| Item | Value |
|---|---|
| Controller | **DWC2 OTG** (`13500000.otg_new`) - **dual-role** (host *or* device) |
| Current mode | device; gadget `serial_demo` exposing **ACM** only (VID 0x0525 / PID 0xa4a7) → this *is* our serial shell |
| USB-DAC mode | stock "UAC" work-mode reconfigures the gadget to **USB Audio Class** (device-as-DAC for a host PC) |
**Untapped:** DWC2 is OTG, so **USB host mode is physically possible** - mounting USB
storage, or driving an *external* USB DAC (device as a pure transport). Stock only ships
device-mode (serial + UAC). Host-mode would need role switch + the right gadget/host
config and likely a USB-C OTG adapter.
---
## 10. Misc
- **RTC:** `rtc0` present and correct - real hardware clock.
- **ADC:** SoC SAR ADC, 6 aux channels (`/dev/jz_adc_aux_0..5`) - analog reads (e.g.
jack/line detect, if wired).
- **Watchdog:** hardware `/dev/jz_watchdog`.
- **LED:** **none.** `/sys/class/leds` is empty and there is **no physical LED** on the
unit (confirmed visually). The `RGB_LEVEL`/`RGB_STATUS` fields in mq_player's config blob
are vestigial, inherited from the halley5 reference design / other FiiO products.
→ diskOS should plan **no LED features**.
- **Motion sensors:** none on I²C (no accel/gyro) - no tilt/gesture input despite the
round watch-like shape.
---
## 11. Stock-uses vs hardware-can-do (gap list)
| Capability | HW supports | Stock uses | diskOS opportunity |
|---|---|---|---|
| MSA SIMD | Yes (128-bit) | UI not built for it (unknown) | Rebuild LVGL/DSP `-mmsa` → faster render/effects |
| LCDC overlay planes | 4 (fb0-3) | fb0 only | HW-composited art/video layer (spinning cover, backdrops) |
| I²S rate | 768k/64b/8ch | ≤384k/DSD256 (DAC-limited) | none beyond DAC; already maxes the DAC |
| DSD native + DoP | Yes | Yes | parity - reuse driver ioctls |
| Quad balanced DACs | Yes | Yes | direct ioctl control for bit-perfect / HW volume |
| USB host (OTG) | Yes | No (device-only) | USB storage / external USB-DAC transport |
| UAC2 gadget | Yes | Yes (UAC mode) | expose/control USB-DAC from diskOS |
| USB-C PD | Yes (AW35615) | basic charge | PD-aware fast charge / power-role UI |
| Physical keys | Yes (x2000_key) | Yes | map HW buttons in diskOS |
| WiFi 5 GHz | **No** | - | hard ceiling: 2.4 GHz only |
| GPU / VPU / HW JPEG | **No** | - | hard ceiling: video is CPU-only (MSA-assisted at best) |
| Thermal / DVFS | **Not exposed** | - | no power/thermal tuning via standard sysfs |
| LED | **None** | vestigial config | none - drop from plans |
---
## 12. "Write our own mq_player / mq_ui?" - assessment
**mq_ui: already done.** diskOS *is* our own UI; it reuses stock `mq_player` purely as an
audio engine over mqueue IPC. No reason to change that split for UI work.
**mq_player: a full rewrite is high-effort but the local-playback core is feasible.**
What a from-scratch local player needs, and how hard each piece is:
| Piece | Feasibility | Notes |
|---|---|---|
| Decode | **Easy** | reuse on-device `libavcodec.so.58` (same as stock) |
| PCM out | **Easy** | tinyalsa/libasound to card 0; up to 768k/64b |
| DAC control (rate/DSD/filter/volume/mute) | **Medium** | open `/dev/cs43131*` and replay the kernel-driver **ioctls** - must RE the ioctl numbers + sequences from `mq_player.asm` (bounded but real work). This is the **critical enabler**. |
| Native DSD / DoP | **Medium** | once DAC ioctls are known, feed the right format |
| MCU glue (keys/charge/USB-detect/power) | **Medium** | stock player talks to the FiiO MCU; our player must too (MCU command surface mapped during RE) |
| Streaming receivers (AirPlay/DLNA/Roon/QPlay/BT-sink) | **Hard** | large independent stacks - **do not rewrite**; keep stock or graft open-source (e.g. shairport-sync) |
**Recommendation (hybrid, incremental):**
1. **Keep stock mq_player** as the engine for now - it already handles DAC/DSD/streaming/
MCU and diskOS drives it fine over IPC.
2. **RE the `cs43131` ioctl interface regardless** - it's the key that lets diskOS (or a
future thin player) control bit-perfect output, hardware volume, DSD, and filters
directly. Highest-value RE next step.
3. **Only build our own *local* player** if we need something stock won't expose - e.g.
software parametric EQ, ReplayGain, custom gapless/crossfade, or a DSP chain - using
ffmpeg + tinyalsa + the RE'd DAC ioctls. Leave streaming to stock.
The streaming receivers and MCU dependency are what make a *complete* replacement
expensive; the audiophile local-playback path is the tractable, high-value slice.
---
## Appendix: probe commands (reproducible)
- SoC/mem: `cat /proc/cpuinfo /proc/meminfo`, `uname -a`
- Audio: `cat /proc/asound/{cards,pcm}`, `aplay -l`, `aplay -D hw:0,0 --dump-hw-params /dev/zero`, `amixer -c0 controls`
- I²C map: `for d in /sys/bus/i2c/devices/*/name; do echo "$d: $(cat $d)"; done`
- Power: `cat /sys/class/power_supply/*/{type,capacity,voltage_now,status}`
- Storage: `cat /proc/partitions /proc/mtd`
- USB: `ls /sys/class/udc`, `cat /sys/class/udc/*/state`, `ls /sys/kernel/config/usb_gadget/*`
- Wireless: `dmesg | grep -iE 'dhd|bcm|chip'`, `hciconfig -a`, `ls /lib/firmware/{wifi_bcm,bt_bcm}`
- Display/input: `cat /sys/class/graphics/fb0/{name,virtual_size,bits_per_pixel}`, `ls /dev/fb*`, `cat /proc/bus/input/devices`
- Engines: `ls /dev | grep -iE 'ipu|vpu|jpeg|adc|watchdog'`