6.8 KiB
Anbernic RG35XX Plus
Device-specific reference for the target hardware. For build instructions see 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
- 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.
Input Devices
Three input devices are registered via /proc/bus/input/devices:
| Device | Handlers | Purpose |
|---|---|---|
axp2202-pek |
event0 |
Power button (KEY_POWER, code 116) |
ANBERNIC-keys |
event1, js0 |
Gamepad (d-pad, face buttons) |
dierct-keys-polled |
event2 |
Shoulder buttons, menu/function keys |
- logind:
HandlePowerKey=ignorein/etc/systemd/logind.conf— systemd does not act on the power button, leaving it free for userspace handling. - evtest: Available at
/usr/bin/evtestfor debugging input events.
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 (&) solauncher.shcan return.dmenu_ln— App dispatcher: selects which binary to run based on config files, wraps execution inapp_scheduling()(which sleeps 30s on crash before retrying), and supports/tmp/.nextfor 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
-
Copy the binary and wrapper to the device:
scp build/sdlamp2 root@<device>:/mnt/vendor/bin/sdlamp2 scp tools/rg35xx-wrapper.sh root@<device>:/mnt/vendor/bin/rg35xx-wrapper.sh -
Add the config check to
/mnt/vendor/ctrl/dmenu_ln. In the section whereCMDoverrides are checked (after the existingmuos.ini/vpRun.inichecks, before theapp_schedulingcall), add:if [ -f "/mnt/vendor/sdlamp2.ini" ];then CMD="/mnt/vendor/bin/rg35xx-wrapper.sh" fiThe wrapper script handles device-specific concerns (WiFi hotspot, power button monitoring) and launches sdlamp2 as its main foreground process. See
tools/rg35xx-wrapper.shfor details. -
Enable sdlamp2 on boot:
touch /mnt/vendor/sdlamp2.ini -
Disable (revert to normal menu):
rm /mnt/vendor/sdlamp2.ini
What's preserved
Everything else in the boot chain continues to work:
- Charging mode — handled in
loadapp.shbefore the restart loop - LED/backlight control —
brightCtrl.binstarted bylauncher.sh - Clean shutdown — sdlamp2 handles SIGTERM/SIGINT, saving position and volume before exit. The wrapper script can trigger
poweroffafter sdlamp2 exits - Restart on exit — if sdlamp2 exits cleanly (status 0), the restart loop in
loadapp.shre-launches it immediately - Crash recovery — if sdlamp2 crashes (non-zero exit),
app_schedulingsleeps 30s then the loop retries - Easy revert — removing
sdlamp2.inirestores the stock menu on next boot