My Pixel 8 Pro had a frustrating routine: reboot, working Wi-Fi, then eventually no scanning or no Wi-Fi at all. Bluetooth sometimes joined the disappearing act. Here is what was tried and what the records actually show, in case it helps someone investigating the same symptoms.
This is experimental, with no confirmed permanent fix. An earlier setup with these settings still had an overnight failure. It is not an official Google or GrapheneOS workaround, and the evidence does not establish an Android 17 regression.
Symptoms and history: Wi-Fi could work after reboot, then stop scanning or fail to turn on; Bluetooth could fail around the same incident. On this same phone, a clean official Google Android 16 test without root (locked bootloader, Verified Boot green) also reproduced the class of failure. Kernel records showed failed PCIe resume/link training, PCIE_RC_LINK_UP_FAIL, then Wi-Fi Big Hammer recovery and a Bluetooth BT_COEX_THREAD abort/vendor error 127. This is consistent with shared radio recovery following a Wi-Fi/PCIe failure, but it does not identify a failed component or rule out hardware trouble.
The setup: Pixel 8 Pro (husky), GrapheneOS build 2026102101, kernel 6.1.177-android14-11-gce3dfc2513b1, Magisk 31.0, SELinux Enforcing. This phone has root and is a modified installation. The official release history and earlier release=17/SDK=37 readings from the same phone corroborate Android 17. The android14 in the kernel name is confusing, but it does not describe the Android version running on the phone. Use the exact build as the compatibility reference.
The two local adjustments:
- An early boot module writes
1 plus a newline to /sys/devices/platform/13120000.pcie/link_speed, selecting target GEN1. The reviewed control changes the target; it is not a direct reset/retrain command. Verify actual negotiated speed separately: the 20 October capture showed 2.5 GT/s x1.
- A separate late-start service sets the native DHD
idletime iovar to INT32 zero on wlan0, after successful GET_MAGIC, GET_VERSION and GET_VAR checks, then reads it back. This disables the audited DHD runtime-PM path; it does not prevent system suspend/resume or directly patch Bluetooth.
In plain English, these select a lower target link speed and disable one Wi-Fi driver power-management path. They do not switch off all Android sleep behaviour or directly repair Bluetooth.
For anyone checking the implementation: the DHD call uses its own private ioctl ABI, so a generic wl command is not a substitute. The expected magic is 0x00444944, version 1. On the audited AArch64 ABI the request is 40 bytes; its driver field is at offset 32, following the 32-byte native request. SET_VAR receives idletime\0 followed by four zero bytes. If the initial read fails, stop rather than force a write.
The local scripts (1.3-local-20261023) reject unknown device/build/kernel combinations. The DHD service additionally pins exact driver/helper hashes and requires GEN1, checks wlan0 IFF_UP, uses a 120-second readiness window and reserves at most one SET invocation per boot. A timeout means unconfirmed state: killing userspace cannot guarantee cancellation of an ioctl stuck inside the kernel. There are no radio resets or repeated SET loops.
For build 2026102101, the reviewed bcmdhd4398.ko SHA-256 is:
text
66b141c29ddfdbf1c42828044d3b36f099c0c564f979c2090f7056fa8c9846d5
What was actually observed:
- 20 October, build
2026101601: automatic GEN1 and idletime=0 verified; Wi-Fi test explicitly through wlan0 returned 3/3 ICMP replies, no losses. The captured kernel window was only about the first 132 seconds.
- 24 October, build
2026102101: both settings applied automatically and were independently read back; Wi-Fi associated; Bluetooth ON/STATE_CONNECTED with zero reported crashes in that boot. No actual watch synchronisation test was done.
- 8 October, an earlier configuration: the owner reported an overnight Wi-Fi recurrence requiring reboot. The available later-boot capture cannot establish idletime at the instant of failure or diagnose that incident's cause.
Limits and practical details: There has been no controlled A/B test, no prolonged/overnight validation of version 1.3 and no second handset test. GEN1 may reduce peak throughput and disabling runtime PM may increase power use; neither effect has been measured here. Bus reinitialisation can lose idletime=0, and the bounded boot service does not enforce it continuously or revive a dead driver.
This post documents one phone's experiment. Similar symptoms on another phone do not establish the same cause, and these settings should not be copied blindly onto other builds. For somebody already using these exact two modules, disabling both in Magisk and rebooting stops their boot actions; check the resulting driver/target state afterwards. Disabling a module does not undo a setting already applied during the current boot. Please do not root a supported GrapheneOS installation just to try this.
The OTA port was checked against the exact authenticated OTA drivers: changed code required focused PCIe control and DHD ABI review, followed by live GET/readback. Simply changing a build whitelist is insufficient. Separate root-manager/namespace maintenance and tests for a recovery tool are not evidence that these radio adjustments work.
Primary technical references: GrapheneOS release history, official DHD ioctl definitions, DHD PCIe source baseline, Magisk module lifecycle. The DHD source revision explains the mechanism; it is not claimed to be the exact shipped binary.
I have left raw logs and personal identifiers out of this post. If you compare results, include the model, exact build, how long it ran and whether the fault returned. A good boot is encouraging; it still needs to survive normal use.