Appendix · recipe

Finishing plan

The next bench sequence when honor and the phone are reachable again.

This page is the handoff checklist for the current port state. It exists so the next hardware session starts from known facts instead of rediscovering them from terminal scrollback.

Current known state

That leaves one immediate hypothesis: the SDIO card interrupt is armed in CCCR and unmasked in DWMMC, but the card clock is being low-power gated while the bus is idle. The next code patch is 5c4b836 Keep DWMMC clock running for SDIO IRQ , which clears DWMMC CLKENA_LP while SDIO IRQ mode is enabled and records the resulting clock state in the passive sdio_state sysctl.

Next hardware session

Do not start with a transfer. First prove the new kernel boots and that the WiFi module can attach without making WiFi persistent.

╞═ build and deploy #124 with WiFi disabled ═╡
mise run patch
mise run build-kernel
mise run build-module -- bwfm_sdio

mise run kernel:deploy:phone
mise run module:deploy:phone -- bwfm_sdio

ssh pinephone 'sudo sysrc bwfm_sdio_enable=NO'
ssh pinephone 'sudo reboot' || true

After USB networking returns:

╞═ verify the safe boot ═╡
ssh pinephone '
  uname -a
  sha256 -q /boot/kernel/kernel
  sha256 -q /boot/kernel/bwfm_sdio.ko
  sysrc -n bwfm_sdio_enable
  kldstat -v | egrep "bwfm|sdiob|dwmmc" || true
'

Expected: new kernel hash, new module hash, bwfm_sdio_enable=NO, and no loaded bwfm_sdio module.

WiFi IRQ test

Start serial capture before loading WiFi. The serial log is the durable record if the phone panics before USB SSH returns.

╞═ attach WiFi once and inspect IRQ state ═╡
mise run serial:capture:daemon -- wifi-sdio-irq

timeout 45 ssh pinephone '
  date
  sudo service bwfm_sdio onestart
  date
  sysctl debug.bwfm_state 2>&1 || true
  for oid in $(sysctl -aN | awk "/dev\\.rockchip_dwmmc\\.[0-9]+\\.sdio_state|dev\\.sdiob\\.[0-9]+\\.irq_state/"); do
    sysctl "$oid"
  done
  ifconfig wlan0 2>/dev/null || true
'

Expected after 5c4b836 Keep DWMMC clock running for SDIO IRQ :

If attach fails or the phone panics here, stop. Do not run a transfer. Use the serial log plus kernel.prev recovery.

Transfer matrix

Only run transfers after the attach state is clean.

╞═ small transfer with passive debug polling ═╡
ssh pinephone 'sudo service netif start wlan0; ifconfig wlan0'

SIZE_MIB=2 \
POLL_SECS=5 \
DEBUG_TIMEOUT=5 \
TRANSFER_TIMEOUT=120 \
DUMP_TRACE=0 \
mise run debug:wifi:transfer -- both sdio-irq-clock-fix-small
╞═ poll fallback rate matrix ═╡
# Defaults: MATRIX_RATES="100 50 20 10", MATRIX_SIZE_MIBS="2 8".
# The script restores dev.bwfm_sdio.0.poll_fallback_hz when it finishes.
POLL_SECS=5 \
DEBUG_TIMEOUT=5 \
TRANSFER_TIMEOUT=120 \
DUMP_TRACE=0 \
mise run debug:wifi:fallback-matrix -- both poll-fallback

The transfer harness now emits the same WiFi receipt block before, during, and after each leg: debug.bwfm_state, debug.bwfm_fw_state, DWMMC sdio_state and sdio_intrs, sdiob irq_state, the bwfm_sdio power-save/fallback-rate sysctls, ifconfig, and netstat -I wlan0 -b. mise run debug:wifi:phone -- <name> emits the same block for one-shot state captures. Both scripts now append mise run debug:wifi:analyze output to the log so the first screen of the receipt says whether this was irq-active-watchdog, irq-armed-poll-fallback, poll-only, or an insufficient legacy capture.

Decision points:

Poll fallback plan

This was the contingency plan while the host delivered zero SDIO function interrupts. It is no longer the primary path after c312250 dwmmc: use Rockchip SDIO interrupt bit , and it should not be used to hide a clock regression after 8c61d6d sdiob: enable SDIO run clock by default :

WiFi polish plan

The throughput blocker is closed enough to move to policy and coverage:

The fallback can be acceptable for recovery if it is predictable, bounded, and named correctly. It should not replace a working IRQ path.

Bluetooth audio reconfirmation

After WiFi is either fixed or explicitly demoted to fallback mode, rerun A2DP as a clean confidence test. The recent bench session proved on-device speaker audio again; it did not re-prove Bluetooth speaker output.

╞═ A2DP-only confidence check ═╡
# Pick the paired speaker's bdaddr from /dev/bluetooth or the pairing log.
ssh pinephone 'ls /dev/bluetooth 2>/dev/null || true'

# Start the existing A2DP script/tooling against that peer.
# Keep /dev/dsp0 / RT5640 playback out of the test so loudspeaker output
# cannot be mistaken for A2DP.

Pass condition: audible output from the external Bluetooth speaker, with the phone loudspeaker silent.

After WiFi and A2DP

The next subsystem arc is still modem:

Sensors and UI policy remain bounded follow-ups: proximity screen blanking, orientation policy, LED patterns, and headphone/microphone routing.