ESPHome 2026.8.0b4
Loading...
Searching...
No Matches
btstack_memory.cpp
Go to the documentation of this file.
1// Replaces the gatt_client / hci_connection static pools baked into
2// arduino-pico's prebuilt liblwip-bt.a (built with MAX_NR_GATT_CLIENTS 1,
3// MAX_NR_HCI_CONNECTIONS 2) with pools sized from ESPHOME_BLE_GATT_CLIENT_COUNT.
4// add_btstack_pool_overrides() in this component's codegen emits the matching
5// -Wl,--wrap flags, requested by bluetooth_connection when more than one GATT
6// backend registers; single-backend builds emit no flags and this file
7// compiles to nothing, leaving the prebuilt pools in charge. Layout safety:
8// the framework defines ENABLE_CLASSIC / ENABLE_BLE for every user TU
9// whenever PIO_FRAMEWORK_ARDUINO_ENABLE_BLUETOOTH is set (this component
10// always sets it), so sizeof() here matches the archive.
11
13
14#if defined(USE_RP2040_BLE) && defined(USE_BLE_GATT_CLIENT) && (ESPHOME_BLE_GATT_CLIENT_COUNT > 1)
15
16#include <btstack.h>
17
18#include <cstring>
19
21namespace {
22
23// Pinned against arduino-pico 6.0.0's prebuilt archives: a framework bump (or
24// a changed ENABLE_* macro) shifting the struct layout must fail the build
25// here, not overrun the pool blocks at runtime. Sizes differ per core
26// architecture (measured from each archive's own storage symbols). GCC only:
27// the clang-tidy frontend lays these structs out differently, and the guard
28// targets the real link.
29#ifndef __clang__
30#ifdef __riscv
31static_assert(sizeof(gatt_client_t) == 140 && sizeof(hci_connection_t) == 3740, "BTstack layout changed");
32#else
33static_assert(sizeof(gatt_client_t) == 128 && sizeof(hci_connection_t) == 3688, "BTstack layout changed");
34#endif
35#endif // __clang__
36
37// One gatt_client_t per configured connection slot. An hci_connection_t is
38// held from gap_connect() to DISCONNECTION_COMPLETE (scanning holds none);
39// +1 mirrors the prebuilt library's own headroom (2 connections for 1 GATT
40// client) so a teardown/re-connect overlap can never starve a slot.
41constexpr int HCI_CONNECTION_POOL_SIZE = ESPHOME_BLE_GATT_CLIENT_COUNT + 1;
42
43// NOLINTBEGIN(cppcoreguidelines-avoid-non-const-global-variables,cert-err58-cpp)
44gatt_client_t gatt_client_storage[ESPHOME_BLE_GATT_CLIENT_COUNT];
45btstack_memory_pool_t gatt_client_pool;
46hci_connection_t hci_connection_storage[HCI_CONNECTION_POOL_SIZE];
47btstack_memory_pool_t hci_connection_pool;
48
49// Static init: pool_create only links a free list through its own storage,
50// and BTstack first allocates long after static construction.
51struct PoolInit {
52 PoolInit() {
53 btstack_memory_pool_create(&gatt_client_pool, gatt_client_storage, ESPHOME_BLE_GATT_CLIENT_COUNT,
54 sizeof(gatt_client_t));
55 btstack_memory_pool_create(&hci_connection_pool, hci_connection_storage, HCI_CONNECTION_POOL_SIZE,
56 sizeof(hci_connection_t));
57 }
58} pool_init;
59// NOLINTEND(cppcoreguidelines-avoid-non-const-global-variables,cert-err58-cpp)
60
61} // namespace
62
63// Exact semantics of btstack_memory.c's static-pool arm: zeroed block on
64// success, NULL when exhausted; free returns the block to the pool. The
65// prebuilt pools stay resident in .bss (~7.4 KB, kept live by
66// btstack_memory_init in the archive) — dead weight here, not a leak.
67// NOLINTBEGIN(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp,readability-identifier-naming)
68extern "C" gatt_client_t *__real_btstack_memory_gatt_client_get(void);
69extern "C" void __real_btstack_memory_gatt_client_free(gatt_client_t *gatt_client);
70extern "C" hci_connection_t *__real_btstack_memory_hci_connection_get(void);
71extern "C" void __real_btstack_memory_hci_connection_free(hci_connection_t *hci_connection);
72
73namespace {
74// Fails the link if the corresponding --wrap flag is missing: __real_* only
75// exists while --wrap is in effect, and each wrap function anchors its own
76// symbol so dropping any single flag fails loudly. A code reference is used
77// because the framework links with --gc-sections, which discards an
78// unreferenced data anchor regardless of [[gnu::used]] (and this toolchain
79// does not emit SHF_GNU_RETAIN for [[gnu::retain]]).
80template<typename T> void anchor_wrap(T *symbol) { asm volatile("" ::"r"(symbol)); }
81} // namespace
82
83extern "C" {
84
87 void *buffer = btstack_memory_pool_get(&gatt_client_pool);
88 if (buffer != nullptr) {
89 memset(buffer, 0, sizeof(gatt_client_t));
90 }
91 return static_cast<gatt_client_t *>(buffer);
92}
93
94void __wrap_btstack_memory_gatt_client_free(gatt_client_t *gatt_client) {
96 btstack_memory_pool_free(&gatt_client_pool, gatt_client);
97}
98
101 void *buffer = btstack_memory_pool_get(&hci_connection_pool);
102 if (buffer != nullptr) {
103 memset(buffer, 0, sizeof(hci_connection_t));
104 }
105 return static_cast<hci_connection_t *>(buffer);
106}
107
108void __wrap_btstack_memory_hci_connection_free(hci_connection_t *hci_connection) {
110 btstack_memory_pool_free(&hci_connection_pool, hci_connection);
111}
112
113} // extern "C"
114// NOLINTEND(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp,readability-identifier-naming)
115
116} // namespace esphome::rp2040_ble
117
118#endif // USE_RP2040_BLE && USE_BLE_GATT_CLIENT && ESPHOME_BLE_GATT_CLIENT_COUNT > 1
hci_connection_t * __wrap_btstack_memory_hci_connection_get(void)
void __wrap_btstack_memory_hci_connection_free(hci_connection_t *hci_connection)
void __wrap_btstack_memory_gatt_client_free(gatt_client_t *gatt_client)
void __real_btstack_memory_gatt_client_free(gatt_client_t *gatt_client)
void __real_btstack_memory_hci_connection_free(hci_connection_t *hci_connection)
gatt_client_t * __wrap_btstack_memory_gatt_client_get(void)
hci_connection_t * __real_btstack_memory_hci_connection_get(void)
gatt_client_t * __real_btstack_memory_gatt_client_get(void)