sdlamp2/docs/rg35xx-plus.md
Michael Smith 8a638acdd8 Skin template system, separate prev sprite, reorganize device scripts
Add tools/gen_skin_template.py to generate a labeled 642x420 PNG template
for creating custom spritesheets. Move rg35xx device scripts from tools/
to device/rg35xx/. Point prev_sprite at its own cell (bottom-center) so
Prev and Next can have distinct icons.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-14 23:47:21 +01:00

9.4 KiB
Raw Permalink Blame History

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=ignore in /etc/systemd/logind.conf — systemd does not act on the power button, leaving it free for userspace handling.
  • evtest: Available at /usr/bin/evtest for 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 (&) 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.

Framebuffer

Property Value
Device /dev/fb0
Visible size 640x480
Virtual size 640x960 (double-buf)
Bits per pixel 32 (BGRA)
Stride 2560 bytes (640 * 4)

No standard framebuffer image tools (fbv, fbi, psplash) are installed. To display a PNG on the framebuffer, decode with Python3+PIL and write raw BGRA pixels to /dev/fb0.

Backlight Control

There is no sysfs backlight interface (/sys/class/backlight/ is empty) and the stock brightCtrl.bin daemon does not expose a usable control mechanism. The backlight is controlled via the Allwinner /dev/disp ioctl interface:

Ioctl Number Description
DISP_SET_BRIGHTNESS 0x102 Set backlight brightness (0255)
DISP_GET_BRIGHTNESS 0x103 Get current backlight brightness

Both ioctls take an argument buffer of 4 unsigned longs (struct { unsigned long args[4]; }):

  • args[0] = screen index (always 0)
  • args[1] = brightness value (for SET; ignored for GET)
  • Return value (for GET) is in args[0] after the ioctl call

Setting brightness to 0 turns the screen off completely. The original value (typically ~50) can be restored to turn it back on. This is used by rg35xx-screen-monitor.py for idle timeout and power button toggle.

Stock Firmware Assets

Shutdown-related assets in /mnt/vendor/res1/shutdown/:

File Description Size
goodbye.png Shutdown screen (RGB, 640x480) Matches display
lowpower.png Low battery warning

The boot logo is at /mnt/vendor/res1/boot/logo.png.

Deploying sdlamp2

Overview

The dmenu_ln script already supports switching the startup binary via config files (e.g. muos.inimuos.bin). We follow the same pattern to launch sdlamp2 instead of the default menu.

Setup

  1. Copy the binary, wrapper, and screen monitor to the device:

    scp build/sdlamp2 root@rg35xx:/mnt/vendor/bin/sdlamp2
    scp device/rg35xx/rg35xx-wrapper.sh root@rg35xx:/mnt/vendor/bin/rg35xx-wrapper.sh
    scp device/rg35xx/rg35xx-screen-monitor.py root@rg35xx:/mnt/vendor/bin/rg35xx-screen-monitor.py
    
  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:

    if [ -f "/mnt/vendor/sdlamp2.ini" ];then
        CMD="/mnt/vendor/bin/rg35xx-wrapper.sh"
    fi
    

    The wrapper script handles device-specific concerns (WiFi hotspot, power button monitoring) and launches sdlamp2 as its main foreground process. See device/rg35xx/rg35xx-wrapper.sh for details.

  3. Enable sdlamp2 on boot:

    touch /mnt/vendor/sdlamp2.ini
    
  4. 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.sh before the restart loop
  • LED/backlight controlbrightCtrl.bin started by launcher.sh
  • Clean shutdown — sdlamp2 handles SIGTERM/SIGINT, saving position and volume before exit. The wrapper displays the stock goodbye.png on the framebuffer and calls poweroff
  • 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