mirror of
				https://github.com/espressif/esp-idf.git
				synced 2025-11-03 22:08:28 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			781 lines
		
	
	
		
			24 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			781 lines
		
	
	
		
			24 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
 * 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
 |