/* * 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