- fixed multiple CVE bugs of Bluetooth Classic controller on ESP32-S31
- coex: improved BR/EDR ACL active scheduling in case of Wi-Fi coexistence
- SCO: reject unexpected LMP PDU received in SCO related LMP procedures
A 15 ms link with max_ce_len=0 sent one DLE PDU per event and capped
badge frames at ~17 KB/s. Request 7.5–15 ms and a long CE, and refill
the single CoC TX slot instead of treating EBUSY as a hard failure.
Return GATT_INVALID_HANDLE instead of GATT_NOT_FOUND when Read Multiple
(Variable) response assembly fails, since 0x0A is not a valid ATT error
for these PDUs per Core Spec Vol 3 Part F.
last_param[] is sized by BLE_MESH_ADV_INST_TYPES_NUM (the number of mesh
advertising instance types) but was indexed by inst_id, the controller
advertising instance id (Kconfig range 0-3). With multi-adv disabled the
type count is 1, so any non-zero CONFIG_BLE_MESH_ADV_INST_ID caused an
out-of-bounds access into last_param[].
Map inst_id to inst_type with the new bt_mesh_get_adv_inst_type_by_inst_id()
helper and index last_param by inst_type in both the nimble and bluedroid
host adapters. Add a bounds check at bt_le_ext_adv_start() entry to guard
against unmapped instance ids.
Count successful frame bytes and share Global SN with snapshots.
Track snapshot attempts with a separate 24-bit anchor_count. Keep protocol v8.
Scan writer-held buffers during periodic flush and require a published FREE bit.
Keep UART0 periodic draining active when producers are disabled.
Document late pending-seal hints and update existing snapshot checks.
- concurrent-writer integrity and park/wake soak cases: many writers
across shared and reserve transports keep frames intact and SNs
unique through continuous park/wake turnover;
- the deinit race case wakes parked writers before joining, so the
join cannot deadlock on a task that must first observe the closed
gate;
- the perf observer latches its final counters only from FLUSH
snapshots: periodic frames arriving after the FLUSH snapshot carry
interval counters that the flush reset, and must not overwrite the
final result;
- README describes the timestamp prefix by writer path.
Consolidates the concurrency review fixes for the unified pool:
- the shared ESP Timer task never waits for a transport: its identity
is checked on the would-wait path only, so its callbacks always
return and dispatch can progress (this also bounds claim()'s
backpressure);
- transports accepted from the FREE bitmap are ACQUIRE-loaded: the
recycler publishes pos/pending_seal with a STORE_RELEASE(FREE)
without holding the candidate lock, and the acceptance load pairs
with that publication;
- a waiter whose scan bounced off a candidate lock is re-advertised:
the releasing side re-checks availability after the lock release,
inside the same seq_cst window as the waiter-count read, so a
transport that became claimable while locked cannot strand its wake;
- waiters never self-wake on an unpublished FREE transport:
notification for a FREE transport fires only when its free-bitmap
hint is already published, so a registered waiter cannot mint and
consume its own wake tokens in a self-sustaining spin;
- flush and deinit drains re-check writer references after observing a
zero waiter count: a writer waking between the two loads re-acquires
its reference before unregistering, and the seq_cst-fenced re-read
must observe it before the drain concludes.
Protocol v8: every ENCODE record carries the one-byte id of the task
that formatted it, replacing the incremental TASK_SWITCH marker scheme.
Attribution becomes a property of the record: the per-source CAS lock,
the last_task_handle writeback, and the try-lock contention drops are
deleted, so concurrent writers to one source can no longer lose records
to attribution races.
The registry is a self-contained module (ble_log_task_registry.c/h,
structured like the UART redirection writer): an append-only name-keyed
table shared by every ENCODE writer, 16 bytes of RAM per entry, sized by
CONFIG_BLE_LOG_TASK_ID_MAX. Word compares resolve ids lock-free on the
record path; the registration CAS serializes only the cold path (once
per task lifetime), and a record resolves its writer id after its claim
succeeds, so the claim's lifetime reference pins the registry epoch and
the id cannot cross an init/deinit boundary. A full registry or a
contended registration degrades that record to the unknown id (0xFF)
and still emits it. ISR callers stop at the lookup-miss branch before
registration mutates shared state.
Bindings are module-owned system output: every periodic snapshot window
broadcasts one INTERNAL frame packing one fixed-layout record per
registered entry on the registry's own dedicated transport, with a
sequence of its own (a gap counts a skipped broadcast window, never a
lost snapshot). A busy transport skips the window and the next one
rebroadcasts, so a receiver that joined late converges on the next
window.
BLE_LOG_VERSION is bumped to 8: old decoders must not parse the new
record layout. The compression encoders reject truncated NULL-buffer
records instead of committing partial payloads, and the test app enables
host compression with a 4-entry registry so the table-full path is
reachable on target.
Keep Host capture for Bluedroid and legacy VHCI NimBLE while retaining controller HCI records for transports without Host capture. Respect the HCI logging switch and preserve controller source mapping and payloads.
Extend metadata regression coverage and document capture selection.
The NON_YIELD flag in bit 7 of the frame source byte forced every
receiver to mask the byte before decoding the source, while carrying
little information beyond what the source ID already implies. Protocol
v7 is not released yet, so drop the flag instead of versioning it:
the source byte now carries the bare frozen ble_log_src_t value.
The internal pool reservation for non-yieldable contexts
(BLE_LOG_POOL_NON_YIELD_RESERVE_CNT) is untouched: it is memory
management, not wire format.
Update the golden bytes (source byte 0x87 -> 0x07, recomputed
checksum) and rename the critical-section frame capture test
accordingly; the walker structs lose the redundant source_meta
field.
ble_log_lbm.c/h were superseded by the v2 LBM (protocol v7) and are
not part of the build; the two files only referenced each other. The
deprecated Kconfig entries stay untouched - downstream test apps may
still pin the old symbols (e.g. BLE_LOG_LL_HCI_LOG_PAYLOAD_LEN_LIMIT).
Post-switch polish folded in: version info built in place inside lbm
init, pool-internal tidying, simplified runtime submit/defer paths,
per-snapshot clock sampling, shared waiter-count RMW in the acquire
path, and dropped zero-value transport field inits.
TS sync now always runs: the periodic tick (TS sample, OPEN transport
flush, internal snapshot) exists in every build, so the unified periodic
output no longer depends on BLE_LOG_TS_ENABLED and idle systems without
dispatch activity still flush every second.
- New BLE_LOG_TS_SYNC_TOGGLE_IO_ENABLED gates only the analyzer toggle
IO (GPIO config, level writes, reset); with it unset, TS snapshots
still sample the clocks and carry io_level = 0. BLE_LOG_TS_ENABLED
keeps its prompt as a deprecated compatibility entry that selects
BLE_LOG_TS_SYNC_TOGGLE_IO_ENABLED, so existing projects keep the old
toggle behavior, and the TS trigger entries drop their dependency on
it.
- ble_log_ts is no longer a separate module: its 95-line shell (clock
sampling, toggle IO management) joins ble_log_rt.c, the only runtime
that drives it. ble_log_ts_info_t moves to ble_log_lbm_v2.h (the
snapshot interface that consumes it), the BLE_LOG_GET_LC_TS chip
table moves next to its single caller, the init/deinit folds into
ble_log_rt_init/deinit, the reset folds into ble_log_sync_enable, and
the sampler becomes a void static now that its failure paths are gone.
- The runtime hook is gone: with the tick always present, its throttled
defer-callback fallback for runtime-disabled sync served no one. The
TS tick is the only periodic output source; ble_log_sync_enable(false)
now means full periodic silence, and ble_log_rt_dispatch loses its
return value. The version-info regression now arms the tick instead
of the hook.
- BLE_LOG_GET_LC_TS branches on CONFIG_BT_DUAL_MODE_ARCH: the
dual-mode-arch controllers (ESP32-H4, ESP32-S31) expose their link
layer timer as r_sched_timer_getCurrentTimeU32, but the symbol is
obfuscated in the current prebuilt libraries, so those targets report
lc_ts = 0 for now; call the accessor once the libraries export it.
The Gen 2 branch keeps r_ble_lll_timer_current_tick_get (defined by
every C5/C6/C61/H2/H21 library, verified by nm and by linking
ble_log_test for ESP32-C6); ESP32-H4 and ESP32-S31 fall out of it.
- ble_log_ts_info_update's old shape is gone entirely: the heap-allocated
global ts_info and its critical section memcpy were leftovers from the
pointer-return API; the sampler writes the caller object in place and
keeps only the toggle IO phase as cross-call state. int_src_code is
filled outside the critical section; the phase toggle stays inside to
exclude the write in ble_log_sync_enable.
Idle systems no longer touch the controller clock: the legacy
accessors dereference controller state and INIT precedes controller
initialization, so the sampler returns lc_ts = 0 while the controller
is idle instead of reading it. The deferred dispatch callback drains
only the queue depth observed at entry and re-arms itself for arrivals
left behind, so it cannot monopolize the shared ESP timer task.
Partially-filled OPEN transports previously waited for a capacity seal
or an explicit ble_log_flush(); an app that never calls flush loses the
parked frames at test end. Three triggers now cover the gap:
- The periodic output tick (the TS trigger, or the runtime hook when
TS is disabled) flushes OPEN transports ahead of the periodic
snapshot. The flusher never waits for a lock: a transport whose lock
is held is left a pending-seal marker instead.
- The next claim that takes the transport lock sees the marker and
seals the buffered frames before scanning on for another transport;
if no writer ever returns, the next periodic pass seals the
transport uncontended.
- ble_log_deinit() drains the remaining OPEN transports after the
writer gate closes and before the runtime queue is destroyed; the
peripheral deinit wait completes the delivery.
The marker is cleared by seal_and_send (the choke point of every seal
path), by the direct FREE return in ble_log_commit's error path, and on
recycle, so it never survives a transport lifecycle. The full-barrier
ble_log_flush() semantics are unchanged.
Tests cover the pending-seal handoff (claim-locked flush hook), the deinit drain of a parked sub-capacity burst, and the real ble_log_deinit() path through the auto-recycle hook.
Move the stream write interface (redir_get_trans / redir_seal /
stream_write / stream_flush) out of ble_log_lbm_v2.c into a dedicated
ble_log_redir.c with its own internal header, matching the rt/ts
module layout. The pool keeps the recycle inflight hook and the flush
hooks.
The REDIR console stream keeps its own 24-bit frame sequence in
ble_log_redir_t: a raw console stream is not a log attempt, so it
never consumes the Global SN (which would fake loss gaps). g_frame_sn
stays TU-local to ble_log_lbm_v2.c, and the redundant modulo masks
are dropped: BLE_LOG_MAKE_FRAME_META enforces the 24-bit wire field at
the packing site, so BLE_LOG_SN_MODULO is removed.
REDIR payloads are a raw console stream with no timestamp prefix: a
per-frame ESP timer timestamp anchored at most ~1s of aggregated text,
cost a breaking wire change, and had no consumer on the receiver side.
Alignment with the core timeline uses receiver arrival time (bounded
by the periodic redirection flush).
Compression encoders claim pool storage through the new
ble_log_claim/ble_log_commit interface and encode in place: the
per-channel static payload buffers and their CAS busy management go
away, and each record is one ENCODE frame. The timestamp and CAS
helper macros (and their lbm call sites) are capitalized to the
all-uppercase convention for function-like macros as part of the
rewrite.
Test coverage lands with the feature:
- ble_log_test: golden v7 wire bytes, snapshot layout, pool exhaustion
and reserve use, claim/commit stale-handle rejection, periodic
snapshot busy/loss, FLUSH reset semantics; the stale-claim
assertions are sabotage-verified to fail when the state or
generation check is deleted;
- ble_log_rt_test: runtime dispatch latency and batch drain, TS-valid
snapshot cadence, deinit races across repeated init/deinit rounds
(valid HCI macro inputs, corrected regression setup);
- ble_log_perf_test: per-writer throughput and cycle measurement
against the unified pool with the shared/reserve split and paced
no-loss profiles.
Replace the legacy multi-LBM transport layer with one shared pool:
- one bitmap-indexed pool of CONFIG_BLE_LOG_POOL_TRANS_CNT transports
with a non-yield reserve for ISR and critical-section writers;
- per-transport state machine (FREE/OPEN/CLAIMED/SENDING) guarded by a
per-transport CAS lock; bitmaps are only candidate hints;
- claim/commit interface for the compressed-log path; claim bookkeeping
lives in the pool (ble_log_pool_claim_t), not in the peripheral
transport;
- dedicated Internal Snapshot transport and fixed-layout snapshot
record (reason flags, version block, lc/esp/os clock samples, pool
state, compact core statistics);
- source-local 24-bit wire sequences and compact per-source counters;
- UART console redirection stream writer (ble_log_redir_t);
- CMakeLists compiles ble_log_lbm_v2.c instead of ble_log_lbm.c; the
legacy ble_log_lbm.c/.h stay in-tree, out of the build, until their
removal;
- the public ble_log_src_t ABI is restored and frozen; protocol v7
wire source IDs are a separate internal enum (ble_log_wire_src_t)
and the older BLE Stack sources map onto the v7 core sources
(LL_TASK/LL_ISR -> LL, LL_HCI/HCI -> HCI, HOST -> CUSTOM);
BLE_LOG_VERSION 7;
- Kconfig: pool sizing options replace the LBM options, whose names
remain as hidden deprecated symbols for backward sdkconfig
compatibility; BLE_LOG_HOST_SIDE_HCI_LOG_ENABLED becomes the single
BLE_LOG_HCI_LOG_ENABLED switch for host and controller HCI traffic
(Bluedroid and NimBLE host call sites follow the rename), enabled
by default on all targets instead of only where
BLE_LOG_IS_ESP_LEGACY_CONTROLLER; the old name keeps a deprecated
promptless shim that selects the new symbol, so sdkconfig files
still pinning it keep host HCI logging enabled;
- util: ble_log_cas_acquire/release become macros; multi-call-site
pool helpers are no longer inline (IRAM);
- test app record structs gain a source_meta field for the v7 wire
source byte;
- the Internal Snapshot no longer carries a schema_version: the record
is self-identifying via int_src_code and the version block.
ble_log_write_hci encoded the direction in bit 7 of the caller's type
byte and then cleared that bit unconditionally after the write,
leaving the caller's buffer mutated. Save the complete type byte, set
bit 7 only around the synchronous copy, and restore it afterwards;
the direction argument is now treated as a boolean.
HCI H4 type bytes (0x01..0x04) always have bit 7 clear, so no legal
input observes a behavior change.