feat: Intel BT firmware download, A2dp & Bluetooth audio progress

This commit is contained in:
2026-06-03 18:05:17 +02:00
parent ee6d1a388e
commit 0f16785c9f
22 changed files with 6452 additions and 273 deletions
+142 -21
View File
@@ -7,8 +7,10 @@
#include "Bluetooth.hpp"
#include "Hci.hpp"
#include "A2dp.hpp"
#include "IntelFirmware.hpp"
#include <Drivers/USB/Xhci.hpp>
#include <Drivers/USB/UsbDevice.hpp>
#include <Fs/Vfs.hpp>
#include <Terminal/Terminal.hpp>
#include <CppLib/Stream.hpp>
#include <Libraries/Memory.hpp>
@@ -26,6 +28,15 @@ namespace Drivers::USB::Bluetooth {
static uint8_t g_slotId = 0;
static uint8_t g_bdAddr[6] = {};
// 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;
// Forward declaration: firmware-dependent HCI bring-up, run once VFS is up.
static void CompleteInit();
// Intel Bluetooth device IDs
static bool IsIntelBt(uint16_t vid, uint16_t pid) {
if (vid != 0x8087) return false;
@@ -76,7 +87,9 @@ namespace Drivers::USB::Bluetooth {
<< " subver=" << (uint64_t)lver.LmpSubversion << base::dec;
}
// Read Intel version to check firmware state
// Read legacy Intel version for diagnostics (TLV parts return this in
// a different layout; the authoritative state check happens inside the
// firmware download path below via the TLV version).
Hci::IntelVersion ver = {};
if (!Hci::ReadIntelVersion(&ver)) {
KernelLogStream(WARNING, "BT") << "Failed to read Intel BT version";
@@ -85,23 +98,24 @@ namespace Drivers::USB::Bluetooth {
<< " FW variant=" << base::hex << (uint64_t)ver.FwVariant
<< " FW rev=" << (uint64_t)ver.FwRevision << "."
<< (uint64_t)ver.FwBuildNum << base::dec;
if (ver.FwVariant == 0x23) {
KernelLogStream(OK, "BT") << "Intel BT firmware already loaded (operational mode)";
} else if (ver.FwVariant == 0x06) {
KernelLogStream(WARNING, "BT") << "Intel BT in bootloader mode, firmware not loaded";
KernelLogStream(WARNING, "BT") << "Bluetooth will have limited functionality without firmware";
} else if (!hciVersionOk) {
// Standard HCI commands failed AND Intel version is zeros/unknown
// -> controller is in bootloader mode, needs firmware download
KernelLogStream(WARNING, "BT") << "Intel BT in bootloader mode (FW not loaded by UEFI)";
KernelLogStream(WARNING, "BT") << "Bluetooth requires firmware download for full functionality";
} else {
KernelLogStream(INFO, "BT") << "Intel BT firmware variant: "
<< base::hex << (uint64_t)ver.FwVariant;
}
}
// Run the firmware download path. This reads the TLV version, and if
// the controller is in bootloader mode, loads the matching .sfi image
// from the ramdisk, secure-sends it, boots the operational firmware
// and applies DDC parameters. Returns true if the controller ends up
// operational (either already loaded, or freshly downloaded).
if (!DownloadIntelFirmware()) {
KernelLogStream(WARNING, "BT")
<< "Intel BT firmware not loaded; limited functionality";
// Standard init already issued an HCI Reset above.
return true;
}
// The operational firmware just (re)booted. Give it a clean reset and
// re-enable the Intel vendor event mask before the generic HCI setup.
Hci::Reset();
Hci::IntelSetEventMask();
return true;
}
@@ -137,10 +151,31 @@ namespace Drivers::USB::Bluetooth {
// so it must be queued to receive them.
Hci::StartEventPipe();
// Intel-specific initialization (includes HCI Reset)
// 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.
if (!Fs::Vfs::IsDriveRegistered(0)) {
g_initPending = true;
KernelLogStream(INFO, "BT") << "Transport up; deferring init until ramdisk is mounted";
return;
}
CompleteInit();
}
// =========================================================================
// CompleteInit — firmware-dependent HCI bring-up (needs VFS/ramdisk)
// =========================================================================
static void CompleteInit() {
auto* dev = Xhci::GetDevice(g_slotId);
if (!dev) return;
// Intel-specific initialization (firmware download + HCI Reset)
bool didReset = false;
if (IsIntelBt(dev->VendorId, dev->ProductId)) {
if (InitIntelBluetooth(slotId)) {
if (InitIntelBluetooth(g_slotId)) {
didReset = true; // InitIntelBluetooth already sent HCI Reset
} else {
KernelLogStream(WARNING, "BT") << "Intel BT init failed, continuing with basic HCI";
@@ -164,6 +199,13 @@ namespace Drivers::USB::Bluetooth {
<< (uint64_t)g_bdAddr[1] << ":" << (uint64_t)g_bdAddr[0] << base::dec;
}
// NOTE: an earlier build overrode the BD_ADDR via 0xFC31 to dodge a
// (since disproven) stale-bond theory. Removed: the BD_ADDR is an input
// to the SSP authentication confirmation, and if the override only
// changes the advertised address but not the address the firmware uses
// in the crypto, the two sides compute different confirmations and
// pairing fails (Simple Pairing Complete = 0x05). Use the real address.
// Read buffer size
uint16_t aclLen = 0, aclNum = 0;
uint8_t scoLen = 0;
@@ -185,18 +227,40 @@ namespace Drivers::USB::Bluetooth {
// Enable Simple Secure Pairing
Hci::WriteSSPMode(1);
// Set event mask to receive relevant events
uint8_t eventMask[8] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x1F, 0x00, 0x20};
// Set event mask to receive relevant events. Octet 6 (events 0x31-0x38)
// MUST be enabled for Secure Simple Pairing: IO Capability Request
// (0x31, bit 48), IO Capability Response (0x32), User Confirmation
// Request (0x33), Simple Pairing Complete (0x36) all live there. It was
// 0x00 -> the controller started SSP but the IO-Capability Request event
// never reached us, so pairing always timed out with auth failure 0x05.
uint8_t eventMask[8] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x1F, 0xFF, 0x20};
Hci::SendCommand(Hci::OP_SET_EVENT_MASK, eventMask, 8);
Hci::WaitCommandComplete(Hci::OP_SET_EVENT_MASK);
// Enable inquiry + page scan (discoverable and connectable)
Hci::WriteScanEnable(0x03);
// Load persisted bonds so previously-paired devices reconnect without
// re-pairing (VFS is up by the time CompleteInit runs).
Hci::LoadLinkKeys();
g_initialized = true;
KernelLogStream(OK, "BT") << "Bluetooth adapter initialized successfully";
}
// =========================================================================
// ServiceDeferredInit — run boot-deferred bring-up once VFS is ready
// =========================================================================
void ServiceDeferredInit() {
if (!g_initPending || g_initialized) return;
if (!Fs::Vfs::IsDriveRegistered(0)) return; // ramdisk still not mounted
g_initPending = false;
KernelLogStream(INFO, "BT") << "Ramdisk mounted; completing Bluetooth init";
CompleteInit();
}
// =========================================================================
// Public queries
// =========================================================================
@@ -255,6 +319,8 @@ namespace Drivers::USB::Bluetooth {
int Connect(const uint8_t* bdAddr, uint32_t timeoutMs) {
if (!g_initialized || !bdAddr) return -1;
Hci::ResetEventTrace(); // capture the pairing/SSP event sequence
if (!Hci::CreateConnection(bdAddr)) return -1;
// Wait for Connection Complete event
@@ -271,7 +337,61 @@ namespace Drivers::USB::Bluetooth {
for (int j = 0; j < 6; j++) {
if (conn->BdAddr[j] != bdAddr[j]) { match = false; break; }
}
if (match) return 0;
if (match) {
// The headset (in pairing mode) drives Secure Simple
// Pairing itself right after the ACL link comes up. Let
// authentication + encryption finish BEFORE opening any
// L2CAP/AVDTP channels: doing A2DP on a not-yet-
// authenticated link races with the pairing handshake and
// the headset drops us (reason 0x05). Drain events here
// so the IO-capability / user-confirm / link-key / encrypt
// events all get serviced.
//
// We are the initiator: request authentication so the
// controller starts Secure Simple Pairing (Link Key
// Request -> our negative reply -> IO Capability Request
// -> ... ). The headset does not start this on its own.
// NB this only works now that octet 6 of the event mask
// is enabled so the IO-Capability Request event actually
// reaches us; before that this produced 03 17 06 05.
Hci::AuthenticateLink(conn->Handle);
uint64_t t0 = Timekeeping::GetMilliseconds();
while (Timekeeping::GetMilliseconds() - t0 < 5000) {
Xhci::PollEvents();
Hci::DrainEvents();
// Send queued pairing replies reliably (top-level,
// not nested under PollEvents).
Hci::ProcessPendingCommands();
if (!conn->Active) break; // link dropped during pairing
if (conn->Encrypted) break; // authenticated + encrypted -> ready
for (int k = 0; k < 200; k++) asm volatile("pause" ::: "memory");
}
// Bring up the A2DP source stream (signaling + media
// channels, SBC negotiation) only once the link is
// secured. Without a media stream the headset also drops
// the link (reason 0x13), so this keeps it engaged too.
if (conn->Active) {
// Let the link settle after Encryption Change before
// dialing L2CAP: some sinks ignore a CONN_REQ that
// arrives the instant encryption completes. Drain
// (don't blind-sleep) so the ACL RX ring stays live.
uint64_t st = Timekeeping::GetMilliseconds();
while (Timekeeping::GetMilliseconds() - st < 300) {
Xhci::PollEvents();
Hci::DrainEvents();
for (int k = 0; k < 200; k++) asm volatile("pause" ::: "memory");
}
A2dp::StartSource();
}
// Persist any new link key now (process context), even if
// the link later dropped, so the disk write never stalls
// the nested pairing event handler.
Hci::FlushLinkKeys();
Hci::DumpEventTrace(); // show the pairing/SSP sequence
return 0;
}
}
}
@@ -280,6 +400,7 @@ namespace Drivers::USB::Bluetooth {
}
}
Hci::DumpEventTrace(); // show whatever events did arrive
return -1; // Timeout
}