The previous essay catalogued where we are. This one catalogs where we’re going. Some of these sections are weeks of work; others are months. A couple are pull-requests-from-anyone-with-the-hardware. None of them are speculative — every one has been read into deep enough to know roughly what the shape is.
A side-by-side audit of bwfm/dwmmc, rk_vop, fusb302+rk818, and rt5640 against their Linux mainline (and where applicable OpenBSD) references landed on 2026-04-30 — see appendix: cross-driver audit for the prioritized punch list with file:line citations. The first code pass for the three open wedges (rk_vop modeset-lock, fusb302 PE_DISABLED after Hard_Reset, intermittent bwfm weirdness) is now in the overlay; this essay describes the remaining bench arcs and the audit tracks the driver-level predicates.
WiFi: BCM43455 over SDIO — ◐ partial
WiFi is the current finishing target. The AP6255 module’s Broadcom BCM43455 side scans, associates to a WPA2/PSK AP, completes the four-way handshake through wpa_supplicant, installs PTK/GTK, and gets DHCP on wlan0 at boot. Verified live on 2026-04-28: wlan0 associated to the home AP, inet 192.168.1.142, parent bwfm_sdio0, AES-CCM, 11g.
We have a complete native bwfm(4) driver ported from OpenBSD. It loads, enumerates the chip (rev 6, rambase 0x198000, ramsize 800 KB), downloads firmware and the PPP-specific NVRAM, runs the ARM core reset sequence with readback verification, loads the Broadcom CLM blob, hands scan results to FreeBSD net80211 with real RX channel metadata, mirrors firmware join events into the synthetic node state, advertises 802.3 encapsulation for FullMAC data frames, and passes the selected RSN IE to firmware with the Broadcom wpaie iovar. 871c39d Mark firmware-associated bwfm nodes 066702e Use 802.3 transmit encapsulation for bwfm 2862f34 Pass WPA IE to bwfm firmware 9e5446a Gate bwfm diagnostics
That is the normal boot path: bwfm_sdio loads before netif, wlans_bwfm_sdio0="wlan0" creates the VAP, ifconfig_wlan0="WPA SYNCDHCP" starts wpa_supplicant and waits for DHCP, and mise run wifi:boot:phone can reinstall that plumbing without restarting all of netif. Once that task runs, WiFi owns the default route; USB networking remains a direct management link, not the phone’s internet path.
The earlier IRQ work has 5c4b836 Keep DWMMC clock running for SDIO IRQ , which changes the DWMMC host behavior when SDIO IRQ mode is enabled:
- track
sdio_intr_enabledin the DWMMC softc - clear
CLKENA_LPwhile an SDIO function IRQ is armed - issue the update-clock command after changing
CLKENA - expose
clkena, clock-update count, and clock-update error through the passivesdio_statesysctl
That patch exists because DWMMC’s low-power clock mode can stop the card clock while the bus is idle. SDIO function interrupts are delivered over the SDIO card interrupt signal; if the clock is gated, the host may never see the pending interrupt even though CCCR interrupt enable is set correctly.
Work log: 2026-04-29 TX stability pass
The first transfer harness runs did exactly what they were supposed to do: turn “WiFi works” into a repeatable stress path. mise run debug:wifi:transfer phone-to-host crash-repro streams a bounded dd over WiFi while the host keeps polling wlan0, netstat -I wlan0 -n -b, and passive driver state over USB. Early versions also hit live dump sysctls from the debug path, which was the wrong shape for a crash-prone bus path: one run wedged while querying debug.bwfm_state, and another drowned the framebuffer console in DRM panic debris. 4551918 Avoid SDIO transactions in debug sysctls made the state sysctls cached/passive so the test harness can observe without issuing new SDIO transactions. The early transfer runs at 0a3d215 Add WiFi transfer debug harness / eaef93a Fix WiFi transfer harness quoting and the bounce-buffer guard at 4ea189c Harden bwfm SDIO bounce buffer bounds still wedged during a 2 MiB phone-to-host transfer: USB SSH stopped answering, WiFi ping failed, and the transfer task died with rc=143 after 67-82 seconds.
The ownership bug was in the TX handoff between net80211, bwfm(4), and the SDIO bus backend. f606fc5 Fix bwfm net80211 TX mbuf ownership made the driver treat a submitted TX mbuf as owned by the firmware/SDIO completion path, not by the caller that had already handed it off. edea1e8 Improve bwfm TX queue accounting then separated queued, in-flight, and completed TX accounting so the driver stops reporting every busy-but-progressing packet as an output error. 6a4965e Use net80211 TX completion helper replaced the local completion shim with ieee80211_tx_complete(), which is the right net80211 contract for returning a TX frame and status to the stack. 3ae7795 Defer bwfm firmware key deletes moved firmware key deletes out of the immediate net80211 key callback path; the driver now defers the delete work so the firmware command does not run in a context that can deadlock the rest of the radio path.
The proof point is logs/wifi-transfer/20260429-164032-tx-complete-keydelete-fix.log, from head 3ae7795. The same 2 MiB phone-to-host transfer completed with rc=0 in 49 seconds, post-transfer WiFi ping returned 3/3 packets, Ierrs stayed at 0, and Oerrs stayed flat at 2 while Opkts advanced from 17 to 1527. That is not a throughput result; it is a stability result. The previous successful run at f606fc5 completed but inflated Oerrs from 2 to 433, which is why the accounting pass mattered.
Current investigation: the radio is stable enough to move from crash-finding to polish. The RK3399 SDIO interrupt path can deliver function interrupts after the bit-24 mask fix; the low-rate watchdog remains as a safety net, not the primary notification path. The throughput culprit turned out to be below bwfm(4): the SDIO card clock was left at identification speed (800000) until the SDIO bridge explicitly requested a post-discovery run clock.
Work log: 2026-04-29 SDIO IRQ enablement
After the TX pass landed, the next series of commits prepared the SDIO interrupt path itself rather than the bwfm side. 916fe51 Add SDIO IRQ support for bwfm wires bwfm into the SDIO function-IRQ API: claim function 1, write CCCR interrupt enable, and route the IRQ callback into the existing watchdog task. 66f6174 Fix dwmmc DMA interrupt handling with SDIO IRQ fixes the matching DWMMC side — the host’s DMA interrupt handling was racing with the SDIO function IRQ when both were unmasked, so the card-IRQ bit was being eaten before the bus driver could see it. db845c8 Keep bwfm poll fallback during IRQ bringup intentionally keeps the poll fallback armed during IRQ bringup, so a missed IRQ degrades to “slow but alive” instead of “wedged.”
The next two commits were observational, not functional. 3a9470a Instrument SDIO IRQ bringup state + c907a95 Add passive WiFi IRQ state sysctls add dev.sdiob.*.irq_state and the passive dev.rockchip_dwmmc.*.sdio_state sysctls so the harness can ask “is function 1 claimed, is CCCR enabled, what’s the host intmask, is the card clock gated” without issuing SDIO transactions. fe0d986 Make WiFi transfer trace dumps opt-in + 4551918 Avoid SDIO transactions in debug sysctls + 1dc2f2a Bound WiFi transfer debug snapshots made the harness’s debug snapshots opt-in (DUMP_TRACE=0 by default) and bounded the snapshot size, so an idle observer can no longer drag the bus into a wedge.
The first 2026-05-05 bench pass proved that CLKENA_LP was not the whole
story. The decisive mismatch was the SDIO interrupt bit: Rockchip DWMMC uses
bit 24 for slot 0, so the previous bit-16 mask armed the wrong interrupt.
c312250 dwmmc: use Rockchip SDIO interrupt bit adds a Rockchip sdio_mask=0x01000000; 9703c32 bwfm: defer SDIO IRQ tasking during attach then prevents the first real IRQ from queueing the SDIO task
while kldload bwfm_sdio is still in attach. With both patches on kernel
#160, service bwfm_sdio onestart completed, dev.rockchip_dwmmc.0.sdio_intrs
advanced, and the 2 MiB transfer receipt
logs/wifi-transfer/20260505-095119-sdio-bit24-irq-active.log classified the
run as irq-active-watchdog.
Two negative controls then narrowed the remaining problem. First, fb81d0f rk_vop: mask vblank interrupts when idle masked idle rk_vop vblank interrupts and dropped rk_vop0
from roughly 413k interrupts/sec to single digits/sec; USB SSH got less awful,
but a 16 MiB HTTP fetch over WiFi stayed at roughly 42 KB/s. Second, 2458d80 bwfm: expose firmware power save control exposed dev.bwfm_sdio.0.power_save so firmware PM/MPC can
be proven instead of guessed: power_save=1 reports pm=2 mpc=1, 0 reports
pm=0 mpc=0, and toggling back leaves low-power policy enabled. Throughput
was already poor with PM/MPC off, so the next target is the SDIO data path
itself: glomming, credits, frame batching, or task scheduling.
Work log: 2026-05-06 SDIO run clock
The final throughput blocker was visible in the clock tree, not in the radio state. While transfers were stuck around 31-43 KB/s, the live phone reported:
hw.clock.clk_sdio.frequency: 800000
hw.clock.clk_sdio_c.frequency: 800000
hw.clock.hclk_sdio.frequency: 99000000
That is the identification/probe clock, not a usable SDIO data clock. The
first fix, cf62d1b dwmmc: raise RK3399 SDIO CIU clock for high-speed traffic , made the RK3399 DWMMC update path keep
sc->bus_hz in sync when the CIU clock changes. The missing second half was
that the MMCCAM SDIO path never ran the storage-card “set frequency to f_max”
sequence after selecting an I/O-only card. 04b3817 sdiob: request run clock after SDIO discovery added the
SDIO bridge handoff after CCCR/FBR discovery, 6e1221b sdiob: gate SDIO run clock experiment briefly
gated it behind debug.sdiob_run_clock_hz for recovery, and 8c61d6d sdiob: enable SDIO run clock by default made the 25 MHz request the default.
The post-reboot receipt on 2026-05-06 had the expected doubled Rockchip CIU clock:
debug.sdiob_run_clock_hz: 25000000
hw.clock.clk_sdio.frequency: 50000000
hw.clock.clk_sdio_c.frequency: 50000000
sdiob_set_run_clock: SDIO run clock requested at 25000000 Hz
The same-method transfer changed by roughly two orders of magnitude. Before
the clock fix, a 256 KiB local HTTP fetch over WiFi was around 31-43 KB/s.
After 8c61d6d sdiob: enable SDIO run clock by default , a post-reboot 256 KiB fetch forced over
wlan0 measured 2336 kBps; phone-to-host nc upload of the same size
completed in 0.14s; debug.bwfm_perf showed cmd53_err=0, rx_err=0,
tx_err=0. Enabling the RX glom parser is still useful for diagnostics and
batching (rx_glom_errors=0 under load), but a clock-only negative control
with glom disabled still measured 2034 kBps, so the primary fix is the SDIO
run clock.
The larger current-boot pass removed the obvious “small transfer only” doubt.
With power_save=1, pm=2, mpc=1, clk_sdio=50000000, and
irqmode=irq-active-watchdog, an 8 MiB host-to-phone HTTP fetch over wlan0
measured 1504 kBps, a 32 MiB fetch measured 2294 kBps, and an 8 MiB
phone-to-host nc upload completed in 7.55s. The before/after driver
counters stayed clean: cmd53_err=0, rx_err=0, tx_err=0, and
rx_glom_errors=0.
Finish criteria
The WiFi arc is not finished until these are true:
- Cold boot loads the deployed
bwfm_sdio.ko, createswlan0, associates, and binds DHCP without manual repair. dev.rockchip_dwmmc.0.sdio_intrsincrements during traffic anddebug.bwfm_statestays inirq-active-watchdogwithcmd53_err=0,rx_err=0, andtx_err=0.dev.bwfm_sdio.0.power_savedefaults to1, reportspm=2 mpc=1, can be toggled to0for performance tests, and can be toggled back without restarting the interface.- Throughput has post-reboot proof points at ~2.3 MB/s for 256 KiB local HTTP,
1504 kBpsfor 8 MiB HTTP,2294 kBpsfor 32 MiB HTTP, and an 8 MiB upload in7.55s, all overwlan0. - Remaining coverage: one wrong-password failure, broader AP/auth behavior, and a long idle period with
power_save=1.
See appendix: finishing plan for the exact command sequence.
Bluetooth audio reconfirmation — ▸ next
A2DP has been confirmed in the Bluetooth arc, but the latest bench session mixed two different audio paths: on-device loudspeaker output and Bluetooth A2DP output. Before calling the phone “daily-use audio ready,” rerun a clean Bluetooth-only playback test after the WiFi IRQ pass. The test should deliberately mute or avoid /dev/dsp0 / RT5640 output, start the A2DP virtual_oss backend against the speaker’s /dev/bluetooth/<bdaddr> path, and confirm audio at the external speaker while dev.pcm.* on the phone remains idle.
This is not expected to require new kernel work. It is a documentation and confidence pass so the site does not accidentally count loudspeaker playback as Bluetooth playback.
Modem: Quectel EG25-G — ▸ next
Mostly upstream of us. The hard part — USB enumeration — was solved as a side effect of the DWC3 / USB2PHY / VBUS-regulator work (essays 4 and 5). The modem appears as multi-interface USB CDC once kill switch #1 is on. AT commands work over /dev/cuaU*.
The first repeatable steps are now mise run modem:power:phone and mise run modem:status:phone. The power task asserts the two 4G regulators, keeps W_DISABLE_N high, leaves AP_READY and DTR in the awake state, runs the same reset/PWRKEY timing used by eg25-manager’s PinePhone Pro profile, then waits for USB enumeration. The status task captures USB devices, loaded modem-related drivers, /dev/cuaU* nodes, recent dmesg, and bounded AT query output (AT, ATI, AT+CPIN?, AT+CSQ, AT+QCFG="usbnet"). On hardware, /dev/cuaU0.2 and /dev/cuaU0.3 answer AT; the modem reports firmware EG25GGBR07A08M2G, usbnet=0, and +CME ERROR: 10 for SIM state.
What’s missing: moving the GPIO power sequence from a task into boot-time service plumbing if we want the modem always on; a nano-to-micro SIM adapter (mechanical, $1); APN setup and ECM mode (AT+QCFG="usbnet",1) for a cdce(4) interface; a ModemManager-equivalent — sxmo’s shell scripts (sxmo_modemcall.sh, sxmo_modemsms.sh) are portable with minor changes. Estimated effort once the adapter is in hand: a week for cellular data, a week for SMS, a couple weeks for a usable call UI. All userspace.
Sensors — ◐ partial
Mostly on I2C, plus one SARADC consumer, and each is bounded driver work:
- Accelerometer (MPU-6500, addr 0xd0). Basic driver is working:
dev.mpu6500.0.*exposes raw accel/gyro/temperature values. Next step is orientation policy, not bus bring-up. - Proximity/light (STK3311). Basic driver is working:
dev.stk3311.0.*exposes chip ID, raw ALS/PS samples, andnear. Next step is tuning an opt-in screen-blanking policy around omfreebdy’sscreen-toggle. - Magnetometer (AF8133J). Driver candidate is compile-ready for DTS
&i2c4addr0x1c; next step is benchingchip_variant, raw axis movement, andlast_sample_error=0. - Volume buttons (SARADC). Working now: local
rk_saradc+rk_adc_keysemitsKEY_VOLUMEUP/KEY_VOLUMEDOWNon hardware. Remaining cleanup, if we want it, is purely around threshold/debounce polish. - Power button (GPIO key). Working in userland now: omfreebdy binds Sway’s
XF86PowerOfftoscreen-toggle, so the panel can be blanked and restored without suspending the phone. - Notification LED. Kernel side already exposes the RGB
gpioleddevices; omfreebdy now hasphone-ledplus a narrow root helper for userland policy.
Volume buttons, the power button, STK3311, MPU-6500, and the RGB LED have crossed from “missing plumbing” to “raw hardware available.” The best next work is policy: proximity screen blanking in a dry-run monitor first, then maybe orientation-aware UI and battery/notification LED patterns.
PineTab2 (RK3566) — ◐ partial
Phase 3. Different SoC (RK3566), different WiFi (BES2600 — use a USB
dongle), different panel (BOE TH101MB31IG002-28A MIPI DSI). The first
implementation pass is now in tree: imported PineTab2 DTS files,
rk3566-pinetab2-v2.0-freebsd.dts, PINETAB2, and build tasks for the
tablet DTB/kernel. The first-boot wrapper now disables DSI/panel,
cameras, audio, PCIe, GPU, and BES2600 SDIO so the next real milestone
is hardware proof on the small surface: USB-C serial, micro-HDMI,
storage, USB networking, then I2C enumeration for RK817/Goodix/SC7A20
plus input proof for SARADC buttons and the GPIO EV_SW tablet/hall
switches before DSI work starts.
On-device audio — ● working
Closed in essay 13. The fix wasn’t a one-line format mismatch in the PCM framework after all — it was a one-line TLV polarity bug in the codec init: RT5640_DAC1_DIG_VOL = 0x0000 is -65.62 dB, not 0 dB (4ce7a60). Loudspeaker plays through the HPO path → external simple-audio-amplifier on PB3. Open follow-ups: headphone-jack detect, speaker/HP routing switch, headset mic, earpiece, and modem call routing. The internal-mic blocker moved across three layers on 2026-05-06: kernel #187 starts TX and RX together like Linux and finally returns nonzero DMIC samples; kernel #188 removes sleeping I2C from rt5640_dai_trigger, so capture closes cleanly without the sound-lock assertion; kernel #189 clears both RK I2S FIFOs only after the last stream stops, which makes playback, capture, and a full-duplex monitor close cleanly. The bench also caught a board-level false negative: privacy switch #3 disables the onboard microphones, so an otherwise-correct DMIC route can sound like static. With the switch enabled, the first intelligible internal-mic setting was dmic_clk_div=3, dmic_edge=3, adc_dig_volume=32 (DMIC=0xb860, ADC_DIG_VOL=0x2020). Kernel #190 defaults to that setting and confirmed capture/playback after reboot.
GPU stress beyond Sway — ◐ partial
The normal path is finally useful: Sway renders on Mali-T860 (Panfrost), kernel #77 closes GL clients without panicking, and the 2026-05-05 Sway bench put vblank_mode=0 glxgears -info at 1339 FPS average. glmark2 also runs on Panfrost now: kernel #175 completed the short 320x480 -b :duration=1 pass with score 882 and the longer 420x760 default pass with score 609. The 420x760 run had no panfrost timeouts, no resets, and no stuck jobs after 2b134eb panfrost: wait for GPU reset completion fixed reset-completion polling and added a hard-reset fallback. The run did exercise thermal policy: the guard entered warm level around GPU 72.777 °C, capped GPU max-auto to 400 MHz and CPU to 1200 MHz, then cooldown restored GPU max-auto and adaptive CPU frequency. A later attempt to move CPU caps into FreeBSD cpufreq at CPUFREQ_PRIO_KERN caused eMMC root read timeouts on kernel #191, so the shipping path is back to sysctl caps plus the powerd-yield guard. Two MMU fault counters were latched (status=0x660003c2, addr=0x2000000) without a visible failure, so WebGL/browser soak still needs attention. The fixed-clock gap is closed: 59bde6a panfrost: add OPP-aware GPU DVFS added OPP-aware voltage/clock transitions, d3b80a7 panfrost: avoid active-job DVFS transitions prevents active-job OPP changes, and 3bfc1d0 panfrost: avoid max clock for light auto work keeps light compositor work from waking the GPU straight to max-auto. Kernel #172 idles at OPP1 (297 MHz / 825 mV) with occasional OPP0, and ramps to OPP4 (594 MHz / 925 mV) under sustained glxgears; the Sway-visible test measured 1468 FPS in performance mode and 1448 FPS in auto mode with no panfrost faults.
The remaining display/GPU question now has two known signatures. The older one is the modeset-lock wedge (essay 08 — drm_modeset_lock.c:268 + drm_atomic_helper.c:617/667/892 warnings followed by hw_done / flip_done timeouts taking USB-Ethernet down with it). Status as of 2026-05-04: re-confirmed on #145. kldunload bwfm_sdio && kldload bwfm_sdio over ssh produces the exact same signature — so the trigger isn’t Hyprland-specific. Anything that yanks a busy DRM-adjacent code path while pages are in flight will reproduce it. rk_vop’s 21b88a8 rk_vop: ack only fired interrupts; restore wait_for_vblanks + 36d4794 rk_vop: latch page-flip events until FS_INTR + 3f72b0e rk_vop: roll back unproven vblank latch were necessary but not sufficient; the lock handling itself in rk_vop_plane_atomic_update (or the atomic-helper path that calls it) is the actual bug.
The new 2026-05-06 browser signatures split into two buckets. Firefox startup once left glxtest alive and Sway unkillable in panfrost_ioctl_wait_bo → dma_resv_wait_timeout_rcu; that remains the real Panfrost-side browser wedge to catch. YouTube playback then exposed a separate media path: PulseAudio’s OSS thread pegged CPU while Sway and Panfrost were still reachable. The live overlay now disables PulseAudio autospawn and asks Firefox cubeb for OSS directly through mobile-config-firefox; the follow-up YouTube run avoided PulseAudio and left Panfrost with no timeouts or resets, but Firefox/media processes saturated the CPUs. The next bench session should run mise run debug:gpu:wedge:phone -- firefox-glxtest or -- youtube-media at the first sign of a frozen UI, before trying to kill Firefox or restart Sway, so the log classifies panfrost-bo-wait, drm-modeset-wedge, media-audio-spin, or firefox-media-cpu.
The next GPU bench session has the tooling in place. 279739c scripts/wedge-repro: deterministic load generator for the modeset-lock wedge adds scripts/wedge-repro, a deterministic load generator that drives Hyprland + package operations + WiFi traffic at the same time, and mise run debug:gpu:wedge:phone now captures the read-only process stacks, DRM/Panfrost sysctls, Sway IPC, dmesg, and thermal state needed when the phone is still reachable over SSH. e22bae6 drm: gate WARN_ON kdb_backtrace+panic behind sysctls for wedge debugging gates the WARN_ON → kdb_backtrace → panic escalation behind sysctls so the wedge can be observed without the kernel committing suicide before the post-mortem can be captured. The actual work is to bisect whether the active trigger is Panfrost fence completion, plane-state readback ordering inside rk_vop_plane_atomic_update, or genuine atomic-helper re-entrancy from a non-IOCTL caller, and resolve. Only after that does the “trim debug scaffolding and explain Panfrost patches upstream” work make sense.
USB-PD / fusb302 — ● working
PD negotiation is no longer on the “next” list. fusb302(4) negotiates 9 V / 3 A repeatably on a fresh boot, the rk818 charger raises its input current limit to 3000 mA on Transition_Sink → Ready, and the battery actually charges ( 8da75ba rk818: clear OTG/SWITCH2 in DCDC_EN — battery now charges from PD — clearing DCDC_EN[7] so the chip’s own OTG 5 V boost stops fighting the PD source on VBUS). The Type-C policy now leaves unrelated SWITCH2_EN alone ( 4d190f3 rk818: decouple SWITCH2 from Type-C OTG ). See essay 16 for the bring-up arc, appendix: rk818 not actually charging for how the charge fault was found, and appendix: USB-C / PD verification for the verification recipe.
The remaining open work on the PD path is bench validation, not first-code:
- USB-C OTG / source role — the FUSB302 source-role CC poll path,
dev.fusb302.0.source_rolesysctl, battery-voltage guard, rk818 source-mode sequencing, and thehw.dwc3.force_host=1boot override combined to enumerate a full downstream USB tree on 2026-05-08: USB-2 first (VIA Labs USB 2.0 hub IC + AX88179A + NS1081 + Chicony keyboard + Anker management iface), then a few hours later the same Anker hub came up at SuperSpeed withusb3_enable=1+flip=1routing SS lanes throughrk_typec_phy: VIA USB 3.0 hub, AX88179 Ethernet, and NS1081 storage all enumerated at 5.0 Gbps.axge(4)is now inPINEPHONE_PROand brings the AX88179 up asue0(no carrier yet — RJ45 was unplugged). See appendix: USB-C source role and host-mode bring-up for both receipts. The remaining work is dynamic role/orientation: todayforce_hostandflipare boot tunables, so a cable reseat that flips orientation silently breaks SS until the next reboot. A real Type-C role-switch bridge that re-roles DWC3 and re-inits the phy fromfusb302(4)notifications is the next item. - Hard-reset recovery soak — received Hard_Reset now writes
FUSB302_RESET_PD, waitsPD_T_SAFE_0V_MS = 650, and returns throughSNK_Startup; locally-sent Hard_Reset now waitsPD_T_PS_HARD_RESET_MS = 30before startup. The next bench pass should trigger both directions and confirm the driver never falls back toPE_DISABLED.
Smaller follow-ups
- Upstream the CRU selective write filter — replace our allowlist with
CLK_IS_CRITICALindev/clk/. - Upstream virtual_oss path-buffer patches to FreeBSD ports
audio/virtual_oss. - Upstream
ng_h4frameto FreeBSD’s netgraph tree. - Replace
bt_a2dp.sh’s sleep-and-grep polling with an event-driven C tool over the raw HCI socket. - RK3399 thermal protection — TSADC readout now uses RK3399 hardware Q-select (
TSADCV3_AUTO_Q_SEL_EN) and tracks the MPU-6500 die sensor within a few degrees.0497253thermal: add runtime tsadc irq bench added a runtime comparator bench,e4017e7thermal: enable tsadc hardware qsel enabled hardware QSEL, anda856481thermal: tighten tsadc irq bench accounting tightened the bench accounting. Kernel#195passedmise run thermal:phone -- irq-bench: code-domain fire/nofire cases worked on both CPU and GPU channels, native-q cases stayed as the diagnostic negative control, and the run exited withINT_EN=0,irq_enabled=0, andtshut_enabled=0. The software governor follows the local patterns already working inpanfrost_dvfs_task(timer -> taskqueue -> OPP cap),fusb302(deadline callout plus worker-owned state machine), andbwfm_sdio(writable policy sysctl backed by driver state):rk_tsadcsamples on a low-ratecallout(9), queues policy work ontaskqueue_thread, exposes state/counter sysctls, caps GPU through Panfrost max-auto, and caps CPU throughdev.cpu.N.freqwhilephone_thermal_guardyieldspowerd.mise run thermal:phone -- soft-testis the regression proof for soft caps. The remaining work is a recovery-attached hardware TSHUT bench and a safe kernel-priority cpufreq design.
What we want from anyone reading this
The PinePhone Pro is the most porting-friendly phone in existence. Hardware kill switches let you debug WiFi without the radio on. Serial console is a 3.5 mm jack. U-Boot is open. SPI flash is reprogrammable. Every interesting peripheral has a public datasheet or a Linux driver to reverse-engineer. If you have the phone, an SD card, and a FreeBSD machine to cross-build on, you can be running the kernel from this tree by lunchtime tomorrow.
The work that’s left isn’t waiting for hardware vendors or firmware blobs (with the partial exception of BCM4345C0.hcd, and even there the right blob is freely distributable). It’s waiting for engineers willing to read source code and write drivers. WiFi, modem call control, sensors, the PineTab2 — every one is a contained project with a clear scope. None require all of the rest to be done first.