Repository navigation
Bring up Q25 input, Panfrost rendering and truthful battery status - #52
Draft
patrickjquinn wants to merge 132 commits into
Draft
patrickjquinn wants to merge 132 commits into
patrickjquinn wants to merge 132 commits into
Conversation
Stop the perpetual welcome glow opacity animation under Qt software rendering and reduced motion; retain accelerated animation. This is the QML source used by the deployed 639e396d efficiency candidate, without its diagnostic main.cpp instrumentation. Declare Q25 scale policy as FORCE_DPI=240 in its device manifest and generated profile overlay, including the normal greetd session entry. Leave physical panel DPI unset. The target is reserved bring-up metadata, not a new image/flash pipeline; no CPU, kernel or GPU policy is installed. Validation: seven device manifests valid; generated profile and overlay SHA512 checks pass; shell scripts and greetd TOML pass. Qt 6.11.2 qmllint exits 0 for baseline and candidate with identical 122 existing warnings and no new warnings. Earlier offscreen software steady CPU sample fell 20.67% to 1.83%; device observations were not a controlled same-scale A/B.
The shared udev rule hides touchscreens from libinput, but the Shell started its evdev touch reader only on EGLFS. LinuxFB therefore had no touchscreen consumer. Start the same Qt InputSupport reader on both backends, including colon-qualified platform specifications; preserve generic-plugin override behavior. Validation: exact source condition passed 24 backend/generic-plugin combinations. main.cpp compiled and linked alone against the cached aarch64 musl Qt6.11.1 SDK and existing Rust objects. Remove the temporary Q25 LIBINPUT_IGNORE_DEVICE-clearing override when deploying to avoid duplicate libinput/evdev touch readers. Physical touch validation remains parent-owned.
Select a package-owned greetd config through its service drop-in and start both sessions through q25-shell-session. This sets LinuxFB/software/basic/fullscreen and DPI240 after PAM, then delegates to the normal launcher for D-Bus, paths and cgroups. Preserve explicit render-loop choices in the normal launcher while defaulting to threaded. Replace the earlier base-config symlink takeover. Keep the reviewed Q25 CPU4-7 policy and process-scoped noafbc workaround from the source handoff. GPU module packaging remains a separate delivery: retain device-mediatek-g99/greetd dependencies rather than depend on its unmerged q25-gpu-render package. Validation: four empty-environment/config/launcher tests pass; seven device manifests valid; overlay and launcher checksums and shell syntax pass. No binary build, frozen image changes or target calls.
Append pipeline patch0052 so tmpfiles copies defaults only into missing entries and the first-run seeder preserves existing configuration even when its marker is absent. Keep empty files, dangling config symlinks and authorized keys through repeated boot. Validation: reproduce failures with the original generated seeder, apply patch0052 with fuzz0, then pass all four tests against the actual generated shell/tmpfiles payload. Patch numbering check passes with only documented absent0011. No image rebuild or deployment.
|
Formatting changes required clang-format -i <files>
qmlformat -i <files> (must be from Qt 6)
cargo fmt --manifest-path crates/Cargo.toml --allCommit the updated files and push again. |
…n recovery Deckard d5c3178..83e46e5: the slimmed Android 16 runtime; idle apps' anonymous memory paged out instead of the container's file cache being reclaimed (an Android cold start after idle went from 10-11 s back to ~0.75 s on the HackberryPi); and a container daemon that finishes an interrupted teardown instead of refusing every later start.
Alpine edge moved to clippy 1.99, whose pedantic assert_is_empty rejects assert!(x.is_empty()): CI stopped at reef-backend and would have stopped at pilotd and keel-actions-schema next. Each check now binds the value once and prints it when it fails. battery_measurement's tests take the workspace's usual unwrap allowance. The whole workspace is clean under clippy 1.99 with -D warnings; pilotd, reef-backend and keel-actions-schema tests pass.
Doze left a keyboard's light on all night (the Q25's sidecar exposes it as q25::kbd_backlight). Turning the screen off now saves the level of every LED named *::kbd_backlight and writes 0; waking restores exactly that level, so a light the user had at 0 stays at 0, and a repeated off does not overwrite the saved level. It scans on each off, since the LED's module may load after the shell starts. On the Q25 via the power key: 255 -> 0 in Doze -> 255 on wake; a user level of 0 stayed 0; five cycles at 128 restored 128 each time.
The Q25's call, menu, back and end keys did nothing. Call answers a ringing call or opens Phone; end declines or hangs up, else goes home; menu (KEY_APPSELECT, which Qt has no key for, so it is matched by scan code) toggles Active Frames and the app grid, or goes home from an app; back is back. The incoming-call overlay's answer and decline are now functions the keys share with its buttons. Verified on the Q25: each key from home and from Phone, with the shell staying up; locked, the keys do nothing.
a412694 cleared a toplevel's cached roles on destruction; popups kept theirs. QML reads the surface's xdgSurface property, so a popup whose role died before its wl_surface left a dangling pointer behind. The unread xdgPopup property is dropped rather than tracked. Ran on the Q25: unlock, Phone open/close and the grid toggle with the shell staying up.
patrickjquinn
force-pushed
the
fix/q25-software-welcome-and-scale
branch
from
October 30, 2026 21:33
9961083 to
189c7bb
Compare
The keyboard light driver was loaded by hand, so it was gone after a reboot. The device package now loads it after the keyboard (softdep) with its control options, and udev's existing LED rule lets the shell write it. systemd-backlight saved the light's level at shutdown, and Doze had usually put it out, so it came back at 0 and the shell restored that 0 on every wake. That unit is masked for this light; the keyboard's own 255 is the boot level. Verified on the Q25 across two reboots, the second from Doze: both modules load, the light comes up at 255, goes to 0 in Doze and back to 255 on wake. The .ko itself is still installed by hand, like the keyboard driver; neither is in a kernel package yet.
The network actor probed NetworkManager's devices once, at start. A Wi-Fi adapter that came later (a firmware load after boot, a USB dongle, a NetworkManager restart) was never seen, so Settings said "no adapter" while NetworkManager had one. It now re-probes, and rescans, whenever NetworkManager's Devices change, and drops a device path that is gone. Verified on the Q25: with the shell running, Wi-Fi brought up late and NetworkManager restarted, Settings lists the nearby networks.
The first pipeline image for the Q25 only replaces the root file on the carrier, so the risk is in what the new root lacks compared with the hand-staged one the phone runs today. This compares the apk package set, systemd enablement and the /etc and /usr/libexec file lists (type, mode, owner, sha256 or link target) and fails on any difference that no --explain rule accounts for. The live side is captured read-only over ssh with python3 -I -B so no bytecode lands in the phone's root. The image side is read straight from the ext4 file: debugfs rdump ran at about 2 MB/s on the build drive and loses ownership, and rootless fuse2fs refuses to mount, so a small extent-only reader walks the image instead. Its test builds an image with mkfs.ext4 -d and checks it against the source tree.
per-phone-state.toml lists what any new root will differ in on a lived-in phone (identity, clock, connections, TuneD state, runtime bytecode, sshd enablement). hand-staged-20261030.toml records what was copied and enabled by hand on top of q25-duranium-20261030-eff639e, grouped as Q25-IMAGE-PLAN §1.4 (a) packaged, (b) unpackaged and (d) junk. With both files the frozen eff639e image diffs against the live phone with no unexplained difference (97 explained: 15 unit, 82 file, 0 package). A pipeline image is checked with per-phone-state.toml alone, so every hand-staged item has to be in its packages.
The first image keeps the proven kernel, carrier initramfs and DTB, so the new boot entry is the current default entry with only q25.rootfs_file changed. The script moves the image onto the carrier under a new name (never overwriting one), writes the entry, backs up loader.conf as loader.conf.before-NAME and points default at the new entry; the old entry stays as the fallback. rollback restores loader.conf and removes the entries but leaves the image to inspect. --counted builds the §3.4 layout instead: marathon-q25-b-NAME+3.conf, a marathon-q25-a-fallback.conf copy of the current entry, and a glob default. It stays opt-in until T2/T3 prove boot counting and a bless unit exists, since without one a good entry falls back after three boots. sync is scoped to the touched filesystem with sync -f.
The Q25 target now has scripts/build-zinwa-q25-image.sh. It builds the root alone and leaves the phone's ESP, kernel and initramfs as they are, so build.bootloader is none, not uki.
The package still shipped the first hx32062se, whose proximity thresholds were the stock 3800/3300. On the Q25 the sensor reads 0 with nothing near and only 500-1400 with a finger over it, so it never reported near. Its ALS reading also included the chip's ~273k dark offset, so lux was meaningless. This is the v7 driver the phone has been running: near defaults to 600 (a near_level parameter, then the DT proximity-near-level, override it), the chip's far threshold sits at 65% of near, and the ALS value is offset-corrected from efuse 0x95 with the stock gain-1 lux scale. The shipped binary is the phone's module (sha256 7b4e9ac6) without debug info. Rebuilt from this source with build.sh's flags against the 7.0.0-q25-cfi-test tree it matches under kocmp.py.
hx32062se takes its near level from the DT before its own default of 600, and both the Q25's live DTB and the board DTS in the kernel series still say proximity-near-level = <3800>, the stock value. The sensor never reads above about 1400, so with that level it never reports near. The phone has been running with a hand-placed /etc/modprobe.d/q25-hx.conf setting near_level=600; ship the same option in the device package so a fresh root keeps it. It can go once the DTB carries 600.
The phone runs a newer wlan_drv_gen4m_6789 (sha256 1390ea66) than the package shipped, so a fresh root would quietly lose the two knobs the display-interference A/B test uses: - q25_online_scan (runtime, default 1): 0 makes mtk_cfg80211_scan return -EBUSY while connected, which stops bgscan and UI scans from dwelling off-channel; - q25_pdma_bt_size (load time, default 3, the stock value): caps the WFDMA burst size asicPdmaConfig programs. Defaults keep stock behaviour. 0002 gains the four hunks; the shipped binary is the phone's. Rebuilt from the donor plus this 0002 against the 7.0.0-q25-cfi-test tree it matches under kocmp.py, and wmt_chrdev_wifi from the same build still matches the shipped one.
abuild -d skips build-base as well as the dependencies, so a NO_DEPS build of an aport that compiles (modemmanager-mtk-ccci) stopped at meson's "Unknown compiler(s): cc".
abuild -d leaves libmm-glib uninstalled, so tracing the package's shared-library dependencies failed with "libmm-glib.so.0: path not found". modemmanager-mtk-ccci depends only on Alpine packages, so it builds with them; the q25-* aports still build without depends, since those are each other.
abuild's srcdir holds symlinks to the downloaded sources, and zstd
skips a symlinked input file ("is a symbolic link, ignoring"), which
-q hid: package() failed with no message. Reading the image on stdin
follows the link.
…ips it build-device-marathon-apk.sh generated the profile only when device-profile.conf stood alone on a line of the APKBUILD. The Q25's source= starts with it, so nothing was generated and abuild checksum failed. Match the file name as a word instead; the same device aports still get a profile and enchilada, which ships none, still gets none.
The overlay shipped a whole wpa_supplicant.service because the frozen image's wpa_supplicant had no unit. The pipeline image installs wpa_supplicant-systemd, which owns that file, so apk refused the overlay. Depend on wpa_supplicant-systemd and keep only the Q25 difference, the control socket and syslog flags, in a drop-in.
Pipeline patch 0054: build-image.py forwards MARATHON_DEV_SSH_KEY to mkosi with --environment. PassEnvironment alone dropped it, so images built since 0050 had no authorized key and sshd disabled; the first Q25 pipeline root came out that way.
Android's netd in Deckard's container programs legacy iptables, and on the Q25 none of those tables exist: ip6tables-restore fails with "unable to initialize table 'filter'", system_server restarts every two minutes and Android never finishes booting. The netfilter set already builds every legacy table and the matches the kernel config enables; ship ip_tables, ip6_tables, the filter, nat, mangle and raw tables for both families, ipt_REJECT and ip6t_REJECT with nf_reject_ipv4/6, and xt_mark, xt_conntrack, xt_comment, xt_multiport, xt_tcpmss, xt_TCPMSS, xt_addrtype and xt_REDIRECT. The kernel config leaves connmark, state, u32, owner, bpf, pkttype, policy, length, limit, socket, NFLOG, nfnetlink_log, CT, IDLETIMER, quota, hashlimit and iprange unset, so those cannot be built. depmod -e against the kernel's System.map resolves every symbol, and the vermagic matches the modules already shipped.
netd in the container cannot load modules itself, so the tables are loaded at boot, as on the HackberryPi.
The slider's floor was bias level 4 of 20 (4.4 V). Level 1 (4.1 V), stock's lowest, shows no ghosting on the panel and is visibly dimmer, so the floor is now 0.05. The OCP2131 bias is the only brightness control: nothing answers at 0x11, so there is no WLED driver.
The motor hangs off the MT6366 vibr LDO, as stock's regulator-vibrator node says, but the board DT has no haptic node and the kernel leaves regulator-haptic unbuilt. q25-haptic creates the regulator-haptic device with a 2.8 V supply aliased to vibr by name; the mainline driver, built unmodified, turns it into an FF_RUMBLE input device, which the shell's haptics already drive. Loaded at boot from modules-load.d. Verified on the phone: a 300 ms enable of vibr buzzes the motor, and the module pair registers input regulator-haptic with FF_RUMBLE.
With the legacy tables loaded, netd's iptables-restore still fails on connmark, CONNMARK and bpf, so its rule set never applies. The kernel config leaves these and the rest of netd's matches and targets unset. None of their sources needs kernel-side support that the config lacks, so the net set builds them out of tree with their options forced to m: xt_connmark, xt_bpf, xt_owner, xt_u32, xt_state, xt_pkttype, xt_limit, xt_quota, xt_hashlimit, xt_iprange, xt_length, xt_IDLETIMER, xt_NFLOG with nfnetlink_log, and xt_CT. nfnetlink_log reads the bridge port through br_private.h, since the config has BRIDGE_NETFILTER. xt_socket is left out: nf_socket_ipv4/6 call udp4_lib_lookup and udp6_lib_lookup, which the kernel only builds with NF_SOCKET or NF_TPROXY set. The modules load through iptables' own autoload once ip_tables is in. The vermagic matches the shipped modules, and depmod -e against the kernel's System.map resolves every symbol.
netd routes its blackhole network through dummy0 and logs "cannot find interface dummy0" without it. The kernel config has DUMMY=m but the kernel APK does not ship it, so build it from drivers/net/dummy.c with the net set. It is not loaded at boot: netd only looks dummy0 up, and loading the module creates dummy0 in the host's namespace, not the container's. Creating it with "ip link add dummy0 type dummy" in the container's namespace autoloads the module.
Android never ran on the Q25: polkit refused the phone user's start of deckard-container, and without persist.deckard.multi_windows each app ran inside one Android display that Marathon never mapped, so launches timed out with no surface. Ship the HackberryPi's polkit rule and a deckard.cfg seed: multi-window, idle freeze off, density 240, and Marathon's app area as Android's display, 720x633 px at scale 1.5, so 480x422. Verified on the phone: Android boots in 20 s and microG opens as a Marathon window.
The host read the shell's ambience with a block_on on the GUI thread over the process's shared session connection, and the read never returned within its second: every app connected to a host with no ambience and took it later as a change. Read it in follow_ambience once the change stream is subscribed, so a change during the read is not lost. Traced on the Q25: the host now holds the ambience 250 ms after start.
Every app faded in twice: once by the shell's reveal, then out and back in about 400 ms later, its first frames in Keel's default colours. Keel fetches the host's properties asynchronously; the ambience arrived after the window mapped, counted as a change, and Keel ran its 400/600/400 ms ambience fade. Before mapping, fetch the ambience from the host over a short peer connection and pass it to Keel's ShellClient while the window is hidden, where Keel skips the fade. The later reply then matches and does nothing. Verified on the Q25 by sampling the scanout during launches: one fade, in the ambience's colours from the first frame.
The Quick Settings shade and the Hub peek showed a frozen, blurred copy of the wallpaper whatever was behind them. They now frost the layers behind: the open app's surface, or the wallpaper and the home screen. AppBackdropBlur captures its layers at a quarter of its size, blurs them there into a layer and scales that up filtered, so the blur runs on 180x180 on the Q25 instead of full size with mipmaps. A live capture redraws only when a layer changes and only while visible. The lock screen's frozen backdrop uses the same path. blurMax is now in the small texture's pixels. Checked on the Q25 over the calculator and over home.
Deckard 51e20b5: a fresh install has the images but no instance, and
nothing ran Init, so the first start failed ("deckard doesn't exist")
and Android never booted. Start now initializes from the installed
images when the instance is missing. Found on the Zinwa Q25.
Lint_TestReachability failed: nothing ran test_q25_root_diff.py or test_q25_root_install.py. Register both; the diff test builds its ext4 fixture with mkfs.ext4, so CI installs e2fsprogs. Both pass in alpine:edge as an ordinary user.
deckard-images 23.2.20261101: the 20261028 images with netd rebuilt from Marathon-Deckard image/lineage-23.2 f8500aa, which logs and carries on when XfrmController cannot open its netlink socket instead of exiting. Without it Android never finished booting on the Zinwa Q25. The vendor image is unchanged. Tested on the Q25 with the released artifact: boot_completed in 20 s, network inside Android, apps open as Marathon windows.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The Q25 device profile now applies 1.5× scaling and native evdev touch through greetd’s PAM session. Home seeding preserves migrated settings and keys. Guarded Panfrost startup checks the tested module/shim/KMS closure and rail relationship, then selects GPU rendering or the software fallback.
Battery state stays unknown when the kernel provides no measured capacity. Shell and Settings preserve charging independently, avoid false 0% warnings/shutdown, and clear invalidated UPower values. The status bar uses a battery glyph without a question-mark percentage placeholder. A separate recorded AUXADC patch fixes voltage scaling; it is not yet wired into the canonical kernel APK recipe.
Validation:
Bring-up limits: CPU0 offline, GUI affinity 4–7, temporary GPU PM-on/390 MHz policy, KMSRO with firmware SimpleDRM scanout, mutable carrier root. Native MediaTek display, dynamic GPU power, accurate battery percentage, modem operation and full phone qualification remain open. See devices/zinwa/q25/VALIDATION-20261030.md for measurements and limitations.