wip: async BT firmware download attempt — DO NOT MERGE, breaks AX211 bring-up

Deferring the Intel BT firmware download off the boot path made the AX211
bootloader stop answering after the first FC05; even the final synchronous
revert freezes boot, so one of the 'neutral' fixes kept in this diff breaks
the bring-up on its own (candidates: BT-TRACE logging inside TransferCallback,
unconditional interrupt-IN re-queue after error completions on a halted EP,
xHCI interrupt-IN ZLP len fix interacting with HID, InPollContext owner
check).  Full history + next experiments in memory notes, 2026-07-05/06.

Co-Authored-By: Claude Fable 5 <[email protected]>
This commit is contained in:
2026-07-06 15:46:06 +02:00
co-authored by Claude Fable 5
parent 6a5eafb4fc
commit 8bf3ceb457
9 changed files with 163 additions and 31 deletions
+36 -7
View File
@@ -15,6 +15,8 @@
#include <CppLib/Stream.hpp>
#include <Libraries/Memory.hpp>
#include <Timekeeping/ApicTimer.hpp>
#include <Hal/SmpBoot.hpp>
#include <atomic>
using namespace Kt;
@@ -31,8 +33,10 @@ namespace Drivers::USB::Bluetooth {
// True when the USB transport is up but the firmware-dependent HCI init is
// still waiting for the ramdisk (drive 0) to be mounted. Set when an
// adapter enumerates during the boot port scan, which runs before the boot
// filesystems are mounted; cleared by ServiceDeferredInit() once VFS is up.
static bool g_initPending = false;
// filesystems are mounted; claimed (atomically -- the pickup runs from the
// idle loop, concurrently with the rest of the system) by
// ServiceDeferredInit() once VFS is up.
static std::atomic<bool> g_initPending{false};
// Forward declaration: firmware-dependent HCI bring-up, run once VFS is up.
static void CompleteInit();
@@ -236,15 +240,19 @@ namespace Drivers::USB::Bluetooth {
// Start the event pipe BEFORE sending any HCI commands.
// HCI command responses arrive as events on the interrupt IN endpoint,
// so it must be queued to receive them.
// so it must be queued to receive them. Deliberately done HERE, at
// enumeration time, not in the deferred bring-up: this preserves the
// exact transport timing of the original synchronous boot path.
Hci::StartEventPipe();
// The firmware download path reads the .sfi/.ddc images from the
// ramdisk (drive 0). Adapters present at boot enumerate during the
// xHCI port scan, which runs before the boot filesystems are mounted,
// so defer the firmware-dependent bring-up until VFS is available.
// The idle loop (ServiceDeferredInit) picks it up after boot, keeping
// the multi-second firmware download off the boot-critical path.
if (!Fs::Vfs::IsDriveRegistered(0)) {
g_initPending = true;
g_initPending.store(true, std::memory_order_release);
KernelLogStream(INFO, "BT") << "Transport up; deferring init until ramdisk is mounted";
return;
}
@@ -362,12 +370,33 @@ namespace Drivers::USB::Bluetooth {
// =========================================================================
void ServiceDeferredInit() {
if (!g_initPending || g_initialized) return;
if (!g_initPending.load(std::memory_order_relaxed) || g_initialized) return;
if (!Fs::Vfs::IsDriveRegistered(0)) return; // ramdisk still not mounted
if (Xhci::InPollContext()) return; // never nest under PollEvents
g_initPending = false;
KernelLogStream(INFO, "BT") << "Ramdisk mounted; completing Bluetooth init";
// Claim the pending init (this runs from the idle loop; make sure only
// one pass performs the bring-up). The firmware download inside takes
// seconds -- running it here instead of on the boot path is what keeps
// boot fast.
bool expected = true;
if (!g_initPending.compare_exchange_strong(expected, false,
std::memory_order_acquire)) return;
KernelLogStream(INFO, "BT") << "Completing deferred Bluetooth init in background";
// Reserve this CPU for the duration. The bring-up overlaps desktop
// startup, and if the scheduler tick pulls this idle context away
// whenever a process is ready, the HCI waits' wall-clock timeouts
// expire with almost no polling done. Reserved, the bring-up runs
// uninterrupted here while processes use other CPUs; on a single-CPU
// system this briefly pauses userspace, matching the old synchronous
// behavior minus the boot-path stall.
auto* cpu = Smp::GetCurrentCpuData();
if (cpu) cpu->reservedForKernelWork = true;
Hci::SetFwTrace(true); // bounded per-completion event-pipe trace
CompleteInit();
Hci::SetFwTrace(false);
if (cpu) cpu->reservedForKernelWork = false;
}
// =========================================================================