Skip to content

Commit a42a802

Browse files
committed
extmod/modbluetooth: Make all HCI transports trace in the same format.
- Use HCI_TRACE macro consistently. - Use the same colour formatting. - Add a tool to convert to .pcap for Wireshark. Signed-off-by: Jim Mussared <[email protected]>
1 parent ab31e23 commit a42a802

File tree

3 files changed

+150
-9
lines changed

3 files changed

+150
-9
lines changed

extmod/nimble/hal/hal_uart.c

+6-3
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,9 @@
3939
#endif
4040

4141
#define HCI_TRACE (0)
42+
#define COL_OFF "\033[0m"
43+
#define COL_GREEN "\033[0;32m"
44+
#define COL_BLUE "\033[0;34m"
4245

4346
static hal_uart_tx_cb_t hal_uart_tx_cb;
4447
static void *hal_uart_tx_arg;
@@ -71,11 +74,11 @@ void hal_uart_start_tx(uint32_t port) {
7174
}
7275

7376
#if HCI_TRACE
74-
printf("< [% 8d] %02x", (int)mp_hal_ticks_ms(), mp_bluetooth_hci_cmd_buf[0]);
77+
printf(COL_GREEN "< [% 8d] %02x", (int)mp_hal_ticks_ms(), mp_bluetooth_hci_cmd_buf[0]);
7578
for (size_t i = 1; i < len; ++i) {
7679
printf(":%02x", mp_bluetooth_hci_cmd_buf[i]);
7780
}
78-
printf("\n");
81+
printf(COL_OFF "\n");
7982
#endif
8083

8184
mp_bluetooth_hci_uart_write(mp_bluetooth_hci_cmd_buf, len);
@@ -92,7 +95,7 @@ int hal_uart_close(uint32_t port) {
9295

9396
STATIC void mp_bluetooth_hci_uart_char_cb(uint8_t chr) {
9497
#if HCI_TRACE
95-
printf("> %02x\n", chr);
98+
printf(COL_BLUE "> [% 8d] %02x" COL_OFF "\n", (int)mp_hal_ticks_ms(), chr);
9699
#endif
97100
hal_uart_rx_cb(hal_uart_rx_arg, chr);
98101
}

ports/unix/mpbthciport.c

+10-6
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,11 @@
4646
#include <string.h>
4747

4848
#define DEBUG_printf(...) // printf(__VA_ARGS__)
49-
#define DEBUG_HCI_DUMP (0)
49+
50+
#define HCI_TRACE (0)
51+
#define COL_OFF "\033[0m"
52+
#define COL_GREEN "\033[0;32m"
53+
#define COL_BLUE "\033[0;34m"
5054

5155
uint8_t mp_bluetooth_hci_cmd_buf[4 + 256];
5256

@@ -234,8 +238,8 @@ int mp_bluetooth_hci_uart_readchar(void) {
234238
ssize_t bytes_read = read(uart_fd, &c, 1);
235239

236240
if (bytes_read == 1) {
237-
#if DEBUG_HCI_DUMP
238-
printf("[% 8ld] RX: %02x\n", mp_hal_ticks_ms(), c);
241+
#if HCI_TRACE
242+
printf(COL_BLUE "> [% 8ld] RX: %02x" COL_OFF "\n", mp_hal_ticks_ms(), c);
239243
#endif
240244
return c;
241245
} else {
@@ -250,12 +254,12 @@ int mp_bluetooth_hci_uart_write(const uint8_t *buf, size_t len) {
250254
return 0;
251255
}
252256

253-
#if DEBUG_HCI_DUMP
254-
printf("[% 8ld] TX: %02x", mp_hal_ticks_ms(), buf[0]);
257+
#if HCI_TRACE
258+
printf(COL_GREEN "< [% 8ld] TX: %02x", mp_hal_ticks_ms(), buf[0]);
255259
for (size_t i = 1; i < len; ++i) {
256260
printf(":%02x", buf[i]);
257261
}
258-
printf("\n");
262+
printf(COL_OFF "\n");
259263
#endif
260264

261265
return write(uart_fd, buf, len);

tools/hci_trace_to_pcap.py

+134
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
#!/usr/bin/env python3
2+
#
3+
# This file is part of the MicroPython project, http://micropython.org/
4+
#
5+
# The MIT License (MIT)
6+
#
7+
# Copyright (c) 2023 Jim Mussared
8+
#
9+
# Permission is hereby granted, free of charge, to any person obtaining a copy
10+
# of this software and associated documentation files (the "Software"), to deal
11+
# in the Software without restriction, including without limitation the rights
12+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13+
# copies of the Software, and to permit persons to whom the Software is
14+
# furnished to do so, subject to the following conditions:
15+
#
16+
# The above copyright notice and this permission notice shall be included in
17+
# all copies or substantial portions of the Software.
18+
#
19+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25+
# THE SOFTWARE.
26+
27+
# Simple script to take the output printed when HCI_TRACE is enabled in
28+
# unix/mpbthciport.c, extmod/btstack/btstack_hci_uart.c,
29+
# extmod/nimble/hal/hal_uart.c and turn it into a .pcap file suitable for use
30+
# with Wireshark.
31+
32+
import re
33+
import sys
34+
import struct
35+
36+
# pcap file header:
37+
# typedef struct pcap_hdr_s {
38+
# guint32 magic_number; /* magic number */
39+
# guint16 version_major; /* major version number */
40+
# guint16 version_minor; /* minor version number */
41+
# gint32 thiszone; /* GMT to local correction */
42+
# guint32 sigfigs; /* accuracy of timestamps */
43+
# guint32 snaplen; /* max length of captured packets, in octets */
44+
# guint32 network; /* data link type */
45+
# } pcap_hdr_t;
46+
47+
# pcap record header
48+
# typedef struct pcaprec_hdr_s {
49+
# guint32 ts_sec; /* timestamp seconds */
50+
# guint32 ts_usec; /* timestamp microseconds */
51+
# guint32 incl_len; /* number of octets of packet saved in file */
52+
# guint32 orig_len; /* actual length of packet */
53+
# } pcaprec_hdr_t;
54+
55+
_LINKTYPE_BLUETOOTH_HCI_H4_WITH_PHDR = 201 # "!I" direction, followed by data
56+
sys.stdout.buffer.write(
57+
struct.pack("!IHHiIII", 0xA1B2C3D4, 2, 4, 0, 0, 65535, _LINKTYPE_BLUETOOTH_HCI_H4_WITH_PHDR)
58+
)
59+
60+
_DIR_CONTROLLER_TO_HOST = 1
61+
_DIR_HOST_TO_CONTROLLER = 0
62+
63+
reassemble_timestamp = 0
64+
reassemble_packet = bytearray()
65+
66+
with open(sys.argv[1], "r") as f:
67+
for line in f:
68+
line = line.strip()
69+
m = re.match("([<>]) \\[ *([0-9]+)\\] ([A-Fa-f0-9:]+)", line)
70+
if not m:
71+
continue
72+
73+
timestamp = int(m.group(2))
74+
data = bytes.fromhex(m.group(3).replace(":", ""))
75+
76+
if m.group(1) == "<":
77+
# Host to controller.
78+
# These are always complete.
79+
sys.stdout.buffer.write(
80+
struct.pack(
81+
"!IIIII",
82+
timestamp // 1000,
83+
timestamp % 1000 * 1000,
84+
len(data) + 4,
85+
len(data) + 4,
86+
_DIR_HOST_TO_CONTROLLER,
87+
)
88+
)
89+
sys.stdout.buffer.write(data)
90+
if m.group(1) == ">":
91+
# Controller to host.
92+
# Several of the sources print byte-at-a-time so need to reconstruct packets.
93+
94+
if not reassemble_packet:
95+
# Use the timestamp of the first fragment.
96+
reassemble_timestamp = timestamp
97+
98+
reassemble_packet.extend(data)
99+
100+
if len(reassemble_packet) > 4:
101+
plen = 0
102+
if reassemble_packet[0] == 1:
103+
# command
104+
plen = 3 + reassemble_packet[3]
105+
elif reassemble_packet[0] == 2:
106+
# acl
107+
plen = 5 + reassemble_packet[3] + (reassemble_packet[4] << 8)
108+
elif reassemble_packet[0] == 4:
109+
# event
110+
plen = 3 + reassemble_packet[2]
111+
112+
if len(reassemble_packet) >= plen:
113+
# Got a complete packet.
114+
data = reassemble_packet[0:plen]
115+
reassemble_packet = reassemble_packet[plen:]
116+
reassemble_timestamp = timestamp
117+
sys.stdout.buffer.write(
118+
struct.pack(
119+
"!IIIII",
120+
reassemble_timestamp // 1000,
121+
reassemble_timestamp % 1000 * 1000,
122+
len(data) + 4,
123+
len(data) + 4,
124+
_DIR_CONTROLLER_TO_HOST,
125+
)
126+
)
127+
sys.stdout.buffer.write(data)
128+
129+
if reassemble_packet:
130+
print(
131+
"Error: Unknown byte in HCI stream. Remainder:",
132+
reassemble_packet.hex(":"),
133+
file=sys.stderr,
134+
)

0 commit comments

Comments
 (0)