feat: Raspberry Pi 4/400 Bluetooth over mini UART (H4, Broadcom patchram, firmware mailbox BT_ON); move BT stack to Drivers/Bluetooth
This commit is contained in:
1 parent
808e1d66fd
commit
ffee9002a6
38 files changed
+2176
-141
No files matched your search
@@ -0,0 +1,493 @@
|
||||
/*
|
||||
* Broadcom.cpp
|
||||
* Broadcom/Cypress UART Bluetooth controller bring-up (Raspberry Pi 4/400)
|
||||
* Copyright (c) 2026 Daniel Hammer
|
||||
*/
|
||||
|
||||
#include "Broadcom.hpp"
|
||||
#include "Hci.hpp"
|
||||
#include <ACPI/AcpiDevices.hpp>
|
||||
#include <ACPI/AML/AmlInterpreter.hpp>
|
||||
#include <Arch/Cpu.hpp>
|
||||
#include <CppLib/Stream.hpp>
|
||||
#include <Drivers/Platform/RpiFirmware.hpp>
|
||||
#include <Drivers/Platform/RpiGpio.hpp>
|
||||
#include <Drivers/Serial/BtUart.hpp>
|
||||
#include <Drivers/Serial/Uart.hpp>
|
||||
#include <Fs/Vfs.hpp>
|
||||
#include <Libraries/Memory.hpp>
|
||||
#include <Memory/Heap.hpp>
|
||||
#include <Terminal/Terminal.hpp>
|
||||
#include <Timekeeping/Timer.hpp>
|
||||
|
||||
// The Raspberry Pi 4B (CYW43455) and 400 (CYW43456) carry a Cypress radio
|
||||
// whose Bluetooth half speaks HCI over a UART (H4) on GPIO 30-33: CTS, RTS,
|
||||
// TXD, RXD. Its power switch, BT_ON, is line 0 of the VideoCore firmware's
|
||||
// GPIO expander.
|
||||
//
|
||||
// Bring-up follows Linux's hci_bcm + btbcm:
|
||||
// 1. power on, wait ~100 ms, HCI Reset at the controller's 115200 baud;
|
||||
// 2. Read Local Version: the LMP subversion names the chip and so the
|
||||
// patch file (BCM4345C0.hcd, BCM4345C5.hcd);
|
||||
// 3. raise both ends to the operating speed (vendor 0xFC18, answered at
|
||||
// the old speed);
|
||||
// 4. Download Minidriver (0xFC2E), wait 50 ms, then send every record of
|
||||
// the .hcd file -- each one is a complete HCI command, Write RAM
|
||||
// (0xFC4C) and finally Launch RAM (0xFC4E) -- and wait 250 ms;
|
||||
// 5. the patched firmware restarts at 115200: HCI Reset there, and raise
|
||||
// the speed again.
|
||||
//
|
||||
// Raspberry Pi OS's btuart runs the controller at 460800 without flow
|
||||
// control on the mini UART and at 3 Mbaud with it on the PL011. The mini
|
||||
// UART's FIFO is 8 bytes deep, so here its hardware RTS is used whenever
|
||||
// the controller is still reachable with it on, which allows 921600.
|
||||
|
||||
using namespace Kt;
|
||||
|
||||
namespace Drivers::Bluetooth::Broadcom {
|
||||
|
||||
namespace BtUart = Drivers::Serial::BtUart;
|
||||
namespace RpiFirmware = Drivers::Platform::RpiFirmware;
|
||||
namespace RpiGpio = Drivers::Platform::RpiGpio;
|
||||
|
||||
// GPIO 30-33 carry CTS, RTS, TXD, RXD: alternate function 3 routes them
|
||||
// to the PL011, 5 to the mini UART. Pulls as Linux's bcm2711 pinctrl
|
||||
// sets them (uart0_ctsrts_gpio30, uart0_gpio32).
|
||||
static constexpr uint32_t PIN_CTS = 30;
|
||||
static constexpr uint32_t PIN_RTS = 31;
|
||||
static constexpr uint32_t PIN_TXD = 32;
|
||||
static constexpr uint32_t PIN_RXD = 33;
|
||||
|
||||
static constexpr uint32_t InitialBaud = 115200;
|
||||
|
||||
// LMP subversions of the ROM firmware -> patch file (Linux btbcm's
|
||||
// bcm_uart_subver_table).
|
||||
struct ChipName {
|
||||
uint16_t subversion;
|
||||
const char* name;
|
||||
};
|
||||
static constexpr ChipName g_chips[] = {
|
||||
{ 0x6119, "BCM4345C0" }, // CYW43455: Raspberry Pi 3B+, 4B
|
||||
{ 0x6606, "BCM4345C5" }, // CYW43456: Raspberry Pi 400, CM4
|
||||
};
|
||||
|
||||
static bool g_flowControl = false;
|
||||
static bool g_powerSwitch = true;
|
||||
|
||||
// =========================================================================
|
||||
// Platform probe
|
||||
// =========================================================================
|
||||
|
||||
static bool EndsWith(const char* s, const char* suffix) {
|
||||
int sl = 0, xl = 0;
|
||||
while (s[sl]) sl++;
|
||||
while (suffix[xl]) xl++;
|
||||
if (xl > sl) return false;
|
||||
for (int i = 0; i < xl; i++) {
|
||||
if (s[sl - xl + i] != suffix[i]) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// The controller's UartSerialBusV2 descriptor (ACPI 6.4, 6.4.3.8.2.3)
|
||||
// names the UART it hangs off in its ResourceSource string.
|
||||
static bool ReadUartSource(int32_t node, char* source, int maxLength) {
|
||||
Hal::AML::Object crs;
|
||||
if (!Hal::AML::GetInterpreter().EvaluateChild(node, "_CRS", nullptr, 0, crs) ||
|
||||
crs.Type != Hal::AML::ObjectType::Buffer) {
|
||||
return false;
|
||||
}
|
||||
const uint8_t* data = crs.Buffer.Data;
|
||||
uint32_t length = crs.Buffer.Length;
|
||||
uint32_t pos = 0;
|
||||
while (pos < length) {
|
||||
uint8_t tag = data[pos];
|
||||
if (!(tag & 0x80)) {
|
||||
if ((tag & 0x78) == 0x78) break; // end tag
|
||||
pos += 1 + (tag & 0x07);
|
||||
continue;
|
||||
}
|
||||
if (pos + 3 > length) break;
|
||||
uint32_t descLength = (uint32_t)data[pos + 1] | ((uint32_t)data[pos + 2] << 8);
|
||||
uint32_t end = pos + 3 + descLength;
|
||||
if (end > length) break;
|
||||
static constexpr uint8_t SerialBusTag = 0x8E, SerialBusUart = 3;
|
||||
if (tag == SerialBusTag && descLength >= 9 && data[pos + 5] == SerialBusUart) {
|
||||
uint32_t typeDataLength = (uint32_t)data[pos + 10] | ((uint32_t)data[pos + 11] << 8);
|
||||
uint32_t s = pos + 12 + typeDataLength;
|
||||
int n = 0;
|
||||
while (s < end && data[s] != 0 && n < maxLength - 1) source[n++] = (char)data[s++];
|
||||
source[n] = '\0';
|
||||
return n > 0;
|
||||
}
|
||||
pos = end;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool ReadInteger(int32_t node, const char* name, uint64_t& value) {
|
||||
Hal::AML::Object obj;
|
||||
if (!Hal::AML::GetInterpreter().EvaluateChild(node, name, nullptr, 0, obj) ||
|
||||
obj.Type != Hal::AML::ObjectType::Integer) {
|
||||
return false;
|
||||
}
|
||||
value = obj.Integer;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ProbePlatform() {
|
||||
Hal::AcpiDevices::DeviceInfo bt{};
|
||||
if (!Hal::AcpiDevices::FindDeviceByHid("BCM2EA6", bt)) return false;
|
||||
|
||||
// Which UART: the descriptor's source, else the firmware's "UART in
|
||||
// use" flag (0: the PL011 is the console, Bluetooth has the mini UART).
|
||||
BtUart::Kind kind = BtUart::Kind::MiniUart;
|
||||
char source[64] = {};
|
||||
uint64_t uartInUse = 0;
|
||||
if (ReadUartSource(bt.NodeIndex, source, sizeof(source))) {
|
||||
kind = EndsWith(source, "URT0") ? BtUart::Kind::Pl011 : BtUart::Kind::MiniUart;
|
||||
} else if (ReadInteger(bt.NodeIndex, "URIU", uartInUse)) {
|
||||
kind = uartInUse == 0 ? BtUart::Kind::MiniUart : BtUart::Kind::Pl011;
|
||||
}
|
||||
bool mini = kind == BtUart::Kind::MiniUart;
|
||||
|
||||
Hal::AcpiDevices::DeviceInfo uart{};
|
||||
if (!Hal::AcpiDevices::FindDeviceByHid(mini ? "BCM2836" : "BCM2837", uart)) {
|
||||
KernelLogStream(WARNING, "BT-BCM") << bt.Path << ": its UART "
|
||||
<< (source[0] ? source : (mini ? "(mini UART)" : "(PL011)")) << " is not in ACPI";
|
||||
return false;
|
||||
}
|
||||
|
||||
Hal::AML::ResourceList resources;
|
||||
uint64_t base = 0, length = 0;
|
||||
if (!Hal::AcpiDevices::EvaluateCrs(uart.NodeIndex, resources) ||
|
||||
!resources.GetMemory(base, length) || base == 0) {
|
||||
KernelLogStream(WARNING, "BT-BCM") << uart.Path << ": no register block in _CRS";
|
||||
return false;
|
||||
}
|
||||
|
||||
uint64_t console = Drivers::Serial::ConsoleAddress();
|
||||
if (console != 0 && (console & ~0xFFFULL) == (base & ~0xFFFULL)) {
|
||||
KernelLogStream(WARNING, "BT-BCM") << "Bluetooth's UART " << uart.Path
|
||||
<< " is the serial console; leaving Bluetooth off";
|
||||
return false;
|
||||
}
|
||||
|
||||
BtUart::Config config{};
|
||||
config.kind = kind;
|
||||
config.mmioPhys = base;
|
||||
config.levelTriggered = true;
|
||||
if (auto* irq = resources.FindFirst(Hal::AML::ResourceType::ExtendedIrq)) {
|
||||
config.gsi = irq->ExtendedIrq.Interrupt;
|
||||
config.levelTriggered = (irq->ExtendedIrq.Flags & 1) == 0;
|
||||
config.activeLow = (irq->ExtendedIrq.Flags & 2) != 0;
|
||||
}
|
||||
|
||||
// The mini UART's baud generator runs off the VPU core clock: ask the
|
||||
// firmware for its current rate, else take the rate the firmware
|
||||
// patched into the table (MUCR; the placeholder 0x1000000 means it
|
||||
// did not). The PL011's reference clock is fixed (CLCK, 48 MHz).
|
||||
uint64_t tableClock = 0;
|
||||
uint32_t clockHz = 0;
|
||||
if (mini) {
|
||||
if (!RpiFirmware::GetClockRate(RpiFirmware::CLOCK_CORE, clockHz)) {
|
||||
if (ReadInteger(uart.NodeIndex, "MUCR", tableClock) && tableClock > 0x1000000)
|
||||
clockHz = (uint32_t)tableClock;
|
||||
else
|
||||
clockHz = 500000000;
|
||||
}
|
||||
} else {
|
||||
clockHz = ReadInteger(uart.NodeIndex, "CLCK", tableClock) && tableClock != 0
|
||||
? (uint32_t)tableClock : 48000000;
|
||||
}
|
||||
config.clockHz = clockHz;
|
||||
|
||||
RpiGpio::Function function = mini ? RpiGpio::Function::Alt5 : RpiGpio::Function::Alt3;
|
||||
bool pinsOk = RpiGpio::SetFunction(PIN_CTS, function)
|
||||
&& RpiGpio::SetFunction(PIN_RTS, function)
|
||||
&& RpiGpio::SetFunction(PIN_TXD, function)
|
||||
&& RpiGpio::SetFunction(PIN_RXD, function)
|
||||
&& RpiGpio::SetPull(PIN_CTS, RpiGpio::Pull::Up)
|
||||
&& RpiGpio::SetPull(PIN_RTS, RpiGpio::Pull::None)
|
||||
&& RpiGpio::SetPull(PIN_TXD, RpiGpio::Pull::None)
|
||||
&& RpiGpio::SetPull(PIN_RXD, RpiGpio::Pull::Up);
|
||||
if (!pinsOk) {
|
||||
KernelLogStream(WARNING, "BT-BCM")
|
||||
<< "No GPIO controller (BCM2845): UART pins left as the firmware set them";
|
||||
}
|
||||
|
||||
if (!BtUart::Initialize(config)) return false;
|
||||
|
||||
Hci::InitializeUart();
|
||||
Hci::SetVendor(Hci::Vendor::Broadcom);
|
||||
KernelLogStream(OK, "BT-BCM") << "Bluetooth controller " << bt.Path << " on "
|
||||
<< uart.Path << " (GSI " << (uint64_t)config.gsi << ")";
|
||||
return true;
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// Controller bring-up
|
||||
// =========================================================================
|
||||
|
||||
static void PowerCycle() {
|
||||
if (g_powerSwitch) {
|
||||
if (RpiFirmware::SetGpioState(RpiFirmware::EXPGPIO_BT_ON, false)) {
|
||||
Timekeeping::BusyWaitMs(20);
|
||||
RpiFirmware::SetGpioState(RpiFirmware::EXPGPIO_BT_ON, true);
|
||||
// hci_bcm allows 100-120 ms to power on and leave reset.
|
||||
Timekeeping::BusyWaitMs(150);
|
||||
} else {
|
||||
g_powerSwitch = false;
|
||||
KernelLogStream(WARNING, "BT-BCM")
|
||||
<< "Cannot switch BT_ON through the firmware; assuming the controller is powered";
|
||||
}
|
||||
}
|
||||
Hci::ResetUartReceiver();
|
||||
}
|
||||
|
||||
static bool ResetController(uint32_t timeoutMs) {
|
||||
if (!Hci::SendCommand(Hci::OP_RESET, nullptr, 0)) return false;
|
||||
return Hci::WaitCommandComplete(Hci::OP_RESET, nullptr, 0, timeoutMs);
|
||||
}
|
||||
|
||||
// Reach the controller at its power-on 115200 baud. Hardware RTS is tried
|
||||
// first: if the RTS line is wired the wrong way round for it, the
|
||||
// controller sees RTS deasserted and never answers, and the retry without
|
||||
// it tells the two cases apart.
|
||||
static bool SyncAtInitialBaud() {
|
||||
BtUart::SetBaud(InitialBaud);
|
||||
static constexpr bool attempts[] = { true, true, false, false };
|
||||
for (bool flow : attempts) {
|
||||
BtUart::SetFlowControl(flow);
|
||||
Hci::ResetUartReceiver();
|
||||
if (ResetController(1000)) {
|
||||
g_flowControl = flow;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static uint32_t PickOperatingBaud() {
|
||||
uint32_t wanted;
|
||||
if (BtUart::GetKind() == BtUart::Kind::Pl011) {
|
||||
wanted = g_flowControl ? 3000000 : 921600;
|
||||
} else if (!BtUart::HasInterrupt()) {
|
||||
// Polled 8-byte FIFO: stay where nothing overruns.
|
||||
return InitialBaud;
|
||||
} else {
|
||||
wanted = g_flowControl ? 921600 : 460800;
|
||||
}
|
||||
static constexpr uint32_t rates[] = { 3000000, 2000000, 921600, 460800, 230400 };
|
||||
for (uint32_t rate : rates) {
|
||||
if (rate <= wanted && BtUart::BaudReachable(rate)) return rate;
|
||||
}
|
||||
return InitialBaud;
|
||||
}
|
||||
|
||||
// Both ends to `baud`. The controller answers 0xFC18 at the old speed,
|
||||
// then switches. False leaves the host at its old speed; the controller
|
||||
// may or may not have switched (the caller power-cycles to be sure).
|
||||
static bool SwitchBaud(uint32_t baud) {
|
||||
uint32_t oldBaud = BtUart::GetBaud();
|
||||
uint8_t params[6] = {
|
||||
0x00, 0x00,
|
||||
(uint8_t)baud, (uint8_t)(baud >> 8), (uint8_t)(baud >> 16), (uint8_t)(baud >> 24),
|
||||
};
|
||||
if (!Hci::SendCommand(Hci::OP_BCM_UPDATE_BAUD_RATE, params, sizeof(params)) ||
|
||||
!Hci::WaitCommandComplete(Hci::OP_BCM_UPDATE_BAUD_RATE, nullptr, 0, 1000)) {
|
||||
KernelLogStream(WARNING, "BT-BCM") << "Controller refused " << (uint64_t)baud << " baud";
|
||||
return false;
|
||||
}
|
||||
if (!BtUart::SetBaud(baud)) return false;
|
||||
Timekeeping::BusyWaitMs(10);
|
||||
Hci::ResetUartReceiver();
|
||||
|
||||
Hci::LocalVersion ver{};
|
||||
if (Hci::ReadLocalVersion(&ver)) return true;
|
||||
|
||||
KernelLogStream(WARNING, "BT-BCM") << "No answer at " << (uint64_t)baud << " baud";
|
||||
BtUart::SetBaud(oldBaud);
|
||||
Hci::ResetUartReceiver();
|
||||
return false;
|
||||
}
|
||||
|
||||
static const char* ChipFor(uint16_t subversion) {
|
||||
for (const auto& chip : g_chips) {
|
||||
if (chip.subversion == subversion) return chip.name;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Load 0:/os/firmware/brcm/<chip>.hcd. Returns nullptr (and logs) when
|
||||
// the file is missing; the caller frees the image.
|
||||
static uint8_t* LoadPatch(const char* chip, uint64_t& size) {
|
||||
char path[64];
|
||||
const char* prefix = "0:/os/firmware/brcm/";
|
||||
int n = 0;
|
||||
for (const char* p = prefix; *p; p++) path[n++] = *p;
|
||||
for (const char* p = chip; *p && n < 56; p++) path[n++] = *p;
|
||||
for (const char* p = ".hcd"; *p; p++) path[n++] = *p;
|
||||
path[n] = '\0';
|
||||
|
||||
Fs::Vfs::BackendFile file;
|
||||
if (Fs::Vfs::OpenBackendFile(path, file) < 0) {
|
||||
KernelLogStream(WARNING, "BT-BCM") << "Firmware patch not found: " << path;
|
||||
return nullptr;
|
||||
}
|
||||
size = Fs::Vfs::GetBackendFileSize(file);
|
||||
uint8_t* image = size ? (uint8_t*)Memory::g_heap->Request(size) : nullptr;
|
||||
if (image == nullptr) {
|
||||
Fs::Vfs::CloseBackendFile(file);
|
||||
return nullptr;
|
||||
}
|
||||
int got = Fs::Vfs::ReadBackendFile(file, image, 0, size);
|
||||
Fs::Vfs::CloseBackendFile(file);
|
||||
if (got != (int)size) {
|
||||
Memory::g_heap->Free(image);
|
||||
KernelLogStream(ERROR, "BT-BCM") << "Short read of " << path;
|
||||
return nullptr;
|
||||
}
|
||||
KernelLogStream(INFO, "BT-BCM") << "Loading " << path << " (" << size << " bytes)";
|
||||
return image;
|
||||
}
|
||||
|
||||
static bool DownloadPatch(const uint8_t* image, uint64_t size) {
|
||||
if (!Hci::SendCommand(Hci::OP_BCM_DOWNLOAD_MINIDRV, nullptr, 0) ||
|
||||
!Hci::WaitCommandComplete(Hci::OP_BCM_DOWNLOAD_MINIDRV, nullptr, 0, 2000)) {
|
||||
KernelLogStream(ERROR, "BT-BCM") << "Download Minidriver failed";
|
||||
return false;
|
||||
}
|
||||
Timekeeping::BusyWaitMs(50);
|
||||
|
||||
uint64_t start = Timekeeping::GetMilliseconds();
|
||||
uint64_t off = 0;
|
||||
uint32_t records = 0;
|
||||
while (off + 3 <= size) {
|
||||
uint16_t opcode = (uint16_t)image[off] | ((uint16_t)image[off + 1] << 8);
|
||||
uint8_t paramLength = image[off + 2];
|
||||
off += 3;
|
||||
if (off + paramLength > size) {
|
||||
KernelLogStream(ERROR, "BT-BCM") << "Truncated patch record " << (uint64_t)records;
|
||||
return false;
|
||||
}
|
||||
if (!Hci::SendCommand(opcode, image + off, paramLength)) return false;
|
||||
bool acked = Hci::WaitCommandComplete(opcode, nullptr, 0, 2000);
|
||||
if (!acked && opcode != Hci::OP_BCM_LAUNCH_RAM) {
|
||||
KernelLogStream(ERROR, "BT-BCM") << "Patch record " << (uint64_t)records
|
||||
<< " (opcode " << base::hex << (uint64_t)opcode << base::dec << ") failed";
|
||||
return false;
|
||||
}
|
||||
off += paramLength;
|
||||
records++;
|
||||
}
|
||||
// The patched firmware starts after Launch RAM; btbcm waits 250 ms.
|
||||
Timekeeping::BusyWaitMs(250);
|
||||
KernelLogStream(OK, "BT-BCM") << "Patch downloaded: " << (uint64_t)records << " records in "
|
||||
<< (Timekeeping::GetMilliseconds() - start) << " ms at "
|
||||
<< (uint64_t)BtUart::GetBaud() << " baud";
|
||||
return true;
|
||||
}
|
||||
|
||||
static void LogVersion(const char* chip, const Hci::LocalVersion& ver) {
|
||||
uint16_t sub = ver.LmpSubversion;
|
||||
KernelLogStream(OK, "BT-BCM") << (chip ? chip : "Broadcom controller") << " ("
|
||||
<< (uint64_t)((sub & 0xE000) >> 13) << "." << (uint64_t)((sub & 0x1F00) >> 8)
|
||||
<< "." << (uint64_t)(sub & 0x00FF) << ") build " << (uint64_t)(ver.HciRevision & 0x0FFF)
|
||||
<< ", " << (uint64_t)BtUart::GetBaud() << " baud, "
|
||||
<< (g_flowControl ? "RTS flow control" : "no flow control");
|
||||
}
|
||||
|
||||
// One complete bring-up. `fast` raises the speed for the download and
|
||||
// afterwards; without it everything stays at 115200.
|
||||
static bool Attempt(bool fast) {
|
||||
PowerCycle();
|
||||
if (!SyncAtInitialBaud()) {
|
||||
KernelLogStream(ERROR, "BT-BCM") << "Controller does not answer HCI Reset"
|
||||
<< (g_powerSwitch ? "" : " (BT_ON was never switched on: it is likely unpowered)");
|
||||
return false;
|
||||
}
|
||||
|
||||
Hci::LocalVersion rom{};
|
||||
if (!Hci::ReadLocalVersion(&rom)) return false;
|
||||
const char* chip = ChipFor(rom.LmpSubversion);
|
||||
KernelLogStream(INFO, "BT-BCM") << "ROM firmware: LMP subversion " << base::hex
|
||||
<< (uint64_t)rom.LmpSubversion << ", HCI revision " << (uint64_t)rom.HciRevision
|
||||
<< base::dec << (chip ? " -> " : " (unknown chip)") << (chip ? chip : "");
|
||||
|
||||
uint32_t operating = fast ? PickOperatingBaud() : InitialBaud;
|
||||
if (operating != InitialBaud && !SwitchBaud(operating)) return false;
|
||||
|
||||
uint64_t size = 0;
|
||||
uint8_t* image = chip ? LoadPatch(chip, size) : nullptr;
|
||||
if (image != nullptr) {
|
||||
bool ok = DownloadPatch(image, size);
|
||||
Memory::g_heap->Free(image);
|
||||
if (!ok) return false;
|
||||
|
||||
// The patched firmware comes up at 115200 again.
|
||||
BtUart::SetBaud(InitialBaud);
|
||||
Hci::ResetUartReceiver();
|
||||
bool reset = false;
|
||||
for (int i = 0; i < 3 && !reset; i++) reset = ResetController(1000);
|
||||
if (!reset) {
|
||||
KernelLogStream(ERROR, "BT-BCM") << "Patched firmware does not answer HCI Reset";
|
||||
return false;
|
||||
}
|
||||
if (operating != InitialBaud && !SwitchBaud(operating)) return false;
|
||||
} else {
|
||||
KernelLogStream(WARNING, "BT-BCM") << "Running the controller's ROM firmware";
|
||||
}
|
||||
|
||||
Hci::LocalVersion ver{};
|
||||
if (Hci::ReadLocalVersion(&ver)) LogVersion(chip, ver);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Initialize() {
|
||||
if (Hci::GetTransport() != Hci::Transport::Uart) return false;
|
||||
if (Attempt(true)) return true;
|
||||
KernelLogStream(WARNING, "BT-BCM") << "Retrying bring-up at 115200 baud";
|
||||
return Attempt(false);
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// BD_ADDR
|
||||
// =========================================================================
|
||||
|
||||
// Addresses are in HCI order (addr[0] = least significant octet).
|
||||
static bool IsDefaultAddress(const uint8_t* addr) {
|
||||
bool allZero = true, allOnes = true;
|
||||
for (int i = 0; i < 6; i++) {
|
||||
if (addr[i] != 0x00) allZero = false;
|
||||
if (addr[i] != 0xFF) allOnes = false;
|
||||
}
|
||||
if (allZero || allOnes) return true;
|
||||
// The chip-wide defaults Linux's btbcm lists for these parts (the
|
||||
// BCM4345C5's is 43:45:C5:00:1F:AC) start with 0x43, which has the
|
||||
// IEEE group bit set: no assigned device address does.
|
||||
return (addr[5] & 0x01) != 0;
|
||||
}
|
||||
|
||||
void FixDefaultAddress() {
|
||||
uint8_t addr[6];
|
||||
if (!Hci::ReadBdAddr(addr) || !IsDefaultAddress(addr)) return;
|
||||
|
||||
uint8_t mac[6];
|
||||
if (!RpiFirmware::GetBoardMacAddress(mac)) {
|
||||
KernelLogStream(WARNING, "BT-BCM")
|
||||
<< "Controller has its chip-wide default BD_ADDR and no board MAC is available";
|
||||
return;
|
||||
}
|
||||
// The board's Ethernet MAC is unique to it and in the Raspberry Pi's
|
||||
// own address block.
|
||||
uint8_t board[6];
|
||||
for (int i = 0; i < 6; i++) board[i] = mac[5 - i];
|
||||
if (Hci::SetBdAddr(board)) {
|
||||
KernelLogStream(OK, "BT-BCM") << "Replaced the chip-wide default BD_ADDR with the board's";
|
||||
} else {
|
||||
KernelLogStream(WARNING, "BT-BCM") << "Controller rejected the board BD_ADDR";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Reference in new issue
Block a user