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.
15 KiB
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_mqaflag (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:
fbshotreads 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:
rtc0present 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/ledsis empty and there is no physical LED on the unit (confirmed visually). TheRGB_LEVEL/RGB_STATUSfields 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):
- Keep stock mq_player as the engine for now - it already handles DAC/DSD/streaming/ MCU and diskOS drives it fine over IPC.
- RE the
cs43131ioctl 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. - 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'