Skip to content

Commit 3146964

Browse files
committed
Merge branch 'bugfix/fix_hid_crash_v5.4' into 'release/v5.4'
fix(ble): Fix crash issue during logging (v5.4) See merge request espressif/esp-idf!35511
2 parents 446aa35 + 285e4ac commit 3146964

File tree

7 files changed

+295
-14
lines changed

7 files changed

+295
-14
lines changed

components/bt/controller/esp32/bt.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1345,7 +1345,8 @@ static esp_err_t esp_bt_controller_rom_mem_release(esp_bt_mode_t mode)
13451345

13461346
//already released
13471347
if (!(mode & btdm_dram_available_region[0].mode)) {
1348-
return ESP_ERR_INVALID_STATE;
1348+
ESP_LOGW(BTDM_LOG_TAG, "%s already released, mode %d",__func__, mode);
1349+
return ESP_OK;
13491350
}
13501351

13511352
for (int i = 0; i < sizeof(btdm_dram_available_region)/sizeof(btdm_dram_available_region_t); i++) {

examples/bluetooth/bluedroid/ble/gatt_client/main/gattc_demo.c

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -517,4 +517,23 @@ void app_main(void)
517517
ESP_LOGE(GATTC_TAG, "set local MTU failed, error code = %x", local_mtu_ret);
518518
}
519519

520+
521+
/*
522+
* This code is intended for debugging and prints all HCI data.
523+
* To enable it, turn on the "BT_HCI_LOG_DEBUG_EN" configuration option.
524+
* The output HCI data can be parsed using the script:
525+
* esp-idf/tools/bt/bt_hci_to_btsnoop.py.
526+
* For detailed instructions, refer to esp-idf/tools/bt/README.md.
527+
*/
528+
529+
/*
530+
while (1) {
531+
extern void bt_hci_log_hci_data_show(void);
532+
extern void bt_hci_log_hci_adv_show(void);
533+
bt_hci_log_hci_data_show();
534+
bt_hci_log_hci_adv_show();
535+
vTaskDelay(1000 / portNUM_PROCESSORS);
536+
}
537+
*/
538+
520539
}

examples/bluetooth/blufi/main/blufi_example_main.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
2+
* SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
33
*
44
* SPDX-License-Identifier: Unlicense OR CC0-1.0
55
*/
@@ -313,15 +313,15 @@ static void example_event_callback(esp_blufi_cb_event_t event, esp_blufi_cb_para
313313
ESP_ERROR_CHECK( esp_wifi_set_mode(param->wifi_mode.op_mode) );
314314
break;
315315
case ESP_BLUFI_EVENT_REQ_CONNECT_TO_AP:
316-
BLUFI_INFO("BLUFI requset wifi connect to AP\n");
316+
BLUFI_INFO("BLUFI request wifi connect to AP\n");
317317
/* there is no wifi callback when the device has already connected to this wifi
318318
so disconnect wifi before connection.
319319
*/
320320
esp_wifi_disconnect();
321321
example_wifi_connect();
322322
break;
323323
case ESP_BLUFI_EVENT_REQ_DISCONNECT_FROM_AP:
324-
BLUFI_INFO("BLUFI requset wifi disconnect from AP\n");
324+
BLUFI_INFO("BLUFI request wifi disconnect from AP\n");
325325
esp_wifi_disconnect();
326326
break;
327327
case ESP_BLUFI_EVENT_REPORT_ERROR:

examples/bluetooth/esp_hid_host/main/esp_hid_host_main.c

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -76,35 +76,45 @@ void hidh_callback(void *handler_args, esp_event_base_t base, int32_t id, void *
7676
case ESP_HIDH_OPEN_EVENT: {
7777
if (param->open.status == ESP_OK) {
7878
const uint8_t *bda = esp_hidh_dev_bda_get(param->open.dev);
79-
ESP_LOGI(TAG, ESP_BD_ADDR_STR " OPEN: %s", ESP_BD_ADDR_HEX(bda), esp_hidh_dev_name_get(param->open.dev));
80-
esp_hidh_dev_dump(param->open.dev, stdout);
79+
if (bda) {
80+
ESP_LOGI(TAG, ESP_BD_ADDR_STR " OPEN: %s", ESP_BD_ADDR_HEX(bda), esp_hidh_dev_name_get(param->open.dev));
81+
esp_hidh_dev_dump(param->open.dev, stdout);
82+
}
8183
} else {
8284
ESP_LOGE(TAG, " OPEN failed!");
8385
}
8486
break;
8587
}
8688
case ESP_HIDH_BATTERY_EVENT: {
8789
const uint8_t *bda = esp_hidh_dev_bda_get(param->battery.dev);
88-
ESP_LOGI(TAG, ESP_BD_ADDR_STR " BATTERY: %d%%", ESP_BD_ADDR_HEX(bda), param->battery.level);
90+
if (bda) {
91+
ESP_LOGI(TAG, ESP_BD_ADDR_STR " BATTERY: %d%%", ESP_BD_ADDR_HEX(bda), param->battery.level);
92+
}
8993
break;
9094
}
9195
case ESP_HIDH_INPUT_EVENT: {
9296
const uint8_t *bda = esp_hidh_dev_bda_get(param->input.dev);
93-
ESP_LOGI(TAG, ESP_BD_ADDR_STR " INPUT: %8s, MAP: %2u, ID: %3u, Len: %d, Data:", ESP_BD_ADDR_HEX(bda), esp_hid_usage_str(param->input.usage), param->input.map_index, param->input.report_id, param->input.length);
94-
ESP_LOG_BUFFER_HEX(TAG, param->input.data, param->input.length);
97+
if (bda) {
98+
ESP_LOGI(TAG, ESP_BD_ADDR_STR " INPUT: %8s, MAP: %2u, ID: %3u, Len: %d, Data:", ESP_BD_ADDR_HEX(bda), esp_hid_usage_str(param->input.usage), param->input.map_index, param->input.report_id, param->input.length);
99+
ESP_LOG_BUFFER_HEX(TAG, param->input.data, param->input.length);
100+
}
95101
break;
96102
}
97103
case ESP_HIDH_FEATURE_EVENT: {
98104
const uint8_t *bda = esp_hidh_dev_bda_get(param->feature.dev);
99-
ESP_LOGI(TAG, ESP_BD_ADDR_STR " FEATURE: %8s, MAP: %2u, ID: %3u, Len: %d", ESP_BD_ADDR_HEX(bda),
100-
esp_hid_usage_str(param->feature.usage), param->feature.map_index, param->feature.report_id,
101-
param->feature.length);
102-
ESP_LOG_BUFFER_HEX(TAG, param->feature.data, param->feature.length);
105+
if (bda) {
106+
ESP_LOGI(TAG, ESP_BD_ADDR_STR " FEATURE: %8s, MAP: %2u, ID: %3u, Len: %d", ESP_BD_ADDR_HEX(bda),
107+
esp_hid_usage_str(param->feature.usage), param->feature.map_index, param->feature.report_id,
108+
param->feature.length);
109+
ESP_LOG_BUFFER_HEX(TAG, param->feature.data, param->feature.length);
110+
}
103111
break;
104112
}
105113
case ESP_HIDH_CLOSE_EVENT: {
106114
const uint8_t *bda = esp_hidh_dev_bda_get(param->close.dev);
107-
ESP_LOGI(TAG, ESP_BD_ADDR_STR " CLOSE: %s", ESP_BD_ADDR_HEX(bda), esp_hidh_dev_name_get(param->close.dev));
115+
if (bda) {
116+
ESP_LOGI(TAG, ESP_BD_ADDR_STR " CLOSE: %s", ESP_BD_ADDR_HEX(bda), esp_hidh_dev_name_get(param->close.dev));
117+
}
108118
break;
109119
}
110120
default:

tools/bt/README.md

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
---
2+
3+
### **Usage Instructions**
4+
5+
The **`bt_hci_to_btsnoop.py`** script parses Bluetooth HCI logs and converts them into the BTSnoop format, allowing for detailed analysis. It reads an input log file, processes each line, and writes HCI packets to an output file.
6+
7+
---
8+
9+
### **How to Capture HCI Logs**
10+
11+
To use this tool, you need to enable Bluetooth HCI debug mode in your ESP-IDF project and capture HCI logs as follows:
12+
13+
#### 1. Enable Bluetooth HCI Debug Mode
14+
- Open the ESP-IDF configuration menu:
15+
- **Top → Component config → Bluetooth**
16+
- Enable: `[x] Enable Bluetooth HCI debug mode`
17+
18+
#### 2. Capture Logs in the `app_main` Code
19+
In your application code (e.g., `app_main`), call the following functions to log HCI data and advertisement logs:
20+
21+
```c
22+
while (1)
23+
{
24+
extern void bt_hci_log_hci_data_show(void);
25+
extern void bt_hci_log_hci_adv_show(void);
26+
bt_hci_log_hci_data_show(); // Display HCI data logs
27+
bt_hci_log_hci_adv_show(); // Display HCI advertisement logs
28+
vTaskDelay(1000 / portNUM_PROCESSORS);
29+
}
30+
```
31+
32+
#### 3. Save Logs to a File
33+
Run the application and redirect the serial output to a log file:
34+
35+
```bash
36+
idf.py -p /dev/ttyUSB0 monitor >> all_log.txt
37+
```
38+
39+
- **Note:** Replace `/dev/ttyUSB0` with your device's actual port (e.g., `/dev/ttyUSB1`, `COM3`, etc.).
40+
41+
#### 4. Manual Log Creation (Optional)
42+
Alternatively, manually create a log file containing HCI data in the expected format.
43+
44+
---
45+
46+
### **Running the Script**
47+
48+
To parse the logs and generate a BTSnoop file, run:
49+
50+
```bash
51+
python bt_hci_to_btsnoop.py -p <input_log_file> -o <output_tag>
52+
```
53+
54+
#### **Parameters**
55+
- `-p` or `--path`: Path to the input log file (e.g., `all_log.txt`). Required.
56+
- `-o` or `--output`: A tag for the output file name. Required.
57+
58+
#### **Example Command**
59+
```bash
60+
python bt_hci_to_btsnoop.py -p /path/to/input_log.txt -o 123
61+
```
62+
63+
This creates the file: `./parsed_logs/parsed_log_123.btsnoop.log`.
64+
65+
#### **Help Information**
66+
For detailed usage options, run:
67+
```bash
68+
python bt_hci_to_btsnoop.py -h
69+
```
70+
71+
---
72+
73+
### **Generated Output**
74+
75+
The parsed file will be saved as `parsed_log_<output_tag>.btsnoop.log` in the BTSnoop format.
76+
77+
### **Supported Tools for Viewing HCI Logs**
78+
79+
1. **Wireshark**
80+
- **Download:** [Wireshark](https://www.wireshark.org/)
81+
- Compatible with **Windows**, **Linux**, and **macOS** platforms.
82+
- Use it to open `.btsnoop.log` files for detailed analysis of HCI packets.
83+
84+
2. **Wireless Protocol Suite** (Teledyne LeCroy)
85+
- **Download:** [Wireless Protocol Suite](https://www.teledynelecroy.com/support/softwaredownload/psgdocuments.aspx?standardid=2&mseries=672)
86+
- Supported exclusively on the **Windows** platform.
87+
88+
3. **btmon** (Linux only)
89+
- **Description:** A command-line tool included with the BlueZ Bluetooth stack for Linux.
90+
- Use the following command to analyze HCI details directly:
91+
```bash
92+
btmon -r <btsnoop log>
93+
```
94+
---
95+
96+
### **Sample Log Format**
97+
98+
Below is an example of expected log entries:
99+
100+
```plaintext
101+
e6 C:35 0c 05 01 01 00 01 00
102+
e7 H:01 00 2a 00 26 00 04 00 12 2a 00 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 21 22
103+
e8 E:13 05 01 01 00 01 00
104+
e9 D:01 20 05 00 01 00 04 00 13
105+
ea C:35 0c 05 01 01 00 01 00
106+
```
107+
108+
---
109+
110+
### **Notes**
111+
- Ensure valid input file paths and output directories.
112+
- Verify read/write permissions for files.
113+
- The script expects a specific log file format; unexpected formats may cause errors.
114+
115+
By following these steps, you can easily convert HCI logs into the BTSnoop format for analysis.

tools/bt/bt_hci_to_btsnoop.py

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
# SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
2+
# SPDX-License-Identifier: Apache-2.0
3+
import argparse
4+
import os
5+
import re
6+
import struct
7+
import time
8+
9+
parsed_num = 0
10+
all_line_num = 0
11+
12+
13+
def create_new_bt_snoop_file(filename: str) -> None:
14+
with open(filename, 'wb') as f:
15+
magic = b'btsnoop\x00'
16+
f.write(magic)
17+
version = 1
18+
datalink = 1002
19+
header = struct.pack('>II', version, datalink)
20+
f.write(header)
21+
22+
23+
def append_hci_to_bt_snoop_file(filename: str, direction: int, data: str) -> None:
24+
if os.path.exists(filename):
25+
print(f'Appending to existing file: {filename}')
26+
else:
27+
print(f'Creating new file: {filename}')
28+
create_new_bt_snoop_file(filename)
29+
30+
# Ensure data is converted to bytearray from hex string
31+
data_bytes = bytearray.fromhex(data) # Convert hex string to bytearray
32+
33+
with open(filename, 'ab') as f: # 'ab' mode for appending binary data
34+
global parsed_num
35+
parsed_num += 1
36+
data_len = len(data_bytes)
37+
orig_len = data_len
38+
incl_len = data_len
39+
packet_flags = direction + (data_bytes[0] != 1 or data_bytes[0] != 3) * 2
40+
cumulative_drops = 0
41+
# Calculate the timestamp with an offset from a predefined reference time(0x00DCDDB30F2F8000).
42+
timestamp_us = int(round(time.time() * 1000000)) + 0x00DCDDB30F2F8000
43+
44+
# Writing structured data followed by the byte array data
45+
f.write(struct.pack('>IIIIQ', orig_len, incl_len, packet_flags, cumulative_drops, timestamp_us))
46+
f.write(data_bytes) # Write bytearray (binary data) to file
47+
48+
49+
def log_data_clean(data: str) -> str:
50+
cleaned = re.sub(r'.*?<pre>', '', data)
51+
return cleaned
52+
53+
54+
def is_adv_report(data: str) -> bool:
55+
is_binary = all(re.fullmatch(r'[0-9a-fA-F]{2}', part) for part in data.split())
56+
return is_binary
57+
58+
59+
def parse_log(input_path: str, output_tag: str) -> None:
60+
"""
61+
Parses the specified log file and saves the results to an output file.
62+
63+
Args:
64+
input_path (str): Path to the input log file.
65+
output_tag (str): Name tag for the output file.
66+
67+
Returns:
68+
None
69+
"""
70+
global parsed_num
71+
global all_line_num
72+
if not os.path.exists(input_path):
73+
print(f"Error: The file '{input_path}' does not exist.")
74+
return
75+
76+
# Define the output directory and create it if it doesn't exist
77+
output_dir = './parsed_logs'
78+
os.makedirs(output_dir, exist_ok=True)
79+
80+
# Define the output file name based on the tag
81+
output_file = os.path.join(output_dir, f'parsed_log_{output_tag}.btsnoop.log')
82+
83+
with open(input_path, 'r', encoding='utf-8') as infile:
84+
# Example: Parse each line and save processed results
85+
for line in infile:
86+
try:
87+
all_line_num += 1
88+
line = log_data_clean(line)
89+
line = line.replace('C:', '01 ')
90+
line = line.replace('E:', '04 ')
91+
line = line[3:]
92+
if line[0] == 'H':
93+
line = line.replace('H:', '02 ')
94+
append_hci_to_bt_snoop_file(output_file, 0, line)
95+
continue
96+
if line[0] == 'D':
97+
line = line.replace('D:', '02 ')
98+
append_hci_to_bt_snoop_file(output_file, 1, line)
99+
continue
100+
if line.startswith('01'):
101+
append_hci_to_bt_snoop_file(output_file, 0, line)
102+
continue
103+
if line.startswith('04'):
104+
append_hci_to_bt_snoop_file(output_file, 1, line)
105+
continue
106+
if is_adv_report(line):
107+
line = '04 3e ' + line
108+
append_hci_to_bt_snoop_file(output_file, 1, line)
109+
except Exception as e:
110+
print(f'Exception: {e}')
111+
112+
if parsed_num > 0:
113+
print(f'Log parsing completed, parsed_num {parsed_num}, all_line_num {all_line_num}.\nOutput saved to: {output_file}')
114+
else:
115+
print('No data could be parsed.')
116+
117+
118+
def main() -> None:
119+
# Define command-line arguments
120+
parser = argparse.ArgumentParser(description='Log Parsing Tool')
121+
parser.add_argument('-p', '--path', required=True, help='Path to the input log file')
122+
parser.add_argument('-o', '--output', required=True, help='Name tag for the output file')
123+
124+
# Parse arguments
125+
args = parser.parse_args()
126+
input_path = args.path
127+
output_tag = args.output
128+
129+
# Call the log parsing function
130+
parse_log(input_path, output_tag)
131+
132+
133+
if __name__ == '__main__':
134+
main()

tools/ci/exclude_check_tools_files.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,3 +59,5 @@ tools/legacy_exports/export_legacy.sh
5959
tools/legacy_exports/export_legacy.ps1
6060
tools/legacy_exports/export_legacy.bat
6161
tools/ci/idf_build_apps_dump_soc_caps.py
62+
tools/bt/bt_hci_to_btsnoop.py
63+
tools/bt/README.md

0 commit comments

Comments
 (0)