Files
b0hemia e0bc4785e9 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.
2026-08-26 15:26:14 +10:00

299 lines
15 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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'`