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


(cherry picked from commit a181fc156d)

Co-authored-by: Zhou Xiao <zhouxiao@espressif.com>
This commit is contained in:
Zhou Xiao
2026-04-03 21:14:40 +08:00
parent 3c622ec40e
commit e4f952d8cd
6 changed files with 137 additions and 35 deletions
+10 -7
View File
@@ -1021,6 +1021,16 @@ idf_component_register(SRCS "${srcs}"
PRIV_REQUIRES "${bt_priv_requires}" PRIV_REQUIRES "${bt_priv_requires}"
LDFRAGMENTS "${ldscripts}") 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(CONFIG_BLE_COMPRESSED_LOG_ENABLE)
if(LOG_COMPRESSION_TARGET) if(LOG_COMPRESSION_TARGET)
add_dependencies(${COMPONENT_LIB} ${LOG_COMPRESSION_TARGET}) add_dependencies(${COMPONENT_LIB} ${LOG_COMPRESSION_TARGET})
@@ -1055,13 +1065,6 @@ if(CONFIG_BT_ENABLED)
if(CONFIG_BT_LE_CONTROLLER_LOG_WRAP_PANIC_HANDLER_ENABLE) if(CONFIG_BT_LE_CONTROLLER_LOG_WRAP_PANIC_HANDLER_ENABLE)
target_link_options(${COMPONENT_LIB} INTERFACE "-Wl,--wrap=esp_panic_handler") target_link_options(${COMPONENT_LIB} INTERFACE "-Wl,--wrap=esp_panic_handler")
endif() 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) if(CONFIG_IDF_TARGET_ESP32C6)
add_prebuilt_library(libble_app add_prebuilt_library(libble_app
"${CMAKE_CURRENT_LIST_DIR}/controller/lib_esp32c6/esp32c6-bt-lib/esp32c6/libble_app.a" "${CMAKE_CURRENT_LIST_DIR}/controller/lib_esp32c6/esp32c6-bt-lib/esp32c6/libble_app.a"
+2 -1
View File
@@ -152,6 +152,7 @@ if BLE_LOG_ENABLED
choice BLE_LOG_PRPH_CHOICE choice BLE_LOG_PRPH_CHOICE
prompt "BLE Log peripheral choice" prompt "BLE Log peripheral choice"
default BLE_LOG_PRPH_UART_DMA if SOC_UHCI_SUPPORTED
default BLE_LOG_PRPH_DUMMY default BLE_LOG_PRPH_DUMMY
help help
Choose BLE Log peripheral Choose BLE Log peripheral
@@ -203,7 +204,7 @@ if BLE_LOG_ENABLED
config BLE_LOG_PRPH_UART_DMA_BAUD_RATE config BLE_LOG_PRPH_UART_DMA_BAUD_RATE
int "Baud rate of UART port for UART DMA transport" int "Baud rate of UART port for UART DMA transport"
default 921600 default 3000000
help help
Determine the baud rate of UART port Determine the baud rate of UART port
@@ -35,6 +35,9 @@ typedef enum {
BLE_LOG_SRC_HCI, BLE_LOG_SRC_HCI,
BLE_LOG_SRC_ENCODE, BLE_LOG_SRC_ENCODE,
/* UART redirection (PORT 0 only) */
BLE_LOG_SRC_REDIR,
BLE_LOG_SRC_MAX, BLE_LOG_SRC_MAX,
} ble_log_src_t; } 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 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 void ble_log_lbm_release(ble_log_lbm_t *lbm);
BLE_LOG_STATIC 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, 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, uint16_t len,
const uint8_t *addr_append, uint16_t len_append, bool omdata); 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); 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 BLE_LOG_IRAM_ATTR BLE_LOG_STATIC
void ble_log_stat_mgr_update(ble_log_src_t src_code, uint32_t len, bool lost) 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: BLE_LOG_IRAM_ATTR BLE_LOG_STATIC
* 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_prph_trans_t **ble_log_lbm_get_trans(ble_log_lbm_t *lbm, size_t log_len) 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 */ /* Check if available buffer can contain incoming log */
@@ -324,6 +353,76 @@ deref:
BLE_LOG_REF_COUNT_RELEASE(&lbm_ref_count); 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 */ /* PUBLIC INTERFACE */
/* ------------------------ */ /* ------------------------ */
@@ -16,6 +16,12 @@
#include "ble_log_prph.h" #include "ble_log_prph.h"
#include "freertos/FreeRTOS.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" #include "freertos/semphr.h"
/* ------------------------- */ /* ------------------------- */
@@ -173,8 +179,12 @@ enum {
/* --------------------------- */ /* --------------------------- */
bool ble_log_lbm_init(void); bool ble_log_lbm_init(void);
void ble_log_lbm_deinit(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_lbm_enable(bool enable);
void ble_log_write_enh_stat(void); 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__ */ #endif /* __BLE_LOG_LBM_H__ */
@@ -13,7 +13,6 @@
#if BLE_LOG_PRPH_UART_DMA_REDIR #if BLE_LOG_PRPH_UART_DMA_REDIR
#include "ble_log.h" #include "ble_log.h"
#include "ble_log_rt.h"
#include "ble_log_lbm.h" #include "ble_log_lbm.h"
#include "esp_timer.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. /* 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) { if (xSemaphoreTake(redir_lbm->mutex, 0) == pdTRUE) {
int trans_idx = redir_lbm->trans_idx; ble_log_lbm_stream_flush(redir_lbm, BLE_LOG_SRC_REDIR);
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;
}
xSemaphoreGive(redir_lbm->mutex); xSemaphoreGive(redir_lbm->mutex);
} }
} }
@@ -132,6 +125,7 @@ bool ble_log_prph_init(size_t trans_cnt)
goto exit; goto exit;
} }
BLE_LOG_MEMSET(redir_lbm, 0, sizeof(ble_log_lbm_t)); BLE_LOG_MEMSET(redir_lbm, 0, sizeof(ble_log_lbm_t));
redir_lbm->lock_type = BLE_LOG_LBM_LOCK_MUTEX;
/* Transport initialization */ /* Transport initialization */
for (int i = 0; i < BLE_LOG_TRANS_PING_PONG_BUF_CNT; i++) { 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 */ /* Release redirection LBM */
if (redir_lbm) { if (redir_lbm) {
/* Release mutex */
if (redir_lbm->mutex) { if (redir_lbm->mutex) {
xSemaphoreTake(redir_lbm->mutex, portMAX_DELAY); xSemaphoreTake(redir_lbm->mutex, portMAX_DELAY);
ble_log_lbm_stream_flush(redir_lbm, BLE_LOG_SRC_REDIR);
xSemaphoreGive(redir_lbm->mutex); xSemaphoreGive(redir_lbm->mutex);
vSemaphoreDelete(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) void ble_log_redir_uart_tx_chars(const char *src, size_t len)
{ {
xSemaphoreTake(redir_lbm->mutex, portMAX_DELAY); xSemaphoreTake(redir_lbm->mutex, portMAX_DELAY);
ble_log_prph_trans_t **trans = ble_log_lbm_get_trans(redir_lbm, len); ble_log_lbm_stream_write(redir_lbm, BLE_LOG_SRC_REDIR,
if (trans) { (const uint8_t *)src, len);
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);
}
}
xSemaphoreGive(redir_lbm->mutex); xSemaphoreGive(redir_lbm->mutex);
} }