diff --git a/components/bt/common/ble_log/README.md b/components/bt/common/ble_log/README.md index eb9c0802efe..35a654ae8c1 100644 --- a/components/bt/common/ble_log/README.md +++ b/components/bt/common/ble_log/README.md @@ -19,8 +19,9 @@ flowchart TD The shared pool contains `CONFIG_BLE_LOG_POOL_TRANS_CNT` transports. The last `CONFIG_BLE_LOG_POOL_NON_YIELD_RESERVE_CNT` transports are reserved for ISR and -other contexts that cannot yield. Ordinary public writes are non-blocking. -Only the ordinary controller LL task path waits for a shared transport. +other contexts that cannot yield. Every yieldable-context writer, from the +public API and claims to the controller LL task, waits for a shared transport; +only ISR and critical-section writers fail fast. The Internal Snapshot and UART0 redirection transports are not members of the bitmap pool. @@ -48,8 +49,7 @@ offset size field `frame_meta` is: ```text -bits 0..6 base source -bit 7 NON_YIELD +bits 0..7 source bits 8..31 sequence number, low 24 bits ``` @@ -74,9 +74,8 @@ redirection flush) is the alignment reference against the core timeline. ### Sources -The public `ble_log_src_t` ABI is frozen and its values are the base on-wire -source IDs of protocol v7 frames. Receivers must mask the `NON_YIELD` bit -before decoding the base source: +The public `ble_log_src_t` ABI is frozen and its values are the on-wire +source IDs of protocol v7 frames: ```text 0 INTERNAL @@ -100,7 +99,6 @@ console batch). `ble_log_init()` resets all three sequences, and its required `INIT` snapshot starts a new receiver epoch. They remain continuous through `FLUSH` within that epoch. Callers must not write until `ble_log_init()` returns, so the `INIT` snapshot is submitted first. -Actual ISR and critical-section records carry `NON_YIELD` in source bit 7. Controller-side HCI records are not emitted by BLE Log, and the controller no longer maintains its own internal LL HCI log. Host-side Bluedroid and NimBLE diff --git a/components/bt/common/ble_log/include/ble_log.h b/components/bt/common/ble_log/include/ble_log.h index b62ace0ac2e..c5ce103819d 100644 --- a/components/bt/common/ble_log/include/ble_log.h +++ b/components/bt/common/ble_log/include/ble_log.h @@ -67,7 +67,9 @@ void ble_log_deinit(void); bool ble_log_enable(bool enable); /* Blocking; call only from a caller-owned task, not an ISR or system callback. */ void ble_log_flush(void); +/* Waits for a shared transport in yieldable contexts; ISR and critical-section callers fail fast. */ bool ble_log_write_hex(ble_log_src_t src_code, const uint8_t *addr, size_t len); +/* Same backpressure as ble_log_write_hex(): yieldable claims wait for a shared transport. */ uint8_t *ble_log_claim(ble_log_src_t src_code, size_t max_len, uint32_t *handle); void ble_log_commit(uint32_t handle, size_t actual_len); void ble_log_dump_to_console(void); diff --git a/components/bt/common/ble_log/src/ble_log_lbm_v2.c b/components/bt/common/ble_log/src/ble_log_lbm_v2.c index 092596489f4..c21ba964df9 100644 --- a/components/bt/common/ble_log/src/ble_log_lbm_v2.c +++ b/components/bt/common/ble_log/src/ble_log_lbm_v2.c @@ -99,7 +99,6 @@ typedef struct { * only needs buf/size/pos and the lifecycle state for DMA handoff. */ typedef struct { uint8_t src_code; /* source of the pending claim */ - uint8_t non_yield; /* claimed from a non-yieldable context */ uint16_t max_len; /* maximum committed payload length */ uint32_t frame_sn; /* Global SN consumed at claim entry */ uint32_t generation; /* stale-handle detection, wraps 24-bit */ @@ -149,7 +148,7 @@ BLE_LOG_STATIC void ble_log_pool_seal_and_send(ble_log_prph_trans_t *trans); BLE_LOG_STATIC void ble_log_pool_seal_open_trans(void); BLE_LOG_STATIC void ble_log_pool_write_frame(ble_log_prph_trans_t *trans, uint32_t frame_sn, - uint8_t source_meta, + ble_log_src_t src_code, const uint8_t *prefix, uint16_t prefix_len, const uint8_t *addr, uint16_t len, @@ -497,18 +496,17 @@ ble_log_prph_trans_t *ble_log_pool_acquire(size_t log_len, /* -------------------------------------- */ BLE_LOG_IRAM_ATTR BLE_LOG_STATIC void ble_log_pool_write_frame(ble_log_prph_trans_t *trans, uint32_t frame_sn, - uint8_t source_meta, + ble_log_src_t src_code, const uint8_t *prefix, uint16_t prefix_len, const uint8_t *addr, uint16_t len, const uint8_t *addr_append, uint16_t len_append, bool omdata) { uint8_t *buf = trans->buf + trans->pos; uint16_t payload_len = prefix_len + len + len_append; - ble_log_src_t src_code = (ble_log_src_t)BLE_LOG_SRC_ID(source_meta); ble_log_stat_mgr_t *stat_mgr = &stat_mgr_ctx[src_code]; ble_log_frame_head_t frame_head = { .length = payload_len, - .frame_meta = BLE_LOG_MAKE_FRAME_META(source_meta, frame_sn), + .frame_meta = BLE_LOG_MAKE_FRAME_META(src_code, frame_sn), }; /* Memory operation */ @@ -584,7 +582,7 @@ uint8_t *ble_log_claim(ble_log_src_t src_code, size_t max_len, uint32_t *handle) xTaskGetSchedulerState() != taskSCHEDULER_RUNNING; size_t payload_capacity = sizeof(timestamp) + max_len; ble_log_prph_trans_t *trans = - ble_log_pool_acquire(payload_capacity, non_yield, false); + ble_log_pool_acquire(payload_capacity, non_yield, !non_yield); if (!trans) { ble_log_stat_mgr_mark_lost(src_code); BLE_LOG_REF_COUNT_RELEASE(&lbm_ref_count); @@ -598,7 +596,6 @@ uint8_t *ble_log_claim(ble_log_src_t src_code, size_t max_len, uint32_t *handle) #endif ble_log_pool_claim_t *claim = &pool_claim_ctx[trans->id]; claim->src_code = (uint8_t)src_code; - claim->non_yield = non_yield; claim->max_len = (uint16_t)max_len; claim->frame_sn = frame_sn; claim->generation = (claim->generation + 1) & 0x00ffffffU; @@ -648,11 +645,9 @@ void ble_log_commit(uint32_t handle, size_t actual_len) uint16_t payload_len = (uint16_t)(sizeof(uint32_t) + actual_len); ble_log_stat_mgr_t *stat_mgr = &stat_mgr_ctx[src_code]; - uint8_t source_meta = BLE_LOG_MAKE_SOURCE_META(src_code, - claim->non_yield); ble_log_frame_head_t frame_head = { .length = payload_len, - .frame_meta = BLE_LOG_MAKE_FRAME_META(source_meta, claim->frame_sn), + .frame_meta = BLE_LOG_MAKE_FRAME_META(src_code, claim->frame_sn), }; BLE_LOG_MEMCPY(trans->buf + trans->pos, &frame_head, BLE_LOG_FRAME_HEAD_LEN); ble_log_pool_finish_frame(trans, payload_len, stat_mgr); @@ -1125,13 +1120,12 @@ bool ble_log_write_hex(ble_log_src_t src_code, const uint8_t *addr, size_t len) uint32_t timestamp = BLE_LOG_TIMESTAMP_NOW(); size_t payload_len = sizeof(timestamp) + len; ble_log_prph_trans_t *trans = - ble_log_pool_acquire(payload_len, !can_yield, false); + ble_log_pool_acquire(payload_len, !can_yield, can_yield); if (!trans) { goto failed; } - uint8_t source_meta = BLE_LOG_MAKE_SOURCE_META(src_code, !can_yield); - ble_log_pool_write_frame(trans, frame_sn, source_meta, + ble_log_pool_write_frame(trans, frame_sn, src_code, (const uint8_t *)×tamp, sizeof(timestamp), addr, (uint16_t)len, NULL, 0, false); @@ -1194,8 +1188,7 @@ void ble_log_write_hex_ll(uint32_t len, const uint8_t *addr, goto failed; } - uint8_t source_meta = BLE_LOG_MAKE_SOURCE_META(src_code, non_yield); - ble_log_pool_write_frame(trans, frame_sn, source_meta, + ble_log_pool_write_frame(trans, frame_sn, src_code, NULL, 0, addr, (uint16_t)len, addr_append, (uint16_t)len_append, omdata); diff --git a/components/bt/common/ble_log/src/internal_include/ble_log_lbm_v2.h b/components/bt/common/ble_log/src/internal_include/ble_log_lbm_v2.h index f78f7e4c896..a61f337e566 100644 --- a/components/bt/common/ble_log/src/internal_include/ble_log_lbm_v2.h +++ b/components/bt/common/ble_log/src/internal_include/ble_log_lbm_v2.h @@ -31,10 +31,8 @@ typedef struct { #define BLE_LOG_FRAME_HEAD_LEN (sizeof(ble_log_frame_head_t)) #define BLE_LOG_FRAME_TAIL_LEN (sizeof(uint32_t)) #define BLE_LOG_FRAME_OVERHEAD (BLE_LOG_FRAME_HEAD_LEN + BLE_LOG_FRAME_TAIL_LEN) -#define BLE_LOG_MAKE_SOURCE_META(src, non_yield) \ - (((src) & BLE_LOG_SRC_ID_MASK) | ((non_yield) ? BLE_LOG_SRC_FLAG_NON_YIELD : 0)) -#define BLE_LOG_MAKE_FRAME_META(source_meta, sn) \ - (((source_meta) & 0xffU) | (((sn) & 0x00ffffffU) << 8)) +#define BLE_LOG_MAKE_FRAME_META(src, sn) \ + (((src) & 0xffU) | (((sn) & 0x00ffffffU) << 8)) /* ------------------------------------- */ /* Unified Buffer Pool Defines */ @@ -59,13 +57,8 @@ typedef struct { /* --------------------------------------- */ /* Protocol v7 Source ID Space */ /* --------------------------------------- */ -/* The frozen public ble_log_src_t values are the base on-wire and statistic - * source IDs. Bit 7 of the frame source byte carries NON_YIELD metadata, so - * receivers must mask it before decoding the base source. */ -#define BLE_LOG_SRC_ID_MASK 0x7f -#define BLE_LOG_SRC_FLAG_NON_YIELD 0x80 -#define BLE_LOG_SRC_ID(source_meta) ((source_meta) & BLE_LOG_SRC_ID_MASK) -#define BLE_LOG_SRC_IS_NON_YIELD(source_meta) (((source_meta) & BLE_LOG_SRC_FLAG_NON_YIELD) != 0) +/* The frozen public ble_log_src_t values are the on-wire and statistic + * source IDs: the frame source byte carries the bare enum value. */ /* Statistic slots in the Internal Snapshot: every public source that can * produce frames, i.e. CUSTOM through ENCODE. INTERNAL frames carry their diff --git a/components/bt/common/ble_log/test_apps/ble_log_perf_test/README.md b/components/bt/common/ble_log/test_apps/ble_log_perf_test/README.md index 50fd303a5ec..dc505520509 100644 --- a/components/bt/common/ble_log/test_apps/ble_log_perf_test/README.md +++ b/components/bt/common/ble_log/test_apps/ble_log_perf_test/README.md @@ -31,8 +31,9 @@ Runtime dispatch behavior and latency are covered by the sibling - `write_hex cycles`: single writer, no link cap, payload 8/32/64/128 B. The scheduler remains active during each measured call, followed by an unmeasured one-tick pacing delay so the no-loss profile does not become a saturation test. -- `write_hex drop path cycles`: saturated 2 Mbps link, measures the cost of a - failed (dropped) write without the no-loss pacing delay. +- `write_hex drop path cycles`: saturated 2 Mbps link, measures the backpressure + cost of a parked write (wait and wake on transport recycle) without the + no-loss pacing delay. - `write_hex_ll cycles`: payload 8/32/64/128 B; plus a 32+32 B append case. - `compressed write cycles`: workload matrix of the compressed entry points — U32 args (0/1/2/mixed), U64 values (full 8B / leading-zero LZ / zero), diff --git a/components/bt/common/ble_log/test_apps/ble_log_perf_test/main/test_ble_log_main.c b/components/bt/common/ble_log/test_apps/ble_log_perf_test/main/test_ble_log_main.c index 2642e629c31..0e874caf815 100644 --- a/components/bt/common/ble_log/test_apps/ble_log_perf_test/main/test_ble_log_main.c +++ b/components/bt/common/ble_log/test_apps/ble_log_perf_test/main/test_ble_log_main.c @@ -35,10 +35,8 @@ bool test_ble_log_walk_frames(const uint8_t *data, size_t len, } if (observer) { - uint8_t source_meta = head.frame_meta & 0xff; test_ble_log_frame_t frame = { - .src = BLE_LOG_SRC_ID(source_meta), - .source_meta = source_meta, + .src = (ble_log_src_t)(head.frame_meta & 0xff), .sn = head.frame_meta >> 8, .payload = data + offset + BLE_LOG_FRAME_HEAD_LEN, .payload_len = head.length, diff --git a/components/bt/common/ble_log/test_apps/ble_log_perf_test/main/test_ble_log_main.h b/components/bt/common/ble_log/test_apps/ble_log_perf_test/main/test_ble_log_main.h index 4e6abd1a6ce..1f82cf73b33 100644 --- a/components/bt/common/ble_log/test_apps/ble_log_perf_test/main/test_ble_log_main.h +++ b/components/bt/common/ble_log/test_apps/ble_log_perf_test/main/test_ble_log_main.h @@ -14,7 +14,6 @@ typedef struct { ble_log_src_t src; - uint8_t source_meta; uint32_t sn; const uint8_t *payload; size_t payload_len; diff --git a/components/bt/common/ble_log/test_apps/ble_log_perf_test/main/test_ble_log_perf.c b/components/bt/common/ble_log/test_apps/ble_log_perf_test/main/test_ble_log_perf.c index 5679d0e24df..fdf0ee0180e 100644 --- a/components/bt/common/ble_log/test_apps/ble_log_perf_test/main/test_ble_log_perf.c +++ b/components/bt/common/ble_log/test_apps/ble_log_perf_test/main/test_ble_log_perf.c @@ -979,8 +979,9 @@ TEST_CASE("BLE Log write_hex cycles (single writer, link=0)", "[ble_log][perf][c } } -/* Saturated link: most writes fail. Measures the drop-path cost and - * cross-checks client failed counts against the LBM's lost counters. */ +/* Saturated link: yieldable writers park on backpressure instead of dropping; + * measures the park/wake cost and cross-checks client failed counts against + * the LBM's lost counters. */ TEST_CASE("BLE Log write_hex drop path cycles (link=2Mbps)", "[ble_log][perf][cycle][ignore]") { perf_run_cfg_t cfg = { diff --git a/components/bt/common/ble_log/test_apps/ble_log_rt_test/main/test_ble_log_main.c b/components/bt/common/ble_log/test_apps/ble_log_rt_test/main/test_ble_log_main.c index 9db296dd963..f8856c86936 100644 --- a/components/bt/common/ble_log/test_apps/ble_log_rt_test/main/test_ble_log_main.c +++ b/components/bt/common/ble_log/test_apps/ble_log_rt_test/main/test_ble_log_main.c @@ -36,10 +36,8 @@ bool test_ble_log_walk_frames(const uint8_t *data, size_t len, } if (observer) { - uint8_t source_meta = head.frame_meta & 0xff; test_ble_log_frame_t frame = { - .src = BLE_LOG_SRC_ID(source_meta), - .source_meta = source_meta, + .src = (ble_log_src_t)(head.frame_meta & 0xff), .sn = head.frame_meta >> 8, .payload = data + offset + BLE_LOG_FRAME_HEAD_LEN, .payload_len = head.length, diff --git a/components/bt/common/ble_log/test_apps/ble_log_rt_test/main/test_ble_log_main.h b/components/bt/common/ble_log/test_apps/ble_log_rt_test/main/test_ble_log_main.h index 4e6abd1a6ce..1f82cf73b33 100644 --- a/components/bt/common/ble_log/test_apps/ble_log_rt_test/main/test_ble_log_main.h +++ b/components/bt/common/ble_log/test_apps/ble_log_rt_test/main/test_ble_log_main.h @@ -14,7 +14,6 @@ typedef struct { ble_log_src_t src; - uint8_t source_meta; uint32_t sn; const uint8_t *payload; size_t payload_len; diff --git a/components/bt/common/ble_log/test_apps/ble_log_test/README.md b/components/bt/common/ble_log/test_apps/ble_log_test/README.md index 47b48cf12a2..6649298b486 100644 --- a/components/bt/common/ble_log/test_apps/ble_log_test/README.md +++ b/components/bt/common/ble_log/test_apps/ble_log_test/README.md @@ -14,7 +14,7 @@ It covers: - literal protocol-v7 framing and fixed Internal Snapshot ABI; - build, library, chip, and protocol versions inside the snapshot; -- task and `NON_YIELD` source metadata plus HCI direction encoding; +- task and critical-section writes plus HCI direction encoding; - direct compression claim/commit, stale handles, and per-source serialization; - oversized-record rejection, flush sequence continuity, pool exhaustion, and non-yield reserve use; - periodic snapshot busy/loss behavior; diff --git a/components/bt/common/ble_log/test_apps/ble_log_test/main/test_ble_log_main.c b/components/bt/common/ble_log/test_apps/ble_log_test/main/test_ble_log_main.c index 4048079c8cf..0d0ec263587 100644 --- a/components/bt/common/ble_log/test_apps/ble_log_test/main/test_ble_log_main.c +++ b/components/bt/common/ble_log/test_apps/ble_log_test/main/test_ble_log_main.c @@ -35,10 +35,8 @@ bool test_ble_log_walk_frames(const uint8_t *data, size_t len, } if (observer) { - uint8_t source_meta = head.frame_meta & 0xff; test_ble_log_frame_t frame = { - .src = BLE_LOG_SRC_ID(source_meta), - .source_meta = source_meta, + .src = (ble_log_src_t)(head.frame_meta & 0xff), .sn = head.frame_meta >> 8, .payload = data + offset + BLE_LOG_FRAME_HEAD_LEN, .payload_len = head.length, diff --git a/components/bt/common/ble_log/test_apps/ble_log_test/main/test_ble_log_main.h b/components/bt/common/ble_log/test_apps/ble_log_test/main/test_ble_log_main.h index 4e6abd1a6ce..1f82cf73b33 100644 --- a/components/bt/common/ble_log/test_apps/ble_log_test/main/test_ble_log_main.h +++ b/components/bt/common/ble_log/test_apps/ble_log_test/main/test_ble_log_main.h @@ -14,7 +14,6 @@ typedef struct { ble_log_src_t src; - uint8_t source_meta; uint32_t sn; const uint8_t *payload; size_t payload_len; diff --git a/components/bt/common/ble_log/test_apps/ble_log_test/main/test_ble_log_rt.c b/components/bt/common/ble_log/test_apps/ble_log_test/main/test_ble_log_rt.c index 655b1cccdb5..f79c10a1f51 100644 --- a/components/bt/common/ble_log/test_apps/ble_log_test/main/test_ble_log_rt.c +++ b/components/bt/common/ble_log/test_apps/ble_log_test/main/test_ble_log_rt.c @@ -242,16 +242,15 @@ static void capture_golden_frame(const test_ble_log_frame_t *frame, void *ctx) TEST_CASE("BLE Log v7 framing matches golden bytes", "[ble_log][wire]") { static const uint8_t golden_frame[] = { - 0x05, 0x00, 0x87, 0xde, 0xc0, 0x00, + 0x05, 0x00, 0x07, 0xde, 0xc0, 0x00, 0x78, 0x56, 0x34, 0x12, 0xab, - 0xf1, 0x12, 0x54, 0x88, + 0xf1, 0x12, 0xd4, 0x88, }; static const uint8_t golden_payload[] = { 0x78, 0x56, 0x34, 0x12, 0xab, }; TEST_ASSERT_EQUAL_UINT8(7, BLE_LOG_VERSION); - TEST_ASSERT_EQUAL_HEX8(0x80, BLE_LOG_SRC_FLAG_NON_YIELD); TEST_ASSERT_EQUAL_UINT8(1, BLE_LOG_SRC_CORE_FIRST); TEST_ASSERT_EQUAL_UINT8(7, BLE_LOG_SRC_CORE_COUNT); TEST_ASSERT_EQUAL_UINT8(7, BLE_LOG_SRC_ENCODE); @@ -275,7 +274,7 @@ TEST_CASE("BLE Log v7 framing matches golden bytes", "[ble_log][wire]") TEST_ASSERT_EQUAL_UINT8(7, BLE_LOG_LL_FLAG_HCI_UPSTREAM); #endif TEST_ASSERT_EQUAL_HEX32( - 0x00c0de87, BLE_LOG_MAKE_FRAME_META(0x87, 0x00c0de)); + 0x00c0de07, BLE_LOG_MAKE_FRAME_META(0x07, 0x00c0de)); TEST_ASSERT_EQUAL_HEX32( 0x00000007, BLE_LOG_MAKE_FRAME_META(0x07, 0x01000000)); @@ -286,7 +285,6 @@ TEST_CASE("BLE Log v7 framing matches golden bytes", "[ble_log][wire]") &capture)); TEST_ASSERT_EQUAL_size_t(1, capture.count); TEST_ASSERT_EQUAL_UINT8(BLE_LOG_SRC_ENCODE, capture.frame.src); - TEST_ASSERT_EQUAL_HEX8(0x87, capture.frame.source_meta); TEST_ASSERT_EQUAL_HEX32(0x00c0de, capture.frame.sn); TEST_ASSERT_EQUAL_size_t(sizeof(golden_payload), capture.frame.payload_len); TEST_ASSERT_EQUAL_MEMORY(golden_payload, capture.frame.payload, @@ -402,7 +400,7 @@ TEST_CASE("BLE Log sync IO APIs retain runtime lifecycle checks", "[ble_log]") typedef struct { bool task_frame; - bool non_yield_frame; + bool critical_frame; bool hci_downstream_frame; bool hci_upstream_frame; bool claimed_frame; @@ -422,10 +420,9 @@ static void capture_frame_meta(const test_ble_log_frame_t *frame, void *ctx) uint8_t marker = frame->payload[sizeof(uint32_t)]; if (frame->src == BLE_LOG_SRC_CUSTOM && marker == 0x11) { - capture->task_frame = !BLE_LOG_SRC_IS_NON_YIELD(frame->source_meta); + capture->task_frame = true; } else if (frame->src == BLE_LOG_SRC_CUSTOM && marker == 0x22) { - capture->non_yield_frame = - BLE_LOG_SRC_IS_NON_YIELD(frame->source_meta); + capture->critical_frame = true; } else if (frame->src == BLE_LOG_SRC_HCI && marker == 0x01) { capture->hci_downstream_frame = true; } else if (frame->src == BLE_LOG_SRC_HCI && marker == 0x82) { @@ -437,7 +434,7 @@ static void capture_frame_meta(const test_ble_log_frame_t *frame, void *ctx) } } -TEST_CASE("BLE Log marks non-yield context and commits claimed payload", +TEST_CASE("BLE Log writes from critical sections and commits claimed payload", "[ble_log][lbm]") { const uint8_t task_marker = 0x11; @@ -511,7 +508,7 @@ TEST_CASE("BLE Log marks non-yield context and commits claimed payload", } TEST_ASSERT_TRUE(capture.task_frame); - TEST_ASSERT_TRUE(capture.non_yield_frame); + TEST_ASSERT_TRUE(capture.critical_frame); TEST_ASSERT_TRUE(capture.hci_downstream_frame); TEST_ASSERT_TRUE(capture.hci_upstream_frame); TEST_ASSERT_TRUE(capture.claimed_frame); @@ -1065,6 +1062,138 @@ TEST_CASE("BLE Log deinit hands residual transports to the peripheral", TEST_ASSERT_TRUE(ble_log_init()); } +typedef struct { + SemaphoreHandle_t started; + SemaphoreHandle_t done; + bool result; +} blocked_writer_ctx_t; + +static void blocked_write_task(void *arg) +{ + blocked_writer_ctx_t *ctx = arg; + static const uint8_t marker = 0x57; + xSemaphoreGive(ctx->started); + ctx->result = ble_log_write_hex(BLE_LOG_SRC_CUSTOM, &marker, + sizeof(marker)); + xSemaphoreGive(ctx->done); + vTaskDelete(NULL); +} + +static void blocked_claim_task(void *arg) +{ + blocked_writer_ctx_t *ctx = arg; + uint32_t handle; + xSemaphoreGive(ctx->started); + uint8_t *payload = ble_log_claim(BLE_LOG_SRC_ENCODE, 1, &handle); + ctx->result = payload != NULL; + if (payload) { + payload[0] = 0x58; + } + ble_log_commit(handle, 1); + xSemaphoreGive(ctx->done); + vTaskDelete(NULL); +} + +typedef struct { + bool write_frame; + bool claim_frame; +} blocked_capture_t; + +static void capture_blocked_markers(const test_ble_log_frame_t *frame, void *ctx) +{ + blocked_capture_t *capture = ctx; + if (frame->payload_len != sizeof(uint32_t) + 1) { + return; + } + uint8_t marker = frame->payload[sizeof(uint32_t)]; + if (frame->src == BLE_LOG_SRC_CUSTOM && marker == 0x57) { + capture->write_frame = true; + } else if (frame->src == BLE_LOG_SRC_ENCODE && marker == 0x58) { + capture->claim_frame = true; + } +} + +TEST_CASE("BLE Log task writers wait for a shared transport", "[ble_log][lbm]") +{ + static const uint8_t full_payload[ + BLE_LOG_MAX_PAYLOAD_LEN - sizeof(uint32_t)] = {0}; + + TEST_ASSERT_TRUE(ble_log_enable(true)); + ble_log_lbm_flush_open_trans(); + for (int round = 0; round < 2; round++) { + TEST_ASSERT_TRUE(ble_log_rt_drain()); + while (ble_log_prph_test_read(s_read_buf, sizeof(s_read_buf), + 0, 0, NULL) > 0) { + } + } + + /* Keep every task-usable transport SENDING in the test peripheral. */ + for (int i = 0; i < BLE_LOG_POOL_SHARED_CNT; i++) { + TEST_ASSERT_TRUE(ble_log_write_hex(BLE_LOG_SRC_CUSTOM, full_payload, + sizeof(full_payload))); + } + + blocked_writer_ctx_t writers[2] = {0}; + for (int i = 0; i < 2; i++) { + writers[i].started = xSemaphoreCreateBinary(); + writers[i].done = xSemaphoreCreateBinary(); + TEST_ASSERT_NOT_NULL(writers[i].started); + TEST_ASSERT_NOT_NULL(writers[i].done); + } + TEST_ASSERT_EQUAL(pdTRUE, + xTaskCreate(blocked_write_task, "ble_log_wait1", + TEST_LIFECYCLE_STACK_SIZE, &writers[0], + TEST_LIFECYCLE_PRIO, NULL)); + TEST_ASSERT_EQUAL(pdTRUE, + xTaskCreate(blocked_claim_task, "ble_log_wait2", + TEST_LIFECYCLE_STACK_SIZE, &writers[1], + TEST_LIFECYCLE_PRIO, NULL)); + for (int i = 0; i < 2; i++) { + TEST_ASSERT_TRUE(xSemaphoreTake(writers[i].started, + pdMS_TO_TICKS(1000))); + } + vTaskDelay(1); + /* No shared transport is free: both yieldable writers must be parked. */ + for (int i = 0; i < 2; i++) { + TEST_ASSERT_EQUAL(pdFALSE, xSemaphoreTake(writers[i].done, 0)); + } + + /* Recycling transports wakes the parked writers; neither write is lost. */ + TEST_ASSERT_TRUE(ble_log_rt_drain()); + for (int i = 0; i < 2; i++) { + size_t len = ble_log_prph_test_read(s_read_buf, sizeof(s_read_buf), + pdMS_TO_TICKS(TEST_READ_TIMEOUT_MS), + 0, NULL); + TEST_ASSERT_GREATER_THAN_size_t(0, len); + } + for (int i = 0; i < 2; i++) { + TEST_ASSERT_TRUE(xSemaphoreTake(writers[i].done, pdMS_TO_TICKS(1000))); + TEST_ASSERT_TRUE(writers[i].result); + } + + ble_log_lbm_flush_open_trans(); + TEST_ASSERT_TRUE(ble_log_rt_drain()); + blocked_capture_t capture = {0}; + for (int i = 0; i < BLE_LOG_TRANS_TOTAL_CNT; i++) { + size_t len = ble_log_prph_test_read(s_read_buf, sizeof(s_read_buf), + pdMS_TO_TICKS(TEST_READ_TIMEOUT_MS), + 0, NULL); + if (!len) { + break; + } + TEST_ASSERT_TRUE(test_ble_log_walk_frames(s_read_buf, len, + capture_blocked_markers, + &capture)); + } + TEST_ASSERT_TRUE(capture.write_frame); + TEST_ASSERT_TRUE(capture.claim_frame); + + for (int i = 0; i < 2; i++) { + vSemaphoreDelete(writers[i].started); + vSemaphoreDelete(writers[i].done); + } +} + #define SNAPSHOT_CAPTURE_MAX 8 typedef struct { @@ -1229,12 +1358,6 @@ TEST_CASE("BLE Log deinit closes a parked LL writer before racing enable", sizeof(full_payload))); } static const uint8_t reserve_marker = 0x56; - TEST_ASSERT_FALSE(ble_log_write_hex(BLE_LOG_SRC_CUSTOM, - &reserve_marker, - sizeof(reserve_marker))); - uint32_t exhausted_handle; - TEST_ASSERT_NULL(ble_log_claim(BLE_LOG_SRC_ENCODE, 1, - &exhausted_handle)); portMUX_TYPE reserve_mux = portMUX_INITIALIZER_UNLOCKED; portENTER_CRITICAL(&reserve_mux);