feat(bt/ble_log): implement UART redirection stream write

Introduce stream write API with deferred frame encapsulation for UART
redirection on port 0. Redirected data is now properly framed (header +
payload + checksum) instead of being sent as raw ASCII, preventing frame
parser sync oscillation on the receiver.

- Add stream_seal/stream_write/stream_flush in ble_log_lbm.c
- Add BLE_LOG_SRC_REDIR source and BLE_LOG_UART_REDIR_ENABLED gate
- Simplify redir_uart_tx_chars and timer callback to use stream API
- Flush pending stream data in ble_log_prph_deinit
- Make get_trans static (no external callers after refactor)
- Move UART wrap linker flags outside CONFIG_BT_ENABLED guard
- Default UART DMA peripheral when SOC_UHCI_SUPPORTED
- Default baud rate 921600 -> 3000000
This commit is contained in:
Zhou Xiao
2026-03-22 01:20:05 +08:00
parent 24a368974e
commit 490acad912
6 changed files with 137 additions and 35 deletions
+10 -7
View File
@@ -1023,6 +1023,16 @@ idf_component_register(SRCS "${srcs}"
PRIV_REQUIRES "${bt_priv_requires}"
LDFRAGMENTS "${ldscripts}")
# UART redir wrap flags — needed whenever BLE Log uses UART DMA on port 0,
# regardless of whether BLE controller is enabled.
if(DEFINED CONFIG_BLE_LOG_PRPH_UART_DMA_PORT)
if(CONFIG_BLE_LOG_PRPH_UART_DMA_PORT EQUAL 0)
target_link_libraries(${COMPONENT_LIB} INTERFACE "-Wl,--wrap=uart_tx_chars")
target_link_libraries(${COMPONENT_LIB} INTERFACE "-Wl,--wrap=uart_write_bytes")
target_link_libraries(${COMPONENT_LIB} INTERFACE "-Wl,--wrap=uart_write_bytes_with_break")
endif()
endif()
if(CONFIG_BLE_COMPRESSED_LOG_ENABLE)
if(LOG_COMPRESSION_TARGET)
add_dependencies(${COMPONENT_LIB} ${LOG_COMPRESSION_TARGET})
@@ -1056,13 +1066,6 @@ if(CONFIG_BT_ENABLED)
if(CONFIG_BT_LE_CONTROLLER_LOG_WRAP_PANIC_HANDLER_ENABLE)
target_link_libraries(${COMPONENT_LIB} INTERFACE "-Wl,--wrap=esp_panic_handler")
endif()
if(DEFINED CONFIG_BLE_LOG_PRPH_UART_DMA_PORT)
if(CONFIG_BLE_LOG_PRPH_UART_DMA_PORT EQUAL 0)
target_link_libraries(${COMPONENT_LIB} INTERFACE "-Wl,--wrap=uart_tx_chars")
target_link_libraries(${COMPONENT_LIB} INTERFACE "-Wl,--wrap=uart_write_bytes")
target_link_libraries(${COMPONENT_LIB} INTERFACE "-Wl,--wrap=uart_write_bytes_with_break")
endif()
endif()
if(CONFIG_IDF_TARGET_ESP32C6)
add_prebuilt_library(libble_app "controller/lib_${target}/${target}-bt-lib/esp32c6/libble_app.a"
REQUIRES esp_phy)
+2 -1
View File
@@ -152,6 +152,7 @@ if BLE_LOG_ENABLED
choice BLE_LOG_PRPH_CHOICE
prompt "BLE Log peripheral choice"
default BLE_LOG_PRPH_UART_DMA if SOC_UHCI_SUPPORTED
default BLE_LOG_PRPH_DUMMY
help
Choose BLE Log peripheral
@@ -203,7 +204,7 @@ if BLE_LOG_ENABLED
config BLE_LOG_PRPH_UART_DMA_BAUD_RATE
int "Baud rate of UART port for UART DMA transport"
default 921600
default 3000000
help
Determine the baud rate of UART port
@@ -35,6 +35,9 @@ typedef enum {
BLE_LOG_SRC_HCI,
BLE_LOG_SRC_ENCODE,
/* UART redirection (PORT 0 only) */
BLE_LOG_SRC_REDIR,
BLE_LOG_SRC_MAX,
} ble_log_src_t;
+104 -5
View File
@@ -27,9 +27,15 @@ BLE_LOG_STATIC ble_log_stat_mgr_t *stat_mgr_ctx[BLE_LOG_SRC_MAX] = {0};
BLE_LOG_STATIC ble_log_lbm_t *ble_log_lbm_acquire(void);
BLE_LOG_STATIC void ble_log_lbm_release(ble_log_lbm_t *lbm);
BLE_LOG_STATIC
ble_log_prph_trans_t **ble_log_lbm_get_trans(ble_log_lbm_t *lbm, size_t log_len);
BLE_LOG_STATIC
void ble_log_lbm_write_trans(ble_log_prph_trans_t **trans, ble_log_src_t src_code,
const uint8_t *addr, uint16_t len,
const uint8_t *addr_append, uint16_t len_append, bool omdata);
#if BLE_LOG_UART_REDIR_ENABLED
BLE_LOG_STATIC
void ble_log_lbm_stream_seal(ble_log_prph_trans_t **trans, ble_log_src_t src_code);
#endif /* BLE_LOG_UART_REDIR_ENABLED */
BLE_LOG_STATIC void ble_log_stat_mgr_update(ble_log_src_t src_code, uint32_t len, bool lost);
/* ------------------------- */
@@ -131,6 +137,33 @@ void ble_log_lbm_write_trans(ble_log_prph_trans_t **trans, ble_log_src_t src_cod
}
}
#if BLE_LOG_UART_REDIR_ENABLED
BLE_LOG_IRAM_ATTR BLE_LOG_STATIC
void ble_log_lbm_stream_seal(ble_log_prph_trans_t **trans, ble_log_src_t src_code)
{
if ((*trans)->pos <= BLE_LOG_FRAME_HEAD_LEN) {
return;
}
uint16_t payload_len = (*trans)->pos - BLE_LOG_FRAME_HEAD_LEN;
ble_log_stat_mgr_t *stat_mgr = stat_mgr_ctx[src_code];
uint32_t frame_sn = BLE_LOG_GET_FRAME_SN(&(stat_mgr->frame_sn));
ble_log_frame_head_t frame_head = {
.length = payload_len,
.frame_meta = BLE_LOG_MAKE_FRAME_META(src_code, frame_sn),
};
BLE_LOG_MEMCPY((*trans)->buf, &frame_head, BLE_LOG_FRAME_HEAD_LEN);
uint32_t checksum = ble_log_fast_checksum((*trans)->buf, (*trans)->pos);
BLE_LOG_MEMCPY((*trans)->buf + (*trans)->pos, &checksum, BLE_LOG_FRAME_TAIL_LEN);
(*trans)->pos += BLE_LOG_FRAME_TAIL_LEN;
ble_log_stat_mgr_update(src_code, payload_len, false);
ble_log_rt_queue_trans(trans);
}
#endif /* BLE_LOG_UART_REDIR_ENABLED */
BLE_LOG_IRAM_ATTR BLE_LOG_STATIC
void ble_log_stat_mgr_update(ble_log_src_t src_code, uint32_t len, bool lost)
{
@@ -263,11 +296,7 @@ void ble_log_lbm_deinit(void)
}
}
/* Note:
* The function below should be private, but when UART redirection is required,
* it would be a waste to implement get transport function again, thus
* make it available internally */
BLE_LOG_IRAM_ATTR
BLE_LOG_IRAM_ATTR BLE_LOG_STATIC
ble_log_prph_trans_t **ble_log_lbm_get_trans(ble_log_lbm_t *lbm, size_t log_len)
{
/* Check if available buffer can contain incoming log */
@@ -324,6 +353,76 @@ deref:
BLE_LOG_REF_COUNT_RELEASE(&lbm_ref_count);
}
#if BLE_LOG_UART_REDIR_ENABLED
/* ------------------------------------------------- */
/* STREAM WRITE INTERFACE */
/* */
/* Stream mode appends raw data into a transport */
/* buffer with deferred frame encapsulation: */
/* - Header space is reserved on first write */
/* - Data is memcpy'd after the reserved header */
/* - Header and checksum are filled in at seal */
/* */
/* get_trans(lbm, 0) reuse safety: */
/* */
/* get_trans auto-queues a buffer raw (no seal) when */
/* free_space < log_len + FRAME_OVERHEAD. With */
/* log_len = 0, this triggers at free_space < 10. */
/* */
/* To prevent unsealed stream data from being sent */
/* raw, stream_write auto-seals at free_space <= 10. */
/* This guarantees that any unsealed stream buffer */
/* seen by get_trans always has free_space > 10, */
/* so get_trans returns it directly without queuing. */
/* ------------------------------------------------- */
BLE_LOG_IRAM_ATTR
void ble_log_lbm_stream_write(ble_log_lbm_t *lbm, ble_log_src_t src_code,
const uint8_t *data, size_t len)
{
while (len > 0) {
ble_log_prph_trans_t **trans = ble_log_lbm_get_trans(lbm, 0);
if (!trans) {
ble_log_stat_mgr_update(src_code, len, true);
return;
}
if ((*trans)->pos == 0) {
(*trans)->pos = BLE_LOG_FRAME_HEAD_LEN;
}
uint16_t available = BLE_LOG_TRANS_FREE_SPACE((*trans));
if (available <= BLE_LOG_FRAME_TAIL_LEN) {
ble_log_lbm_stream_seal(trans, src_code);
continue;
}
available -= BLE_LOG_FRAME_TAIL_LEN;
size_t to_write = (len < available) ? len : available;
BLE_LOG_MEMCPY((*trans)->buf + (*trans)->pos, data, to_write);
(*trans)->pos += to_write;
data += to_write;
len -= to_write;
if (BLE_LOG_TRANS_FREE_SPACE((*trans)) <= BLE_LOG_FRAME_OVERHEAD) {
ble_log_lbm_stream_seal(trans, src_code);
}
}
}
BLE_LOG_IRAM_ATTR
void ble_log_lbm_stream_flush(ble_log_lbm_t *lbm, ble_log_src_t src_code)
{
int trans_idx = lbm->trans_idx;
for (int i = 0; i < BLE_LOG_TRANS_PING_PONG_BUF_CNT; i++) {
ble_log_prph_trans_t **trans = &(lbm->trans[trans_idx]);
if (!(*trans)->prph_owned && (*trans)->pos > BLE_LOG_FRAME_HEAD_LEN) {
ble_log_lbm_stream_seal(trans, src_code);
}
trans_idx = (trans_idx + 1) % BLE_LOG_TRANS_PING_PONG_BUF_CNT;
}
}
#endif /* BLE_LOG_UART_REDIR_ENABLED */
/* ------------------------ */
/* PUBLIC INTERFACE */
/* ------------------------ */
@@ -16,6 +16,12 @@
#include "ble_log_prph.h"
#include "freertos/FreeRTOS.h"
#if defined(CONFIG_BLE_LOG_PRPH_UART_DMA) && (CONFIG_BLE_LOG_PRPH_UART_DMA_PORT == 0)
#define BLE_LOG_UART_REDIR_ENABLED (1)
#else
#define BLE_LOG_UART_REDIR_ENABLED (0)
#endif
#include "freertos/semphr.h"
/* ------------------------- */
@@ -173,8 +179,12 @@ enum {
/* --------------------------- */
bool ble_log_lbm_init(void);
void ble_log_lbm_deinit(void);
ble_log_prph_trans_t **ble_log_lbm_get_trans(ble_log_lbm_t *lbm, size_t log_len);
void ble_log_lbm_enable(bool enable);
void ble_log_write_enh_stat(void);
#if BLE_LOG_UART_REDIR_ENABLED
void ble_log_lbm_stream_write(ble_log_lbm_t *lbm, ble_log_src_t src_code,
const uint8_t *data, size_t len);
void ble_log_lbm_stream_flush(ble_log_lbm_t *lbm, ble_log_src_t src_code);
#endif
#endif /* __BLE_LOG_LBM_H__ */
@@ -13,7 +13,6 @@
#if BLE_LOG_PRPH_UART_DMA_REDIR
#include "ble_log.h"
#include "ble_log_rt.h"
#include "ble_log_lbm.h"
#include "esp_timer.h"
@@ -71,16 +70,10 @@ BLE_LOG_IRAM_ATTR BLE_LOG_STATIC void esp_timer_cb_flush_log(void *arg)
}
/* Non-blocking trylock: skip if mutex is held by a writer.
* The periodic timer will retry on the next tick. */
* The periodic timer will retry on the next tick.
* stream_flush is a no-op when buffer is empty. */
if (xSemaphoreTake(redir_lbm->mutex, 0) == pdTRUE) {
int trans_idx = redir_lbm->trans_idx;
for (int i = 0; i < BLE_LOG_TRANS_PING_PONG_BUF_CNT; i++) {
ble_log_prph_trans_t **trans = &(redir_lbm->trans[trans_idx]);
if (!(*trans)->prph_owned && (*trans)->pos) {
ble_log_rt_queue_trans(trans);
}
trans_idx = !trans_idx;
}
ble_log_lbm_stream_flush(redir_lbm, BLE_LOG_SRC_REDIR);
xSemaphoreGive(redir_lbm->mutex);
}
}
@@ -132,6 +125,7 @@ bool ble_log_prph_init(size_t trans_cnt)
goto exit;
}
BLE_LOG_MEMSET(redir_lbm, 0, sizeof(ble_log_lbm_t));
redir_lbm->lock_type = BLE_LOG_LBM_LOCK_MUTEX;
/* Transport initialization */
for (int i = 0; i < BLE_LOG_TRANS_PING_PONG_BUF_CNT; i++) {
@@ -195,9 +189,9 @@ void ble_log_prph_deinit(void)
/* Release redirection LBM */
if (redir_lbm) {
/* Release mutex */
if (redir_lbm->mutex) {
xSemaphoreTake(redir_lbm->mutex, portMAX_DELAY);
ble_log_lbm_stream_flush(redir_lbm, BLE_LOG_SRC_REDIR);
xSemaphoreGive(redir_lbm->mutex);
vSemaphoreDelete(redir_lbm->mutex);
}
@@ -287,16 +281,8 @@ BLE_LOG_IRAM_ATTR BLE_LOG_STATIC
void ble_log_redir_uart_tx_chars(const char *src, size_t len)
{
xSemaphoreTake(redir_lbm->mutex, portMAX_DELAY);
ble_log_prph_trans_t **trans = ble_log_lbm_get_trans(redir_lbm, len);
if (trans) {
uint8_t *buf = (*trans)->buf + (*trans)->pos;
BLE_LOG_MEMCPY(buf, src, len);
(*trans)->pos += len;
if (BLE_LOG_TRANS_FREE_SPACE((*trans)) <= BLE_LOG_FRAME_OVERHEAD) {
ble_log_rt_queue_trans(trans);
}
}
ble_log_lbm_stream_write(redir_lbm, BLE_LOG_SRC_REDIR,
(const uint8_t *)src, len);
xSemaphoreGive(redir_lbm->mutex);
}