feat: Intel BT firmware download, A2dp & Bluetooth audio progress
This commit is contained in:
@@ -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
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user