Add initial support for LessOS#107
Conversation
Implements generic LessOS bootstrap that routes to platform-specific launchers based on LESSOS_PLATFORM environment variable. Changes: - Add workspace/rk3566/ platform (RK3566 SoC devices for LessOS) - Add workspace/all/paks/LessUI/lessos/init.sh (generic bootstrap) - Add rk3566 to platforms.json with lessos flag - Add lessos target to Makefile - Add skeleton/SYSTEM/rk3566/ structure
- Add LESSOS_PLATFORM env var check to updater (falls back to cpuinfo) - Update lessos/init.sh to copy .tmp_update and run updater - Create rk3566/install/boot.sh platform boot script - Add rk3566 to PLATFORMS list and Makefile.copy for boot assets - Bundle .tmp_update into lessos/ directory in Makefile special target - Fix tg5040 boot.sh for consistency (while loop, fallback reboot)
- Add HAS_OPENGLES flag for Mali-G52 GPU - Use GLVideo for effects instead of SDL2 - Remove hardcoded SDL_VIDEODRIVER (let SDL2 auto-detect)
Adds paths.h/paths.c to read LESSOS_STORAGE environment variable at startup, falling back to compile-time SDCARD_PATH for other platforms. Enables dynamic storage paths for LessOS (internal vs external SD). - Created paths.h with runtime path globals (g_sdcard_path, etc.) - Created paths.c with Paths_init() implementation - Added paths.c to common build sources - Updated launch.sh template to export LESSOS_STORAGE
Replace compile-time path macros with runtime g_* globals throughout launcher, player, and tool paks. All code now calls Paths_init() at startup and uses dynamic paths. - Updated launcher, player, Clock, and shui to call Paths_init() - Replaced SDCARD_PATH, ROMS_PATH, etc. with g_* runtime globals - Added debug logging for ROM discovery and emulator detection
Fixes display issues and boot flow for rk3566 running on LessOS. - show.c: Remove SDL_WINDOW_FULLSCREEN_DESKTOP (requires window manager) - show.c: Add error handling and diagnostic logging - boot.sh: Add boot logging to /lessui-boot.log - boot.sh: Fix working directory after atomic update deletes .tmp_update - Enable OpenGL ES rendering for rk3566 platform
Add paths_stub.c for unit tests and update all test builds to link against it. All 1508 tests pass. - Created paths_stub.c with pre-initialized test paths - Updated Makefile.qa to include PATHS_STUB in test builds - Added rk3566 platform to System Report pak
There was a problem hiding this comment.
Pull request overview
This pull request adds support for LessOS, a new operating system for retro gaming handhelds, along with the RK3566 platform (Rockchip SoC) that runs on it. The changes enable LessUI to run on devices like the PowKiddy RGB30 and Anbernic RG353 series through LessOS.
Key changes:
- Added complete RK3566 platform implementation with hardware-specific drivers, settings library, and boot utilities
- Modified boot scripts to support both stock OS and LessOS environments with conditional path detection
- Integrated LessOS bootstrap into the build system with platform detection and updater modifications
Reviewed changes
Copilot reviewed 19 out of 24 changed files in this pull request and generated 13 comments.
Show a summary per file
| File | Description |
|---|---|
| workspace/tg5040/install/boot.sh | Removed sync call and reboot logic, switched to while loop for launcher |
| workspace/rk3566/install/boot.sh | New RK3566 boot script with LessOS storage path support |
| workspace/rk3566/show/show.c | SDL2-based boot splash display utility (has null pointer issues) |
| workspace/rk3566/show/Makefile | Build configuration for show utility |
| workspace/rk3566/platform/platform.h | Hardware definitions and button mappings for RK3566 devices |
| workspace/rk3566/platform/platform.c | Platform-specific implementation using SDL2 render backend |
| workspace/rk3566/platform/Makefile.env | Compiler flags for ARM Cortex-A55 architecture |
| workspace/rk3566/platform/Makefile.copy | File copy rules for platform binaries |
| workspace/rk3566/libmsettings/* | Shared memory settings library for volume/brightness control |
| workspace/rk3566/Makefile | Platform build configuration |
| workspace/rk3566/README.md | Comprehensive documentation for RK3566 platform (contains RGB30-specific references) |
| workspace/all/paks/config/platforms.json | Added rk3566 platform configuration with LessOS flag |
| workspace/all/paks/LessUI/platforms/rk3566/init.sh | Platform initialization script for LessOS |
| skeleton/SYSTEM/rk3566/* | Platform skeleton files (system.cfg, bin/shutdown, empty directories) |
| skeleton/BOOT/lessos/init.sh | LessOS bootstrap entry point |
| skeleton/BOOT/common/updater | Modified to detect LessOS platform and use appropriate storage paths |
| Makefile | Added rk3566 to platform list and LessOS-specific packaging targets |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 39 out of 44 changed files in this pull request and generated 11 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| if (shm_fd==-1 && errno==EEXIST) { // already exists | ||
| puts("Settings client"); | ||
| shm_fd = shm_open(SHM_KEY, O_RDWR, 0644); | ||
| settings = mmap(NULL, shm_size, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0); |
There was a problem hiding this comment.
If shm_open() fails for the client path (line 65), the code doesn't check if shm_fd is valid before calling mmap(). This could lead to undefined behavior. Add error checking: if (shm_fd == -1) { perror("shm_open failed"); exit(1); }
|
|
||
|
|
||
| void InitSettings(void) { | ||
| sprintf(SettingsPath, "%s/msettings.bin", getenv("USERDATA_PATH")); |
There was a problem hiding this comment.
The sprintf function is used without bounds checking. If USERDATA_PATH environment variable contains a very long string, this could overflow the 256-byte SettingsPath buffer. Consider using snprintf instead to prevent buffer overflow.
| void SetRawVolume(int val) { // 0 - 100 | ||
| // printf("SetRawVolume(%i)\n", val); fflush(stdout); | ||
| char cmd[256]; | ||
| sprintf(cmd, "amixer sset -M 'Master' %i%% &> /dev/null", val); |
There was a problem hiding this comment.
The sprintf calls with user-controlled val parameter could lead to buffer overflow. Although val is expected to be numeric, using snprintf with explicit buffer size would be safer. Consider replacing with snprintf(cmd, sizeof(cmd), ...).
| // printf("SetJack(%i)\n", value); fflush(stdout); | ||
|
|
||
| char cmd[256]; | ||
| sprintf(cmd, "amixer cset name='Playback Path' '%s' &> /dev/null", value?"HP":"SPK"); |
There was a problem hiding this comment.
The sprintf call could overflow the 256-byte buffer if the string value ("HP" or "SPK") is somehow modified. Use snprintf(cmd, sizeof(cmd), ...) instead for bounds checking.
| void SetBrightness(int value) { | ||
| if (settings->hdmi) return; | ||
|
|
||
| int raw; | ||
| switch (value) { | ||
| // TODO: redo, range is 0-255 | ||
| case 0: raw=4; break; // 0 | ||
| case 1: raw=6; break; // 2 | ||
| case 2: raw=10; break; // 4 | ||
| case 3: raw=16; break; // 6 | ||
| case 4: raw=32; break; // 16 | ||
| case 5: raw=48; break; // 16 | ||
| case 6: raw=64; break; // 16 | ||
| case 7: raw=96; break; // 32 | ||
| case 8: raw=128; break; // 32 | ||
| case 9: raw=192; break; // 64 | ||
| case 10: raw=255; break; // 64 | ||
| } |
There was a problem hiding this comment.
The SetBrightness function doesn't handle invalid input values. If value is outside the range 0-10, the raw variable will be uninitialized, leading to undefined behavior. Add a default case or bounds check to handle invalid inputs.
| run_platform_install "$SYSTEM_PATH/$PLATFORM/bin/install.sh" "$LOG_FILE" | ||
|
|
||
| if [ "$ACTION" = "installing" ]; then | ||
| log_info "Rebooting..." | ||
| reboot | ||
| fi | ||
| fi |
There was a problem hiding this comment.
The automatic reboot after installation has been removed. Previously, if ACTION was "installing", the script would reboot immediately after installation. Now the script continues to the launch loop instead. This is a behavioral change that should be intentional - verify that this is the desired behavior for new installations on tg5040.
|
|
||
| // Build all derived paths | ||
| (void)snprintf(g_roms_path, PATHS_MAX_LEN, "%s/Roms", g_sdcard_path); | ||
| (void)snprintf(g_root_system_path, PATHS_MAX_LEN, "%s/.system/", g_sdcard_path); |
There was a problem hiding this comment.
The g_root_system_path includes a trailing slash in line 52 ("%s/.system/"), but other paths don't consistently use trailing slashes. This inconsistency could lead to malformed paths if code elsewhere doesn't expect the trailing slash. Consider removing it for consistency, or document why it's needed here.
| (void)snprintf(g_root_system_path, PATHS_MAX_LEN, "%s/.system/", g_sdcard_path); | |
| (void)snprintf(g_root_system_path, PATHS_MAX_LEN, "%s/.system", g_sdcard_path); |
|
|
||
|
|
||
| void InitSettings(void) { | ||
| sprintf(SettingsPath, "%s/msettings.bin", getenv("USERDATA_PATH")); |
There was a problem hiding this comment.
If getenv("USERDATA_PATH") returns NULL, this will cause undefined behavior (dereferencing a NULL pointer). Add a NULL check before using the environment variable, or provide a default fallback path.
| char path[256]; | ||
| snprintf(path, sizeof(path), "%s", argv[1]); |
There was a problem hiding this comment.
The snprintf call is redundant here - it simply copies argv[1] to path without any formatting. This can be replaced with a simpler bounds-checked string copy or just use argv[1] directly in subsequent calls.
| shm_fd = shm_open(SHM_KEY, O_RDWR | O_CREAT | O_EXCL, 0644); // see if it exists | ||
| if (shm_fd==-1 && errno==EEXIST) { // already exists | ||
| puts("Settings client"); | ||
| shm_fd = shm_open(SHM_KEY, O_RDWR, 0644); | ||
| settings = mmap(NULL, shm_size, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0); | ||
| } | ||
| else { // host | ||
| puts("Settings host"); // should always be keymon | ||
| is_host = 1; | ||
| // we created it so set initial size and populate | ||
| ftruncate(shm_fd, shm_size); | ||
| settings = mmap(NULL, shm_size, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0); |
There was a problem hiding this comment.
If mmap() fails (returns MAP_FAILED), the code doesn't check for this condition. Subsequently accessing the settings pointer would cause a segmentation fault. Add error checking after both mmap() calls: if (settings == MAP_FAILED) { perror("mmap failed"); exit(1); }
No description provided.