aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCraig Jennings <c@cjennings.net>2026-09-16 15:10:34 -0400
committerCraig Jennings <c@cjennings.net>2026-09-16 15:10:34 -0400
commit221bf506a5619ae4d635ced55f13f5549dc351d4 (patch)
tree4f70e81d949fd06c35241f95d4fbdbfd3821730e
parent12ee214f1ab1271b9e4082e05ca44d6cd8a4b03a (diff)
downloadarchsetup-221bf506a5619ae4d635ced55f13f5549dc351d4.tar.gz
archsetup-221bf506a5619ae4d635ced55f13f5549dc351d4.zip
chore(tasks): file the MT7925 Bluetooth bug and the AX210 swapHEADmain
Three headsets drop HFP call audio on velox's MT7925, and the kernel's sentinel-handle errors match a pending upstream firmware report, so I'm replacing the card with an Intel AX210. I also filed the net doctor captive-portal task and archived two aged completed tasks.
-rw-r--r--archive/task-archive.org136
-rw-r--r--todo.org386
2 files changed, 384 insertions, 138 deletions
diff --git a/archive/task-archive.org b/archive/task-archive.org
index 4f2fdaa..dfd273d 100644
--- a/archive/task-archive.org
+++ b/archive/task-archive.org
@@ -2968,3 +2968,139 @@ ratio and absent on velox; full evidence and rationale in
Scope: installer step + rule file + tests per existing shapes, and apply both
live to velox over tailscale (daily-driver sync — neither exists there today).
+** DONE [#B] Velox touchpad interrupt line is dead — needs a part or a BIOS fix :bug:velox:hardware:
+CLOSED: [2026-08-15 Sat]
+:PROPERTIES:
+:CREATED: [2026-08-15 Sat]
+:LAST_REVIEWED: 2026-08-15
+:END:
+*Fixed 2026-08-15 23:05 by reseating the correct connector* — a seating fault
+all along, no part needed. Verified at the kernel level on the 23:05 boot: the
+=did not ack reset within 1000 ms= message is gone (clean handshake), and the
+interrupt count went 0 → 1795. Power-key events also zero, so both faults from
+the mainboard swap are closed.
+
+What made this take three attempts is worth keeping: two of the connectors on
+that board were decoys. The input-cover ribbon looked like the obvious suspect
+and fixing it *did* resolve the power button, which made it look like the whole
+answer. Then the 4-pin connector next to the printed =TOUCHPAD= label looked
+like the touchpad's own — and its cable is silkscreened =PIN 1-2 - GND /
+PIN 3-4 - VCC=, four contacts of pure power, incapable of carrying i2c or an
+interrupt. Reading that silkscreen off the photo is what ruled it out and sent
+the search to the ribbon that actually crosses to the mainboard.
+
+The ordered touchpad becomes a spare, which is what Craig wanted from it anyway.
+The diagnostic path below is left intact — it is the reusable part: =dmesg=
+for the i2c-HID reset message and the interrupt count in =/proc/interrupts=
+together separate "device absent" from "device present but its interrupt line is
+open", and a live USB separates hardware from software in two minutes.
+Split from the ribbon-reseat task 2026-08-15 once the reseat fixed the power
+button and left this untouched — they are two faults, not one.
+
+*Diagnosed to the interrupt line specifically, with software eliminated.*
+- The i2c *data* path works. =i2c_hid_acpi= read the HID descriptor, returned
+ the right product ID (=093A:0274=), =hid-multitouch= bound, and input6/7/8/9
+ were created. A descriptor read is a real bus transaction, so the device is
+ electrically present and answering.
+- The *interrupt* path never fires. IRQ 81, =amd_gpio= hwirq 8, level-triggered,
+ =actions=PIXA3854:00= — the handler is correctly registered on the pin the
+ firmware names. Count is 0 across all 24 CPUs, including during active
+ swiping.
+- =dmesg=: =i2c_hid_acpi i2c-PIXA3854:00: device did not ack reset within 1000 ms=.
+ The i2c-HID reset handshake is acknowledged *by the device asserting the
+ interrupt*, so the first operation needing that line already failed at boot,
+ before anything touched the pad. That is why the fault reproduces on any boot
+ in ten seconds.
+- *Software ruled out by live USB.* Same "did not ack reset" message and no
+ pointer movement under Ubuntu's kernel (2026-08-15). Not a driver, not
+ libinput, not Hyprland, not this install.
+
+Three candidates remain, all needing a part or firmware:
+1. Open conductor on the touchpad's own cable or a bad contact at either end.
+ Framework sells "Touchpad Cable" as a discrete spare, so it is separately
+ replaceable — and the input-cover ribbon reseat would not have touched it.
+2. The touchpad module's interrupt output is dead while its i2c slave still
+ answers. Indistinguishable from 1 without swapping parts.
+3. Firmware naming the wrong GPIO. The DSDT says =amd_gpio= pin 8; if this
+ board revision routes the interrupt elsewhere, the kernel watches a pin that
+ never toggles. Plausible because the mainboard is days old to this machine
+ and its firmware already needed the PSR workaround. BIOS is 03.05
+ (2025-10-30); kernel 6.18.44-1-lts.
+
+*The connector that was reseated is NOT the touchpad's — confirmed from the
+board photo.* Craig reseated the 4-pin connector near the printed word
+=TOUCHPAD=. Its cable is silkscreened =PIN 1-2 - GND / PIN 3-4 - VCC= — four
+contacts, all of them power. No clock, no data, no interrupt; almost certainly
+the keyboard backlight feed. An i2c-HID touchpad cannot run through it, so that
+reseat could never have fixed this, and *the free retry remains untried*.
+Photo: [[file:working/velox-touchpad-interrupt/touchpad-module-underside-2026-08-15.jpg][working/velox-touchpad-interrupt/touchpad-module-underside-2026-08-15.jpg]].
+
+Visible on that board: the controller IC marked =PCT3854= (matching the kernel's
+=PIXA3854=), a larger =CON3= carrying a blue-backed ribbon with "26" marked
+beside it, a white ZIF past the Framework QR label, and a further connector at
+the board's end. The one that matters is whichever ribbon physically *leaves the
+input cover and reaches the mainboard* — that is the touchpad cable, and its far
+end is the press-fit connector at the board. Reseat both ends of that one before
+fitting any new part.
+
+*BIOS 04.02 exists but does not look relevant.* Checked 2026-08-15 with velox
+on AC at 90%: fwupd offers 0.0.3.5 → 0.0.4.2. Read the changelog — the only
+touchpad line is haptic-touchpad support for the Laptop 13 *Pro* chassis, and
+this machine has a conventional PixArt =PIXA3854=. The rest is BIOS Setup
+layout, option naming, TPM behavior, iGPU defaults, PMF slider. Nothing about
+GPIO routing or interrupt configuration. So candidate 3's cheap test is weaker
+than it looked when it was filed sight-unseen; still worth doing (unlisted
+fixes happen, and ACPI tables change), just no longer the front-runner.
+Deliberately deferred past the flight — a cleared NVRAM is the failure that
+started this whole rebuild. Boot-entry recovery reference captured at
+[[file:docs/2026-08-15-velox-uefi-boot-entry-reference.org][docs/2026-08-15-velox-uefi-boot-entry-reference.org]].
+
+Order of attack on return, cheapest first: reseat the touchpad's *own* press
+connector at the mainboard (free, untried) → BIOS 04.02 → fit the replacement
+touchpad. Craig's call 2026-08-15: order the parts now anyway, since they are
+worth holding as spares regardless of which candidate wins.
+
+*What to order.* The replacement *Touchpad* ships with the Touchpad Cable
+pre-installed, so that single part covers candidates 1 and 2 together — no need
+to buy both to cover both. A bare Touchpad Cable is worth adding only as a cheap
+spare. The *Input Cover* is a different and more expensive part, and nothing
+points at it: the keyboard works, so the input-cover ribbon is carrying signal.
+Framework's marketplace renders its catalogue in JavaScript, so prices could not
+be read programmatically — search "Touchpad" under Laptop 13 parts.
+
+*Also worth a Framework support ticket* — the touchpad died coincident with
+their mainboard swap, which may put it inside whatever recourse that carries.
+
+Grading: Major severity (a laptop's built-in pointer is entirely dead — the
+counter-argument is that an external mouse is a complete workaround, which
+would make it Minor; I took Major because losing the integrated pointer degrades
+the machine's portability, which is the whole point of the laptop) x every user,
+every time = P1 = [#A]. Filed [#B] rather than [#A] only because an [#A] must
+carry a date and Craig's return date isn't known yet — date it and raise it to
+[#A] when it is.
+
+Workaround in the meantime: Bluetooth mouse, already in use.
+** DONE [#C] hypridle.conf is generated per-machine but tracked :refactor:dotfiles:
+CLOSED: [2026-08-14 Fri]
+:PROPERTIES:
+:CREATED: [2026-08-14 Fri]
+:LAST_REVIEWED: 2026-08-14
+:END:
+Fixed in dotfiles 83ae7aa. The render is untracked and gitignored; the
+store is the only source of truth; hypridle-start renders at session start
+and owns the fallback ordering. Both machines reconciled and re-stowed:
+velox's tree is clean for the first time today and still carries its own
+policy (dim 5, lock 10, suspend-then-hibernate 30), ratio's config is
+byte-unchanged.
+=hyprland/.config/hypr/hypridle.conf= is rendered by the settings panel
+from each machine's own stage config, and it is also a tracked file stowed
+to every machine. So a machine whose idle policy differs from the
+committed default carries permanent working-tree dirt, and every pull
+there needs a stash/pop dance (velox, twice on 2026-08-14). Worse, the
+committed copy is whichever machine last committed it, which is how a
+desktop ended up tracking a laptop's suspend-then-hibernate line.
+Options to weigh: gitignore the rendered file and track only a template or
+the stage defaults; render to a non-stowed path and have hypridle read
+that; or keep it tracked but commit a machine-neutral render. The first
+looks right — the store already holds the real source of truth, and the
+rendered file is a build artifact.
diff --git a/todo.org b/todo.org
index d330a44..969c1da 100644
--- a/todo.org
+++ b/todo.org
@@ -46,6 +46,254 @@ below):
input-side-spec.org (DRAFT, four decisions open).
* Archsetup Open Work
+** TODO [#B] Swap velox's MT7925 for an Intel AX210 :chore:velox:hardware:
+:PROPERTIES:
+:CREATED: [2026-09-16 Wed]
+:LAST_REVIEWED: 2026-09-16
+:END:
+Ordered from the Framework Marketplace on 2026-09-16; waiting on delivery.
+An Intel AX210 will replace velox's MediaTek MT7925
+(RZ717, Filogic 360) in the M.2 2230 slot. The ordered part is AX210.NGWG.NV
+(the .NV suffix means no vPro). A vPro card won't work in the Framework, so
+check the part number on the card when it arrives.
+
+Why: HFP call audio on the MT7925 fails in firmware. It sends zero-filled SCO
+frames on sentinel handle 0x0E00, and the kernel logs "SCO packet for unknown
+connection handle 3584". It's pending upstream with no fix. Three headsets fail
+on velox. The MT7925 is also step 4 of the hibernate-freeze mitigations. Losing
+WiFi 7 is fine; the AX210 does WiFi 6E and BT 5.3. linux-firmware-intel is
+already installed on velox, and the installer's firmware trim only runs on
+Intel-CPU Framework 13s, so archsetup needs no change. See the Bluetooth wedge
+bug's 2026-09-16 entries.
+
+Before the swap: note the saved WiFi profiles (NetworkManager keeps them, they
+aren't tied to the card), and power off fully (not hibernate). Keep the MT7925
+in case the AX210 is a dud.
+
+After the swap, verify:
+- lspci -k shows the AX210 on iwlwifi; the Bluetooth controller enumerates as
+ Intel (btintel) and hci0 is up.
+- WiFi joins a saved network, and net status/probe/diagnose read it correctly.
+ The net code has an nl80211 path written for the mt7925, so check that the
+ signal line still shows.
+- Re-pair the Sonys and any other Bluetooth devices, since the bonds belong to
+ the old controller's address. Use the agent-backed pairing recipe in the KB,
+ then count key sections to confirm a real bond.
+- A real call load of several minutes in HFP, counting "Failure in Bluetooth
+ audio transport" and kernel "unknown connection handle" errors. Target zero.
+ Note the Intel risk: AX201/AX211 had an eSCO handle-reuse bug with WirePlumber
+ 0.5.17 after repeated profile switching.
+- Suspend/resume and one hibernate cycle with the new card.
+Then close the Bluetooth wedge bug, or regrade it, and update the MT7925 step
+in the hibernate mitigations.
+
+** DOING [#C] Net doctor takes the DNS ladder on a DoT-blocking captive portal :bug:dotfiles:network:
+:PROPERTIES:
+:CREATED: [2026-09-14 Mon]
+:LAST_REVIEWED: 2026-09-14
+:END:
+An open captive-portal network that filters TCP 853 before login, 2026-09-14.
+The network's resolver
+172.20.0.1 answers plain UDP 53, but TCP 853 is filtered until you log in. With
+resolved pinned to DNSOverTLS=yes, every lookup on the WiFi link times out.
+The probe read "no-internet" instead of "captive", and the doctor ran
+repair:dns-test (cleanup-unverified), then repair:dns-override (fail,
+reverted), twice (08:32, 08:35). It never offered portal-login. Craig had to
+tether to his phone. A dns-override to 1.1.1.1 can't pass a walled garden
+anyway. The same flow worked on a different portal on 09-13.
+
+Once DNS resolves (through the tether), the pinned probe sees the portal and
+diagnose recommends net portal.
+
+Also unexplained: the probe log reads "online" on wlp192s0 from 08:43 to
+09:50, then flaps captive/online until 09:52, while a pinned curl shows the
+portal still intercepting. Check whether the probe follows the default route
+(the tether) while labeling the WiFi interface.
+
+Remaining before close: a live doctor run on that network with the tether
+unplugged (the fix is committed and live on velox; ratio gets it on its next
+dotfiles pull).
+
+Follow-ups the review found in existing code (not in this fix):
+- repair.py _extract_portal_url (~603-619) rejects any URL containing a
+ detection-host name anywhere, query string included. This portal echoes the
+ requested URL in its OS= parameter, so the portal URL the probe found gets
+ thrown away, and repair_portal_login (~687) falls back to opening a trigger
+ page (neverssl.com). The login should still work through interception, but
+ the found URL is lost. Fix: match detection hosts against the hostname only,
+ or pass the probe's portal_url through.
+- probe.py CLOUDFLARE_HOSTS: a controller that hosts its login page on 1.1.1.1
+ itself (older Cisco wireless, https://1.1.1.1/login.html) reads as
+ reachable, in both the followed-redirect and body branches.
+
+Grading: Major severity (no path online on such a network without knowing to
+run net portal by hand; the doctor's own repairs can't succeed) × some users,
+sometimes (networks that filter 853 before login; another portal worked) =
+P3 = [#C].
+*** 2026-09-14 Mon @ 12:12:48 -0400 Found the root cause in the probe's IP fallback, fixed it test-first
+My first guess (extend the tunnel-dot shape test to WiFi) was wrong. With DNS
+dead, run_probe falls back to http://1.1.1.1/ over the WiFi. The portal answers that
+literal with HTTP 200 and a meta refresh, so curl's effective URL stays on
+1.1.1.1. classify_ip only checked the effective host, and called the page
+"reachable". So the probe said no-internet, diagnose emitted no portal row, and
+the classifier fell through to dns-test. The hostname path already reads
+body-level redirects; the IP path didn't.
+
+Fix in dotfiles net/src/net/probe.py: classify_ip reads the body with
+extract_portal_url and calls it captive when the target host isn't Cloudflare.
+Tests in tests/net/test_net.py, all using a placeholder-scrubbed portal
+fixture: classify_ip normal, boundary, and error cases (Cloudflare body
+redirect, v6 literal, refresh with no URL, javascript: target, followed
+redirect wins); probe_and_cache end to end; diagnose emitting the portal row;
+doctor --fix running portal-login instead of dns-test/dns-override. Red with
+four failures, green at 991. The live IP probe over that network now
+classifies it captive and finds the portal URL.
+*** 2026-09-14 Mon @ 12:22:23 -0400 Committed and pushed the fix as dotfiles b2688e4
+An isolated review approved it. I added its two Minor test gaps (JS redirect,
+malformed target) and made the comments vendor-generic. Full make test: forked
+and shared runs each 4401 OK, faces 171 pass. I sent an FYI to the dotfiles
+inbox. The net CLI runs from the repo, so velox has the fix now.
+
+** TODO [#B] Bluetooth audio link drops wedge the PipeWire graph on velox :bug:velox:audio:
+:PROPERTIES:
+:CREATED: [2026-09-14 Mon]
+:LAST_REVIEWED: 2026-09-14
+:END:
+First seen 2026-09-14 with the Jabra Speak2 55 MS on velox.
+There are two faults, and the second makes the first much worse.
+
+1. The link drops. "Failure in Bluetooth audio transport" hit six times between
+ 10:09 and 11:10: on A2DP (sep1/fd0) and HFP (fd60), at 10:09, three times
+ around 10:10, 10:54, and 11:10. Each one matches a kernel "Bluetooth: hci0:
+ ACL (or SCO) packet for unknown connection handle" line. The controller is
+ the MediaTek MT7925 (0e8d:7925, btusb; WiFi firmware build 20260813).
+ Suspects I haven't separated yet: MT7925 btusb firmware or driver, range or
+ 2.4 GHz coexistence with the WiFi on the same chip, or A2DP/HFP profile
+ switching when the mic opens.
+2. The graph wedges. After the drops, the Jabra's nodes sat in error and every
+ client round trip hung: pactl info, wpctl status/inspect, pw-dump, and the
+ mic-mute key (four stuck wpctl set-mute processes). pw-cli info 0 still
+ answered, and no thread was spinning. Only a restart of pipewire,
+ pipewire-pulse, and wireplumber cleared it. Versions: pipewire 1.6.8,
+ wireplumber 0.5.17, bluez 5.87, kernel 6.18 LTS.
+
+Grading: Major severity (all audio control is dead until a manual service
+restart; the restart is the only workaround) × most users, frequently (six
+drops in about an hour of use on the one Bluetooth speaker in play) = P2 =
+[#B]. One session of data; re-read the frequency row if it doesn't recur.
+
+Next: on a recurrence, capture btmon and the kernel log across a drop, and
+check whether the wedge follows only HFP use. Try the same speaker on ratio to
+split controller from device. Look upstream for MT7925 "unknown connection
+handle" reports and for wireplumber bluez nodes stuck in error.
+
+*** 2026-09-15 Tue @ 14:12:11 -0400 Reproduced the link drop with a second speaker, a Speak2 75
+The replacement Jabra Speak2 75 dropped the same way while
+paired straight to velox's MT7925: "Failure in Bluetooth audio transport" at
+12:47:20 (A2DP, sep3/fd0) and 12:47:49 (HFP, fd62), with a kernel "hci0: ACL
+packet for unknown connection handle" line at 12:47:21. The graph didn't wedge
+this time; pactl, wpctl, and pw-dump all kept answering. A second device with
+the same signature points further toward the controller side than the speaker.
+I removed velox's pairings for both Jabras, and the 75 now runs over its USB
+cable (0b0e:24ef), which works for playback and mic. Its Link 390 dongle
+(0b0e:2e56) enumerates but was never linked to the speaker, and pairing one
+needs Jabra Direct (Windows/Mac only).
+*** 2026-09-16 Wed @ 13:29:34 -0400 A pairing that never bonded looks like this bug, so rule it out first
+A one-shot bluetoothctl pair, with the adapter at Pairable: no (velox's normal
+state), can leave a bond file with no key section. It shows up as transport and
+AVDTP failures and the device dropping the link. The 09-15 log records the
+Speak2 75 as Bonded: yes, so this doesn't explain the 75's drops. The 55 MS's
+bond state was never checked, and its pairing is gone now. On a recurrence,
+count the device's key sections first (the one-liner in the node).
+[[id:f23b7085-c35e-43c1-ae02-9e9c67e3848b][bluetoothctl one-shot pair can complete without bonding]]
+*** 2026-09-16 Wed @ 13:43:40 -0400 Third device, same kernel handle errors; pairing and settling ruled out
+The Sony WF-1000XM6 (bonded, one LinkKey) failed the same way today, and the
+evidence now points at the MT7925's SCO path.
+
+Work's session: transport failures from 10:44 to 10:50, about 90 clean seconds,
+then six more in two minutes (11:01 to 11:04) during a Meet call, with the buds
+beeping on each disconnect. The kernel logged 23 "SCO packet for unknown
+connection handle" and 38 "ACL packet for unknown connection handle" errors in
+exactly those two windows. mpv played over A2DP for twenty minutes with no
+failures. Every failure came after the card switched to a headset profile. SCO
+carries HFP voice. In work's run, a 35 s full-duplex hold of each HFP codec
+(CVSD, mSBC, LC3-SWB) passed with zero failures while real calls failed. The
+earlier "link settling after a fresh pair" reading was wrong: the quiet stretch
+was just quiet.
+
+Mine, from 13:23: the card churned between profiles for six minutes, then
+pipewire-pulse refused about 200 connections as "too many client application
+connections" (13:30:07 to 13:30:10). pactl, wpctl, and pw-cli info 0 all hung,
+the same wedge as 09-14, and restarting the three user units cleared it. After
+the restart, a 20 s LC3-SWB hold failed: the mic was pure digital zero, both
+bluez nodes went to error, and the kernel logged SCO and ACL handle errors.
+10 s holds of CVSD and mSBC passed. Then at 13:40:00, :17, and :22, three
+headset-profile switches failed the same way while mpv played, each time an
+app opened the mic. WirePlumber then had LC3-SWB saved as the headset profile.
+
+What this rules out: a bad bond (the Sonys are bonded), settling (the failures
+came back under load). The codec isn't ruled out. The only failure with a
+known codec was LC3-SWB (which passed work's hold), CVSD and mSBC have only
+passes on record, and the codec in play during the three 13:40 failures wasn't
+captured. Three devices share the signature, and the handle errors come from
+the controller.
+
+Still untested: WiFi/Bluetooth coexistence on the shared MT7925. velox was on a
+busy shared WiFi network through every failure. Next test: a real call load
+with WiFi off and the network over a USB tether, counting transport failures
+and kernel handle errors. Then a btmon capture across a failure.
+
+Workaround until then: take calls on the Speak2 75 over USB. With autoswitch on,
+any app that opens a mic pulls Bluetooth music into HFP and hits the fault.
+[[id:eaa85ba0-ad4f-4a38-a27f-c39b879c341f][Measure Bluetooth audio dropouts across the load that fails, not a quiet window]]
+*** 2026-09-16 Wed @ 14:42:50 -0400 Codec-filter experiment and a saved-profile trap
+Work tried pinning the call codec at about 14:30 with Craig's go-ahead: it left
+lc3_swb out of monitor.bluez.properties bluez5.codecs and restarted wireplumber.
+The filter does reach HFP. LC3-SWB disappeared, and headset-head-unit became
+MSBC. But every A2DP profile, AAC included, vanished too, even though all the
+A2DP codec names were listed. Work reverted within a minute and all six profiles
+came back. It's unclear whether the config dropped A2DP or the three-second wait
+was just too short for re-enumeration. If codec pinning is still wanted after
+the AX210 swap, retest on a day without calls and wait longer before judging.
+The wireplumber.conf.d directory is empty now, so the revert is on disk.
+
+The saved-profile trap: WirePlumber saves profiles by name, and the name
+headset-head-unit maps to whichever codec is best at load time (LC3-SWB with
+the default config, MSBC under the filter). At 14:42 both the card's
+default-profile and saved-headset-profile read headset-head-unit, so calls
+autoswitch to LC3-SWB. And because default-profile is a headset profile, the
+card may come back in HFP (call-quality music) after a reconnect or a
+wireplumber restart, until something switches it to a2dp-sink. Nothing changed:
+work asked for a hold on velox audio for the rest of 09-16.
+*** 2026-09-16 Wed @ 15:08:18 -0400 Matched to an upstream MT7925 firmware report; fix is a new card
+The kernel's "SCO packet for unknown connection handle 3584" is handle 0x0E00,
+the sentinel handle in an August 2026 linux-bluetooth report: "MT7925
+(0e8d:0717): HFP microphone unusable — firmware delivers zero-filled (e)SCO
+frames on sentinel handle 0x0E00" (bluez/bluetooth-next PR #744). It's the same
+chip under a different USB ID; velox enumerates as 0e8d:7925. That report
+used PipeWire 1.6.8 and MT7925 BT firmware from 20260622 and 20260810, and its
+frames were all zeros, matching the zero-filled LC3-SWB mic hold here. The
+reporter traced it to the firmware's transparent-mode receive path. A
+handle-rewrite patch only proved the payload is zeros. There's no maintainer
+reply, nothing merged, and no fix. The reporter also found it depends on the
+headset. velox at the time: kernel 6.18.51-lts, linux-firmware-mediatek
+20260910, BT firmware built 20260813.
+
+A related report on Intel AX201/AX211 shows handle reuse after repeated
+A2DP/HFP switching with WirePlumber 0.5.17 (velox's version), and the bluetooth
+maintainer called that a firmware bug too. Autoswitch churn makes these faults
+more likely.
+
+So the leading explanation is MediaTek firmware, not the headsets and not our
+config. Coexistence stays untested, but the upstream match means the WiFi-off
+test no longer decides the fix. The fix is replacing the card: an Intel AX210
+is ordered (see the swap task at the top of Open Work). The dotfiles net code
+also calls ratio's card an mt7925, so testing on ratio wouldn't separate
+controller from headset. That's unconfirmed, because ratio didn't answer ssh.
+
+Once the AX210 is in, re-run the call-load test there. Close this bug if it's
+clean, or regrade it if Intel shows the same signature.
+
** TODO [#B] Visual separator between adjacent waybar modules :feature:waybar:dotfiles:quick:
:PROPERTIES:
:CREATED: [2026-09-13 Sun]
@@ -3466,144 +3714,6 @@ The interim shield (=HandlePowerKey=ignore= in
=/etc/systemd/logind.conf.d/powerkey.conf=) is still live; I'm leaving it in
place, since a phantom press with it on costs nothing and without it costs
the session.
-** DONE [#B] Velox touchpad interrupt line is dead — needs a part or a BIOS fix :bug:velox:hardware:
-CLOSED: [2026-08-15 Sat]
-:PROPERTIES:
-:CREATED: [2026-08-15 Sat]
-:LAST_REVIEWED: 2026-08-15
-:END:
-*Fixed 2026-08-15 23:05 by reseating the correct connector* — a seating fault
-all along, no part needed. Verified at the kernel level on the 23:05 boot: the
-=did not ack reset within 1000 ms= message is gone (clean handshake), and the
-interrupt count went 0 → 1795. Power-key events also zero, so both faults from
-the mainboard swap are closed.
-
-What made this take three attempts is worth keeping: two of the connectors on
-that board were decoys. The input-cover ribbon looked like the obvious suspect
-and fixing it *did* resolve the power button, which made it look like the whole
-answer. Then the 4-pin connector next to the printed =TOUCHPAD= label looked
-like the touchpad's own — and its cable is silkscreened =PIN 1-2 - GND /
-PIN 3-4 - VCC=, four contacts of pure power, incapable of carrying i2c or an
-interrupt. Reading that silkscreen off the photo is what ruled it out and sent
-the search to the ribbon that actually crosses to the mainboard.
-
-The ordered touchpad becomes a spare, which is what Craig wanted from it anyway.
-The diagnostic path below is left intact — it is the reusable part: =dmesg=
-for the i2c-HID reset message and the interrupt count in =/proc/interrupts=
-together separate "device absent" from "device present but its interrupt line is
-open", and a live USB separates hardware from software in two minutes.
-Split from the ribbon-reseat task 2026-08-15 once the reseat fixed the power
-button and left this untouched — they are two faults, not one.
-
-*Diagnosed to the interrupt line specifically, with software eliminated.*
-- The i2c *data* path works. =i2c_hid_acpi= read the HID descriptor, returned
- the right product ID (=093A:0274=), =hid-multitouch= bound, and input6/7/8/9
- were created. A descriptor read is a real bus transaction, so the device is
- electrically present and answering.
-- The *interrupt* path never fires. IRQ 81, =amd_gpio= hwirq 8, level-triggered,
- =actions=PIXA3854:00= — the handler is correctly registered on the pin the
- firmware names. Count is 0 across all 24 CPUs, including during active
- swiping.
-- =dmesg=: =i2c_hid_acpi i2c-PIXA3854:00: device did not ack reset within 1000 ms=.
- The i2c-HID reset handshake is acknowledged *by the device asserting the
- interrupt*, so the first operation needing that line already failed at boot,
- before anything touched the pad. That is why the fault reproduces on any boot
- in ten seconds.
-- *Software ruled out by live USB.* Same "did not ack reset" message and no
- pointer movement under Ubuntu's kernel (2026-08-15). Not a driver, not
- libinput, not Hyprland, not this install.
-
-Three candidates remain, all needing a part or firmware:
-1. Open conductor on the touchpad's own cable or a bad contact at either end.
- Framework sells "Touchpad Cable" as a discrete spare, so it is separately
- replaceable — and the input-cover ribbon reseat would not have touched it.
-2. The touchpad module's interrupt output is dead while its i2c slave still
- answers. Indistinguishable from 1 without swapping parts.
-3. Firmware naming the wrong GPIO. The DSDT says =amd_gpio= pin 8; if this
- board revision routes the interrupt elsewhere, the kernel watches a pin that
- never toggles. Plausible because the mainboard is days old to this machine
- and its firmware already needed the PSR workaround. BIOS is 03.05
- (2025-10-30); kernel 6.18.44-1-lts.
-
-*The connector that was reseated is NOT the touchpad's — confirmed from the
-board photo.* Craig reseated the 4-pin connector near the printed word
-=TOUCHPAD=. Its cable is silkscreened =PIN 1-2 - GND / PIN 3-4 - VCC= — four
-contacts, all of them power. No clock, no data, no interrupt; almost certainly
-the keyboard backlight feed. An i2c-HID touchpad cannot run through it, so that
-reseat could never have fixed this, and *the free retry remains untried*.
-Photo: [[file:working/velox-touchpad-interrupt/touchpad-module-underside-2026-08-15.jpg][working/velox-touchpad-interrupt/touchpad-module-underside-2026-08-15.jpg]].
-
-Visible on that board: the controller IC marked =PCT3854= (matching the kernel's
-=PIXA3854=), a larger =CON3= carrying a blue-backed ribbon with "26" marked
-beside it, a white ZIF past the Framework QR label, and a further connector at
-the board's end. The one that matters is whichever ribbon physically *leaves the
-input cover and reaches the mainboard* — that is the touchpad cable, and its far
-end is the press-fit connector at the board. Reseat both ends of that one before
-fitting any new part.
-
-*BIOS 04.02 exists but does not look relevant.* Checked 2026-08-15 with velox
-on AC at 90%: fwupd offers 0.0.3.5 → 0.0.4.2. Read the changelog — the only
-touchpad line is haptic-touchpad support for the Laptop 13 *Pro* chassis, and
-this machine has a conventional PixArt =PIXA3854=. The rest is BIOS Setup
-layout, option naming, TPM behavior, iGPU defaults, PMF slider. Nothing about
-GPIO routing or interrupt configuration. So candidate 3's cheap test is weaker
-than it looked when it was filed sight-unseen; still worth doing (unlisted
-fixes happen, and ACPI tables change), just no longer the front-runner.
-Deliberately deferred past the flight — a cleared NVRAM is the failure that
-started this whole rebuild. Boot-entry recovery reference captured at
-[[file:docs/2026-08-15-velox-uefi-boot-entry-reference.org][docs/2026-08-15-velox-uefi-boot-entry-reference.org]].
-
-Order of attack on return, cheapest first: reseat the touchpad's *own* press
-connector at the mainboard (free, untried) → BIOS 04.02 → fit the replacement
-touchpad. Craig's call 2026-08-15: order the parts now anyway, since they are
-worth holding as spares regardless of which candidate wins.
-
-*What to order.* The replacement *Touchpad* ships with the Touchpad Cable
-pre-installed, so that single part covers candidates 1 and 2 together — no need
-to buy both to cover both. A bare Touchpad Cable is worth adding only as a cheap
-spare. The *Input Cover* is a different and more expensive part, and nothing
-points at it: the keyboard works, so the input-cover ribbon is carrying signal.
-Framework's marketplace renders its catalogue in JavaScript, so prices could not
-be read programmatically — search "Touchpad" under Laptop 13 parts.
-
-*Also worth a Framework support ticket* — the touchpad died coincident with
-their mainboard swap, which may put it inside whatever recourse that carries.
-
-Grading: Major severity (a laptop's built-in pointer is entirely dead — the
-counter-argument is that an external mouse is a complete workaround, which
-would make it Minor; I took Major because losing the integrated pointer degrades
-the machine's portability, which is the whole point of the laptop) x every user,
-every time = P1 = [#A]. Filed [#B] rather than [#A] only because an [#A] must
-carry a date and Craig's return date isn't known yet — date it and raise it to
-[#A] when it is.
-
-Workaround in the meantime: Bluetooth mouse, already in use.
-
-
-** DONE [#C] hypridle.conf is generated per-machine but tracked :refactor:dotfiles:
-CLOSED: [2026-08-14 Fri]
-:PROPERTIES:
-:CREATED: [2026-08-14 Fri]
-:LAST_REVIEWED: 2026-08-14
-:END:
-Fixed in dotfiles 83ae7aa. The render is untracked and gitignored; the
-store is the only source of truth; hypridle-start renders at session start
-and owns the fallback ordering. Both machines reconciled and re-stowed:
-velox's tree is clean for the first time today and still carries its own
-policy (dim 5, lock 10, suspend-then-hibernate 30), ratio's config is
-byte-unchanged.
-=hyprland/.config/hypr/hypridle.conf= is rendered by the settings panel
-from each machine's own stage config, and it is also a tracked file stowed
-to every machine. So a machine whose idle policy differs from the
-committed default carries permanent working-tree dirt, and every pull
-there needs a stash/pop dance (velox, twice on 2026-08-14). Worse, the
-committed copy is whichever machine last committed it, which is how a
-desktop ended up tracking a laptop's suspend-then-hibernate line.
-Options to weigh: gitignore the rendered file and track only a template or
-the stage defaults; render to a non-stowed path and have hypridle read
-that; or keep it tracked but commit a machine-neutral render. The first
-looks right — the store already holds the real source of truth, and the
-rendered file is a build artifact.
** CANCELLED [#B] agent-text relay reports success for a message that went nowhere :bug:
CLOSED: [2026-08-19 Wed]
:PROPERTIES: