From a973bc8d75017f3cd956b1fcb1cb0d9e074c2755 Mon Sep 17 00:00:00 2001 From: Martin Bauer Date: Sun, 20 Sep 2026 12:10:34 +0200 Subject: [PATCH] Make the kiosk autologin survive a reboot, and allow forcing a display mode Two problems on musicdolphin, found after its first reboot. getty@tty1 was never enabled. The role installed the autologin drop-in and the handler restarted the unit, which works until the next boot - these images do not enable getty@tty1 themselves, getty.target pulls in getty-static.service and nothing else, so after a reboot nobody logs in and the kiosk never starts. Enable it as well as starting it. And the attached screen reads back 0 bytes of EDID on both micro-HDMI ports while reporting "disconnected", so the kernel offers no modes, no framebuffer is created, and X exits with "no screens found". That is the signature of a cable or adapter whose DDC/HPD lines are broken but whose video pairs are fine. Forcing the connector on is not enough on its own - with no EDID there is still no mode to set - so allow a `video=` kernel parameter, which forces the output and names the mode. hdmi_force_hotplug=1 is no use here; vc4-kms-v3d ignores it. Co-Authored-By: Claude Opus 5 --- host_vars/musicdolphin.yml | 5 +++++ roles/pi_kiosk/defaults/main.yml | 11 +++++++++++ roles/pi_kiosk/handlers/main.yml | 6 ++++++ roles/pi_kiosk/tasks/main.yml | 31 +++++++++++++++++++++++++++++++ 4 files changed, 53 insertions(+) diff --git a/host_vars/musicdolphin.yml b/host_vars/musicdolphin.yml index 14aad61..bcf819f 100644 --- a/host_vars/musicdolphin.yml +++ b/host_vars/musicdolphin.yml @@ -16,3 +16,8 @@ mediapi_has_dhtsensor: true mediapi_install_kidsmusic: true mediapi_has_monitor: true musicmouse_version: deploy/musicdolphin + +# The attached screen reads back 0 bytes of EDID on both micro-HDMI ports and reports +# "disconnected", so the kernel offers no modes and X dies with "no screens found". +# Force the mode instead. Use the port nearest the USB-C jack (HDMI0 = HDMI-A-1). +pi_kiosk_force_mode: "HDMI-A-1:1920x1080@60e" diff --git a/roles/pi_kiosk/defaults/main.yml b/roles/pi_kiosk/defaults/main.yml index 750db2b..1fc62bd 100644 --- a/roles/pi_kiosk/defaults/main.yml +++ b/roles/pi_kiosk/defaults/main.yml @@ -1,3 +1,14 @@ --- pi_kiosk_user: "kiosk" pi_kiosk_url: "http://localhost:8080" + +# Force a mode on the kiosk output, as a `video=` kernel parameter, e.g. +# "HDMI-A-1:1920x1080@60e" (the trailing `e` forces the output enabled). Empty means +# "let EDID decide", which is what you want whenever EDID can be read. +# +# This is for a display the Pi cannot see: a micro-HDMI cable or adapter whose DDC/HPD +# lines are broken but whose video pairs are fine reads back 0 bytes of EDID and +# reports "disconnected", while displaying perfectly once a mode is forced. Note +# hdmi_force_hotplug=1 does nothing here - it is ignored by the vc4-kms-v3d driver +# these images use. +pi_kiosk_force_mode: "" diff --git a/roles/pi_kiosk/handlers/main.yml b/roles/pi_kiosk/handlers/main.yml index 6ed99e1..a08305a 100644 --- a/roles/pi_kiosk/handlers/main.yml +++ b/roles/pi_kiosk/handlers/main.yml @@ -4,3 +4,9 @@ name: "getty@tty1" state: restarted daemon_reload: "yes" + +- name: Warn that a reboot is needed + ansible.builtin.debug: + msg: >- + /boot/firmware/cmdline.txt changed - the forced display mode only takes effect + after a reboot. diff --git a/roles/pi_kiosk/tasks/main.yml b/roles/pi_kiosk/tasks/main.yml index 42929ec..6e748b2 100644 --- a/roles/pi_kiosk/tasks/main.yml +++ b/roles/pi_kiosk/tasks/main.yml @@ -29,6 +29,17 @@ mode: "0644" notify: Reload systemd and restart getty +- name: Enable the autologin getty on tty1 + # Enabling it is the part that survives a reboot. These images do not enable + # getty@tty1 themselves - getty.target pulls in getty-static.service and nothing + # else - so without this the drop-in above is installed, the handler starts the unit + # once, and the kiosk then silently stops coming up after the next reboot. + ansible.builtin.systemd: + name: "getty@tty1" + enabled: true + state: started + daemon_reload: true + - name: Install .bash_profile (starts X on tty1 login) ansible.builtin.template: src: bash_profile.j2 @@ -57,3 +68,23 @@ src: policies.json dest: /etc/firefox-esr/policies.json mode: "0644" + +- name: Read the current kernel command line + ansible.builtin.slurp: + src: /boot/firmware/cmdline.txt + register: pi_kiosk_cmdline + when: pi_kiosk_force_mode | length > 0 + +- name: Force a mode on the kiosk output + # cmdline.txt is a single line of space-separated parameters. Rebuild it by dropping + # any video= we set before and appending the current one, so changing the mode + # replaces it rather than stacking a second one. + ansible.builtin.copy: + dest: /boot/firmware/cmdline.txt + content: >- + {{ ((pi_kiosk_cmdline.content | b64decode).split() + | reject('match', '^video=') | list + + ['video=' ~ pi_kiosk_force_mode]) | join(' ') }} + mode: "0755" + when: pi_kiosk_force_mode | length > 0 + notify: Warn that a reboot is needed