/* * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ #include #include #include #include "hci_log/bt_hci_log.h" #include "bt_common.h" #include "osi/mutex.h" #include "esp_attr.h" #if (BT_HCI_LOG_INCLUDED == TRUE) #define BT_HCI_LOG_PRINT_TAG (1) #define BT_HCI_LOG_DATA_BUF_SIZE (1024 * HCI_LOG_DATA_BUFFER_SIZE) #define BT_HCI_LOG_ADV_BUF_SIZE (1024 * HCI_LOG_ADV_BUFFER_SIZE) typedef struct { osi_mutex_t mutex_lock; uint64_t log_record_in; uint64_t log_record_out; uint64_t buf_size; uint8_t *p_hci_log_buffer; uint8_t index; bool overflow; } bt_hci_log_t; static const char s_hex_to_char_mapping[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; static bt_hci_log_t g_bt_hci_log_data_ctl = {0}; static bt_hci_log_t g_bt_hci_log_adv_ctl = {0}; esp_err_t bt_hci_log_init(void) { uint8_t *g_bt_hci_log_data_buffer = NULL; uint8_t *g_bt_hci_log_adv_buffer = NULL; g_bt_hci_log_data_buffer = malloc(BT_HCI_LOG_DATA_BUF_SIZE); if (!g_bt_hci_log_data_buffer) { return ESP_ERR_NO_MEM; } g_bt_hci_log_adv_buffer = malloc(BT_HCI_LOG_ADV_BUF_SIZE); if (!g_bt_hci_log_adv_buffer) { if (g_bt_hci_log_data_buffer) { free(g_bt_hci_log_data_buffer); g_bt_hci_log_data_buffer = NULL; } return ESP_ERR_NO_MEM; } memset(g_bt_hci_log_data_buffer, 0, BT_HCI_LOG_DATA_BUF_SIZE); memset(g_bt_hci_log_adv_buffer, 0, BT_HCI_LOG_ADV_BUF_SIZE); memset(&g_bt_hci_log_data_ctl, 0, sizeof(bt_hci_log_t)); g_bt_hci_log_data_ctl.buf_size = BT_HCI_LOG_DATA_BUF_SIZE; g_bt_hci_log_data_ctl.p_hci_log_buffer = g_bt_hci_log_data_buffer; memset(&g_bt_hci_log_adv_ctl, 0, sizeof(bt_hci_log_t)); g_bt_hci_log_adv_ctl.buf_size = BT_HCI_LOG_ADV_BUF_SIZE; g_bt_hci_log_adv_ctl.p_hci_log_buffer = g_bt_hci_log_adv_buffer; osi_mutex_new((osi_mutex_t *)&g_bt_hci_log_data_ctl.mutex_lock); osi_mutex_new((osi_mutex_t *)&g_bt_hci_log_adv_ctl.mutex_lock); return ESP_OK; } esp_err_t bt_hci_log_deinit(void) { if (g_bt_hci_log_data_ctl.p_hci_log_buffer) { free(g_bt_hci_log_data_ctl.p_hci_log_buffer); g_bt_hci_log_data_ctl.p_hci_log_buffer = NULL; } if (g_bt_hci_log_adv_ctl.p_hci_log_buffer) { free(g_bt_hci_log_adv_ctl.p_hci_log_buffer); g_bt_hci_log_adv_ctl.p_hci_log_buffer = NULL; } osi_mutex_free((osi_mutex_t *)&g_bt_hci_log_data_ctl.mutex_lock); osi_mutex_free((osi_mutex_t *)&g_bt_hci_log_adv_ctl.mutex_lock); memset(&g_bt_hci_log_data_ctl, 0, sizeof(bt_hci_log_t)); memset(&g_bt_hci_log_adv_ctl, 0, sizeof(bt_hci_log_t)); return ESP_OK; } #if (BT_HCI_LOG_PRINT_TAG) static char IRAM_ATTR *bt_data_type_to_str(uint8_t data_type) { char *tag = NULL; switch (data_type) { case HCI_LOG_DATA_TYPE_COMMAND: // hci cmd data tag = "C"; break; case HCI_LOG_DATA_TYPE_H2C_ACL: // host to controller hci acl data tag = "H"; break; case HCI_LOG_DATA_TYPE_SCO: // hci sco data tag = "S"; break; case HCI_LOG_DATA_TYPE_EVENT: // hci event tag = "E"; break; case HCI_LOG_DATA_TYPE_ADV: // controller adv report data tag = NULL; break; case HCI_LOG_DATA_TYPE_C2H_ACL: // controller to host hci acl data tag = "D"; break; case HCI_LOG_DATA_TYPE_SELF_DEFINE: // self-defining data tag = "S"; break; default: // unknown data type tag = "U"; break; } return tag; } #endif void bt_hci_log_record_hex(bt_hci_log_t *p_hci_log_ctl, uint8_t *hex, uint8_t hex_len) { uint8_t hci_log_char; uint8_t *g_hci_log_buffer; g_hci_log_buffer = p_hci_log_ctl->p_hci_log_buffer; while (hex_len--) { hci_log_char = ((*hex) >> 4); g_hci_log_buffer[p_hci_log_ctl->log_record_in] = s_hex_to_char_mapping [hci_log_char]; if (++ p_hci_log_ctl->log_record_in >= p_hci_log_ctl->buf_size) { p_hci_log_ctl->log_record_in = 0; } if (p_hci_log_ctl->log_record_in == p_hci_log_ctl->log_record_out) { p_hci_log_ctl->overflow = true; } hci_log_char = ((*hex) & 0x0f); g_hci_log_buffer[p_hci_log_ctl->log_record_in] = s_hex_to_char_mapping [hci_log_char]; if (++p_hci_log_ctl->log_record_in >= p_hci_log_ctl->buf_size) { p_hci_log_ctl->log_record_in = 0; } if (p_hci_log_ctl->log_record_in == p_hci_log_ctl->log_record_out) { p_hci_log_ctl->overflow = true; } g_hci_log_buffer[p_hci_log_ctl->log_record_in] = ' '; if (++ p_hci_log_ctl->log_record_in >= p_hci_log_ctl->buf_size) { p_hci_log_ctl->log_record_in = 0; } if (p_hci_log_ctl->log_record_in == p_hci_log_ctl->log_record_out) { p_hci_log_ctl->overflow = true; } ++ hex; } } void bt_hci_log_record_string(bt_hci_log_t *p_hci_log_ctl, char *string) { uint8_t *g_hci_log_buffer; g_hci_log_buffer = p_hci_log_ctl->p_hci_log_buffer; while (*string != '\0') { g_hci_log_buffer[p_hci_log_ctl->log_record_in] = *string; ++string; if (++p_hci_log_ctl->log_record_in >= p_hci_log_ctl->buf_size) { p_hci_log_ctl->log_record_in = 0; } if (p_hci_log_ctl->log_record_in == p_hci_log_ctl->log_record_out) { p_hci_log_ctl->overflow = true; } } } esp_err_t IRAM_ATTR bt_hci_log_record_data(bt_hci_log_t *p_hci_log_ctl, char *str, uint8_t data_type, uint8_t *data, uint8_t data_len) { osi_mutex_t mutex_lock; uint8_t *g_hci_log_buffer; if (!p_hci_log_ctl->p_hci_log_buffer) { return ESP_FAIL; } g_hci_log_buffer = p_hci_log_ctl->p_hci_log_buffer; if (!g_hci_log_buffer) { return ESP_FAIL; } mutex_lock = p_hci_log_ctl->mutex_lock; osi_mutex_lock(&mutex_lock, OSI_MUTEX_MAX_TIMEOUT); #if (1) // Add hci data index bt_hci_log_record_hex(p_hci_log_ctl, &p_hci_log_ctl->index, 1); #endif #if (BT_HCI_LOG_PRINT_TAG) char *tag = NULL; tag = bt_data_type_to_str(data_type); if (tag) { bt_hci_log_record_string(p_hci_log_ctl, tag); g_hci_log_buffer[p_hci_log_ctl->log_record_in] = ':'; if (++p_hci_log_ctl->log_record_in >= p_hci_log_ctl->buf_size) { p_hci_log_ctl->log_record_in = 0; } if (p_hci_log_ctl->log_record_in == p_hci_log_ctl->log_record_out) { p_hci_log_ctl->overflow = true; } } #endif if (str) { bt_hci_log_record_string(p_hci_log_ctl, str); } bt_hci_log_record_hex(p_hci_log_ctl, data, data_len); g_hci_log_buffer[p_hci_log_ctl->log_record_in] = '\n'; if (++p_hci_log_ctl->log_record_in >= p_hci_log_ctl->buf_size) { p_hci_log_ctl->log_record_in = 0; } if (p_hci_log_ctl->log_record_in == p_hci_log_ctl->log_record_out) { p_hci_log_ctl->overflow = true; } p_hci_log_ctl->index ++; osi_mutex_unlock(&mutex_lock); return ESP_OK; } void bt_hci_log_data_show(bt_hci_log_t *p_hci_log_ctl) { volatile uint64_t log_record_in,log_record_out; uint8_t *g_hci_log_buffer; if (!p_hci_log_ctl->p_hci_log_buffer) { return; } osi_mutex_t mutex_lock = p_hci_log_ctl->mutex_lock; osi_mutex_lock(&mutex_lock, OSI_MUTEX_MAX_TIMEOUT); log_record_in = p_hci_log_ctl->log_record_in; log_record_out = p_hci_log_ctl->log_record_out; g_hci_log_buffer = p_hci_log_ctl->p_hci_log_buffer; if (p_hci_log_ctl->overflow) { log_record_out = log_record_in; printf("%c",g_hci_log_buffer[log_record_out]); if (++log_record_out >= p_hci_log_ctl->buf_size) { log_record_out = 0; } } while (log_record_in != log_record_out) { printf("%c",g_hci_log_buffer[log_record_out]); if (++log_record_out >= p_hci_log_ctl->buf_size) { log_record_out = 0; } } p_hci_log_ctl->log_record_out = log_record_out; p_hci_log_ctl->overflow = false; osi_mutex_unlock(&mutex_lock); } esp_err_t IRAM_ATTR bt_hci_log_record_hci_data(uint8_t data_type, uint8_t *data, uint8_t data_len) { return bt_hci_log_record_data(&g_bt_hci_log_data_ctl, NULL, data_type, data, data_len); } esp_err_t IRAM_ATTR bt_hci_log_record_custom_data(char *string, uint8_t *data, uint8_t data_len) { return bt_hci_log_record_data(&g_bt_hci_log_data_ctl, string, HCI_LOG_DATA_TYPE_SELF_DEFINE, data, data_len); } esp_err_t IRAM_ATTR bt_hci_log_record_hci_adv(uint8_t data_type, uint8_t *data, uint8_t data_len) { return bt_hci_log_record_data(&g_bt_hci_log_adv_ctl, NULL, data_type, data, data_len); } void bt_hci_log_hci_data_show(void) { bt_hci_log_data_show(&g_bt_hci_log_data_ctl); } void bt_hci_log_hci_adv_show(void) { bt_hci_log_data_show(&g_bt_hci_log_adv_ctl); } #endif // (BT_HCI_LOG_INCLUDED == TRUE)