diff --git a/components/bt/CMakeLists.txt b/components/bt/CMakeLists.txt index 8a4cceeacb..fe9ae9ad40 100644 --- a/components/bt/CMakeLists.txt +++ b/components/bt/CMakeLists.txt @@ -162,6 +162,42 @@ if(CONFIG_BT_ENABLED) "common/ble_log/ble_log_spi_out.c" ) + # BLE Log Module + if(CONFIG_BLE_LOG_ENABLED) + # Core source files + list(APPEND srcs + common/ble_log/src/ble_log.c + common/ble_log/src/ble_log_lbm.c + common/ble_log/src/ble_log_rt.c + common/ble_log/src/ble_log_util.c + ) + + # Includes + list(APPEND include_dirs + common/ble_log/include + ) + + # Private includes + list(APPEND priv_include_dirs + common/ble_log/src/internal_include + common/ble_log/src/internal_include/prph + ) + + # Timestamp synchronization extension + if(CONFIG_BLE_LOG_TS_ENABLED) + list(APPEND srcs common/ble_log/src/ble_log_ts.c) + endif() + + # Peripheral interface implementation + if(CONFIG_BLE_LOG_PRPH_DUMMY) + list(APPEND srcs common/ble_log/src/prph/ble_log_prph_dummy.c) + elseif(CONFIG_BLE_LOG_PRPH_SPI_MASTER_DMA) + list(APPEND srcs common/ble_log/src/prph/ble_log_prph_spi_master_dma.c) + elseif(CONFIG_BLE_LOG_PRPH_UART_DMA) + list(APPEND srcs common/ble_log/src/prph/ble_log_prph_uart_dma.c) + endif() + endif() + # Host Bluedroid if(CONFIG_BT_BLUEDROID_ENABLED) @@ -945,6 +981,13 @@ 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(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) add_prebuilt_library(libble_app "controller/lib_${target}/${target}-bt-lib/esp32c6/libble_app.a" REQUIRES esp_phy) diff --git a/components/bt/common/Kconfig.in b/components/bt/common/Kconfig.in index 7e06988373..7ccf123236 100644 --- a/components/bt/common/Kconfig.in +++ b/components/bt/common/Kconfig.in @@ -6,6 +6,10 @@ config BT_ALARM_MAX_NUM This option decides the maximum number of alarms which could be used by Bluetooth host. +menu "BLE Log" + source "$IDF_PATH/components/bt/common/ble_log/Kconfig.in" +endmenu + config BT_BLE_LOG_SPI_OUT_ENABLED bool "Output ble logs to SPI bus (Experimental)" default n diff --git a/components/bt/common/ble_log/Kconfig.in b/components/bt/common/ble_log/Kconfig.in new file mode 100644 index 0000000000..bf31e5259b --- /dev/null +++ b/components/bt/common/ble_log/Kconfig.in @@ -0,0 +1,147 @@ +config BLE_LOG_ENABLED + bool "Enable BLE Log Module (Experimental)" + default n + help + Enable BLE Log Module + +if BLE_LOG_ENABLED + config BLE_LOG_LBM_TRANS_SIZE + int "Buffer size for each peripheral transport" + default 512 + help + There're 2 log buffer managers (LBMs) with compare-and-swap + (CAS) protection, 1 LBM with FreeRTOS mutex protection, 1 LBM + without protection for critical section. Each LBM is managing + 2 ping-pong buffers, which means there will be 4 * 2 * + BLE_LOG_LBM_TRANS_SIZE bytes buffer allocated + + config BLE_LOG_LBM_ATOMIC_LOCK_TASK_CNT + int "Count of log buffer managers with atomic lock protection for task context" + default 2 + help + BLE Log module will search for an LBM with atomic lock protection first; if + all LBMs with atomic lock protection are unavailable, BLE Log module will + try to use the LBM with spin lock protection. So the more LBMs with atomic + lock protection are created, the better the logging performance will be. + + config BLE_LOG_LBM_ATOMIC_LOCK_ISR_CNT + int "Count of log buffer managers with atomic lock protection for ISR context" + default 1 + help + BLE Log module will search for an LBM with atomic lock protection first; if + all LBMs with atomic lock protection are unavailable, BLE Log module will + try to use the LBM with spin lock protection. So the more LBMs with atomic + lock protection are created, the more ISRs can nest. + + config BLE_LOG_LL_ENABLED + bool "Enable BLE Log for Link Layer" + depends on BT_CONTROLLER_ENABLED + default y + select BT_LE_CONTROLLER_LOG_ENABLED + select BT_LE_CONTROLLER_LOG_MODE_BLE_LOG + help + Enable BLE Log for Link Layer + + config BLE_LOG_LBM_LL_TRANS_SIZE + int "Buffer size for each peripheral transport of Link Layer LBM" + depends on BLE_LOG_LL_ENABLED + default 1024 + help + There're 2 Link Layer dedicated log buffer managers (LBMs) with + compare-and-swap (CAS) protection. Each LBM is managing 2 ping- + pong buffers, which means there will be additional 2 * 2 * + BLE_LOG_LBM_LL_TRANS_SIZE bytes buffer allocated + + config BLE_LOG_PAYLOAD_CHECKSUM_ENABLED + bool "Enable payload checksum for BLE Log data integrity check" + default y + help + Checksum is the default method for BLE Log data integrity check, + but for targets with slow CPU speed, it may cause significant system + performance decrease; a compromise could be made to balance the + realtime performance and log data integrity, which is calculating the + checksum of frame head and payload all together by default, or only + calculate the checksum of frame head to minimize performance decrease + + config BLE_LOG_ENH_STAT_ENABLED + bool "Enable enhanced statistics for BLE Log" + default n + help + Enable enhanced statistics for written/lost frame/bytes count, which may + cost additional ~100kB memory + + config BLE_LOG_TS_ENABLED + bool "Enable BLE Log Timestamp Synchronization (TS)" + default n + help + Enable BLE Log TS with external logging module + + config BLE_LOG_SYNC_IO_NUM + int "GPIO number for Timestamp Synchronization (TS) toggle output" + depends on BLE_LOG_TS_ENABLED + default 0 + help + GPIO number for TS toggle output + + choice BLE_LOG_PRPH_CHOICE + prompt "BLE Log peripheral choice" + default BLE_LOG_PRPH_DUMMY + help + Choose BLE Log peripheral + + config BLE_LOG_PRPH_DUMMY + bool "Dummy transport" + help + Dummy transport (dump only) + + config BLE_LOG_PRPH_SPI_MASTER_DMA + bool "Utilize SPI master DMA driver as transport" + help + Utilize SPI master DMA driver as transport + + config BLE_LOG_PRPH_UART_DMA + bool "Utilize UART DMA driver as transport" + help + Utilize UART DMA driver as transport + endchoice + + if BLE_LOG_PRPH_SPI_MASTER_DMA + config BLE_LOG_PRPH_SPI_MASTER_DMA_MOSI_IO_NUM + int "GPIO number of MOSI port for SPI master DMA transport" + default 0 + help + GPIO number of MOSI port for SPI master DMA transport + + config BLE_LOG_PRPH_SPI_MASTER_DMA_SCLK_IO_NUM + int "GPIO number of SCLK port for SPI master DMA transport" + default 0 + help + GPIO number of SCLK port for SPI master DMA transport + + config BLE_LOG_PRPH_SPI_MASTER_DMA_CS_IO_NUM + int "GPIO number of CS port for SPI master DMA transport" + default 0 + help + GPIO number of CS port for SPI master DMA transport + endif + + if BLE_LOG_PRPH_UART_DMA + config BLE_LOG_PRPH_UART_DMA_PORT + int "UART port number for UART DMA transport" + default 0 + help + UART port number for UART DMA + + config BLE_LOG_PRPH_UART_DMA_BAUD_RATE + int "Baud rate of UART port for UART DMA transport" + default 921600 + help + Determine the baud rate of UART port + + config BLE_LOG_PRPH_UART_DMA_TX_IO_NUM + int "GPIO number for UART TX" + default 0 + help + GPIO number for UART TX + endif +endif diff --git a/components/bt/common/ble_log/README.md b/components/bt/common/ble_log/README.md new file mode 100644 index 0000000000..144c1c5d49 --- /dev/null +++ b/components/bt/common/ble_log/README.md @@ -0,0 +1,450 @@ +# BLE Log Module + +A high-performance, modular Bluetooth logging system that provides real-time log capture and transmission capabilities for the ESP-IDF Bluetooth stack. + +## Table of Contents + +- [Overview](#overview) +- [Architecture Design](#architecture-design) +- [Features](#features) +- [Quick Start](#quick-start) +- [Configuration Options](#configuration-options) +- [API Reference](#api-reference) +- [Usage Examples](#usage-examples) +- [Performance & Memory Optimization](#performance--memory-optimization) +- [Troubleshooting](#troubleshooting) +- [Important Notes](#important-notes) + +## Overview + +The BLE Log module is an efficient logging system specifically designed for the ESP-IDF Bluetooth stack, supporting real-time log capture, multi-source log collection, and various transmission methods. This module has been refactored with a modular design, featuring high-concurrency processing capabilities and low-latency characteristics. + +### Main Components + +- **BLE Log Core** (`ble_log.c`): Module core responsible for initialization and coordination of sub-modules +- **Runtime Manager** (`ble_log_rt.c`): Runtime task management for log transmission scheduling +- **Log Buffer Manager** (`ble_log_lbm.c`): Log buffer management supporting multiple locking mechanisms +- **Peripheral Interface** (`ble_log_prph_*.c`): Peripheral interface abstraction layer supporting various transmission methods +- **Timestamp Sync** (`ble_log_ts.c`): Timestamp synchronization module +- **Utility** (`ble_log_util.c`): Common utility functions + +## Features + +### Core Functionality + +- **Multi-source Log Collection**: Supports multiple log sources including Link Layer, Host, HCI, etc. +- **High Concurrency Processing**: Uses atomic and spin lock mechanisms for multi-task concurrent writing +- **Real-time Transmission**: Asynchronous transmission mechanism based on FreeRTOS tasks +- **Data Integrity**: Configurable checksum mechanism ensures data integrity +- **Memory Optimization**: Ping-pong buffer design minimizes memory usage + +### Advanced Features + +- **Timestamp Synchronization**: Supports timestamp synchronization with external devices (optional) +- **Enhanced Statistics**: Detailed logging statistics including loss rate analysis (optional) +- **Link Layer Integration**: Deep integration with ESP-IDF Bluetooth Link Layer +- **Multiple Transmission Methods**: Supports SPI DMA, UART DMA, and Dummy transmission + +### Performance Features + +- **IRAM Optimization**: Critical path code runs in IRAM ensuring low latency +- **Lock-free Design**: Most operations use atomic operations reducing lock contention +- **Buffer Reuse**: Intelligent buffer management reduces memory allocation overhead + +## Quick Start + +### 1. Enable Module + +Enable the BLE Log module in `menuconfig`: + +``` +Component config → Bluetooth → Enable BLE Log Module (Experimental) +``` + +### 2. Basic Configuration + +```c +#include "ble_log.h" + +void app_main() { + // Initialize BLE Log module + if (!ble_log_init()) { + ESP_LOGE(TAG, "Failed to initialize BLE Log"); + return; + } + + // Write log data + uint8_t data[] = {0x01, 0x02, 0x03, 0x04}; + ble_log_write_hex(BLE_LOG_SRC_CUSTOM, data, sizeof(data)); + + // Force flush buffers + ble_log_flush(); + + // Cleanup resources + ble_log_deinit(); +} +``` + +### 3. Link Layer Integration + +When `CONFIG_BLE_LOG_LL_ENABLED` is enabled, Link Layer logs are automatically integrated: + +```c +// Link Layer logs will automatically call this function +void ble_log_write_hex_ll(uint32_t len, const uint8_t *addr, + uint32_t len_append, const uint8_t *addr_append, + uint32_t flag); +``` + +## Configuration Options + +### Basic Configuration + +| Configuration | Default | Description | +|---------------|---------|-------------| +| `CONFIG_BLE_LOG_ENABLED` | n | Enable BLE Log module | +| `CONFIG_BLE_LOG_LBM_TRANS_SIZE` | 512 | Size of each transport buffer | +| `CONFIG_BLE_LOG_LBM_ATOMIC_LOCK_TASK_CNT` | 2 | Number of atomic lock LBMs for task context | +| `CONFIG_BLE_LOG_LBM_ATOMIC_LOCK_ISR_CNT` | 1 | Number of atomic lock LBMs for ISR context | + +### Link Layer Configuration + +| Configuration | Default | Description | +|---------------|---------|-------------| +| `CONFIG_BLE_LOG_LL_ENABLED` | y | Enable Link Layer logging | +| `CONFIG_BLE_LOG_LBM_LL_TRANS_SIZE` | 1024 | Link Layer transport buffer size | + +### Advanced Features + +| Configuration | Default | Description | +|---------------|---------|-------------| +| `CONFIG_BLE_LOG_PAYLOAD_CHECKSUM_ENABLED` | y | Enable payload checksum | +| `CONFIG_BLE_LOG_ENH_STAT_ENABLED` | n | Enable enhanced statistics | +| `CONFIG_BLE_LOG_TS_ENABLED` | n | Enable timestamp synchronization | + +### Transport Method Configuration + +| Transport | Configuration | Description | +|-----------|---------------|-------------| +| Dummy | `CONFIG_BLE_LOG_PRPH_DUMMY` | Debug dummy transport | +| SPI Master DMA | `CONFIG_BLE_LOG_PRPH_SPI_MASTER_DMA` | SPI DMA transport | +| UART DMA | `CONFIG_BLE_LOG_PRPH_UART_DMA` | UART DMA transport | + +## API Reference + +### Core API + +#### `bool ble_log_init(void)` + +Initialize the BLE Log module. + +**Return Value**: +- `true`: Initialization successful +- `false`: Initialization failed + +**Note**: Must be called before using any other APIs. + +#### `void ble_log_deinit(void)` + +Cleanup the BLE Log module and release all resources. + +**Note**: +- All pending logs will be lost after calling this function +- Peripheral interface will be cleaned up first to avoid DMA transmission issues during memory release + +#### `bool ble_log_write_hex(ble_log_src_t src_code, const uint8_t *addr, size_t len)` + +Write hexadecimal log data. + +**Parameters**: +- `src_code`: Log source code +- `addr`: Data pointer +- `len`: Data length + +**Return Value**: +- `true`: Write successful +- `false`: Write failed (module not initialized or insufficient buffer) + +#### `void ble_log_flush(void)` + +Force flush all buffers and send pending logs immediately. + +**Note**: This operation is blocking and will pause module operation until all buffers are cleared. + +#### `void ble_log_dump_to_console(void)` + +Output all buffer contents to console in hexadecimal format for debugging. + +### Log Source Types + +```c +typedef enum { + BLE_LOG_SRC_INTERNAL = 0, // Internal system logs + BLE_LOG_SRC_CUSTOM, // User-defined logs + BLE_LOG_SRC_LL_TASK, // Link Layer task logs + BLE_LOG_SRC_LL_HCI, // Link Layer HCI logs + BLE_LOG_SRC_LL_ISR, // Link Layer interrupt logs + BLE_LOG_SRC_HOST, // Host layer logs + BLE_LOG_SRC_HCI, // HCI layer logs + BLE_LOG_SRC_ENCODE, // Encoding layer logs + BLE_LOG_SRC_MAX, +} ble_log_src_t; +``` + +### Link Layer API (Conditional Compilation) + +#### `void ble_log_write_hex_ll(uint32_t len, const uint8_t *addr, uint32_t len_append, const uint8_t *addr_append, uint32_t flag)` + +Link Layer dedicated log writing interface. + +**Parameters**: +- `len`: Main data length +- `addr`: Main data pointer +- `len_append`: Append data length +- `addr_append`: Append data pointer +- `flag`: Log flag bits + +**Flag Definitions**: +```c +enum { + BLE_LOG_LL_FLAG_CONTINUE = 0, + BLE_LOG_LL_FLAG_END, + BLE_LOG_LL_FLAG_TASK, + BLE_LOG_LL_FLAG_ISR, + BLE_LOG_LL_FLAG_HCI, + BLE_LOG_LL_FLAG_RAW, + BLE_LOG_LL_FLAG_HCI_UPSTREAM, +}; +``` + +### Timestamp Synchronization API (Conditional Compilation) + +#### `bool ble_log_sync_enable(bool enable)` + +Enable or disable timestamp synchronization functionality. + +**Parameters**: +- `enable`: true to enable, false to disable + +**Return Value**: +- `true`: Operation successful +- `false`: Operation failed (module not initialized) + +## Usage Examples + +### Example 1: Basic Logging + +```c +#include "ble_log.h" + +void example_basic_logging() { + // Initialize + if (!ble_log_init()) { + printf("BLE Log init failed\n"); + return; + } + + // Log some example data + uint8_t hci_cmd[] = {0x01, 0x03, 0x0C, 0x00}; // HCI Reset Command + ble_log_write_hex(BLE_LOG_SRC_HCI, hci_cmd, sizeof(hci_cmd)); + + uint8_t host_data[] = {0x02, 0x00, 0x20, 0x0B, 0x00, 0x07, 0x00, 0x04, 0x00, 0x10, 0x01, 0x00, 0xFF, 0xFF, 0x00, 0x28}; + ble_log_write_hex(BLE_LOG_SRC_HOST, host_data, sizeof(host_data)); + + // Force send + ble_log_flush(); + + // Cleanup + ble_log_deinit(); +} +``` + +### Example 2: ISR Context Logging + +```c +void IRAM_ATTR some_isr_handler() { + uint8_t isr_data[] = {0xDE, 0xAD, 0xBE, 0xEF}; + + // Safe to write logs in ISR context + ble_log_write_hex(BLE_LOG_SRC_LL_ISR, isr_data, sizeof(isr_data)); +} +``` + +### Example 3: Logging with Timestamp Synchronization + +```c +void example_with_timestamp_sync() { + if (!ble_log_init()) { + return; + } + + #if CONFIG_BLE_LOG_TS_ENABLED + // Enable timestamp synchronization + ble_log_sync_enable(true); + #endif + + // Log data... + uint8_t data[] = {0x01, 0x02, 0x03}; + ble_log_write_hex(BLE_LOG_SRC_CUSTOM, data, sizeof(data)); + + // Timestamp information will be automatically included in logs + + ble_log_deinit(); +} +``` + +### Example 4: Performance Testing + +```c +void example_performance_test() { + if (!ble_log_init()) { + return; + } + + uint8_t test_data[100]; + for (int i = 0; i < 100; i++) { + test_data[i] = i; + } + + uint32_t start_time = esp_timer_get_time(); + + // Send 1000 logs + for (int i = 0; i < 1000; i++) { + ble_log_write_hex(BLE_LOG_SRC_CUSTOM, test_data, sizeof(test_data)); + } + + ble_log_flush(); + uint32_t end_time = esp_timer_get_time(); + + printf("Time to write 1000 logs: %lu us\n", end_time - start_time); + + ble_log_deinit(); +} +``` + +## Performance & Memory Optimization + +### Memory Usage Estimation + +Memory usage under default configuration: + +``` +Total Buffers = (Atomic Task LBMs + Atomic ISR LBMs + Spin LBMs) × 2 × Transport Buffer Size +Default Config = (2 + 1 + 2) × 2 × 512 = 5120 bytes + +Additional when Link Layer enabled: +LL Buffers = 2 × 2 × 1024 = 4096 bytes + +Additional when Enhanced Statistics enabled: +Statistics Data = Log Source Count × sizeof(ble_log_stat_mgr_t) = 8 × 40 = 320 bytes +``` + +### Performance Optimization Recommendations + +1. **Adjust LBM Count**: Adjust atomic lock LBM count based on concurrency requirements +2. **Buffer Size**: Adjust transport buffer size based on log volume +3. **Transport Method**: Choose optimal transport method based on hardware (SPI DMA typically has best performance) +4. **Checksum**: Consider disabling payload checksum when performance requirements are extremely high + +### Real-time Considerations + +- Critical code paths are marked with `BLE_LOG_IRAM_ATTR` and run in IRAM +- Atomic operations avoid lock contention +- Ping-pong buffers ensure continuous writing + +## Troubleshooting + +### Common Issues + +#### 1. Initialization Failure + +**Symptoms**: `ble_log_init()` returns `false` + +**Possible Causes**: +- Insufficient memory +- Peripheral configuration error +- Duplicate initialization + +**Solutions**: +```c +// Check available memory +printf("Free heap: %d bytes\n", esp_get_free_heap_size()); + +// Ensure initialization only happens once +static bool initialized = false; +if (!initialized) { + initialized = ble_log_init(); +} +``` + +#### 2. Log Loss + +**Symptoms**: Some logs don't appear in output + +**Possible Causes**: +- Buffer overflow +- Transmission speed can't keep up with write speed +- Module not properly initialized + +**Solutions**: +```c +// Enable enhanced statistics to check loss rate +#if CONFIG_BLE_LOG_ENH_STAT_ENABLED +// Statistics will be automatically included in logs +#endif + +// Adjust buffer size +// CONFIG_BLE_LOG_LBM_TRANS_SIZE=1024 + +// Increase atomic lock LBM count +// CONFIG_BLE_LOG_LBM_ATOMIC_LOCK_TASK_CNT=4 +``` + +#### 3. Performance Issues + +**Symptoms**: System response becomes slow + +**Possible Causes**: +- Checksum calculation overhead +- Transmission bottleneck +- Lock contention + +**Solutions**: +```c +// Disable payload checksum +// CONFIG_BLE_LOG_PAYLOAD_CHECKSUM_ENABLED=n + +// Use faster transmission method +// CONFIG_BLE_LOG_PRPH_SPI_MASTER_DMA=y + +// Adjust task priority +#define BLE_LOG_TASK_PRIO configMAX_PRIORITIES-3 +``` + +### Debugging Techniques + +#### 1. Use Dummy Transport for Debugging + +```c +// Select Dummy transport in menuconfig +// Then use dump function to view buffer contents +ble_log_dump_to_console(); +``` + +#### 2. Enable Enhanced Statistics + +```c +// Enable in menuconfig +// CONFIG_BLE_LOG_ENH_STAT_ENABLED=y +// Statistics will be automatically output to logs +``` + +#### 3. Monitor Memory Usage + +```c +void monitor_memory() { + printf("Free heap before init: %d\n", esp_get_free_heap_size()); + ble_log_init(); + printf("Free heap after init: %d\n", esp_get_free_heap_size()); +} +``` diff --git a/components/bt/common/ble_log/include/ble_log.h b/components/bt/common/ble_log/include/ble_log.h new file mode 100644 index 0000000000..e57d80c26c --- /dev/null +++ b/components/bt/common/ble_log/include/ble_log.h @@ -0,0 +1,56 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef __BLE_LOG_H__ +#define __BLE_LOG_H__ + +/* ------- */ +/* BLE Log */ +/* ------- */ + +/* INCLUDE */ +#include +#include +#include + +/* TYPEDEF */ +/* CRITICAL: + * The number of BLE Log source code will directly determine the number of statistic manager + * memory requirements, keep it as less as possible; it's recommended to use subcode for more + * log data structure decoding */ +typedef enum { + /* Internal */ + BLE_LOG_SRC_INTERNAL = 0, + + /* Custom */ + BLE_LOG_SRC_CUSTOM, + + /* BLE Stack */ + BLE_LOG_SRC_LL_TASK, + BLE_LOG_SRC_LL_HCI, + BLE_LOG_SRC_LL_ISR, + BLE_LOG_SRC_HOST, + BLE_LOG_SRC_HCI, + BLE_LOG_SRC_ENCODE, + + BLE_LOG_SRC_MAX, +} ble_log_src_t; + +/* INTERFACE */ +bool ble_log_init(void); +void ble_log_deinit(void); +bool ble_log_enable(bool enable); +void ble_log_flush(void); +bool ble_log_write_hex(ble_log_src_t src_code, const uint8_t *addr, size_t len); +void ble_log_dump_to_console(void); +#if CONFIG_BLE_LOG_LL_ENABLED +void ble_log_write_hex_ll(uint32_t len, const uint8_t *addr, + uint32_t len_append, const uint8_t *addr_append, uint32_t flag); +#endif /* CONFIG_BLE_LOG_LL_ENABLED */ +#if CONFIG_BLE_LOG_TS_ENABLED +bool ble_log_sync_enable(bool enable); +#endif /* CONFIG_BLE_LOG_TS_ENABLED */ + +#endif /* __BLE_LOG_H__ */ diff --git a/components/bt/common/ble_log/src/ble_log.c b/components/bt/common/ble_log/src/ble_log.c new file mode 100644 index 0000000000..97659886b3 --- /dev/null +++ b/components/bt/common/ble_log/src/ble_log.c @@ -0,0 +1,94 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +/* ------- */ +/* BLE Log */ +/* ------- */ + +/* INCLUDE */ +#include "ble_log.h" +#include "ble_log_rt.h" +#include "ble_log_lbm.h" +#include "ble_log_prph.h" +#include "ble_log_util.h" +#if CONFIG_BLE_LOG_TS_ENABLED +#include "ble_log_ts.h" +#endif /* CONFIG_BLE_LOG_TS_ENABLED */ + +/* VARIABLE */ +BLE_LOG_STATIC bool ble_log_inited = false; + +/* INTERFACE */ +bool ble_log_init(void) +{ + /* Avoid double init */ + if (ble_log_inited) { + return true; + } + +#if CONFIG_BLE_LOG_TS_ENABLED + /* Initialize BLE Log TS */ + if (!ble_log_ts_init()) { + goto exit; + } +#endif /* CONFIG_BLE_LOG_TS_ENABLED */ + + /* Initialize BLE Log Runtime */ + if (!ble_log_rt_init()) { + goto exit; + } + + /* Initialize BLE Log LBM */ + if (!ble_log_lbm_init()) { + goto exit; + } + + /* Initialize BLE Log peripheral interface */ + if (!ble_log_prph_init(BLE_LOG_LBM_CNT)) { + goto exit; + } + + /* Initialization done */ + ble_log_inited = true; + ble_log_enable(true); + + /* Write initialization done log */ + ble_log_info_t ble_log_info = { + .int_src_code = BLE_LOG_INT_SRC_INIT_DONE, + .version = BLE_LOG_VERSION, + }; + ble_log_write_hex(BLE_LOG_SRC_INTERNAL, (const uint8_t *)&ble_log_info, sizeof(ble_log_info_t)); + return true; + +exit: + ble_log_deinit(); + return false; +} + +void ble_log_deinit(void) +{ + ble_log_enable(false); + ble_log_inited = false; + + /* CRITICAL: + * BLE Log peripheral interface must be deinitialized at first, + * because there's a risky scenario that may cause severe peripheral + * driver fault - if a log buffer is sent to peripheral driver, and + * ble_log_deinit is called; in this case, if LBM is deinitialized + * before peripheral interface, the log buffer may be freed before + * peripheral driver completing tx, and the result would be faulty */ + ble_log_prph_deinit(); + + /* Deinitialize BLE Log LBM */ + ble_log_lbm_deinit(); + + /* Deinitialize BLE Log Runtime */ + ble_log_rt_deinit(); + +#if CONFIG_BLE_LOG_TS_ENABLED + /* Deinitialize BLE Log TS */ + ble_log_ts_deinit(); +#endif /* CONFIG_BLE_LOG_TS_ENABLED */ +} diff --git a/components/bt/common/ble_log/src/ble_log_lbm.c b/components/bt/common/ble_log/src/ble_log_lbm.c new file mode 100644 index 0000000000..109589a947 --- /dev/null +++ b/components/bt/common/ble_log/src/ble_log_lbm.c @@ -0,0 +1,543 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +/* ------------------------------- */ +/* BLE Log - Log Buffer Management */ +/* ------------------------------- */ + +/* INCLUDE */ +#include "ble_log.h" +#include "ble_log_lbm.h" +#include "ble_log_rt.h" + +/* VARIABLE */ +BLE_LOG_STATIC volatile uint32_t lbm_ref_count = 0; +BLE_LOG_STATIC bool lbm_inited = false; +BLE_LOG_STATIC bool lbm_enabled = false; +BLE_LOG_STATIC ble_log_lbm_ctx_t *lbm_ctx = NULL; +BLE_LOG_STATIC ble_log_stat_mgr_t *stat_mgr_ctx[BLE_LOG_SRC_MAX] = {0}; + +/* PRIVATE FUNCTION DECLARATION */ +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_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_append, uint16_t len_append); +#if CONFIG_BLE_LOG_ENH_STAT_ENABLED +BLE_LOG_STATIC void ble_log_stat_mgr_update(ble_log_src_t src_code, uint32_t len, bool lost); +#endif /* CONFIG_BLE_LOG_ENH_STAT_ENABLED */ + +/* ------------------------- */ +/* PRIVATE INTERFACE */ +/* ------------------------- */ +BLE_LOG_IRAM_ATTR BLE_LOG_STATIC +ble_log_lbm_t *ble_log_lbm_acquire(void) +{ + ble_log_lbm_t *lbm = NULL; + ble_log_lbm_t *atomic_pool; + ble_log_lbm_t *spin_lbm; + int atomic_pool_size; + + if (BLE_LOG_IN_ISR()) { + atomic_pool = lbm_ctx->atomic_pool_isr; + spin_lbm = &(lbm_ctx->spin_isr); + atomic_pool_size = BLE_LOG_LBM_ATOMIC_ISR_CNT; + } else { + atomic_pool = lbm_ctx->atomic_pool_task; + spin_lbm = &(lbm_ctx->spin_task); + atomic_pool_size = BLE_LOG_LBM_ATOMIC_TASK_CNT; + } + + /* Try to acquire atomic LBM first */ + for (int i = 0; i < atomic_pool_size; i++) { + lbm = &atomic_pool[i]; + if (ble_log_cas_acquire(&(lbm->atomic_lock))) { + return lbm; + } + } + + /* Fallback to spinlock LBM */ + lbm = spin_lbm; + BLE_LOG_ACQUIRE_SPIN_LOCK(&(lbm->spin_lock)); + return lbm; +} + +BLE_LOG_IRAM_ATTR BLE_LOG_STATIC +void ble_log_lbm_release(ble_log_lbm_t *lbm) +{ + switch (lbm->lock_type) { + case BLE_LOG_LBM_LOCK_ATOMIC: + ble_log_cas_release(&(lbm->atomic_lock)); + break; + case BLE_LOG_LBM_LOCK_SPIN: + BLE_LOG_RELEASE_SPIN_LOCK(&lbm->spin_lock); + break; + case BLE_LOG_LBM_LOCK_NONE: + default: + break; + } +} + +BLE_LOG_IRAM_ATTR BLE_LOG_STATIC +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_append, uint16_t len_append) +{ + /* Preparation before writing */ + uint8_t *buf = (*trans)->buf + (*trans)->pos; + uint16_t payload_len = len + len_append; + 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), + }; + + /* Memory operation */ + BLE_LOG_MEMCPY(buf, &frame_head, BLE_LOG_FRAME_HEAD_LEN); + if (len) { + BLE_LOG_MEMCPY(buf + BLE_LOG_FRAME_HEAD_LEN, addr, len); + } + if (len_append) { + BLE_LOG_MEMCPY(buf + BLE_LOG_FRAME_HEAD_LEN + len, addr_append, len_append); + } + + /* Data integrity check */ +#if CONFIG_BLE_LOG_PAYLOAD_CHECKSUM_ENABLED + uint32_t checksum = ble_log_fast_checksum((const uint8_t *)buf, BLE_LOG_FRAME_HEAD_LEN + payload_len); +#else /* !CONFIG_BLE_LOG_PAYLOAD_CHECKSUM_ENABLED */ + /* Note: + * Minimum data integrity check is still required for log parsing reliability, + * which can be achieved by validating the checksum of frame head only */ + uint32_t checksum = ble_log_fast_checksum((const uint8_t *)buf, BLE_LOG_FRAME_HEAD_LEN); +#endif /* CONFIG_BLE_LOG_PAYLOAD_CHECKSUM_ENABLED */ + BLE_LOG_MEMCPY(buf + BLE_LOG_FRAME_HEAD_LEN + payload_len, &checksum, BLE_LOG_FRAME_TAIL_LEN); + + /* Update peripheral transport */ + (*trans)->pos += payload_len + BLE_LOG_FRAME_OVERHEAD; + +#if CONFIG_BLE_LOG_ENH_STAT_ENABLED + ble_log_stat_mgr_update(src_code, payload_len, false); +#endif /* CONFIG_BLE_LOG_ENH_STAT_ENABLED */ + + /* Queue trans if full */ + if (BLE_LOG_TRANS_FREE_SPACE((*trans)) <= BLE_LOG_FRAME_OVERHEAD) { + ble_log_rt_queue_trans(trans); + } +} + +#if CONFIG_BLE_LOG_ENH_STAT_ENABLED +BLE_LOG_IRAM_ATTR BLE_LOG_STATIC +void ble_log_stat_mgr_update(ble_log_src_t src_code, uint32_t len, bool lost) +{ + /* Get statistic manager by source code */ + ble_log_stat_mgr_t *stat_mgr = stat_mgr_ctx[src_code]; + + /* Update statistics */ + uint32_t bytes_cnt = len + BLE_LOG_FRAME_OVERHEAD; + if (lost) { + stat_mgr->enh_stat.lost_frame_cnt++; + stat_mgr->enh_stat.lost_bytes_cnt += bytes_cnt; + } else { + stat_mgr->enh_stat.written_frame_cnt++; + stat_mgr->enh_stat.written_bytes_cnt += bytes_cnt; + } +} +#endif /* CONFIG_BLE_LOG_ENH_STAT_ENABLED */ + +/* -------------------------- */ +/* INTERNAL INTERFACE */ +/* -------------------------- */ +bool ble_log_lbm_init(void) +{ + /* Avoid double init */ + if (lbm_inited) { + return true; + } + + /* Initialize LBM context */ + lbm_ctx = (ble_log_lbm_ctx_t *)BLE_LOG_MALLOC(sizeof(ble_log_lbm_ctx_t)); + if (!lbm_ctx) { + goto exit; + } + BLE_LOG_MEMSET(lbm_ctx, 0, sizeof(ble_log_lbm_ctx_t)); + + /* Initialize peripheral transport for common LBMs */ + ble_log_lbm_t *lbm; + for (int i = 0; i < BLE_LOG_LBM_COMMON_CNT; i++) { + lbm = &(lbm_ctx->lbm_common_pool[i]); + for (int j = 0; j < BLE_LOG_TRANS_PING_PONG_BUF_CNT; j++) { + if (!ble_log_prph_trans_init(&(lbm->trans[j]), + CONFIG_BLE_LOG_LBM_TRANS_SIZE)) { + goto exit; + } + } + } + + /* Initialize lock types for atomic pool */ + for (int i = 0; i < BLE_LOG_LBM_ATOMIC_CNT; i++) { + lbm_ctx->atomic_pool[i].lock_type = BLE_LOG_LBM_LOCK_ATOMIC; + } + + /* Initialize lock types for spin pool */ + for (int i = 0; i < BLE_LOG_LBM_SPIN_MAX; i++) { + lbm_ctx->spin_pool[i].lock_type = BLE_LOG_LBM_LOCK_SPIN; + } + +#if CONFIG_BLE_LOG_LL_ENABLED + for (int i = 0; i < BLE_LOG_LBM_LL_MAX; i++) { + lbm = &(lbm_ctx->lbm_ll_pool[i]); + for (int j = 0; j < BLE_LOG_TRANS_PING_PONG_BUF_CNT; j++) { + if (!ble_log_prph_trans_init(&(lbm->trans[j]), + CONFIG_BLE_LOG_LBM_LL_TRANS_SIZE)) { + goto exit; + } + } + } + + /* Initialize lock types for LL pool */ + for (int i = 0; i < BLE_LOG_LBM_LL_MAX; i++) { + lbm_ctx->lbm_ll_pool[i].lock_type = BLE_LOG_LBM_LOCK_NONE; + } +#endif /* CONFIG_BLE_LOG_LL_ENABLED */ + + /* Initialize statistic manager context */ + for (int i = 0; i < BLE_LOG_SRC_MAX; i++) { + stat_mgr_ctx[i] = (ble_log_stat_mgr_t *)BLE_LOG_MALLOC(sizeof(ble_log_stat_mgr_t)); + if (!stat_mgr_ctx[i]) { + goto exit; + } + BLE_LOG_MEMSET(stat_mgr_ctx[i], 0, sizeof(ble_log_stat_mgr_t)); + +#if CONFIG_BLE_LOG_ENH_STAT_ENABLED + stat_mgr_ctx[i]->enh_stat.int_src_code = BLE_LOG_INT_SRC_ENH_STAT; + stat_mgr_ctx[i]->enh_stat.src_code = i; +#endif /* CONFIG_BLE_LOG_ENH_STAT_ENABLED */ + } + + /* Initialization done */ + lbm_ref_count = 0; + lbm_inited = true; + lbm_enabled = false; + return true; + +exit: + ble_log_lbm_deinit(); + return false; +} + +void ble_log_lbm_deinit(void) +{ + /* Set inited flag to false to prevent new references */ + lbm_inited = false; + lbm_enabled = false; + + /* Disable module and wait for all references to be released */ + uint32_t time_waited = 0; + while (lbm_ref_count > 0) { + vTaskDelay(pdMS_TO_TICKS(1)); + BLE_LOG_ASSERT(time_waited++ < 1000); + } + + /* Release statistic manager context */ + for (int i = 0; i < BLE_LOG_SRC_MAX; i++) { + if (stat_mgr_ctx[i]) { + BLE_LOG_FREE(stat_mgr_ctx[i]); + stat_mgr_ctx[i] = NULL; + } + } + + /* Release LBM */ + if (lbm_ctx) { + /* Release peripheral transport for common pools */ + ble_log_lbm_t *lbm; + for (int i = 0; i < BLE_LOG_LBM_CNT; i++) { + lbm = &(lbm_ctx->lbm_pool[i]); + for (int j = 0; j < BLE_LOG_TRANS_PING_PONG_BUF_CNT; j++) { + ble_log_prph_trans_deinit(&(lbm->trans[j])); + } + } + + /* Release LBM context */ + BLE_LOG_FREE(lbm_ctx); + lbm_ctx = NULL; + } +} + +/* Note: + * 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) +{ + /* Check if available buffer can contain incoming log */ + ble_log_prph_trans_t **trans; + for (int i = 0; i < BLE_LOG_TRANS_PING_PONG_BUF_CNT; i++) { + trans = &(lbm->trans[lbm->trans_idx]); + if (!(*trans)->prph_owned) { + /* Return if there's enough free space in current transport */ + if (BLE_LOG_TRANS_FREE_SPACE((*trans)) >= (log_len + BLE_LOG_FRAME_OVERHEAD)) { + return trans; + } + + /* Queue transport if there's insufficient free space */ + if ((*trans)->pos) { + ble_log_rt_queue_trans(trans); + } + } + + /* Current transport unavailable, switch to the other */ + lbm->trans_idx = !lbm->trans_idx; + } + + /* Both ping-pong buffers are unavailable */ + return NULL; +} + +#if CONFIG_BLE_LOG_ENH_STAT_ENABLED +void ble_log_write_enh_stat(void) +{ + BLE_LOG_REF_COUNT_ACQUIRE(&lbm_ref_count); + if (!lbm_enabled) { + goto deref; + } + + for (int i = 0; i < BLE_LOG_SRC_MAX; i++) { + ble_log_enh_stat_t *enh_stat = &(stat_mgr_ctx[i]->enh_stat); + ble_log_write_hex(BLE_LOG_SRC_INTERNAL, (const uint8_t *)enh_stat, sizeof(ble_log_enh_stat_t)); + } + +deref: + BLE_LOG_REF_COUNT_RELEASE(&lbm_ref_count); +} +#endif /* CONFIG_BLE_LOG_ENH_STAT_ENABLED */ + +/* ------------------------ */ +/* PUBLIC INTERFACE */ +/* ------------------------ */ +bool ble_log_enable(bool enable) +{ + if (!lbm_inited) { + return false; + } + lbm_enabled = enable; + return true; +} + +void ble_log_flush(void) +{ + BLE_LOG_REF_COUNT_ACQUIRE(&lbm_ref_count); + if (!lbm_inited) { + goto deref; + } + +#if CONFIG_BLE_LOG_ENH_STAT_ENABLED + /* Write enhanced statistics before module disable */ + ble_log_write_enh_stat(); +#endif /* CONFIG_BLE_LOG_ENH_STAT_ENABLED */ + + /* Write BLE Log flush log */ + ble_log_info_t ble_log_info = { + .int_src_code = BLE_LOG_INT_SRC_FLUSH, + .version = BLE_LOG_VERSION, + }; + ble_log_write_hex(BLE_LOG_SRC_INTERNAL, (const uint8_t *)&ble_log_info, sizeof(ble_log_info_t)); + + /* Disable module and wait for all other references to release */ + bool lbm_enabled_copy = lbm_enabled; + lbm_enabled = false; + uint32_t time_waited = 0; + while (lbm_ref_count > 1) { + vTaskDelay(pdMS_TO_TICKS(1)); + BLE_LOG_ASSERT(time_waited++ < 1000); + } + + /* Queue transports with logs */ + ble_log_lbm_t *lbm; + ble_log_prph_trans_t **trans; + + /* Flush pools */ + for (int i = 0; i < BLE_LOG_LBM_CNT; i++) { + lbm = &(lbm_ctx->lbm_pool[i]); + int trans_idx = lbm->trans_idx; + for (int j = 0; j < BLE_LOG_TRANS_PING_PONG_BUF_CNT; j++) { + trans = &(lbm->trans[trans_idx]); + if (!(*trans)->prph_owned && (*trans)->pos) { + ble_log_rt_queue_trans(trans); + } + trans_idx = !trans_idx; + } + } + + /* Wait for transportation to finish */ + time_waited = 0; + bool in_progress; + do { + in_progress = false; + for (int i = 0; i < BLE_LOG_LBM_CNT; i++) { + lbm = &(lbm_ctx->lbm_pool[i]); + for (int j = 0; j < BLE_LOG_TRANS_PING_PONG_BUF_CNT; j++) { + trans = &(lbm->trans[j]); + in_progress |= (*trans)->prph_owned; + } + } + if (in_progress) { + vTaskDelay(pdMS_TO_TICKS(1)); + BLE_LOG_ASSERT(time_waited++ < 1000); + } + } while (in_progress); + + /* Reset statistics manager after all operations complete */ + for (int i = 0; i < BLE_LOG_SRC_MAX; i++) { + BLE_LOG_MEMSET(stat_mgr_ctx[i], 0, sizeof(ble_log_stat_mgr_t)); +#if CONFIG_BLE_LOG_ENH_STAT_ENABLED + /* Reinitialize enhanced statistics fields */ + stat_mgr_ctx[i]->enh_stat.int_src_code = BLE_LOG_INT_SRC_ENH_STAT; + stat_mgr_ctx[i]->enh_stat.src_code = i; +#endif /* CONFIG_BLE_LOG_ENH_STAT_ENABLED */ + } + + /* Resume enable status */ + lbm_enabled = lbm_enabled_copy; + +deref: + BLE_LOG_REF_COUNT_RELEASE(&lbm_ref_count); +} + +bool ble_log_write_hex(ble_log_src_t src_code, const uint8_t *addr, size_t len) +{ + BLE_LOG_REF_COUNT_ACQUIRE(&lbm_ref_count); + size_t payload_len = len + sizeof(uint32_t); + if (!lbm_enabled) { + goto exit; + } + + /* Get transport */ + ble_log_lbm_t *lbm = ble_log_lbm_acquire(); + ble_log_prph_trans_t **trans = ble_log_lbm_get_trans(lbm, payload_len); + if (!trans) { + ble_log_lbm_release(lbm); + goto exit; + } + + /* Write transport */ + uint32_t os_ts = pdTICKS_TO_MS(BLE_LOG_IN_ISR()? + xTaskGetTickCountFromISR(): + xTaskGetTickCount()); + ble_log_lbm_write_trans(trans, src_code, (const uint8_t *)&os_ts, + sizeof(uint32_t), addr, len); + + /* Release */ + ble_log_lbm_release(lbm); + BLE_LOG_REF_COUNT_RELEASE(&lbm_ref_count); + return true; + +exit: +#if CONFIG_BLE_LOG_ENH_STAT_ENABLED + if (lbm_inited) { + ble_log_stat_mgr_update(src_code, payload_len, true); + } +#endif /* CONFIG_BLE_LOG_ENH_STAT_ENABLED */ + BLE_LOG_REF_COUNT_RELEASE(&lbm_ref_count); + return false; +} + +#if CONFIG_BLE_LOG_LL_ENABLED +BLE_LOG_IRAM_ATTR +void ble_log_write_hex_ll(uint32_t len, const uint8_t *addr, + uint32_t len_append, const uint8_t *addr_append, uint32_t flag) +{ + BLE_LOG_REF_COUNT_ACQUIRE(&lbm_ref_count); + size_t payload_len = len + len_append; + + /* Source code shall be determined before LBM enable status check */ + ble_log_src_t src_code; + bool use_ll_task = false; + if (flag & BIT(BLE_LOG_LL_FLAG_ISR)) { + src_code = BLE_LOG_SRC_LL_ISR; + } else if (flag & BIT(BLE_LOG_LL_FLAG_HCI)) { + src_code = BLE_LOG_SRC_LL_HCI; + } else if (flag & BIT(BLE_LOG_LL_FLAG_HCI_UPSTREAM)) { + src_code = BLE_LOG_SRC_HCI; + } else { + src_code = BLE_LOG_SRC_LL_TASK; + use_ll_task = true; + } + + if (!lbm_enabled) { + goto exit; + } + + /* Determine LBM by flag */ + ble_log_lbm_t *lbm; + if (BLE_LOG_IN_ISR()) { + /* Reuse common LBM acquire logic */ + lbm = ble_log_lbm_acquire(); + } else { + lbm = (use_ll_task)? &(lbm_ctx->lbm_ll_task): &(lbm_ctx->lbm_ll_hci); + } + + /* Get transport */ + ble_log_prph_trans_t **trans = ble_log_lbm_get_trans(lbm, payload_len); + if (!trans) { + ble_log_lbm_release(lbm); + goto exit; + } + + /* Write transport */ + ble_log_lbm_write_trans(trans, src_code, addr, len, addr_append, len_append); + + ble_log_lbm_release(lbm); + BLE_LOG_REF_COUNT_RELEASE(&lbm_ref_count); + return; + +exit: +#if CONFIG_BLE_LOG_ENH_STAT_ENABLED + if (lbm_inited) { + ble_log_stat_mgr_update(src_code, payload_len, true); + } +#endif /* CONFIG_BLE_LOG_ENH_STAT_ENABLED */ + BLE_LOG_REF_COUNT_RELEASE(&lbm_ref_count); + return; +} +#endif /* CONFIG_BLE_LOG_LL_ENABLED */ + +void ble_log_dump_to_console(void) +{ + BLE_LOG_REF_COUNT_ACQUIRE(&lbm_ref_count); + if (!lbm_inited) { + goto deref; + } + + int trans_idx; + ble_log_lbm_t *lbm; + ble_log_prph_trans_t *trans; + BLE_LOG_ENTER_CRITICAL(); + BLE_LOG_CONSOLE("[BLE_LOG_DUMP_START:\n"); + for (int i = 0; i < BLE_LOG_LBM_CNT; i++) { + lbm = &(lbm_ctx->lbm_pool[i]); + trans_idx = lbm->trans_idx; + for (int j = 0; j < BLE_LOG_TRANS_PING_PONG_BUF_CNT; j++) { + trans = lbm->trans[trans_idx]; + BLE_LOG_FEED_WDT(); + + for (int k = 0; k < trans->size; k++) { + BLE_LOG_CONSOLE("%02x ", trans->buf[k]); + if (!(k & 0xFF)) { + BLE_LOG_FEED_WDT(); + } + } + trans_idx = !trans_idx; + } + } + BLE_LOG_CONSOLE("\n:BLE_LOG_DUMP_END]\n\n"); + BLE_LOG_EXIT_CRITICAL(); + +deref: + BLE_LOG_REF_COUNT_RELEASE(&lbm_ref_count); + return; +} diff --git a/components/bt/common/ble_log/src/ble_log_rt.c b/components/bt/common/ble_log/src/ble_log_rt.c new file mode 100644 index 0000000000..382438c0cd --- /dev/null +++ b/components/bt/common/ble_log/src/ble_log_rt.c @@ -0,0 +1,144 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/* ----------------- */ +/* BLE Log - Runtime */ +/* ----------------- */ + +/* INCLUDE */ +#include "ble_log.h" +#include "ble_log_rt.h" +#include "ble_log_lbm.h" + +/* VARIABLE */ +BLE_LOG_STATIC bool rt_inited = false; +BLE_LOG_STATIC TaskHandle_t rt_task_handle = NULL; +BLE_LOG_STATIC QueueHandle_t rt_queue_handle = NULL; +#if CONFIG_BLE_LOG_TS_ENABLED +BLE_LOG_STATIC bool rt_ts_enabled = false; +#endif /* CONFIG_BLE_LOG_TS_ENABLED */ + +/* PRIVATE FUNCTION */ +BLE_LOG_IRAM_ATTR BLE_LOG_STATIC void ble_log_rt_task(void *pvParameters) +{ + (void)pvParameters; + ble_log_prph_trans_t *trans = NULL; + uint32_t curr_os_ts = 0; + uint32_t last_hook_os_ts = 0; +#ifndef UNIT_TEST + while (1) +#endif /* !UNIT_TEST */ + { + /* CRITICAL: + * Blocking queue receive is mandatory for light sleep support */ + if (xQueueReceive(rt_queue_handle, &trans, portMAX_DELAY) == pdTRUE) { + ble_log_prph_send_trans(trans); + } + + /* Task hook */ + curr_os_ts = pdTICKS_TO_MS(xTaskGetTickCount()); + if ((curr_os_ts - last_hook_os_ts) < BLE_LOG_TASK_HOOK_TIMEOUT_MS) { +#ifndef UNIT_TEST + continue; +#else /* UNIT_TEST */ + return; +#endif /* !UNIT_TEST */ + } + last_hook_os_ts = curr_os_ts; + + /* Write BLE Log info log */ + ble_log_info_t ble_log_info = { + .int_src_code = BLE_LOG_INT_SRC_INFO, + .version = BLE_LOG_VERSION, + }; + ble_log_write_hex(BLE_LOG_SRC_INTERNAL, (const uint8_t *)&ble_log_info, sizeof(ble_log_info_t)); + +#if CONFIG_BLE_LOG_TS_ENABLED + if (rt_ts_enabled) { + ble_log_ts_info_t *ts_info = NULL; + ble_log_ts_info_update(&ts_info); + ble_log_write_hex(BLE_LOG_SRC_INTERNAL, (const uint8_t *)ts_info, sizeof(ble_log_ts_info_t)); + } +#endif /* CONFIG_BLE_LOG_TS_ENABLED */ + +#if CONFIG_BLE_LOG_ENH_STAT_ENABLED + ble_log_write_enh_stat(); +#endif /* CONFIG_BLE_LOG_ENH_STAT_ENABLED */ + } +} + +/* INTERFACE */ +bool ble_log_rt_init(void) +{ + if (rt_inited) { + return true; + } + + /* CRITICAL: + * Queue must be initialized before creating task */ + rt_queue_handle = xQueueCreate(BLE_LOG_LBM_CNT, sizeof(ble_log_prph_trans_t *)); + if (!rt_queue_handle) { + goto exit; + } + + /* Initialize task */ + if (xTaskCreate(ble_log_rt_task, "ble_log", BLE_LOG_TASK_STACK_SIZE, NULL, + BLE_LOG_TASK_PRIO, &rt_task_handle) != pdTRUE) { + goto exit; + } + + rt_inited = true; +#if CONFIG_BLE_LOG_TS_ENABLED + rt_ts_enabled = false; +#endif /* CONFIG_BLE_LOG_TS_ENABLED */ + return true; + +exit: + ble_log_rt_deinit(); + return false; +} + +void ble_log_rt_deinit(void) +{ + rt_inited = false; +#if CONFIG_BLE_LOG_TS_ENABLED + rt_ts_enabled = false; +#endif /* CONFIG_BLE_LOG_TS_ENABLED */ + + /* CRITICAL: + * Task must be deinitialized before deinitializing queue */ + if (rt_task_handle) { + vTaskDelete(rt_task_handle); + rt_task_handle = NULL; + } + + /* Release task queue */ + if (rt_queue_handle) { + vQueueDelete(rt_queue_handle); + rt_queue_handle = NULL; + } +} + +BLE_LOG_IRAM_ATTR void ble_log_rt_queue_trans(ble_log_prph_trans_t **trans) +{ + (*trans)->prph_owned = true; + if (BLE_LOG_IN_ISR()) { + xQueueSendFromISR(rt_queue_handle, trans, NULL); + } else { + xQueueSend(rt_queue_handle, trans, portMAX_DELAY); + } +} + +#if CONFIG_BLE_LOG_TS_ENABLED +bool ble_log_sync_enable(bool enable) +{ + if (!rt_inited) { + return false; + } + rt_ts_enabled = enable; + return true; +} +#endif /* CONFIG_BLE_LOG_TS_ENABLED */ diff --git a/components/bt/common/ble_log/src/ble_log_ts.c b/components/bt/common/ble_log/src/ble_log_ts.c new file mode 100644 index 0000000000..38ad21c210 --- /dev/null +++ b/components/bt/common/ble_log/src/ble_log_ts.c @@ -0,0 +1,75 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +/* ----------------------------------- */ +/* BLE Log - Timestamp Synchronization */ +/* ----------------------------------- */ + +/* INCLUDE */ +#include "ble_log_ts.h" + +/* VARIABLE */ +BLE_LOG_STATIC bool ts_inited = false; +BLE_LOG_STATIC ble_log_ts_info_t *ts_info = NULL; + +/* INTERFACE */ +bool ble_log_ts_init(void) +{ + if (ts_inited) { + return true; + } + + /* Initialize toggle IO */ + gpio_config_t sync_io_conf = { + .intr_type = GPIO_INTR_DISABLE, + .mode = GPIO_MODE_OUTPUT, + .pin_bit_mask = BIT(CONFIG_BLE_LOG_SYNC_IO_NUM), + }; + if (gpio_config(&sync_io_conf) != ESP_OK) { + goto exit; + } + + /* Initialize sync data */ + ts_info = (ble_log_ts_info_t *)BLE_LOG_MALLOC(sizeof(ble_log_ts_info_t)); + if (!ts_info) { + goto exit; + } + BLE_LOG_MEMSET(ts_info, 0, sizeof(ble_log_ts_info_t)); + ts_info->int_src_code = BLE_LOG_INT_SRC_TS; + + ts_inited = true; + return true; + +exit: + ble_log_ts_deinit(); + return false; +} + +void ble_log_ts_deinit(void) +{ + ts_inited = false; + + /* Release sync data */ + if (ts_info) { + BLE_LOG_FREE(ts_info); + ts_info = NULL; + } + + /* Release toggle IO */ + gpio_reset_pin(CONFIG_BLE_LOG_SYNC_IO_NUM); +} + +void ble_log_ts_info_update(ble_log_ts_info_t **info) +{ + BLE_LOG_ENTER_CRITICAL(); + ts_info->io_level = !ts_info->io_level; + gpio_set_level(CONFIG_BLE_LOG_SYNC_IO_NUM, ts_info->io_level); + ts_info->lc_ts = BLE_LOG_GET_LC_TS; + ts_info->esp_ts = esp_timer_get_time(); + ts_info->os_ts = pdTICKS_TO_MS(xTaskGetTickCountFromISR()); + BLE_LOG_EXIT_CRITICAL(); + + *info = ts_info; +} diff --git a/components/bt/common/ble_log/src/ble_log_util.c b/components/bt/common/ble_log/src/ble_log_util.c new file mode 100644 index 0000000000..7657ad92dd --- /dev/null +++ b/components/bt/common/ble_log/src/ble_log_util.c @@ -0,0 +1,48 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/* ----------------- */ +/* BLE Log - Utility */ +/* ----------------- */ + +/* INCLUDE */ +#include "ble_log_util.h" + +/* VARIABLE */ +#ifndef UNIT_TEST +portMUX_TYPE ble_log_spin_lock = portMUX_INITIALIZER_UNLOCKED; +#endif /* !UNIT_TEST */ + +/* INTERNAL INTERFACE */ +BLE_LOG_IRAM_ATTR uint32_t ble_log_fast_checksum(const uint8_t *data, size_t len) +{ + uint32_t sum = 0; + size_t i = 0; + + /* Step 1: Sum up until 4-byte aligned */ + while (((uintptr_t)(data + i) & 0x3) && (i < len)) { + sum += data[i++]; + } + + /* Step 2: Sum up 4-byte aligned blocks */ + const uint32_t *p32 = (const uint32_t *)(data + i); + size_t blocks = (len - i) / 4; + for (size_t b = 0; b < blocks; b++) { + uint32_t v = p32[b]; + sum += (v & 0xFF) + + ((v >> 8) & 0xFF) + + ((v >> 16) & 0xFF) + + ((v >> 24) & 0xFF); + } + i += blocks * 4; + + /* Step 3: Sum up remaining bytes */ + while (i < len) { + sum += data[i++]; + } + + return sum; +} diff --git a/components/bt/common/ble_log/src/internal_include/ble_log_lbm.h b/components/bt/common/ble_log/src/internal_include/ble_log_lbm.h new file mode 100644 index 0000000000..63d0172dbd --- /dev/null +++ b/components/bt/common/ble_log/src/internal_include/ble_log_lbm.h @@ -0,0 +1,177 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef __BLE_LOG_LBM_H__ +#define __BLE_LOG_LBM_H__ + +/* --------------------------------------- */ +/* BLE Log - Log Buffer Management */ +/* --------------------------------------- */ + +/* ---------------- */ +/* Includes */ +/* ---------------- */ +#include "ble_log_prph.h" + +#include "freertos/FreeRTOS.h" +#include "freertos/semphr.h" +#include "freertos/portmacro.h" + +/* ------------------------- */ +/* Log Frame Defines */ +/* ------------------------- */ +typedef struct { + uint16_t length; + uint32_t frame_meta; +} __attribute__((packed)) ble_log_frame_head_t; + +#define BLE_LOG_FRAME_HEAD_LEN (sizeof(ble_log_frame_head_t)) +#define BLE_LOG_FRAME_TAIL_LEN (sizeof(uint32_t)) +#define BLE_LOG_FRAME_OVERHEAD (BLE_LOG_FRAME_HEAD_LEN + BLE_LOG_FRAME_TAIL_LEN) +#define BLE_LOG_MAKE_FRAME_META(src_code, sn) ((src_code & 0xFF) | (sn << 8)) + +/* ---------------------------------- */ +/* Log Buffer Manager Defines */ +/* ---------------------------------- */ +typedef enum { + BLE_LOG_LBM_LOCK_NONE, + BLE_LOG_LBM_LOCK_SPIN, + BLE_LOG_LBM_LOCK_ATOMIC, + BLE_LOG_LBM_LOCK_MUTEX, +} ble_log_lbm_lock_t; + +typedef struct { + int trans_idx; + ble_log_prph_trans_t *trans[BLE_LOG_TRANS_PING_PONG_BUF_CNT]; + ble_log_lbm_lock_t lock_type; + union { + /* BLE_LOG_LBM_LOCK_NONE */ + void *none; + /* BLE_LOG_LBM_LOCK_SPIN */ + portMUX_TYPE spin_lock; + /* BLE_LOG_LBM_LOCK_ATOMIC */ + volatile bool atomic_lock; + /* BLE_LOG_LBM_LOCK_MUTEX */ + SemaphoreHandle_t mutex; + }; +} ble_log_lbm_t; + +/* --------------------------------------- */ +/* Log Buffer Manager Pool Defines */ +/* --------------------------------------- */ +enum { +#if CONFIG_BLE_LOG_LL_ENABLED + BLE_LOG_LBM_LL_TASK, + BLE_LOG_LBM_LL_HCI, +#endif /* CONFIG_BLE_LOG_LL_ENABLED */ + BLE_LOG_LBM_LL_MAX, +}; + +enum { + BLE_LOG_LBM_SPIN_TASK = 0, + BLE_LOG_LBM_SPIN_ISR, + BLE_LOG_LBM_SPIN_MAX, +}; + +#define BLE_LOG_LBM_ATOMIC_TASK_CNT CONFIG_BLE_LOG_LBM_ATOMIC_LOCK_TASK_CNT +#define BLE_LOG_LBM_ATOMIC_ISR_CNT CONFIG_BLE_LOG_LBM_ATOMIC_LOCK_ISR_CNT +#define BLE_LOG_LBM_ATOMIC_CNT (BLE_LOG_LBM_ATOMIC_TASK_CNT +\ + BLE_LOG_LBM_ATOMIC_ISR_CNT) +#define BLE_LOG_LBM_COMMON_CNT (BLE_LOG_LBM_ATOMIC_CNT + BLE_LOG_LBM_SPIN_MAX) +#define BLE_LOG_LBM_CNT (BLE_LOG_LBM_COMMON_CNT + BLE_LOG_LBM_LL_MAX) +#define BLE_LOG_TRANS_CNT (BLE_LOG_LBM_CNT * BLE_LOG_TRANS_PING_PONG_BUF_CNT) + +/* ------------------------------------------ */ +/* Log Buffer Manager Context Defines */ +/* ------------------------------------------ */ +typedef struct { + union { + ble_log_lbm_t lbm_pool[BLE_LOG_LBM_CNT]; + struct { + union { + ble_log_lbm_t lbm_common_pool[BLE_LOG_LBM_COMMON_CNT]; + struct { + union { + ble_log_lbm_t spin_pool[BLE_LOG_LBM_SPIN_MAX]; + struct { + ble_log_lbm_t spin_task; + ble_log_lbm_t spin_isr; + }; + }; + union { + ble_log_lbm_t atomic_pool[BLE_LOG_LBM_ATOMIC_CNT]; + struct { + ble_log_lbm_t atomic_pool_task[BLE_LOG_LBM_ATOMIC_TASK_CNT]; + ble_log_lbm_t atomic_pool_isr[BLE_LOG_LBM_ATOMIC_ISR_CNT]; + }; + }; + }; + }; + union { + ble_log_lbm_t lbm_ll_pool[BLE_LOG_LBM_LL_MAX]; +#if CONFIG_BLE_LOG_LL_ENABLED + struct { + ble_log_lbm_t lbm_ll_task; + ble_log_lbm_t lbm_ll_hci; + }; +#endif /* CONFIG_BLE_LOG_LL_ENABLED */ + }; + }; + }; +} ble_log_lbm_ctx_t; + +/* ---------------------------------------- */ +/* Enhanced Statistics Data Defines */ +/* ---------------------------------------- */ +#if CONFIG_BLE_LOG_ENH_STAT_ENABLED +typedef struct { + uint8_t int_src_code; + uint8_t src_code; + uint32_t written_frame_cnt; + uint32_t lost_frame_cnt; + uint32_t written_bytes_cnt; + uint32_t lost_bytes_cnt; +} __attribute__((packed)) ble_log_enh_stat_t; +#endif /* CONFIG_BLE_LOG_ENH_STAT_ENABLED */ + +/* -------------------------------------- */ +/* Log Statistics Manager Context */ +/* -------------------------------------- */ +typedef struct { + uint32_t frame_sn; +#if CONFIG_BLE_LOG_ENH_STAT_ENABLED + ble_log_enh_stat_t enh_stat; +#endif /* CONFIG_BLE_LOG_ENH_STAT_ENABLED */ +} ble_log_stat_mgr_t; + +#define BLE_LOG_GET_FRAME_SN(VAR) __atomic_fetch_add(VAR, 1, __ATOMIC_RELAXED) + +/* -------------------------- */ +/* Link Layer Defines */ +/* -------------------------- */ +#if CONFIG_BLE_LOG_LL_ENABLED +enum { + BLE_LOG_LL_FLAG_CONTINUE = 0, + BLE_LOG_LL_FLAG_END, + BLE_LOG_LL_FLAG_TASK, + BLE_LOG_LL_FLAG_ISR, + BLE_LOG_LL_FLAG_HCI, + BLE_LOG_LL_FLAG_RAW, + BLE_LOG_LL_FLAG_HCI_UPSTREAM, +}; +#endif /* CONFIG_BLE_LOG_LL_ENABLED */ + +/* --------------------------- */ +/* Internal Interfaces */ +/* --------------------------- */ +bool ble_log_lbm_init(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); +#if CONFIG_BLE_LOG_ENH_STAT_ENABLED +void ble_log_write_enh_stat(void); +#endif /* CONFIG_BLE_LOG_ENH_STAT_ENABLED */ + +#endif /* __BLE_LOG_LBM_H__ */ diff --git a/components/bt/common/ble_log/src/internal_include/ble_log_prph.h b/components/bt/common/ble_log/src/internal_include/ble_log_prph.h new file mode 100644 index 0000000000..d5df67eb3a --- /dev/null +++ b/components/bt/common/ble_log/src/internal_include/ble_log_prph.h @@ -0,0 +1,37 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef __BLE_LOG_PRPH_H__ +#define __BLE_LOG_PRPH_H__ + +/* ------------------------------ */ +/* BLE Log - Peripheral Interface */ +/* ------------------------------ */ + +/* INCLUDE */ +#include "ble_log_util.h" + +/* TYPEDEF */ +typedef struct { + volatile bool prph_owned; + uint8_t *buf; + uint16_t size; + uint16_t pos; + + /* Peripheral implementation specific context */ + void *ctx; +} ble_log_prph_trans_t; + +#define BLE_LOG_TRANS_FREE_SPACE(trans) (trans->size - trans->pos) +#define BLE_LOG_TRANS_PING_PONG_BUF_CNT (2) + +/* INTERFACE */ +bool ble_log_prph_init(size_t trans_cnt); +void ble_log_prph_deinit(void); +bool ble_log_prph_trans_init(ble_log_prph_trans_t **trans, size_t trans_size); +void ble_log_prph_trans_deinit(ble_log_prph_trans_t **trans); +void ble_log_prph_send_trans(ble_log_prph_trans_t *trans); + +#endif /* __BLE_LOG_PRPH_H__ */ diff --git a/components/bt/common/ble_log/src/internal_include/ble_log_rt.h b/components/bt/common/ble_log/src/internal_include/ble_log_rt.h new file mode 100644 index 0000000000..f053049339 --- /dev/null +++ b/components/bt/common/ble_log/src/internal_include/ble_log_rt.h @@ -0,0 +1,32 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef __BLE_LOG_RT_H__ +#define __BLE_LOG_RT_H__ + +/* ----------------- */ +/* BLE Log - Runtime */ +/* ----------------- */ + +/* INCLUDE */ +#include "ble_log_prph.h" +#include "ble_log_ts.h" + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/queue.h" +#include "esp_task.h" + +/* MACRO */ +#define BLE_LOG_TASK_PRIO (ESP_TASK_PRIO_MAX - 1) +#define BLE_LOG_TASK_STACK_SIZE (1024) +#define BLE_LOG_TASK_HOOK_TIMEOUT_MS (1000) + +/* INTERFACE */ +bool ble_log_rt_init(); +void ble_log_rt_deinit(void); +void ble_log_rt_queue_trans(ble_log_prph_trans_t **trans); + +#endif /* __BLE_LOG_RT_H__ */ diff --git a/components/bt/common/ble_log/src/internal_include/ble_log_ts.h b/components/bt/common/ble_log/src/internal_include/ble_log_ts.h new file mode 100644 index 0000000000..11e74e1508 --- /dev/null +++ b/components/bt/common/ble_log/src/internal_include/ble_log_ts.h @@ -0,0 +1,57 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef __BLE_LOG_TS_H__ +#define __BLE_LOG_TS_H__ + +/* ----------------------------------- */ +/* BLE Log - Timestamp Synchronization */ +/* ----------------------------------- */ + +/* INCLUDE */ +#include "ble_log_util.h" + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "esp_timer.h" +#include "driver/gpio.h" + +/* MACRO */ +#if CONFIG_BLE_LOG_LL_ENABLED +/* ESP BLE Controller Gen 2 */ +#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) || defined(CONFIG_IDF_TARGET_ESP32H4) +extern uint32_t r_ble_lll_timer_current_tick_get(void); +#define BLE_LOG_GET_LC_TS r_ble_lll_timer_current_tick_get() +/* ESP BLE Controller Gen 1 */ +#elif defined(CONFIG_IDF_TARGET_ESP32C2) +extern uint32_t r_os_cputime_get32(void); +#define BLE_LOG_GET_LC_TS r_os_cputime_get32() +/* Legacy BLE Controller (Wait for support) */ +// #elif defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32S3) +// extern uint32_t lld_read_clock_us(void); +// #define BLE_LOG_GET_LC_TS lld_read_clock_us() +#else /* Other targets */ +#define BLE_LOG_GET_LC_TS 0 +#endif /* BLE targets */ +#else /* !CONFIG_BLE_LOG_LL_ENABLED */ +#define BLE_LOG_GET_LC_TS 0 +#endif /* CONFIG_BLE_LOG_LL_ENABLED */ + +/* TYPEDEF */ +typedef struct { + uint8_t int_src_code; + uint8_t io_level; + uint32_t lc_ts; + uint32_t esp_ts; + uint32_t os_ts; +} __attribute__((packed)) ble_log_ts_info_t; + +/* INTERFACE */ +bool ble_log_ts_init(void); +void ble_log_ts_deinit(void); +void ble_log_ts_info_update(ble_log_ts_info_t **ts_info); + +#endif /* __BLE_LOG_TS_H__ */ diff --git a/components/bt/common/ble_log/src/internal_include/ble_log_util.h b/components/bt/common/ble_log/src/internal_include/ble_log_util.h new file mode 100644 index 0000000000..04c468ab0a --- /dev/null +++ b/components/bt/common/ble_log/src/internal_include/ble_log_util.h @@ -0,0 +1,162 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef __BLE_LOG_UTIL_H__ +#define __BLE_LOG_UTIL_H__ + +/* ----------------- */ +/* BLE Log - Utility */ +/* ----------------- */ + +/* INCLUDE */ +#include +#include +#include +#include + +#include "esp_bit_defs.h" + +#ifndef UNIT_TEST +#include "freertos/portmacro.h" +#include "esp_heap_caps.h" +#include "esp_rom_serial_output.h" +#endif /* !UNIT_TEST */ + +/* MACRO */ +/* Unit test */ +#ifndef UNIT_TEST + +/* Reference counting macros */ +#define BLE_LOG_REF_COUNT_ACQUIRE(VAR) __atomic_fetch_add(VAR, 1, __ATOMIC_ACQUIRE) +#define BLE_LOG_REF_COUNT_RELEASE(VAR) __atomic_fetch_sub(VAR, 1, __ATOMIC_RELEASE) + +/* Specifier */ +#define BLE_LOG_STATIC static +#define BLE_LOG_INLINE inline + +/* Section */ +#define BLE_LOG_IRAM_ATTR IRAM_ATTR + +/* Memory operation */ +#define BLE_LOG_MEM_CAP (MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT | MALLOC_CAP_DMA) +#define BLE_LOG_MALLOC(size) heap_caps_malloc(size, BLE_LOG_MEM_CAP) +#define BLE_LOG_FREE(ptr) heap_caps_free(ptr) +#define BLE_LOG_MEMCPY(dst, src, len) memcpy(dst, src, len) +#define BLE_LOG_MEMSET(ptr, value, len) memset(ptr, value, len) + +/* Critical section wrapper */ +#ifndef CONFIG_BLE_LOG_LL_ENABLED +extern portMUX_TYPE ble_log_spin_lock; +#define BLE_LOG_ENTER_CRITICAL() portENTER_CRITICAL_SAFE(&ble_log_spin_lock); +#define BLE_LOG_EXIT_CRITICAL() portEXIT_CRITICAL_SAFE(&ble_log_spin_lock); +#else /* CONFIG_BLE_LOG_LL_ENABLED */ +/* Note + * It's mandatory to use the same spin lock with Link Layer in multi-core system */ +extern uint32_t npl_freertos_hw_enter_critical(void); +extern void npl_freertos_hw_exit_critical(uint32_t ctx); +#define BLE_LOG_ENTER_CRITICAL() npl_freertos_hw_enter_critical() +#define BLE_LOG_EXIT_CRITICAL() npl_freertos_hw_exit_critical(0) +#endif /* !CONFIG_BLE_LOG_LL_ENABLED */ + +#define BLE_LOG_ACQUIRE_SPIN_LOCK(spin_lock) portENTER_CRITICAL_SAFE(spin_lock) +#define BLE_LOG_RELEASE_SPIN_LOCK(spin_lock) portEXIT_CRITICAL_SAFE(spin_lock) + +#define BLE_LOG_IN_ISR() xPortInIsrContext() +#define BLE_LOG_CONSOLE esp_rom_printf +#define BLE_LOG_ASSERT(expr) assert(expr) + +extern void esp_panic_handler_feed_wdts(void); +#define BLE_LOG_FEED_WDT() esp_panic_handler_feed_wdts() + +/* INLINE */ +BLE_LOG_IRAM_ATTR static inline +bool ble_log_cas_acquire(volatile bool *cas_lock) +{ + bool expected = false; + return __atomic_compare_exchange_n( + cas_lock, &expected, true, false, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED + ); +} + +BLE_LOG_IRAM_ATTR static inline +void ble_log_cas_release(volatile bool *cas_lock) +{ + __atomic_store_n(cas_lock, false, __ATOMIC_RELEASE); +} + +#else /* UNIT_TEST */ + +/* Reference counting macros */ +#define BLE_LOG_REF_COUNT_ACQUIRE(VAR) (*VAR)++ +#define BLE_LOG_REF_COUNT_RELEASE(VAR) (*VAR)-- + +/* Specifier*/ +#define BLE_LOG_STATIC +#define BLE_LOG_INLINE + +/* Section */ +#define BLE_LOG_IRAM_ATTR + +/* Memory operation */ +void *mocked_malloc(size_t size); +void mocked_free(void *ptr); +void mocked_memcpy(void *dst, const void *src, size_t len); +void mocked_memset(void *ptr, int value, size_t len); +#define BLE_LOG_MALLOC(size) mocked_malloc(size) +#define BLE_LOG_FREE(ptr) mocked_free(ptr) +#define BLE_LOG_MEMCPY(dst, src, len) mocked_memcpy(dst, src, len) +#define BLE_LOG_MEMSET(ptr, value, len) mocked_memset(ptr, value, len) + +/* Critical section wrapper */ +void mocked_enter_critical(void); +void mocked_exit_critical(void); +#define BLE_LOG_ENTER_CRITICAL() mocked_enter_critical() +#define BLE_LOG_EXIT_CRITICAL() mocked_exit_critical() + +/* FreeRTOS API wrapper */ +bool mocked_in_isr(void); +#define BLE_LOG_IN_ISR() mocked_in_isr() + +/* Spin lock wrapper */ +void mocked_acquire_spin_lock(void *spin_lock); +void mocked_release_spin_lock(void *spin_lock); +#define BLE_LOG_ACQUIRE_SPIN_LOCK(spin_lock) mocked_acquire_spin_lock(spin_lock) +#define BLE_LOG_RELEASE_SPIN_LOCK(spin_lock) mocked_release_spin_lock(spin_lock) + +/* Printf wrapper */ +void mocked_printf(const char *fmt, ...); +#define BLE_LOG_CONSOLE mocked_printf + +/* Assert wrapper */ +void mocked_assert(bool expr); +#define BLE_LOG_ASSERT(expr) mocked_assert(expr) + +#define BLE_LOG_FEED_WDT() + +bool ble_log_cas_acquire(volatile bool *cas_lock); +void ble_log_cas_release(volatile bool *cas_lock); +#endif /* UNIT_TEST */ + +#define BLE_LOG_VERSION (2) + +/* TYPEDEF */ +typedef enum { + BLE_LOG_INT_SRC_INIT_DONE, + BLE_LOG_INT_SRC_TS, + BLE_LOG_INT_SRC_ENH_STAT, + BLE_LOG_INT_SRC_INFO, + BLE_LOG_INT_SRC_FLUSH, + BLE_LOG_INT_SRC_MAX, +} ble_log_int_src_t; + +typedef struct { + uint8_t int_src_code; + uint8_t version; +} __attribute__((packed)) ble_log_info_t; + +/* INTERFACE */ +uint32_t ble_log_fast_checksum(const uint8_t *data, size_t len); + +#endif /* __BLE_LOG_UTIL_H__ */ diff --git a/components/bt/common/ble_log/src/internal_include/prph/ble_log_prph_dummy.h b/components/bt/common/ble_log/src/internal_include/prph/ble_log_prph_dummy.h new file mode 100644 index 0000000000..4463acad22 --- /dev/null +++ b/components/bt/common/ble_log/src/internal_include/prph/ble_log_prph_dummy.h @@ -0,0 +1,21 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef __BLE_LOG_PRPH_DUMMY_H__ +#define __BLE_LOG_PRPH_DUMMY_H__ + +/* ----------------------------------------------- */ +/* BLE Log - Peripheral-specific Transport - Dummy */ +/* ----------------------------------------------- */ + +/* INCLUDE */ +#include "ble_log_prph.h" + +/* TYPEDEF */ +typedef struct { + uint8_t *trans_buf; +} ble_log_prph_trans_ctx_t; + +#endif /* __BLE_LOG_PRPH_DUMMY_H__ */ diff --git a/components/bt/common/ble_log/src/internal_include/prph/ble_log_prph_spi_master_dma.h b/components/bt/common/ble_log/src/internal_include/prph/ble_log_prph_spi_master_dma.h new file mode 100644 index 0000000000..6e99c3d863 --- /dev/null +++ b/components/bt/common/ble_log/src/internal_include/prph/ble_log_prph_spi_master_dma.h @@ -0,0 +1,22 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef __BLE_LOG_PRPH_SPI_MASTER_DMA_H__ +#define __BLE_LOG_PRPH_SPI_MASTER_DMA_H__ + +/* ----------------------------------------------- */ +/* BLE Log - Peripheral Interface - SPI Master DMA */ +/* ----------------------------------------------- */ + +/* INCLUDE */ +#include "ble_log_prph.h" + +#include +#include "driver/spi_master.h" + +/* TYPEDEF */ +typedef spi_transaction_t ble_log_prph_trans_ctx_t; + +#endif /* __BLE_LOG_PRPH_SPI_MASTER_DMA_H__ */ diff --git a/components/bt/common/ble_log/src/internal_include/prph/ble_log_prph_uart_dma.h b/components/bt/common/ble_log/src/internal_include/prph/ble_log_prph_uart_dma.h new file mode 100644 index 0000000000..def76faaac --- /dev/null +++ b/components/bt/common/ble_log/src/internal_include/prph/ble_log_prph_uart_dma.h @@ -0,0 +1,32 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef __BLE_LOG_PRPH_UART_DMA_H__ +#define __BLE_LOG_PRPH_UART_DMA_H__ + +/* ----------------------------------------- */ +/* BLE Log - Peripheral Interface - UART DMA */ +/* ----------------------------------------- */ + +/* INCLUDE */ +#include "ble_log_prph.h" + +#include "driver/uart.h" +#include "driver/uhci.h" + +/* MACRO */ +#if CONFIG_BLE_LOG_PRPH_UART_DMA_PORT == UART_NUM_0 +#define BLE_LOG_PRPH_UART_DMA_REDIR (1) +#else +#define BLE_LOG_PRPH_UART_DMA_REDIR (0) +#endif /* CONFIG_BLE_LOG_PRPH_UART_DMA_PORT == UART_NUM_0 */ + +/* TYPEDEF */ +typedef struct { + ble_log_prph_trans_t *trans; + uint8_t trans_buf[0]; +} ble_log_prph_trans_ctx_t; + +#endif /* __BLE_LOG_PRPH_UART_DMA_H__ */ diff --git a/components/bt/common/ble_log/src/prph/ble_log_prph_dummy.c b/components/bt/common/ble_log/src/prph/ble_log_prph_dummy.c new file mode 100644 index 0000000000..4fd9c48a7f --- /dev/null +++ b/components/bt/common/ble_log/src/prph/ble_log_prph_dummy.c @@ -0,0 +1,86 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +/* ----------------------------------------------- */ +/* BLE Log - Peripheral-specific Transport - Dummy */ +/* ----------------------------------------------- */ + +/* INCLUDE */ +#include "ble_log_prph_dummy.h" + +/* INTERFACE */ +bool ble_log_prph_init(size_t trans_cnt) +{ + (void)trans_cnt; + return true; +} + +void ble_log_prph_deinit(void) +{ +} + +bool ble_log_prph_trans_init(ble_log_prph_trans_t **trans, size_t trans_size) +{ + /* Validate inputs */ + if (!trans || !trans_size) { + return false; + } + + /* Initialize peripheral transport data */ + *trans = (ble_log_prph_trans_t *)BLE_LOG_MALLOC(sizeof(ble_log_prph_trans_t)); + if (!(*trans)) { + goto exit; + } + BLE_LOG_MEMSET(*trans, 0, sizeof(ble_log_prph_trans_t)); + (*trans)->size = trans_size; + + /* Initialize peripheral-specific transport context */ + ble_log_prph_trans_ctx_t *dummy_trans_ctx = (ble_log_prph_trans_ctx_t *)BLE_LOG_MALLOC(sizeof(ble_log_prph_trans_ctx_t)); + if (!dummy_trans_ctx) { + goto exit; + } + BLE_LOG_MEMSET(dummy_trans_ctx, 0, sizeof(ble_log_prph_trans_ctx_t)); + (*trans)->ctx = (void *)dummy_trans_ctx; + + /* Initialize log buffer */ + (*trans)->buf = (uint8_t *)BLE_LOG_MALLOC(trans_size); + if (!(*trans)->buf) { + goto exit; + } + BLE_LOG_MEMSET((*trans)->buf, 0, trans_size); + dummy_trans_ctx->trans_buf = (*trans)->buf; + return true; + +exit: + ble_log_prph_trans_deinit(trans); + return false; +} + +void ble_log_prph_trans_deinit(ble_log_prph_trans_t **trans) +{ + /* Validate inputs */ + if (!trans || !(*trans)) { + return; + } + + /* Release log buffer */ + if ((*trans)->buf) { + BLE_LOG_FREE((*trans)->buf); + } + + /* Release peripheral-specific transport context */ + if ((*trans)->ctx) { + BLE_LOG_FREE((*trans)->ctx); + } + + /* Release peripheral transport data */ + BLE_LOG_FREE(*trans); + *trans = NULL; +} + +void ble_log_prph_send_trans(ble_log_prph_trans_t *trans) +{ + (void)trans; +} diff --git a/components/bt/common/ble_log/src/prph/ble_log_prph_spi_master_dma.c b/components/bt/common/ble_log/src/prph/ble_log_prph_spi_master_dma.c new file mode 100644 index 0000000000..41cbfd1721 --- /dev/null +++ b/components/bt/common/ble_log/src/prph/ble_log_prph_spi_master_dma.c @@ -0,0 +1,184 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +/* ----------------------------------------------- */ +/* BLE Log - Peripheral Interface - SPI Master DMA */ +/* ----------------------------------------------- */ + +/* INCLUDE */ +#include "ble_log_prph_spi_master_dma.h" + +#include "esp_timer.h" + +/* MACRO */ +#define BLE_LOG_SPI_BUS SPI2_HOST +#define BLE_LOG_SPI_MAX_TRANSFER_SIZE (10240) +#define BLE_LOG_SPI_TRANS_ITVL_MIN_US (30) + +/* VARIABLE */ +BLE_LOG_STATIC bool prph_inited = false; +BLE_LOG_STATIC spi_device_handle_t dev_handle = NULL; +BLE_LOG_STATIC uint32_t last_tx_done_ts = 0; + +/* PRIVATE FUNCTION DECLARATION */ +BLE_LOG_STATIC void spi_master_dma_tx_done_cb(spi_transaction_t *spi_trans); +BLE_LOG_STATIC void spi_master_dma_pre_tx_cb(spi_transaction_t *spi_trans); + +/* PRIVATE FUNCTION */ +BLE_LOG_IRAM_ATTR BLE_LOG_STATIC void spi_master_dma_tx_done_cb(spi_transaction_t *spi_trans) +{ + /* SPI slave performance issue workaround */ + last_tx_done_ts = esp_timer_get_time(); + + /* Recycle transport */ + ble_log_prph_trans_t *trans = (ble_log_prph_trans_t *)(spi_trans->user); + trans->pos = 0; + trans->prph_owned = false; +} + +BLE_LOG_IRAM_ATTR BLE_LOG_STATIC void spi_master_dma_pre_tx_cb(spi_transaction_t *spi_trans) +{ + /* SPI slave performance issue workaround */ + while ((esp_timer_get_time() - last_tx_done_ts) < BLE_LOG_SPI_TRANS_ITVL_MIN_US) {} +} + +/* INTERFACE */ +bool ble_log_prph_init(size_t trans_cnt) +{ + /* Avoid double init */ + if (prph_inited) { + return true; + } + + /* SPI master initialization */ + spi_bus_config_t bus_config = { + .miso_io_num = -1, + .mosi_io_num = CONFIG_BLE_LOG_PRPH_SPI_MASTER_DMA_MOSI_IO_NUM, + .sclk_io_num = CONFIG_BLE_LOG_PRPH_SPI_MASTER_DMA_SCLK_IO_NUM, + .quadwp_io_num = -1, + .quadhd_io_num = -1, + .max_transfer_sz = BLE_LOG_SPI_MAX_TRANSFER_SIZE, +#if CONFIG_SPI_MASTER_ISR_IN_IRAM + .intr_flags = ESP_INTR_FLAG_IRAM +#endif // CONFIG_SPI_MASTER_ISR_IN_IRAM + }; + if (spi_bus_initialize(BLE_LOG_SPI_BUS, &bus_config, SPI_DMA_CH_AUTO) != ESP_OK) { + goto exit; + } + + spi_device_interface_config_t dev_config = { + .clock_speed_hz = SPI_MASTER_FREQ_20M, + .mode = 0, + .spics_io_num = CONFIG_BLE_LOG_PRPH_SPI_MASTER_DMA_CS_IO_NUM, + .queue_size = trans_cnt, + .post_cb = spi_master_dma_tx_done_cb, + .pre_cb = spi_master_dma_pre_tx_cb, + .flags = SPI_DEVICE_NO_RETURN_RESULT + }; + if (spi_bus_add_device(BLE_LOG_SPI_BUS, &dev_config, &dev_handle) != ESP_OK) { + goto exit; + } + + /* Initialization done */ + prph_inited = true; + return true; + +exit: + ble_log_prph_deinit(); + return false; +} + +void ble_log_prph_deinit(void) +{ + prph_inited = false; + if (dev_handle) { + /* Drain all queued transactions */ + if (spi_device_acquire_bus(dev_handle, portMAX_DELAY) == ESP_OK) { + spi_device_release_bus(dev_handle); + spi_bus_remove_device(dev_handle); + dev_handle = NULL; + } + } + + /* Note: We don't care if the bus has been inited or not */ + spi_bus_free(BLE_LOG_SPI_BUS); +} + +bool ble_log_prph_trans_init(ble_log_prph_trans_t **trans, size_t trans_size) +{ + /* Validate inputs */ + if (!trans || !trans_size) { + return false; + } + + /* Initialize peripheral transport data */ + *trans = (ble_log_prph_trans_t *)BLE_LOG_MALLOC(sizeof(ble_log_prph_trans_t)); + if (!(*trans)) { + goto exit; + } + BLE_LOG_MEMSET(*trans, 0, sizeof(ble_log_prph_trans_t)); + (*trans)->size = trans_size; + + /* Initialize peripheral-specific transport context */ + ble_log_prph_trans_ctx_t *spi_trans_ctx = (ble_log_prph_trans_ctx_t *)BLE_LOG_MALLOC(sizeof(ble_log_prph_trans_ctx_t)); + if (!spi_trans_ctx) { + goto exit; + } + BLE_LOG_MEMSET(spi_trans_ctx, 0, sizeof(ble_log_prph_trans_ctx_t)); + spi_trans_ctx->user = (void *)(*trans); + (*trans)->ctx = (void *)spi_trans_ctx; + + /* Initialize log buffer */ + (*trans)->buf = (uint8_t *)BLE_LOG_MALLOC(trans_size); + if (!(*trans)->buf) { + goto exit; + } + BLE_LOG_MEMSET((*trans)->buf, 0, trans_size); + spi_trans_ctx->tx_buffer = (const void *)(*trans)->buf; + return true; + +exit: + ble_log_prph_trans_deinit(trans); + return false; +} + +void ble_log_prph_trans_deinit(ble_log_prph_trans_t **trans) +{ + /* Validate inputs */ + if (!trans || !(*trans)) { + return; + } + + /* Release log buffer */ + if ((*trans)->buf) { + BLE_LOG_FREE((*trans)->buf); + } + + /* Release peripheral-specific transport context */ + if ((*trans)->ctx) { + BLE_LOG_FREE((*trans)->ctx); + } + + /* Release peripheral transport data */ + BLE_LOG_FREE(*trans); + *trans = NULL; +} + +/* CRITICAL: + * This function is designed to be called by BLE Log Runtime only, + * function call from any other submodules is not allowed */ +BLE_LOG_IRAM_ATTR void ble_log_prph_send_trans(ble_log_prph_trans_t *trans) +{ + spi_transaction_t *spi_trans = (spi_transaction_t *)trans->ctx; + + /* CRITICAL: + * Bytes to bits length conversion is required for tx, and rxlength must be + * cleared regardless of whether it is used for rx as per SPI master driver */ + spi_trans->length = (trans->pos << 3); + spi_trans->rxlength = 0; + if (spi_device_queue_trans(dev_handle, spi_trans, 0) != ESP_OK) { + trans->prph_owned = false; + } +} diff --git a/components/bt/common/ble_log/src/prph/ble_log_prph_uart_dma.c b/components/bt/common/ble_log/src/prph/ble_log_prph_uart_dma.c new file mode 100644 index 0000000000..c366c9b7ec --- /dev/null +++ b/components/bt/common/ble_log/src/prph/ble_log_prph_uart_dma.c @@ -0,0 +1,331 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/* ----------------------------------------- */ +/* BLE Log - Peripheral Interface - UART DMA */ +/* ----------------------------------------- */ + +/* INCLUDE */ +#include "ble_log_prph_uart_dma.h" + +#if BLE_LOG_PRPH_UART_DMA_REDIR +#include "ble_log.h" +#include "ble_log_rt.h" +#include "ble_log_lbm.h" + +#include "esp_timer.h" +#include "driver/uart.h" +#include "driver/uart_vfs.h" +#endif /* BLE_LOG_PRPH_UART_DMA_REDIR */ + +/* MACRO */ +#define BLE_LOG_UART_MAX_TRANSFER_SIZE (10240) +#define BLE_LOG_UART_RX_BUF_SIZE (32) +#define BLE_LOG_UART_DMA_BURST_SIZE (32) +#if BLE_LOG_PRPH_UART_DMA_REDIR +#define BLE_LOG_UART_REDIR_BUF_SIZE (512) +#define BLE_LOG_UART_REDIR_FLUSH_TIMEOUT (100) +#endif /* BLE_LOG_PRPH_UART_DMA_REDIR */ + +/* VARIABLE */ +BLE_LOG_STATIC bool prph_inited = false; +BLE_LOG_STATIC uhci_controller_handle_t dev_handle = NULL; +#if BLE_LOG_PRPH_UART_DMA_REDIR +BLE_LOG_STATIC bool uart_driver_inited = false; +BLE_LOG_STATIC ble_log_lbm_t *redir_lbm = NULL; +BLE_LOG_STATIC uint32_t redir_last_write_ts = 0; +BLE_LOG_STATIC esp_timer_handle_t redir_flush_timer = NULL; +#endif /* BLE_LOG_PRPH_UART_DMA_REDIR */ + +/* PRIVATE FUNCTION DECLARATION */ +BLE_LOG_STATIC bool uart_dma_tx_done_cb( + uhci_controller_handle_t uhci_ctrl, const uhci_tx_done_event_data_t *edata, void *user_ctx); + +/* PRIVATE FUNCTION */ +BLE_LOG_IRAM_ATTR BLE_LOG_STATIC bool uart_dma_tx_done_cb( + uhci_controller_handle_t uhci_ctrl, const uhci_tx_done_event_data_t *edata, void *user_ctx) +{ + /* Unused arguments */ + (void)uhci_ctrl; + (void)user_ctx; + + /* Recycle transport */ + ble_log_prph_trans_ctx_t *uart_trans_ctx = (ble_log_prph_trans_ctx_t *)( + (uint8_t *)edata->buffer - sizeof(ble_log_prph_trans_ctx_t) + ); + ble_log_prph_trans_t *trans = uart_trans_ctx->trans; + trans->pos = 0; + trans->prph_owned = false; + return true; +} + +#if BLE_LOG_PRPH_UART_DMA_REDIR +BLE_LOG_IRAM_ATTR BLE_LOG_STATIC void esp_timer_cb_flush_log(void) +{ + uint32_t os_ts = pdTICKS_TO_MS(xTaskGetTickCount()); + if ((os_ts - redir_last_write_ts) > BLE_LOG_UART_REDIR_FLUSH_TIMEOUT) { + xSemaphoreTake(redir_lbm->mutex, portMAX_DELAY); + int trans_idx = redir_lbm->trans_idx; + 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); + } +} +#endif /* BLE_LOG_PRPH_UART_DMA_REDIR */ + +/* INTERFACE */ +bool ble_log_prph_init(size_t trans_cnt) +{ + /* Avoid double init */ + if (prph_inited) { + return true; + } + + /* Initialize UART */ + uart_config_t uart_config = { + .baud_rate = CONFIG_BLE_LOG_PRPH_UART_DMA_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, + }; + if ((uart_param_config(CONFIG_BLE_LOG_PRPH_UART_DMA_PORT, &uart_config) != ESP_OK) || + (uart_set_pin(CONFIG_BLE_LOG_PRPH_UART_DMA_PORT, + CONFIG_BLE_LOG_PRPH_UART_DMA_TX_IO_NUM, -1, -1, -1) != ESP_OK)) { + goto exit; + } + + /* Initialize UHCI */ + uhci_controller_config_t uhci_config = { + .uart_port = CONFIG_BLE_LOG_PRPH_UART_DMA_PORT, + .tx_trans_queue_depth = trans_cnt, + .max_receive_internal_mem = BLE_LOG_UART_RX_BUF_SIZE, + .max_transmit_size = BLE_LOG_UART_MAX_TRANSFER_SIZE, + .dma_burst_size = BLE_LOG_UART_DMA_BURST_SIZE, + .rx_eof_flags.idle_eof = 1, + }; + uhci_event_callbacks_t uhci_cbs = { + .on_tx_trans_done = uart_dma_tx_done_cb, + }; + if ((uhci_new_controller(&uhci_config, &dev_handle) != ESP_OK) || + (uhci_register_event_callbacks(dev_handle, &uhci_cbs, NULL) != ESP_OK)) { + goto exit; + } + +/* Redirection is required when utilizing UART port 0 */ +#if BLE_LOG_PRPH_UART_DMA_REDIR + /* Initialize a dedicated LBM for redirection */ + redir_lbm = (ble_log_lbm_t *)BLE_LOG_MALLOC(sizeof(ble_log_lbm_t)); + if (!redir_lbm) { + goto exit; + } + BLE_LOG_MEMSET(redir_lbm, 0, sizeof(ble_log_lbm_t)); + + /* Transport initialization */ + for (int i = 0; i < BLE_LOG_TRANS_PING_PONG_BUF_CNT; i++) { + if (!ble_log_prph_trans_init(&(redir_lbm->trans[i]), + BLE_LOG_UART_REDIR_BUF_SIZE)) { + goto exit; + } + } + + /* Mutex initilaization */ + redir_lbm->mutex = xSemaphoreCreateMutex(); + if (!redir_lbm->mutex) { + goto exit; + } + + /* Initialize UART driver for redirection */ + if (!uart_is_driver_installed(UART_NUM_0)) { + uart_driver_install(UART_NUM_0, BLE_LOG_UART_RX_BUF_SIZE, 0, 0, NULL, 0); + uart_driver_inited = true; + } + uart_vfs_dev_use_driver(UART_NUM_0); + + /* Initialize periodic flush timer */ + esp_timer_create_args_t timer_args = { + .callback = (esp_timer_cb_t)esp_timer_cb_flush_log, + .dispatch_method = ESP_TIMER_TASK, + }; + if (esp_timer_create(&timer_args, &redir_flush_timer) != ESP_OK) { + goto exit; + } +#endif /* BLE_LOG_PRPH_UART_DMA_REDIR */ + + prph_inited = true; +#if BLE_LOG_PRPH_UART_DMA_REDIR + esp_timer_start_periodic(redir_flush_timer, BLE_LOG_UART_REDIR_FLUSH_TIMEOUT); +#endif /* BLE_LOG_PRPH_UART_DMA_REDIR */ + + return true; + +exit: + ble_log_prph_deinit(); + return false; +} + +void ble_log_prph_deinit(void) +{ + prph_inited = false; + +#if BLE_LOG_PRPH_UART_DMA_REDIR + /* Release flush timer */ + if (redir_flush_timer) { + esp_timer_stop(redir_flush_timer); + esp_timer_delete(redir_flush_timer); + redir_flush_timer = NULL; + } + + /* Delete UART driver if it's installed by current module */ + if (uart_driver_inited) { + uart_driver_delete(UART_NUM_0); + } + + /* Release redirection LBM */ + if (redir_lbm) { + /* Release mutex */ + if (redir_lbm->mutex) { + xSemaphoreTake(redir_lbm->mutex, portMAX_DELAY); + xSemaphoreGive(redir_lbm->mutex); + vSemaphoreDelete(redir_lbm->mutex); + } + + /* Release transport */ + for (int i = 0; i < BLE_LOG_TRANS_PING_PONG_BUF_CNT; i++) { + ble_log_prph_trans_deinit(&(redir_lbm->trans[i])); + } + + /* Release LBM */ + BLE_LOG_FREE(redir_lbm); + redir_lbm = NULL; + } +#endif /* BLE_LOG_PRPH_UART_DMA_REDIR */ + + if (dev_handle) { + uhci_wait_all_tx_transaction_done(dev_handle, portMAX_DELAY); + uhci_del_controller(dev_handle); + dev_handle = NULL; + } +} + +bool ble_log_prph_trans_init(ble_log_prph_trans_t **trans, size_t trans_size) +{ + /* Validate inputs */ + if (!trans || !trans_size) { + return false; + } + + /* Initialize peripheral transport data */ + *trans = (ble_log_prph_trans_t *)BLE_LOG_MALLOC(sizeof(ble_log_prph_trans_t)); + if (!(*trans)) { + goto exit; + } + BLE_LOG_MEMSET(*trans, 0, sizeof(ble_log_prph_trans_t)); + (*trans)->size = trans_size; + + /* Initialize peripheral-specific transport context */ + size_t trans_ctx_size = sizeof(ble_log_prph_trans_ctx_t) + trans_size; + ble_log_prph_trans_ctx_t *uart_trans_ctx = (ble_log_prph_trans_ctx_t *)BLE_LOG_MALLOC(trans_ctx_size); + if (!uart_trans_ctx) { + goto exit; + } + BLE_LOG_MEMSET(uart_trans_ctx, 0, trans_ctx_size); + + /* Log buffer linking */ + (*trans)->ctx = (void *)uart_trans_ctx; + (*trans)->buf = (uint8_t *)uart_trans_ctx->trans_buf; + uart_trans_ctx->trans = *trans; + return true; + +exit: + ble_log_prph_trans_deinit(trans); + return false; +} + +void ble_log_prph_trans_deinit(ble_log_prph_trans_t **trans) +{ + /* Validate inputs */ + if (!trans || !(*trans)) { + return; + } + + /* Release peripheral-specific transport context */ + if ((*trans)->ctx) { + BLE_LOG_FREE((*trans)->ctx); + } + + /* Release peripheral transport data */ + BLE_LOG_FREE(*trans); + *trans = NULL; +} + +/* CRITICAL: + * This function is designed to be called by BLE Log Runtime only, + * function call from any other submodules is not allowed */ +BLE_LOG_IRAM_ATTR void ble_log_prph_send_trans(ble_log_prph_trans_t *trans) +{ + if (uhci_transmit(dev_handle, trans->buf, trans->pos) != ESP_OK) { + trans->prph_owned = false; + } +} + +/* Redirection is required when utilizing UART port 0 */ +#if BLE_LOG_PRPH_UART_DMA_REDIR +BLE_LOG_IRAM_ATTR BLE_LOG_STATIC +void ble_log_redir_uart_tx_chars(const char *src, size_t len) +{ + xSemaphoreTake(redir_lbm->mutex, portMAX_DELAY); + ble_log_prph_trans_t **trans = ble_log_lbm_get_trans(redir_lbm, len); + if (trans) { + uint8_t *buf = (*trans)->buf + (*trans)->pos; + BLE_LOG_MEMCPY(buf, src, len); + (*trans)->pos += len; + redir_last_write_ts = pdTICKS_TO_MS(xTaskGetTickCount()); + + if (BLE_LOG_TRANS_FREE_SPACE((*trans)) <= BLE_LOG_FRAME_OVERHEAD) { + ble_log_rt_queue_trans(trans); + } + } + xSemaphoreGive(redir_lbm->mutex); +} + +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 (!prph_inited || (uart_num != UART_NUM_0)) { + return __real_uart_tx_chars(uart_num, buffer, len); + } + ble_log_redir_uart_tx_chars(buffer, len); + return len; +} + +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 (!prph_inited || (uart_num != UART_NUM_0)) { + return __real_uart_write_bytes(uart_num, src, size); + } + ble_log_redir_uart_tx_chars(src, size); + return size; +} + +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 (!prph_inited || (uart_num != UART_NUM_0)) { + return __real_uart_write_bytes_with_break(uart_num, src, size, brk_len); + } else { + (void)brk_len; + return __wrap_uart_write_bytes(uart_num, src, size); + } +} +#endif /* BLE_LOG_PRPH_UART_DMA_REDIR */