feat(ble): support ble log uart dma out

This commit is contained in:
Zhou Xiao
2025-07-15 19:57:14 +08:00
parent 4324c48388
commit 534b43e85a
4 changed files with 882 additions and 0 deletions

View File

@@ -137,6 +137,7 @@ if(CONFIG_BT_ENABLED)
"common/osi/semaphore.c"
"porting/mem/bt_osi_mem.c"
"common/ble_log/ble_log_spi_out.c"
"common/ble_log/ble_log_uhci_out.c"
)
# Host Bluedroid
@@ -946,6 +947,11 @@ 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(CONFIG_BT_BLE_LOG_UHCI_OUT_ENABLED)
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()
if(CONFIG_IDF_TARGET_ESP32C6)
add_prebuilt_library(libble_app
"${CMAKE_CURRENT_LIST_DIR}/controller/lib_esp32c6/esp32c6-bt-lib/esp32c6/libble_app.a"

View File

@@ -197,3 +197,64 @@ config BT_BLE_LOG_SPI_OUT_MESH_TASK_CNT
default 3
help
Mesh task count
config BT_BLE_LOG_UHCI_OUT_ENABLED
bool "Output ble logs via UHCI (UART DMA) driver (Experimental)"
default n
help
Output ble logs via UHCI (UART DMA) driver
On enable, BT_BLE_LOG_UHCI_OUT_UART_PORT would be reinited with
BT_BLE_LOG_UHCI_OUT_UART_BAUD_RATE as new baud rate and
BT_BLE_LOG_UHCI_OUT_UART_IO_NUM_TX as new UART Tx IO
config BT_BLE_LOG_UHCI_OUT_UART_PORT
int "UART port connected to UHCI controller"
depends on BT_BLE_LOG_UHCI_OUT_ENABLED
default 0
help
UART port connected to UHCI controller
If UART port 0 is selected, UART VFS Driver, UART ROM Driver
and UART Driver output would be redirected to BLE Log UHCI Out
to solve UART Tx FIFO multi-task access issue
config BT_BLE_LOG_UHCI_OUT_LL_TASK_BUF_SIZE
int "UHCI transaction buffer size for lower layer task logs"
depends on BT_BLE_LOG_UHCI_OUT_ENABLED
default 1024
help
UHCI transaction buffer size for lower layer task logs
config BT_BLE_LOG_UHCI_OUT_LL_ISR_BUF_SIZE
int "UHCI transaction buffer size for lower layer ISR logs"
depends on BT_BLE_LOG_UHCI_OUT_ENABLED
default 1024
help
UHCI transaction buffer size for lower layer ISR logs
config BT_BLE_LOG_UHCI_OUT_LL_HCI_BUF_SIZE
int "UHCI transaction buffer size for lower layer HCI logs"
depends on BT_BLE_LOG_UHCI_OUT_ENABLED
default 1024
help
UHCI transaction buffer size for lower layer HCI logs
config BT_BLE_LOG_UHCI_OUT_UART_NEED_INIT
bool "Enable to init UART port"
depends on BT_BLE_LOG_UHCI_OUT_ENABLED
default y
help
Enable to init UART port
config BT_BLE_LOG_UHCI_OUT_UART_BAUD_RATE
int "Baud rate for BT_BLE_LOG_UHCI_OUT_UART_PORT"
depends on BT_BLE_LOG_UHCI_OUT_UART_NEED_INIT
default 3000000
help
Baud rate for BT_BLE_LOG_UHCI_OUT_UART_PORT
config BT_BLE_LOG_UHCI_OUT_UART_IO_NUM_TX
int "IO number for UART TX port"
depends on BT_BLE_LOG_UHCI_OUT_UART_NEED_INIT
default 0
help
IO number for UART TX port

View File

@@ -0,0 +1,780 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "ble_log/ble_log_uhci_out.h"
#if CONFIG_BT_BLE_LOG_UHCI_OUT_ENABLED
// Private includes
#include "esp_bt.h"
// sdkconfig defines
#define UHCI_OUT_LL_TASK_BUF_SIZE CONFIG_BT_BLE_LOG_UHCI_OUT_LL_TASK_BUF_SIZE
#define UHCI_OUT_LL_ISR_BUF_SIZE CONFIG_BT_BLE_LOG_UHCI_OUT_LL_ISR_BUF_SIZE
#define UHCI_OUT_LL_HCI_BUF_SIZE CONFIG_BT_BLE_LOG_UHCI_OUT_LL_HCI_BUF_SIZE
#define UHCI_OUT_UART_PORT CONFIG_BT_BLE_LOG_UHCI_OUT_UART_PORT
#define UHCI_OUT_UART_NEED_INIT CONFIG_BT_BLE_LOG_UHCI_OUT_UART_NEED_INIT
#if UHCI_OUT_UART_NEED_INIT
#define UHCI_OUT_UART_BAUD_RATE CONFIG_BT_BLE_LOG_UHCI_OUT_UART_BAUD_RATE
#define UHCI_OUT_UART_IO_NUM_TX CONFIG_BT_BLE_LOG_UHCI_OUT_UART_IO_NUM_TX
#endif // UHCI_OUT_UART_NEED_INIT
// Private defines
#define UHCI_OUT_MAX_TRANSFER_SIZE (10240)
#define UHCI_OUT_MALLOC(size) heap_caps_malloc(size, MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT)
#define UHCI_OUT_FLUSH_TIMEOUT_MS (100)
#define UHCI_OUT_FLUSH_TIMEOUT_US (UHCI_OUT_FLUSH_TIMEOUT_MS * 1000)
#define UHCI_OUT_USER_BUF_SIZE (512)
#define UHCI_OUT_UART_PORT0 (0)
#define UHCI_OUT_UART_PORT1 (1)
#define UHCI_OUT_UART_DRIVER_RX_BUF_SIZE (32)
// Queue size defines
#define UHCI_OUT_PING_PONG_BUF_CNT (2)
#define UHCI_OUT_USER_QUEUE_SIZE (UHCI_OUT_PING_PONG_BUF_CNT)
#define UHCI_OUT_LL_QUEUE_SIZE (3 * UHCI_OUT_PING_PONG_BUF_CNT)
#define UHCI_OUT_QUEUE_SIZE (UHCI_OUT_USER_QUEUE_SIZE + UHCI_OUT_LL_QUEUE_SIZE)
// Private typedefs
typedef struct {
// This flag is for multithreading, must be a word, do not modify
volatile uint32_t flag;
uint16_t buf_size;
uint16_t length;
uint8_t buffer[0];
} uhci_out_trans_cb_t;
typedef struct {
uhci_out_trans_cb_t *trans_cb[2];
uint8_t trans_cb_idx;
uint8_t type;
uint16_t lost_frame_cnt;
uint32_t lost_bytes_cnt;
uint32_t frame_sn;
} uhci_out_log_cb_t;
typedef struct {
uint16_t length;
uint8_t source;
uint8_t type;
uint16_t frame_sn;
} __attribute__((packed)) frame_head_t;
typedef struct {
uint8_t type;
uint16_t lost_frame_cnt;
uint32_t lost_bytes_cnt;
} __attribute__((packed)) loss_payload_t;
// Private enums
enum {
TRANS_CB_FLAG_AVAILABLE = 0,
TRANS_CB_FLAG_NEED_QUEUE,
TRANS_CB_FLAG_IN_QUEUE,
};
enum {
LOG_CB_TYPE_USER = 0,
LOG_CB_TYPE_LL,
};
enum {
LOG_CB_LL_SUBTYPE_TASK = 0,
LOG_CB_LL_SUBTYPE_ISR,
LOG_CB_LL_SUBTYPE_HCI
};
enum {
LL_LOG_FLAG_CONTINUE = 0,
LL_LOG_FLAG_END,
LL_LOG_FLAG_TASK,
LL_LOG_FLAG_ISR,
LL_LOG_FLAG_HCI,
LL_LOG_FLAG_RAW,
LL_LOG_FLAG_SYNC
};
enum {
LL_EV_FLAG_ISR_APPEND = 0,
LL_EV_FLAG_FLUSH_LOG,
};
// Private variables
static bool uhci_out_inited = false;
static uhci_controller_handle_t uhci_handle = NULL;
static bool user_log_inited = false;
static SemaphoreHandle_t user_log_mutex = NULL;
static uhci_out_log_cb_t *user_log_cb = NULL;
static uint32_t user_last_write_ts = 0;
static bool ll_log_inited = false;
static uhci_out_log_cb_t *ll_task_log_cb = NULL;
static uhci_out_log_cb_t *ll_isr_log_cb = NULL;
static uhci_out_log_cb_t *ll_hci_log_cb = NULL;
static uint32_t ll_ev_flags = 0;
static uint32_t ll_last_write_ts = 0;
static esp_timer_handle_t flush_timer = NULL;
// Private function declarations
extern void esp_panic_handler_feed_wdts(void);
static int uhci_out_init_trans(uhci_out_trans_cb_t **trans_cb, uint16_t buf_size);
static void uhci_out_deinit_trans(uhci_out_trans_cb_t **trans_cb);
static bool uhci_out_tx_done_cb(uhci_controller_handle_t uhci_ctrl,
const uhci_tx_done_event_data_t *edata, void *user_ctx);
static inline void uhci_out_append_trans(uhci_out_trans_cb_t *trans_cb);
static int uhci_out_log_cb_init(uhci_out_log_cb_t **log_cb, uint16_t buf_size, uint8_t type, uint8_t idx);
static void uhci_out_log_cb_deinit(uhci_out_log_cb_t **log_cb);
static inline bool uhci_out_log_cb_check_trans(uhci_out_log_cb_t *log_cb, uint16_t len, bool *need_append);
static inline void uhci_out_log_cb_append_trans(uhci_out_log_cb_t *log_cb);
static inline void uhci_out_log_cb_flush_trans(uhci_out_log_cb_t *log_cb);
static bool uhci_out_log_cb_write(uhci_out_log_cb_t *log_cb, const uint8_t *addr, uint16_t len,
const uint8_t *addr_append, uint16_t len_append, uint8_t source);
static void uhci_out_log_cb_write_loss(uhci_out_log_cb_t *log_cb);
static void uhci_out_log_cb_dump(uhci_out_log_cb_t *log_cb);
static void esp_timer_cb_log_flush(void);
static void uhci_out_user_write_str(const uint8_t *src, uint16_t len);
#if UHCI_OUT_UART_PORT == UHCI_OUT_UART_PORT0
static void uhci_out_user_write_char(char c);
#endif // UHCI_OUT_UART_PORT == UHCI_OUT_UART_PORT0
static int uhci_out_user_log_init(void);
static void uhci_out_user_log_deinit(void);
static int uhci_out_ll_log_init(void);
static void uhci_out_ll_log_deinit(void);
static void uhci_out_ll_log_flush(void);
#if defined(CONFIG_IDF_TARGET_ESP32H2) || defined(CONFIG_IDF_TARGET_ESP32C6) || defined(CONFIG_IDF_TARGET_ESP32C5) ||\
defined(CONFIG_IDF_TARGET_ESP32C61) || defined(CONFIG_IDF_TARGET_ESP32H21)
extern void r_ble_log_simple_put_ev(void);
#define UHCI_OUT_LL_PUT_EV r_ble_log_simple_put_ev()
#elif defined(CONFIG_IDF_TARGET_ESP32C2)
extern void ble_log_simple_put_ev(void);
#define UHCI_OUT_LL_PUT_EV ble_log_simple_put_ev()
#else
#define UHCI_OUT_LL_PUT_EV
#endif
// Private macros
#define UHCI_OUT_FRAME_HEAD_LEN (sizeof(frame_head_t))
#define UHCI_OUT_FRAME_TAIL_LEN (sizeof(uint32_t))
#define UHCI_OUT_FRAME_OVERHEAD (UHCI_OUT_FRAME_HEAD_LEN + UHCI_OUT_FRAME_TAIL_LEN)
#define UHCI_OUT_GET_FRAME_SN(VAR) __atomic_fetch_add(VAR, 1, __ATOMIC_RELAXED)
// Private functions
static int uhci_out_init_trans(uhci_out_trans_cb_t **trans_cb, uint16_t buf_size)
{
// Memory allocations
size_t cb_size = sizeof(uhci_out_trans_cb_t) + buf_size;
*trans_cb = (uhci_out_trans_cb_t *)UHCI_OUT_MALLOC(cb_size);
if (!(*trans_cb)) {
return -1;
}
memset(*trans_cb, 0, sizeof(uhci_out_trans_cb_t));
// Initialization
(*trans_cb)->buf_size = buf_size;
return 0;
}
static void uhci_out_deinit_trans(uhci_out_trans_cb_t **trans_cb)
{
if (!(*trans_cb)) {
return;
}
free(*trans_cb);
*trans_cb = NULL;
return;
}
IRAM_ATTR static bool uhci_out_tx_done_cb(uhci_controller_handle_t uhci_ctrl,
const uhci_tx_done_event_data_t *edata, void *user_ctx)
{
uhci_out_trans_cb_t *trans_cb = (uhci_out_trans_cb_t *)((uint8_t *)edata->buffer - sizeof(uhci_out_trans_cb_t));
trans_cb->length = 0;
trans_cb->flag = TRANS_CB_FLAG_AVAILABLE;
return true;
}
IRAM_ATTR static inline void uhci_out_append_trans(uhci_out_trans_cb_t *trans_cb)
{
if ((trans_cb->flag != TRANS_CB_FLAG_NEED_QUEUE) || !trans_cb->length) {
return;
}
// Note: If task yield after transmission but before flag set
// flag might be reset in tx done ISR before flag set, leading to buffer access failure
trans_cb->flag = TRANS_CB_FLAG_IN_QUEUE;
if (uhci_transmit(uhci_handle, trans_cb->buffer, trans_cb->length) != ESP_OK) {
goto recycle;
}
return;
recycle:
trans_cb->length = 0;
trans_cb->flag = TRANS_CB_FLAG_AVAILABLE;
return;
}
static int uhci_out_log_cb_init(uhci_out_log_cb_t **log_cb, uint16_t buf_size, uint8_t type, uint8_t idx)
{
// Initialize log control block
*log_cb = (uhci_out_log_cb_t *)UHCI_OUT_MALLOC(sizeof(uhci_out_log_cb_t));
if (!(*log_cb)) {
return -1;
}
memset(*log_cb, 0, sizeof(uhci_out_log_cb_t));
// Initialize transactions
int ret = 0;
for (uint8_t i = 0; i < 2; i++) {
ret |= uhci_out_init_trans(&((*log_cb)->trans_cb[i]), buf_size);
}
if (ret != 0) {
uhci_out_log_cb_deinit(log_cb);
return -1;
}
(*log_cb)->type = (type << 4) | (idx);
return 0;
}
static void uhci_out_log_cb_deinit(uhci_out_log_cb_t **log_cb)
{
if (!(*log_cb)) {
return;
}
for (uint8_t i = 0; i < 2; i++) {
if ((*log_cb)->trans_cb[i]) {
uhci_out_deinit_trans(&((*log_cb)->trans_cb[i]));
}
}
free(*log_cb);
*log_cb = NULL;
return;
}
IRAM_ATTR static inline bool uhci_out_log_cb_check_trans(uhci_out_log_cb_t *log_cb, uint16_t len, bool *need_append)
{
uhci_out_trans_cb_t *trans_cb;
*need_append = false;
for (uint8_t i = 0; i < 2; i++) {
trans_cb = log_cb->trans_cb[log_cb->trans_cb_idx];
if (len > trans_cb->buf_size) {
goto failed;
}
if (trans_cb->flag == TRANS_CB_FLAG_AVAILABLE) {
if ((trans_cb->buf_size - trans_cb->length) >= len) {
return true;
} else {
trans_cb->flag = TRANS_CB_FLAG_NEED_QUEUE;
*need_append = true;
}
}
log_cb->trans_cb_idx = !(log_cb->trans_cb_idx);
}
failed:
log_cb->lost_bytes_cnt += len;
log_cb->lost_frame_cnt++;
return false;
}
// CRITICAL: Shall not be called from ISR!
IRAM_ATTR static inline void uhci_out_log_cb_append_trans(uhci_out_log_cb_t *log_cb)
{
uhci_out_trans_cb_t *trans_cb;
uint8_t idx = !log_cb->trans_cb_idx;
for (uint8_t i = 0; i < 2; i++) {
trans_cb = log_cb->trans_cb[idx];
if (trans_cb->flag == TRANS_CB_FLAG_NEED_QUEUE) {
uhci_out_append_trans(trans_cb);
}
idx = !idx;
}
}
IRAM_ATTR static inline void uhci_out_log_cb_flush_trans(uhci_out_log_cb_t *log_cb)
{
uhci_out_trans_cb_t *trans_cb;
for (uint8_t i = 0; i < 2; i++) {
trans_cb = log_cb->trans_cb[i];
if (trans_cb->length && (trans_cb->flag == TRANS_CB_FLAG_AVAILABLE)) {
trans_cb->flag = TRANS_CB_FLAG_NEED_QUEUE;
}
}
}
// Return value: Need append
IRAM_ATTR static bool uhci_out_log_cb_write(uhci_out_log_cb_t *log_cb, const uint8_t *addr, uint16_t len,
const uint8_t *addr_append, uint16_t len_append, uint8_t source)
{
uhci_out_trans_cb_t *trans_cb = log_cb->trans_cb[log_cb->trans_cb_idx];
uint8_t *buf = trans_cb->buffer + trans_cb->length;
uint16_t total_length = len + len_append;
frame_head_t head = {
.length = total_length,
.source = source,
.type = log_cb->type,
.frame_sn = UHCI_OUT_GET_FRAME_SN(&(log_cb->frame_sn)) & 0xFFFF,
};
memcpy(buf, (const uint8_t *)&head, UHCI_OUT_FRAME_HEAD_LEN);
memcpy(buf + UHCI_OUT_FRAME_HEAD_LEN, addr, len);
if (len_append && addr_append) {
memcpy(buf + UHCI_OUT_FRAME_HEAD_LEN + len, addr_append, len_append);
}
uint32_t checksum = 0;
for (int i = 0; i < UHCI_OUT_FRAME_HEAD_LEN + total_length; i++) {
checksum += buf[i];
}
memcpy(buf + UHCI_OUT_FRAME_HEAD_LEN + total_length, &checksum, UHCI_OUT_FRAME_TAIL_LEN);
trans_cb->length += total_length + UHCI_OUT_FRAME_OVERHEAD;
if ((trans_cb->buf_size - trans_cb->length) <= UHCI_OUT_FRAME_OVERHEAD) {
trans_cb->flag = TRANS_CB_FLAG_NEED_QUEUE;
return true;
}
return false;
}
IRAM_ATTR static void uhci_out_log_cb_write_loss(uhci_out_log_cb_t *log_cb)
{
if (!log_cb->lost_bytes_cnt || !log_cb->lost_frame_cnt) {
return;
}
bool need_append;
uint16_t frame_len = sizeof(loss_payload_t) + UHCI_OUT_FRAME_OVERHEAD;
if (uhci_out_log_cb_check_trans(log_cb, frame_len, &need_append)) {
loss_payload_t payload = {
.type = log_cb->type,
.lost_frame_cnt = log_cb->lost_frame_cnt,
.lost_bytes_cnt = log_cb->lost_bytes_cnt,
};
uhci_out_log_cb_write(log_cb, (const uint8_t *)&payload, sizeof(loss_payload_t),
NULL, 0, BLE_LOG_UHCI_OUT_SOURCE_LOSS);
log_cb->lost_frame_cnt = 0;
log_cb->lost_bytes_cnt = 0;
}
}
static void uhci_out_log_cb_dump(uhci_out_log_cb_t *log_cb)
{
uhci_out_trans_cb_t *trans_cb;
uint8_t *buf;
for (uint8_t i = 0; i < 2; i++) {
// Dump the last transaction before dumping the current transaction
log_cb->trans_cb_idx = !(log_cb->trans_cb_idx);
trans_cb = log_cb->trans_cb[log_cb->trans_cb_idx];
buf = (uint8_t *)trans_cb->buffer;
for (uint16_t j = 0; j < trans_cb->buf_size; j++) {
esp_rom_printf("%02x ", buf[j]);
// Feed watchdogs periodically to avoid wdts timeout
if ((j % 100) == 0) {
esp_panic_handler_feed_wdts();
}
}
}
}
static void esp_timer_cb_log_flush(void)
{
uint32_t os_ts = pdTICKS_TO_MS(xTaskGetTickCount());
if ((os_ts - user_last_write_ts) > UHCI_OUT_FLUSH_TIMEOUT_MS) {
xSemaphoreTake(user_log_mutex, portMAX_DELAY);
uhci_out_log_cb_flush_trans(user_log_cb);
uhci_out_log_cb_append_trans(user_log_cb);
xSemaphoreGive(user_log_mutex);
}
if ((esp_bt_controller_get_status() >= ESP_BT_CONTROLLER_STATUS_INITED) &&
((os_ts - ll_last_write_ts) > UHCI_OUT_FLUSH_TIMEOUT_MS)) {
ll_ev_flags |= BIT(LL_EV_FLAG_FLUSH_LOG);
UHCI_OUT_LL_PUT_EV;
}
esp_timer_start_once(flush_timer, UHCI_OUT_FLUSH_TIMEOUT_US);
}
static void uhci_out_user_write_str(const uint8_t *src, uint16_t len)
{
if (!user_log_inited || !src || !len) {
return;
}
xSemaphoreTake(user_log_mutex, portMAX_DELAY);
bool need_append;
if (uhci_out_log_cb_check_trans(user_log_cb, len, &need_append)) {
uhci_out_trans_cb_t *trans_cb = user_log_cb->trans_cb[user_log_cb->trans_cb_idx];
uint8_t *buf = trans_cb->buffer + trans_cb->length;
memcpy(buf, (const uint8_t *)src, len);
trans_cb->length += len;
}
if (need_append) {
uhci_out_log_cb_append_trans(user_log_cb);
}
user_last_write_ts = pdTICKS_TO_MS(xTaskGetTickCount());
xSemaphoreGive(user_log_mutex);
}
#if UHCI_OUT_UART_PORT == UHCI_OUT_UART_PORT0
static void uhci_out_user_write_char(char c)
{
uhci_out_user_write_str((const uint8_t *)&c, 1);
}
#endif // UHCI_OUT_UART_PORT == UHCI_OUT_UART_PORT0
static int uhci_out_user_log_init(void)
{
if (user_log_inited) {
return 0;
}
// Initialize mutex
user_log_mutex = xSemaphoreCreateMutex();
if (!user_log_mutex) {
goto failed;
}
// Initialize log control block
if (uhci_out_log_cb_init(&user_log_cb, UHCI_OUT_USER_BUF_SIZE, LOG_CB_TYPE_USER, 0) != 0) {
goto failed;
}
// Initialization done
user_log_inited = true;
return 0;
failed:
uhci_out_user_log_deinit();
return -1;
}
static void uhci_out_user_log_deinit(void)
{
user_log_inited = false;
if (!user_log_mutex) {
return;
}
xSemaphoreTake(user_log_mutex, portMAX_DELAY);
uhci_out_log_cb_deinit(&user_log_cb);
xSemaphoreGive(user_log_mutex);
vSemaphoreDelete(user_log_mutex);
user_log_mutex = NULL;
}
static int uhci_out_ll_log_init(void)
{
if (ll_log_inited) {
return 0;
}
if (uhci_out_log_cb_init(&ll_task_log_cb, UHCI_OUT_LL_TASK_BUF_SIZE,
LOG_CB_TYPE_LL, LOG_CB_LL_SUBTYPE_TASK) != 0) {
goto failed;
}
if (uhci_out_log_cb_init(&ll_isr_log_cb, UHCI_OUT_LL_ISR_BUF_SIZE,
LOG_CB_TYPE_LL, LOG_CB_LL_SUBTYPE_ISR) != 0) {
goto failed;
}
if (uhci_out_log_cb_init(&ll_hci_log_cb, UHCI_OUT_LL_HCI_BUF_SIZE,
LOG_CB_TYPE_LL, LOG_CB_LL_SUBTYPE_HCI) != 0) {
goto failed;
}
ll_log_inited = true;
return 0;
failed:
uhci_out_ll_log_deinit();
return -1;
}
static void uhci_out_ll_log_deinit(void)
{
ll_log_inited = false;
uhci_out_log_cb_deinit(&ll_hci_log_cb);
uhci_out_log_cb_deinit(&ll_isr_log_cb);
uhci_out_log_cb_deinit(&ll_task_log_cb);
}
static void uhci_out_ll_log_flush(void)
{
if (!ll_log_inited) {
return;
}
uhci_out_log_cb_write_loss(ll_task_log_cb);
uhci_out_log_cb_write_loss(ll_hci_log_cb);
uhci_out_log_cb_flush_trans(ll_task_log_cb);
uhci_out_log_cb_flush_trans(ll_hci_log_cb);
portMUX_TYPE spinlock = portMUX_INITIALIZER_UNLOCKED;
portENTER_CRITICAL_SAFE(&spinlock);
uhci_out_log_cb_write_loss(ll_isr_log_cb);
uhci_out_log_cb_flush_trans(ll_isr_log_cb);
portEXIT_CRITICAL_SAFE(&spinlock);
uhci_out_log_cb_append_trans(ll_task_log_cb);
uhci_out_log_cb_append_trans(ll_hci_log_cb);
uhci_out_log_cb_append_trans(ll_isr_log_cb);
}
// Public functions
int ble_log_uhci_out_init(void)
{
// Avoid double init
if (uhci_out_inited) {
return 0;
}
#if UHCI_OUT_UART_NEED_INIT
uart_config_t uart_config = {
.baud_rate = UHCI_OUT_UART_BAUD_RATE,
.data_bits = UART_DATA_8_BITS,
.parity = UART_PARITY_DISABLE,
.stop_bits = UART_STOP_BITS_1,
.flow_ctrl = UART_HW_FLOWCTRL_CTS_RTS,
.rx_flow_ctrl_thresh = 122,
};
// Configure UART parameters
uart_param_config(UHCI_OUT_UART_PORT, &uart_config);
uart_set_pin(UHCI_OUT_UART_PORT, UHCI_OUT_UART_IO_NUM_TX, -1, -1, -1);
#endif // UHCI_OUT_UART_NEED_INIT
uhci_controller_config_t uhci_config = {
.uart_port = UHCI_OUT_UART_PORT,
.tx_trans_queue_depth = UHCI_OUT_QUEUE_SIZE,
.max_receive_internal_mem = 1024,
.max_transmit_size = UHCI_OUT_MAX_TRANSFER_SIZE,
.dma_burst_size = 32,
.rx_eof_flags.idle_eof = 1,
};
if (uhci_new_controller(&uhci_config, &uhci_handle) != ESP_OK) {
goto failed;
}
uhci_event_callbacks_t uhci_cbs = {
.on_tx_trans_done = uhci_out_tx_done_cb,
};
uhci_register_event_callbacks(uhci_handle, &uhci_cbs, NULL);
if (uhci_out_user_log_init() != 0) {
goto failed;
}
if (uhci_out_ll_log_init() != 0) {
goto failed;
}
esp_timer_create_args_t timer_args = {
.callback = (esp_timer_cb_t)esp_timer_cb_log_flush,
.dispatch_method = ESP_TIMER_TASK
};
if (esp_timer_create(&timer_args, &flush_timer) != ESP_OK) {
goto failed;
}
#if UHCI_OUT_UART_PORT == UHCI_OUT_UART_PORT0
// Install UART Driver if not installed
if (!uart_is_driver_installed(UHCI_OUT_UART_PORT0)) {
uart_driver_install(UHCI_OUT_UART_PORT0, UHCI_OUT_UART_DRIVER_RX_BUF_SIZE, 0, 0, NULL, 0);
}
// Redirect UART VFS Driver to UART Driver
uart_vfs_dev_use_driver(UHCI_OUT_UART_PORT0);
// Redirect esp_rom_printf to BLE Log UHCI Out
esp_rom_install_channel_putc(1, uhci_out_user_write_char);
esp_rom_install_channel_putc(2, NULL);
#endif // UHCI_OUT_UART_PORT == UHCI_OUT_UART_PORT0
uhci_out_inited = true;
esp_timer_start_once(flush_timer, UHCI_OUT_FLUSH_TIMEOUT_US);
return 0;
failed:
ble_log_uhci_out_deinit();
return -1;
}
void ble_log_uhci_out_deinit(void)
{
uhci_out_inited = false;
if (flush_timer) {
esp_timer_stop(flush_timer);
esp_timer_delete(flush_timer);
flush_timer = NULL;
}
if (uhci_handle) {
uhci_wait_all_tx_transaction_done(uhci_handle, portMAX_DELAY);
uhci_del_controller(uhci_handle);
uhci_handle = NULL;
}
uhci_out_ll_log_deinit();
uhci_out_user_log_deinit();
}
IRAM_ATTR void ble_log_uhci_out_ll_write(uint32_t len, const uint8_t *addr, uint32_t len_append,
const uint8_t *addr_append, uint32_t flag)
{
// Raw logs will come in case of assert, shall be printed to console directly
if (flag & BIT(LL_LOG_FLAG_RAW)) {
if (len && addr) {
for (uint32_t i = 0; i < len; i++) { esp_rom_printf("%02x ", addr[i]); }
}
if (len_append && addr_append) {
for (uint32_t i = 0; i < len_append; i++) { esp_rom_printf("%02x ", addr_append[i]); }
}
if (flag & BIT(LL_LOG_FLAG_END)) { esp_rom_printf("\n"); }
}
if (!ll_log_inited) {
return;
}
bool in_isr = false;
uint8_t source;
uhci_out_log_cb_t *log_cb;
if (flag & BIT(LL_LOG_FLAG_ISR)) {
log_cb = ll_isr_log_cb;
source = BLE_LOG_UHCI_OUT_SOURCE_ESP_ISR;
in_isr = true;
} else if (flag & BIT(LL_LOG_FLAG_HCI)) {
log_cb = ll_hci_log_cb;
source = BLE_LOG_UHCI_OUT_SOURCE_LL_HCI;
} else {
log_cb = ll_task_log_cb;
source = BLE_LOG_UHCI_OUT_SOURCE_ESP;
}
bool need_append;
uint16_t frame_len = len + len_append + UHCI_OUT_FRAME_OVERHEAD;
if (uhci_out_log_cb_check_trans(log_cb, frame_len, &need_append)) {
need_append |= uhci_out_log_cb_write(log_cb, addr, len, addr_append,
len_append, source);
}
ll_last_write_ts = in_isr?\
pdTICKS_TO_MS(xTaskGetTickCountFromISR()):\
pdTICKS_TO_MS(xTaskGetTickCount());
if (need_append) {
if (in_isr) {
ll_ev_flags |= BIT(LL_EV_FLAG_ISR_APPEND);
UHCI_OUT_LL_PUT_EV;
} else {
uhci_out_log_cb_append_trans(log_cb);
}
}
}
IRAM_ATTR void ble_log_uhci_out_ll_log_ev_proc(void)
{
if (!ll_log_inited) {
return;
}
if (ll_ev_flags & BIT(LL_EV_FLAG_ISR_APPEND)) {
uhci_out_log_cb_append_trans(ll_isr_log_cb);
ll_ev_flags &= ~BIT(LL_EV_FLAG_ISR_APPEND);
}
if (ll_ev_flags & BIT(LL_EV_FLAG_FLUSH_LOG)) {
uhci_out_ll_log_flush();
ll_ev_flags &= ~BIT(LL_EV_FLAG_FLUSH_LOG);
}
ll_ev_flags = 0;
}
// Redirect UART Driver to BLE Log UHCI Out
int __real_uart_tx_chars(uart_port_t uart_num, const char *buffer, uint32_t len);
int __wrap_uart_tx_chars(uart_port_t uart_num, const char *buffer, uint32_t len)
{
#if UHCI_OUT_UART_PORT == UHCI_OUT_UART_PORT0
uhci_out_user_write_str((const uint8_t *)buffer, len);
return 0;
#else
return __real_uart_tx_chars(uart_num, buffer, len);
#endif // UHCI_OUT_UART_PORT == UHCI_OUT_UART_PORT0
}
int __real_uart_write_bytes(uart_port_t uart_num, const void *src, size_t size);
int __wrap_uart_write_bytes(uart_port_t uart_num, const void *src, size_t size)
{
#if UHCI_OUT_UART_PORT == UHCI_OUT_UART_PORT0
uhci_out_user_write_str((const uint8_t *)src, size);
return 0;
#else
return __real_uart_write_bytes(uart_num, src, size);
#endif // UHCI_OUT_UART_PORT == UHCI_OUT_UART_PORT0
}
int __real_uart_write_bytes_with_break(uart_port_t uart_num, const void *src, size_t size, int brk_len);
int __wrap_uart_write_bytes_with_break(uart_port_t uart_num, const void *src, size_t size, int brk_len)
{
#if UHCI_OUT_UART_PORT == UHCI_OUT_UART_PORT0
return __wrap_uart_write_bytes(uart_num, src, size);
#else
return __real_uart_write_bytes_with_break(uart_num, src, size, brk_len);
#endif // UHCI_OUT_UART_PORT == UHCI_OUT_UART_PORT0
}
void ble_log_uhci_out_dump_all(void)
{
if (!uhci_out_inited) {
return;
}
#if UHCI_OUT_UART_PORT == UHCI_OUT_UART_PORT0
esp_rom_output_tx_wait_idle(UHCI_OUT_UART_PORT0);
esp_rom_install_uart_printf();
#endif // UHCI_OUT_UART_PORT == UHCI_OUT_UART_PORT0
portMUX_TYPE spinlock = portMUX_INITIALIZER_UNLOCKED;
portENTER_CRITICAL_SAFE(&spinlock);
if (ll_log_inited) {
esp_rom_printf("[DUMP_START:\n");
uhci_out_log_cb_dump(ll_isr_log_cb);
uhci_out_log_cb_dump(ll_task_log_cb);
uhci_out_log_cb_dump(ll_hci_log_cb);
esp_rom_printf("\n:DUMP_END]\n\n");
}
portEXIT_CRITICAL_SAFE(&spinlock);
#if UHCI_OUT_UART_PORT == UHCI_OUT_UART_PORT0
esp_rom_install_channel_putc(1, uhci_out_user_write_char);
#endif // UHCI_OUT_UART_PORT == UHCI_OUT_UART_PORT0
}
#endif // CONFIG_BT_BLE_LOG_UHCI_OUT_ENABLED

View File

@@ -0,0 +1,35 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef __BT_SPI_OUT_H__
#define __BT_SPI_OUT_H__
#include <stdarg.h>
#include <string.h>
#include "driver/uhci.h"
#include "driver/uart.h"
#include "driver/uart_vfs.h"
#include "esp_rom_serial_output.h"
#include "esp_timer.h"
#include "freertos/semphr.h"
// Public enums
enum {
BLE_LOG_UHCI_OUT_SOURCE_ESP = 0,
BLE_LOG_UHCI_OUT_SOURCE_ESP_ISR = 6,
BLE_LOG_UHCI_OUT_SOURCE_LL_HCI = 8,
BLE_LOG_UHCI_OUT_SOURCE_USER = 0x10,
BLE_LOG_UHCI_OUT_SOURCE_LOSS = 0xFF,
};
// Public functions
int ble_log_uhci_out_init(void);
void ble_log_uhci_out_deinit(void);
void ble_log_uhci_out_ll_write(uint32_t len, const uint8_t *addr, uint32_t len_append,
const uint8_t *addr_append, uint32_t flag);
void ble_log_uhci_out_ll_log_ev_proc(void);
void ble_log_uhci_out_dump_all(void);
#endif // __BT_SPI_OUT_H__