diff --git a/CLAUDE.md b/CLAUDE.md index 51388e4..679eba5 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -7,7 +7,8 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co SDLamp2 is a simple SDL2-based audio player written in C, designed for a child to use. It plays long m4a/mp3 files (fairy tale cassette rips) with a cassette-player-like UI: rewind, stop, play, fast-forward, next tape. Inspired by Winamp. See `docs/sdlamp2-fsd.md` for the full functional specification and changelog. -See `docs/TOOLCHAIN.md` for target device details and the arm64 Docker build container. +See `docs/TOOLCHAIN.md` for the arm64 Docker build container. +See `docs/rg35xx-plus.md` for target device details, boot chain, and deployment. ## Build and Run diff --git a/docs/TOOLCHAIN.md b/docs/TOOLCHAIN.md index df4583f..7466cfa 100644 --- a/docs/TOOLCHAIN.md +++ b/docs/TOOLCHAIN.md @@ -1,45 +1,6 @@ -# Cross-Compilation Toolchain +# Build Container -This document describes the target device and the arm64 Docker build container used to produce binaries for it. - -## Target Device - -### Hardware - -- **Device:** Anbernic RG35XX Plus -- **SoC:** Allwinner H700 — quad-core ARM Cortex-A53 @ 1.5 GHz (AArch64 / ARMv8-A) -- **GPU:** Mali G31 MP2 -- **RAM:** 1 GB LPDDR4 -- **Display:** 640×480 IPS - -### Software - -- **OS:** Ubuntu 22.04 LTS (Jammy Jellyfish) — [modified stock firmware](https://github.com/cbepx-me/Anbernic-H700-RG-xx-StockOS-Modification) -- **Kernel:** Linux 4.9.170 aarch64 (proprietary, no source released by Anbernic) -- **Architecture:** aarch64 / arm64 (confirmed via `dpkg --print-architecture`) -- **glibc:** 2.35 - -### Libraries on Device - -All required shared libraries are pre-installed. Most are at `/usr/lib/`, some at `/usr/lib/aarch64-linux-gnu/` (Debian multiarch path): - -| Library | Soname | Notes | -|---------|--------|-------| -| SDL2 | `libSDL2-2.0.so.0.12.0` | SDL 2.0.12 | -| SDL2_image | `libSDL2_image-2.0.so.0.900.0` | SDL2_image 2.0.9, at multiarch path | -| libavcodec | `libavcodec.so.58` | FFmpeg ~4.x | -| libavformat | `libavformat.so.58` | FFmpeg ~4.x | -| libavutil | `libavutil.so.56` | FFmpeg ~4.x | -| libswresample | `libswresample.so.3` | FFmpeg ~4.x | - -### Implications for Cross-Compilation - -- The device userland is Debian/Ubuntu-based (has `dpkg`, `apt-get`, `systemd`) -- Shared libraries are already present — no need to bundle or build them via Buildroot -- A native aarch64 compile (e.g. inside an arm64 Docker container) produces working binaries -- Must link against glibc ≤ 2.35 (use GCC ≤ 12 to avoid GLIBCXX version mismatches) - -## Build Container +arm64 Docker build container for producing device binaries. For target device details see [`rg35xx-plus.md`](rg35xx-plus.md). The `docker-arm64/` directory contains a Docker setup that runs an arm64 Ubuntu 22.04 container via QEMU user-mode emulation. This matches the target device exactly — same distro, same glibc 2.35, same library versions. The project's native `make` (using `pkg-config`) works as-is inside the container, no cross-compilation flags needed. diff --git a/docs/rg35xx-plus.md b/docs/rg35xx-plus.md new file mode 100644 index 0000000..a1816d6 --- /dev/null +++ b/docs/rg35xx-plus.md @@ -0,0 +1,127 @@ +# Anbernic RG35XX Plus + +Device-specific reference for the target hardware. For build instructions see [`TOOLCHAIN.md`](TOOLCHAIN.md). + +## Hardware + +- **Device:** Anbernic RG35XX Plus +- **SoC:** Allwinner H700 — quad-core ARM Cortex-A53 @ 1.5 GHz (AArch64 / ARMv8-A) +- **GPU:** Mali G31 MP2 +- **RAM:** 1 GB LPDDR4 +- **Display:** 640×480 IPS + +## Software + +- **OS:** Ubuntu 22.04 LTS (Jammy Jellyfish) — [modified stock firmware](https://github.com/cbepx-me/Anbernic-H700-RG-xx-StockOS-Modification) +- **Kernel:** Linux 4.9.170 aarch64 (proprietary, no source released by Anbernic) +- **Architecture:** aarch64 / arm64 (confirmed via `dpkg --print-architecture`) +- **glibc:** 2.35 +- **Userland:** Debian/Ubuntu-based (`dpkg`, `apt-get`, `systemd`) + +## Libraries on Device + +All required shared libraries are pre-installed. Most are at `/usr/lib/`, some at `/usr/lib/aarch64-linux-gnu/` (Debian multiarch path): + +| Library | Soname | Notes | +|---------|--------|-------| +| SDL2 | `libSDL2-2.0.so.0.12.0` | SDL 2.0.12 | +| SDL2_image | `libSDL2_image-2.0.so.0.900.0` | SDL2_image 2.0.9, at multiarch path | +| libavcodec | `libavcodec.so.58` | FFmpeg ~4.x | +| libavformat | `libavformat.so.58` | FFmpeg ~4.x | +| libavutil | `libavutil.so.56` | FFmpeg ~4.x | +| libswresample | `libswresample.so.3` | FFmpeg ~4.x | + +Shared libraries are already present — no need to bundle or build them. A native aarch64 compile (e.g. inside the arm64 Docker container) produces working binaries. Must link against glibc ≤ 2.35. + +## Partition Layout + +| Device | Mount point | Filesystem | Contents | +|--------|-------------|------------|----------| +| `/dev/mmcblk0p5` | `/` | ext4 | Root filesystem | +| `/dev/mmcblk0p6` | `/mnt/vendor` | ext4 | Firmware, apps, scripts | +| `/dev/mmcblk0p7` | `/mnt/data` | ext4 | User data | +| `/dev/mmcblk0p8` | `/mnt/mmc` | vfat | ROMs | +| SD card slot | `/mnt/sdcard` | (varies) | External storage | + +## Boot Chain + +The device uses systemd but boots its UI through a SysV init script that systemd auto-wraps via `systemd-sysv-generator`. The chain is intentionally layered: `launcher.sh` runs before partitions are mounted (hardware setup), `loadapp.sh` runs after mounts (filesystem/network setup + app restart loop), and `dmenu_ln` is the app dispatcher. + +``` +systemd (graphical.target) + └─ launcher.service (SysV → auto-generated by systemd-sysv-generator) + └─ /etc/init.d/launcher.sh start + ├── mounts /mnt/vendor + ├── starts brightCtrl.bin (backlight daemon) & + ├── starts cexpert & + └── starts loadapp.sh & + ├── resizes root partition (first boot only) + ├── mounts /mnt/data, /mnt/mmc + ├── charging mode: if boot_mode==1 → charger UI → poweroff + ├── normal mode: starts NetworkManager, bluetooth, wifi + ├── syncs RTC, loads kernel modules, mounts SD card + ├── runs /mnt/mod/ctrl/autostart (if exists) + └── RESTART LOOP: runs /mnt/vendor/ctrl/dmenu_ln repeatedly + └─ /mnt/vendor/ctrl/dmenu_ln + ├── selects binary (default: dmenu.bin) + ├── config overrides: muos.ini → muos.bin, vpRun.ini → ... + ├── runs binary via app_scheduling() + └── on exit: executes /tmp/.next (app dispatch for menu) +``` + +### Why the indirection? + +- **`launcher.sh`** — Pre-mount hardware setup: mounts `/mnt/vendor`, starts backlight and system daemons. Runs as a SysV init script so it integrates with systemd's boot ordering. +- **`loadapp.sh`** — Post-mount filesystem/network setup: mounts data partitions, handles charging mode, starts networking, then enters the restart loop. Runs in the background (`&`) so `launcher.sh` can return. +- **`dmenu_ln`** — App dispatcher: selects which binary to run based on config files, wraps execution in `app_scheduling()` (which sleeps 30s on crash before retrying), and supports `/tmp/.next` for menu-driven app switching. + +### `app_scheduling` behavior + +The `app_scheduling` function in `dmenu_ln` runs the selected binary. If it exits with a non-zero status, it sleeps 30 seconds before returning, which prevents crash loops from consuming all CPU. The outer `while true` loop in `loadapp.sh` then re-invokes `dmenu_ln`, restarting the application. + +## Deploying sdlamp2 + +### Overview + +The `dmenu_ln` script already supports switching the startup binary via config files (e.g. `muos.ini` → `muos.bin`). We follow the same pattern to launch sdlamp2 instead of the default menu. + +### Setup + +1. **Copy the binary** to the device: + + ```sh + scp build/sdlamp2 root@:/mnt/vendor/bin/sdlamp2 + ``` + +2. **Add the config check** to `/mnt/vendor/ctrl/dmenu_ln`. In the section where `CMD` overrides are checked (after the existing `muos.ini` / `vpRun.ini` checks, before the `app_scheduling` call), add: + + ```bash + if [ -f "/mnt/vendor/sdlamp2.ini" ];then + CMD="/mnt/vendor/bin/sdlamp2 /mnt/sdcard/Music" + fi + ``` + + The unquoted `$CMD` in `app_scheduling` undergoes word splitting, so the path argument is passed correctly. + +3. **Enable sdlamp2 on boot:** + + ```sh + touch /mnt/vendor/sdlamp2.ini + ``` + +4. **Disable** (revert to normal menu): + + ```sh + rm /mnt/vendor/sdlamp2.ini + ``` + +### What's preserved + +Everything else in the boot chain continues to work: + +- **Charging mode** — handled in `loadapp.sh` before the restart loop +- **LED/backlight control** — `brightCtrl.bin` started by `launcher.sh` +- **Clean shutdown** — system shutdown scripts unaffected +- **Restart on exit** — if sdlamp2 exits cleanly (status 0), the restart loop in `loadapp.sh` re-launches it immediately +- **Crash recovery** — if sdlamp2 crashes (non-zero exit), `app_scheduling` sleeps 30s then the loop retries +- **Easy revert** — removing `sdlamp2.ini` restores the stock menu on next boot