mirror of
https://github.com/openwrt/video.git
synced 2026-10-12 05:57:07 +04:00
plymouth: add boot splash for KMS devices
Plymouth draws a boot splash straight onto a DRM device and holds the display until whatever follows it takes over, so nothing of the boot is visible between reset and the user interface. On a board with no framebuffer console that is the only way to put anything on screen early. Built without the framebuffer backend, without systemd and upstart integration, and without gtk, pango and freetype, so the runtime needs only libpng and libdrm. msgfmt is needed at build time, for the themes that are built and then not installed. Neither binary answers to --version, so the CI version probe is answered by test-version.sh instead. No theme is installed and none is named in the daemon configuration: a theme is a package of its own and selects itself when it is installed. Four patches are carried. Two are build fixes for musl, which has neither rpmatch() nor execinfo.h. The other two are bugs, both submitted upstream. The drm renderer drops master immediately after opening the device, so every connector probe runs as a client that is not drm master; the kernel declines it a forced probe and returns a cached status of "unknown", and the renderer gives up with "Could not initialize heads", which leaves the splash able to light only a display something else has already probed. The escape key tears the splash down for a text view, guarded only on a VT existing, so on a kernel with CONFIG_VT but no framebuffer console the dummy driver is bound, the text goes nowhere and the screen turns black. Signed-off-by: Daniel Golle <daniel@makrotopia.org>
This commit is contained in:
1 parent
6968dd1804
commit
1a39b47dbd
8 files changed
+483
No files matched your search
@@ -0,0 +1,85 @@
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=plymouth
|
||||
PKG_VERSION:=26.134.222
|
||||
PKG_RELEASE:=1
|
||||
|
||||
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
|
||||
PKG_SOURCE_URL:=https://gitlab.freedesktop.org/plymouth/plymouth/-/archive/$(PKG_VERSION)
|
||||
PKG_HASH:=053687d4e8bb1320c14dafc014ebf4e45427f1cf417ce76b51102fbd61a3350a
|
||||
|
||||
PKG_MAINTAINER:=Daniel Golle <daniel@makrotopia.org>
|
||||
PKG_LICENSE:=GPL-2.0-or-later
|
||||
PKG_LICENSE_FILES:=COPYING
|
||||
|
||||
PKG_BUILD_DEPENDS:=gettext-full/host
|
||||
|
||||
PKG_INSTALL:=1
|
||||
PKG_BUILD_PARALLEL:=1
|
||||
|
||||
include $(INCLUDE_DIR)/package.mk
|
||||
include $(INCLUDE_DIR)/meson.mk
|
||||
|
||||
MESON_ARGS += \
|
||||
-Ddrm=true \
|
||||
-Dudev=enabled \
|
||||
-Dgtk=disabled \
|
||||
-Dpango=disabled \
|
||||
-Dfreetype=disabled \
|
||||
-Ddocs=false \
|
||||
-Dsystemd-integration=false \
|
||||
-Dupstart-monitoring=false \
|
||||
-Dtracing=$(if $(CONFIG_DEBUG),true,false) \
|
||||
-Drelease-file=/etc/openwrt_release \
|
||||
-Drunstatedir=/var/run
|
||||
|
||||
define Package/plymouth/Default
|
||||
SECTION:=utils
|
||||
CATEGORY:=Utilities
|
||||
URL:=https://www.freedesktop.org/wiki/Software/Plymouth/
|
||||
endef
|
||||
|
||||
define Package/plymouth
|
||||
$(call Package/plymouth/Default)
|
||||
TITLE:=Graphical boot splash on a KMS device
|
||||
DEPENDS:=+libpng +libdrm +libudev +libevdev +libxkbcommon +xkeyboard-config
|
||||
endef
|
||||
|
||||
define Package/plymouth/description
|
||||
Draws a boot splash directly on a DRM/KMS device and owns the display
|
||||
until the application that follows it takes over, so the console stays
|
||||
silent from reset to user interface.
|
||||
endef
|
||||
|
||||
define Package/plymouth/conffiles
|
||||
/etc/plymouth/plymouthd.conf
|
||||
endef
|
||||
|
||||
define Build/InstallDev
|
||||
$(INSTALL_DIR) $(1)/usr/include
|
||||
$(CP) $(PKG_INSTALL_DIR)/usr/include/plymouth-1 $(1)/usr/include/
|
||||
$(INSTALL_DIR) $(1)/usr/lib
|
||||
$(CP) $(PKG_INSTALL_DIR)/usr/lib/libply*.so* $(1)/usr/lib/
|
||||
$(INSTALL_DIR) $(1)/usr/lib/pkgconfig
|
||||
$(CP) $(PKG_INSTALL_DIR)/usr/lib/pkgconfig/*.pc $(1)/usr/lib/pkgconfig/
|
||||
endef
|
||||
|
||||
define Package/plymouth/install
|
||||
$(INSTALL_DIR) $(1)/usr/lib
|
||||
$(CP) $(PKG_INSTALL_DIR)/usr/lib/libply*.so* $(1)/usr/lib/
|
||||
$(INSTALL_DIR) $(1)/usr/lib/plymouth
|
||||
$(CP) $(PKG_INSTALL_DIR)/usr/lib/plymouth/* $(1)/usr/lib/plymouth/
|
||||
$(INSTALL_DIR) $(1)/usr/bin
|
||||
$(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/bin/plymouth $(1)/usr/bin/
|
||||
$(INSTALL_DIR) $(1)/usr/sbin
|
||||
$(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/sbin/plymouthd $(1)/usr/sbin/
|
||||
$(INSTALL_DIR) $(1)/usr/share/plymouth
|
||||
$(CP) $(PKG_INSTALL_DIR)/usr/share/plymouth/plymouthd.defaults \
|
||||
$(1)/usr/share/plymouth/
|
||||
$(INSTALL_DIR) $(1)/etc/plymouth
|
||||
$(INSTALL_DATA) ./files/plymouthd.conf $(1)/etc/plymouth/plymouthd.conf
|
||||
$(INSTALL_DIR) $(1)/etc/init.d
|
||||
$(INSTALL_BIN) ./files/plymouth.init $(1)/etc/init.d/plymouth
|
||||
endef
|
||||
|
||||
$(eval $(call BuildPackage,plymouth))
|
||||
@@ -0,0 +1,72 @@
|
||||
#!/bin/sh /etc/rc.common
|
||||
|
||||
START=11
|
||||
STOP=06
|
||||
|
||||
PIDFILE=/var/run/plymouth.pid
|
||||
|
||||
running() {
|
||||
/usr/bin/plymouth --ping 2>/dev/null
|
||||
}
|
||||
|
||||
boot() {
|
||||
start "$@"
|
||||
}
|
||||
|
||||
start() {
|
||||
# The KMS device is not always card0; a render-only node can
|
||||
# enumerate first.
|
||||
ls /dev/dri/card* >/dev/null 2>&1 || return 0
|
||||
running && return 0
|
||||
|
||||
# plymouth only draws a graphical splash when the kernel was asked
|
||||
# for one, and requiring that of every board's boot script would put
|
||||
# a plymouth-ism in front of everyone. Ask for it here instead.
|
||||
# Where the console is a serial port plymouth drops to text on it
|
||||
# and never opens a renderer, so leave those alone.
|
||||
/usr/sbin/plymouthd --mode=boot --pid-file="$PIDFILE" \
|
||||
--ignore-serial-consoles --attach-to-session \
|
||||
--kernel-command-line="$(cat /proc/cmdline) splash" || return 1
|
||||
/usr/bin/plymouth show-splash
|
||||
}
|
||||
|
||||
# Give the display up without going away. The daemon keeps the device
|
||||
# open and the mode configured, so whatever draws next inherits a live
|
||||
# crtc, and the splash can return later without a second modeset.
|
||||
release() {
|
||||
running || return 0
|
||||
/usr/bin/plymouth deactivate
|
||||
}
|
||||
|
||||
stop() {
|
||||
running || return 0
|
||||
/usr/bin/plymouth quit --retain-splash
|
||||
rm -f "$PIDFILE"
|
||||
}
|
||||
|
||||
# procd runs the K scripts as "shutdown", so this is the start of that
|
||||
# sequence rather than the end. Take the display back from whoever has
|
||||
# it, reusing the daemon that has been there since boot.
|
||||
shutdown() {
|
||||
ls /dev/dri/card* >/dev/null 2>&1 || return 0
|
||||
[ -x /usr/sbin/plymouthd ] || return 0
|
||||
|
||||
if running; then
|
||||
/usr/bin/plymouth reactivate
|
||||
/usr/bin/plymouth change-mode --shutdown
|
||||
/usr/bin/plymouth show-splash
|
||||
return 0
|
||||
fi
|
||||
|
||||
/usr/sbin/plymouthd --mode=shutdown --pid-file="$PIDFILE" \
|
||||
--ignore-serial-consoles \
|
||||
--kernel-command-line="$(cat /proc/cmdline) splash" || return 0
|
||||
/usr/bin/plymouth show-splash
|
||||
}
|
||||
|
||||
reload() {
|
||||
/usr/bin/plymouth update --status=reload
|
||||
}
|
||||
|
||||
EXTRA_COMMANDS="release"
|
||||
EXTRA_HELP=" release Drop the display but keep the daemon running"
|
||||
@@ -0,0 +1,3 @@
|
||||
[Daemon]
|
||||
ShowDelay=0
|
||||
DeviceTimeout=8
|
||||
@@ -0,0 +1,42 @@
|
||||
From dd1ec1448548beee9b9c6a6b1a627580a087b179 Mon Sep 17 00:00:00 2001
|
||||
From: Daniel Golle <daniel@makrotopia.org>
|
||||
Date: Thu, 24 Sep 2026 21:07:31 +0100
|
||||
Subject: [PATCH] libply: provide rpmatch where the C library lacks it
|
||||
|
||||
rpmatch() is a GNU extension. Building against musl fails with an
|
||||
implicit declaration, and the call is only used to read a yes or no
|
||||
answer for a boolean command line option, so a plain first-letter test
|
||||
covers what the parser needs.
|
||||
|
||||
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
---
|
||||
src/libply/ply-command-parser.c | 18 ++++++++++++++++++
|
||||
1 file changed, 18 insertions(+)
|
||||
|
||||
--- a/src/libply/ply-command-parser.c
|
||||
+++ b/src/libply/ply-command-parser.c
|
||||
@@ -32,6 +32,24 @@
|
||||
#include "ply-list.h"
|
||||
#include "ply-utils.h"
|
||||
|
||||
+#ifndef __GLIBC__
|
||||
+/* rpmatch() is a GNU extension; the C library need not provide it. */
|
||||
+static int
|
||||
+rpmatch (const char *response)
|
||||
+{
|
||||
+ if (response == NULL)
|
||||
+ return -1;
|
||||
+
|
||||
+ if (response[0] == 'y' || response[0] == 'Y')
|
||||
+ return 1;
|
||||
+
|
||||
+ if (response[0] == 'n' || response[0] == 'N')
|
||||
+ return 0;
|
||||
+
|
||||
+ return -1;
|
||||
+}
|
||||
+#endif
|
||||
+
|
||||
typedef union
|
||||
{
|
||||
bool as_boolean;
|
||||
@@ -0,0 +1,46 @@
|
||||
From 29124ae4d8307bc8332b4678eb8cd4434b4bb872 Mon Sep 17 00:00:00 2001
|
||||
From: Daniel Golle <daniel@makrotopia.org>
|
||||
Date: Thu, 24 Sep 2026 21:08:57 +0100
|
||||
Subject: [PATCH] main: keep the crash handler building without execinfo
|
||||
|
||||
backtrace() and execinfo.h are glibc extensions, so the crash handler
|
||||
fails to build against musl. Guard both the include and the body of
|
||||
write_backtrace, and record that the backtrace is unavailable, leaving
|
||||
the maps dump and the rest of the crash report intact.
|
||||
|
||||
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
---
|
||||
src/main.c | 7 +++++++
|
||||
1 file changed, 7 insertions(+)
|
||||
|
||||
--- a/src/main.c
|
||||
+++ b/src/main.c
|
||||
@@ -2202,7 +2202,9 @@ dump_debug_buffer_to_file (void)
|
||||
|
||||
#include <termios.h>
|
||||
#include <unistd.h>
|
||||
+#ifdef __GLIBC__
|
||||
#include <execinfo.h>
|
||||
+#endif
|
||||
|
||||
#define BACKTRACE_SIZE 1024
|
||||
#define MAPS_SIZE 8192
|
||||
@@ -2252,6 +2254,7 @@ write_maps (int output_fd)
|
||||
static void
|
||||
write_backtrace (int output_fd)
|
||||
{
|
||||
+#ifdef __GLIBC__
|
||||
void *addresses[BACKTRACE_SIZE];
|
||||
int number_of_addresses;
|
||||
|
||||
@@ -2264,6 +2267,10 @@ write_backtrace (int output_fd)
|
||||
backtrace_symbols_fd (addresses + BACKTRACE_FRAMES_TO_SKIP,
|
||||
number_of_addresses - BACKTRACE_FRAMES_TO_SKIP,
|
||||
output_fd);
|
||||
+#else
|
||||
+ write (output_fd, "backtrace: unavailable\n",
|
||||
+ strlen ("backtrace: unavailable\n"));
|
||||
+#endif
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -0,0 +1,105 @@
|
||||
From 0cdc68b17ed3290a233e699dc31e973650b216ee Mon Sep 17 00:00:00 2001
|
||||
From: Daniel Golle <daniel@makrotopia.org>
|
||||
Date: Fri, 25 Sep 2026 11:22:40 +0100
|
||||
Subject: [PATCH] drm: Probe connectors while holding DRM master
|
||||
|
||||
The DRM renderer drops master immediately after opening the device and
|
||||
takes it back only when the splash is activated, so the connector
|
||||
enumeration in query_device () runs as a client that is not DRM master.
|
||||
|
||||
The kernel refuses a forced probe from such a client and returns the
|
||||
cached connection status instead:
|
||||
|
||||
User-space requested a forced probe on [CONNECTOR:84:HDMI-A-1] but is
|
||||
not the DRM master, demoting to read-only probe
|
||||
|
||||
On a display nothing has driven yet that status is unknown, so no output
|
||||
counts as connected, no controller is assigned, and the renderer gives
|
||||
up with "Could not initialize heads". The splash therefore appears only
|
||||
on a screen that some other client has already lit, which on a system
|
||||
without fbdev emulation never happens.
|
||||
|
||||
This commit holds master for the duration of the connector enumeration,
|
||||
dropping it again unless the backend was already active. The probe then
|
||||
detects for real, reads the EDID and brings up a sink that had gone to
|
||||
low power. On an rk3588 board with no other DRM client running, this
|
||||
takes the HDMI connector from unknown to connected at 1920x1080.
|
||||
|
||||
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
---
|
||||
src/plugins/renderers/drm/plugin.c | 57 ++++++++++++++++++++++--------
|
||||
1 file changed, 43 insertions(+), 14 deletions(-)
|
||||
|
||||
--- a/src/plugins/renderers/drm/plugin.c
|
||||
+++ b/src/plugins/renderers/drm/plugin.c
|
||||
@@ -1421,6 +1421,46 @@ check_simpledrm_resolution (ply_renderer
|
||||
return true;
|
||||
}
|
||||
|
||||
+/* Probe every connector, holding DRM master so the kernel performs a real
|
||||
+ * detect rather than handing back the cached connection status.
|
||||
+ */
|
||||
+static bool
|
||||
+probe_outputs (ply_renderer_backend_t *backend,
|
||||
+ ply_output_t *outputs,
|
||||
+ int outputs_len,
|
||||
+ bool force,
|
||||
+ bool *changed)
|
||||
+{
|
||||
+ bool was_master;
|
||||
+ bool ret = true;
|
||||
+ int i;
|
||||
+
|
||||
+ was_master = backend->is_active;
|
||||
+ if (!was_master)
|
||||
+ drmSetMaster (backend->device_fd);
|
||||
+
|
||||
+ backend->connected_count = 0;
|
||||
+ for (i = 0; i < outputs_len; i++) {
|
||||
+ get_output_info (backend, backend->resources->connectors[i], &outputs[i]);
|
||||
+
|
||||
+ if (!force && !check_simpledrm_resolution (backend, &outputs[i])) {
|
||||
+ ret = false;
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
+ if (check_if_output_has_changed (backend, &outputs[i]))
|
||||
+ *changed = true;
|
||||
+
|
||||
+ if (outputs[i].connected)
|
||||
+ backend->connected_count++;
|
||||
+ }
|
||||
+
|
||||
+ if (!was_master)
|
||||
+ drmDropMaster (backend->device_fd);
|
||||
+
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
/* Update our outputs array to match the hardware state and
|
||||
* create and/or remove heads as necessary.
|
||||
* Returns true if any heads were modified.
|
||||
@@ -1447,20 +1487,9 @@ create_heads_for_active_connectors (ply_
|
||||
outputs = calloc (backend->resources->count_connectors, sizeof(*outputs));
|
||||
outputs_len = backend->resources->count_connectors;
|
||||
|
||||
- backend->connected_count = 0;
|
||||
- for (i = 0; i < outputs_len; i++) {
|
||||
- get_output_info (backend, backend->resources->connectors[i], &outputs[i]);
|
||||
-
|
||||
- if (!force && !check_simpledrm_resolution (backend, &outputs[i])) {
|
||||
- free (outputs);
|
||||
- return false;
|
||||
- }
|
||||
-
|
||||
- if (check_if_output_has_changed (backend, &outputs[i]))
|
||||
- changed = true;
|
||||
-
|
||||
- if (outputs[i].connected)
|
||||
- backend->connected_count++;
|
||||
+ if (!probe_outputs (backend, outputs, outputs_len, force, &changed)) {
|
||||
+ free (outputs);
|
||||
+ return false;
|
||||
}
|
||||
|
||||
/* Step 2:
|
||||
@@ -0,0 +1,122 @@
|
||||
From 7d73b0fd2746cd24c758dd75fc73ac92b5162d0b Mon Sep 17 00:00:00 2001
|
||||
From: Daniel Golle <daniel@makrotopia.org>
|
||||
Date: Fri, 25 Sep 2026 13:20:37 +0100
|
||||
Subject: [PATCH] Ignore the escape key when the VT shows nothing
|
||||
|
||||
Escape toggles between the splash and the details view, guarded on
|
||||
there being a VT console to show the details on. The guard asks only
|
||||
whether the local console terminal is a VT, which it is whenever the
|
||||
kernel has CONFIG_VT and a /dev/tty1 node exists, even where no console
|
||||
driver draws that VT.
|
||||
|
||||
On a system built without a framebuffer console that leaves only the
|
||||
dummy driver bound, so escape blanks the screen: the splash is torn
|
||||
down, the details view writes to a VT nobody renders, and the display
|
||||
stays dark until escape is pressed again.
|
||||
|
||||
Require a bound console driver other than the dummy one before treating
|
||||
a VT as somewhere the details view can be read.
|
||||
|
||||
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
---
|
||||
src/main.c | 68 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||
1 file changed, 68 insertions(+)
|
||||
|
||||
--- a/src/main.c
|
||||
+++ b/src/main.c
|
||||
@@ -36,6 +36,7 @@
|
||||
#include <assert.h>
|
||||
#include <values.h>
|
||||
#include <locale.h>
|
||||
+#include <string.h>
|
||||
|
||||
#include <linux/kd.h>
|
||||
#include <linux/vt.h>
|
||||
@@ -56,6 +57,7 @@
|
||||
#include "ply-progress.h"
|
||||
#include "ply-kmsg-reader.h"
|
||||
|
||||
+#define VT_CONSOLE_DIRECTORY "/sys/class/vtconsole"
|
||||
#define BOOT_DURATION_FILE PLYMOUTH_TIME_DIRECTORY "/boot-duration"
|
||||
#define SHUTDOWN_DURATION_FILE PLYMOUTH_TIME_DIRECTORY "/shutdown-duration"
|
||||
|
||||
@@ -1638,6 +1640,70 @@ toggle_between_splash_and_details (state
|
||||
}
|
||||
}
|
||||
|
||||
+static bool
|
||||
+vt_console_driver_draws (const char *directory)
|
||||
+{
|
||||
+ char path[PATH_MAX];
|
||||
+ char name[64];
|
||||
+ FILE *file;
|
||||
+ bool draws;
|
||||
+
|
||||
+ snprintf (path, sizeof(path), "%s/bind", directory);
|
||||
+ file = fopen (path, "r");
|
||||
+
|
||||
+ if (file == NULL)
|
||||
+ return false;
|
||||
+
|
||||
+ draws = (fgetc (file) == '1');
|
||||
+ fclose (file);
|
||||
+
|
||||
+ if (!draws)
|
||||
+ return false;
|
||||
+
|
||||
+ snprintf (path, sizeof(path), "%s/name", directory);
|
||||
+ file = fopen (path, "r");
|
||||
+
|
||||
+ if (file == NULL)
|
||||
+ return false;
|
||||
+
|
||||
+ if (fgets (name, sizeof(name), file) == NULL)
|
||||
+ name[0] = '\0';
|
||||
+
|
||||
+ fclose (file);
|
||||
+
|
||||
+ return strstr (name, "dummy") == NULL;
|
||||
+}
|
||||
+
|
||||
+/* The dummy console driver puts nothing on screen, so a VT it is the only
|
||||
+ * driver for cannot show the details view.
|
||||
+ */
|
||||
+static bool
|
||||
+vt_console_is_visible (void)
|
||||
+{
|
||||
+ char directory[PATH_MAX];
|
||||
+ struct dirent *entry;
|
||||
+ bool visible = false;
|
||||
+ DIR *consoles;
|
||||
+
|
||||
+ consoles = opendir (VT_CONSOLE_DIRECTORY);
|
||||
+
|
||||
+ if (consoles == NULL)
|
||||
+ return true;
|
||||
+
|
||||
+ while (!visible && (entry = readdir (consoles)) != NULL) {
|
||||
+ if (strncmp (entry->d_name, "vtcon", strlen ("vtcon")) != 0)
|
||||
+ continue;
|
||||
+
|
||||
+ snprintf (directory, sizeof(directory), "%s/%s",
|
||||
+ VT_CONSOLE_DIRECTORY, entry->d_name);
|
||||
+ visible = vt_console_driver_draws (directory);
|
||||
+ }
|
||||
+
|
||||
+ closedir (consoles);
|
||||
+
|
||||
+ return visible;
|
||||
+}
|
||||
+
|
||||
static void
|
||||
on_escape_pressed (state_t *state)
|
||||
{
|
||||
@@ -1647,6 +1713,8 @@ on_escape_pressed (state_t *state)
|
||||
if (state->local_console_terminal != NULL) {
|
||||
if (!ply_terminal_is_vt (state->local_console_terminal))
|
||||
has_vt_consoles = false;
|
||||
+ else if (!vt_console_is_visible ())
|
||||
+ has_vt_consoles = false;
|
||||
} else {
|
||||
has_vt_consoles = false;
|
||||
}
|
||||
Executable
+8
@@ -0,0 +1,8 @@
|
||||
#!/bin/sh
|
||||
# Neither binary reports a version: upstream's command parser has no
|
||||
# --version and prints only a usage summary. Check that both still
|
||||
# identify themselves and exit cleanly.
|
||||
/usr/bin/plymouth --help 2>&1 | grep -q '^Splash control client$' || exit 1
|
||||
/usr/sbin/plymouthd --help 2>&1 | grep -q '^Splash server$' || exit 1
|
||||
|
||||
exit 0
|
||||
Reference in new issue
Block a user