xref: /btstack/src/hci_dump.c (revision acb15818d50b860e14489f68a6237686091b95b9)
1 /*
2  * Copyright (C) 2014 BlueKitchen GmbH
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. Neither the name of the copyright holders nor the names of
14  *    contributors may be used to endorse or promote products derived
15  *    from this software without specific prior written permission.
16  * 4. Any redistribution, use, or modification is done solely for
17  *    personal benefit and not for any commercial purpose or for
18  *    monetary gain.
19  *
20  * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS
21  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
24  * RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
27  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
28  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
30  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  *
33  * Please inquire about commercial licensing options at
34  * [email protected]
35  *
36  */
37 
38 #define __BTSTACK_FILE__ "hci_dump.c"
39 
40 /*
41  *  hci_dump.c
42  *
43  *  Dump HCI trace in various formats:
44  *
45  *  - BlueZ's hcidump format
46  *  - Apple's PacketLogger
47  *  - stdout hexdump
48  *
49  *  Created by Matthias Ringwald on 5/26/09.
50  */
51 
52 #include "btstack_config.h"
53 
54 // enable POSIX functions (needed for -std=c99)
55 #define _POSIX_C_SOURCE 200809
56 
57 #include "hci_dump.h"
58 #include "hci.h"
59 #include "hci_transport.h"
60 #include "hci_cmd.h"
61 #include "btstack_run_loop.h"
62 #include <stdio.h>
63 
64 #ifdef HAVE_POSIX_FILE_IO
65 #include <fcntl.h>        // open
66 #include <unistd.h>       // write
67 #include <time.h>
68 #include <sys/time.h>     // for timestamps
69 #include <sys/stat.h>     // for mode flags
70 #endif
71 
72 // BLUEZ hcidump - struct not used directly, but left here as documentation
73 typedef struct {
74     uint16_t    len;
75     uint8_t     in;
76     uint8_t     pad;
77     uint32_t    ts_sec;
78     uint32_t    ts_usec;
79     uint8_t     packet_type;
80 }
81 hcidump_hdr;
82 #define HCIDUMP_HDR_SIZE 13
83 
84 // APPLE PacketLogger - struct not used directly, but left here as documentation
85 typedef struct {
86     uint32_t    len;
87     uint32_t    ts_sec;
88     uint32_t    ts_usec;
89     uint8_t     type;   // 0xfc for note
90 }
91 pktlog_hdr;
92 #define PKTLOG_HDR_SIZE 13
93 
94 static int dump_file = -1;
95 #ifdef HAVE_POSIX_FILE_IO
96 static int dump_format;
97 static uint8_t header_bluez[HCIDUMP_HDR_SIZE];
98 static uint8_t header_packetlogger[PKTLOG_HDR_SIZE];
99 static char time_string[40];
100 static int  max_nr_packets = -1;
101 static int  nr_packets = 0;
102 static char log_message_buffer[256];
103 #endif
104 
105 // levels: debug, info, error
106 static int log_level_enabled[3] = { 1, 1, 1};
107 
108 void hci_dump_open(const char *filename, hci_dump_format_t format){
109 #ifdef HAVE_POSIX_FILE_IO
110     dump_format = format;
111     if (dump_format == HCI_DUMP_STDOUT) {
112         dump_file = fileno(stdout);
113     } else {
114 
115         int oflags = O_WRONLY | O_CREAT | O_TRUNC;
116 #ifdef _WIN32
117         oflags |= O_BINARY;
118 #endif
119 
120         dump_file = open(filename, oflags, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH );
121         if (dump_file < 0){
122             printf("hci_dump_open: failed to open file %s\n", filename);
123         }
124     }
125 #else
126     UNUSED(filename);
127     UNUSED(format);
128 
129     dump_file = 1;
130 #endif
131 }
132 
133 #ifdef HAVE_POSIX_FILE_IO
134 void hci_dump_set_max_packets(int packets){
135     max_nr_packets = packets;
136 }
137 #endif
138 
139 static void printf_packet(uint8_t packet_type, uint8_t in, uint8_t * packet, uint16_t len){
140     switch (packet_type){
141         case HCI_COMMAND_DATA_PACKET:
142             printf("CMD => ");
143             break;
144         case HCI_EVENT_PACKET:
145             printf("EVT <= ");
146             break;
147         case HCI_ACL_DATA_PACKET:
148             if (in) {
149                 printf("ACL <= ");
150             } else {
151                 printf("ACL => ");
152             }
153             break;
154         case HCI_SCO_DATA_PACKET:
155             if (in) {
156                 printf("SCO <= ");
157             } else {
158                 printf("SCO => ");
159             }
160             break;
161         case LOG_MESSAGE_PACKET:
162             printf("LOG -- %s\n", (char*) packet);
163             return;
164         default:
165             return;
166     }
167     printf_hexdump(packet, len);
168 }
169 
170 static void printf_timestamp(void){
171 #ifdef HAVE_POSIX_FILE_IO
172     struct tm* ptm;
173     struct timeval curr_time;
174     gettimeofday(&curr_time, NULL);
175     time_t curr_time_secs = curr_time.tv_sec;
176     /* Obtain the time of day, and convert it to a tm struct. */
177     ptm = localtime (&curr_time_secs);
178     /* assert localtime was successful */
179     if (!ptm) return;
180     /* Format the date and time, down to a single second. */
181     strftime (time_string, sizeof (time_string), "[%Y-%m-%d %H:%M:%S", ptm);
182     /* Compute milliseconds from microseconds. */
183     uint16_t milliseconds = curr_time.tv_usec / 1000;
184     /* Print the formatted time, in seconds, followed by a decimal point and the milliseconds. */
185     printf ("%s.%03u] ", time_string, milliseconds);
186 #else
187     uint32_t time_ms = btstack_run_loop_get_time_ms();
188     int      seconds = time_ms / 1000;
189     int      minutes = seconds / 60;
190     unsigned int hours   = minutes / 60;
191 
192     uint16_t p_ms      = time_ms - (seconds * 1000);
193     uint16_t p_seconds = seconds - (minutes * 60);
194     uint16_t p_minutes = minutes - (hours   * 60);
195     printf("[%02u:%02u:%02u.%03u] ", hours, p_minutes, p_seconds, p_ms);
196 #endif
197 }
198 
199 void hci_dump_packet(uint8_t packet_type, uint8_t in, uint8_t *packet, uint16_t len) {
200 
201     if (dump_file < 0) return; // not activated yet
202 
203 #ifdef HAVE_POSIX_FILE_IO
204 
205     // don't grow bigger than max_nr_packets
206     if (dump_format != HCI_DUMP_STDOUT && max_nr_packets > 0){
207         if (nr_packets >= max_nr_packets){
208             lseek(dump_file, 0, SEEK_SET);
209             // avoid -Wunused-result
210             int res = ftruncate(dump_file, 0);
211             UNUSED(res);
212             nr_packets = 0;
213         }
214         nr_packets++;
215     }
216 
217     // get time
218     struct timeval curr_time;
219     gettimeofday(&curr_time, NULL);
220 
221     // avoid -Wunused-result
222     int res = 0;
223     switch (dump_format){
224         case HCI_DUMP_STDOUT: {
225             printf_timestamp();
226             printf_packet(packet_type, in, packet, len);
227             break;
228         }
229 
230         case HCI_DUMP_BLUEZ:
231             little_endian_store_16( header_bluez, 0, 1 + len);
232             header_bluez[2] = in;
233             header_bluez[3] = 0;
234             little_endian_store_32( header_bluez, 4, (uint32_t) curr_time.tv_sec);
235             little_endian_store_32( header_bluez, 8,            curr_time.tv_usec);
236             header_bluez[12] = packet_type;
237             res = write (dump_file, header_bluez, HCIDUMP_HDR_SIZE);
238             res = write (dump_file, packet, len );
239             break;
240 
241         case HCI_DUMP_PACKETLOGGER:
242             big_endian_store_32( header_packetlogger, 0, PKTLOG_HDR_SIZE - 4 + len);
243             big_endian_store_32( header_packetlogger, 4,  (uint32_t) curr_time.tv_sec);
244             big_endian_store_32( header_packetlogger, 8, curr_time.tv_usec);
245             switch (packet_type){
246                 case HCI_COMMAND_DATA_PACKET:
247                     header_packetlogger[12] = 0x00;
248                     break;
249                 case HCI_ACL_DATA_PACKET:
250                     if (in) {
251                         header_packetlogger[12] = 0x03;
252                     } else {
253                         header_packetlogger[12] = 0x02;
254                     }
255                     break;
256                 case HCI_SCO_DATA_PACKET:
257                     if (in) {
258                         header_packetlogger[12] = 0x09;
259                     } else {
260                         header_packetlogger[12] = 0x08;
261                     }
262                     break;
263                 case HCI_EVENT_PACKET:
264                     header_packetlogger[12] = 0x01;
265                     break;
266                 case LOG_MESSAGE_PACKET:
267                     header_packetlogger[12] = 0xfc;
268                     break;
269                 default:
270                     return;
271             }
272             res = write (dump_file, &header_packetlogger, PKTLOG_HDR_SIZE);
273             res = write (dump_file, packet, len );
274             break;
275 
276         default:
277             break;
278     }
279     UNUSED(res);
280 #else
281 
282     printf_timestamp();
283     printf_packet(packet_type, in, packet, len);
284 
285 #endif
286 }
287 
288 static int hci_dump_log_level_active(int log_level){
289     if (log_level < HCI_DUMP_LOG_LEVEL_DEBUG) return 0;
290     if (log_level > HCI_DUMP_LOG_LEVEL_ERROR) return 0;
291     return log_level_enabled[log_level];
292 }
293 
294 void hci_dump_log_va_arg(int log_level, const char * format, va_list argptr){
295     if (!hci_dump_log_level_active(log_level)) return;
296 
297 #ifdef HAVE_POSIX_FILE_IO
298     if (dump_file >= 0){
299         int len = vsnprintf(log_message_buffer, sizeof(log_message_buffer), format, argptr);
300         hci_dump_packet(LOG_MESSAGE_PACKET, 0, (uint8_t*) log_message_buffer, len);
301         return;
302     }
303 #endif
304 
305     printf_timestamp();
306     printf("LOG -- ");
307     vprintf(format, argptr);
308     printf("\n");
309 }
310 
311 void hci_dump_log(int log_level, const char * format, ...){
312     va_list argptr;
313     va_start(argptr, format);
314     hci_dump_log_va_arg(log_level, format, argptr);
315     va_end(argptr);
316 }
317 
318 #ifdef __AVR__
319 void hci_dump_log_P(int log_level, PGM_P format, ...){
320     if (!hci_dump_log_level_active(log_level)) return;
321     va_list argptr;
322     va_start(argptr, format);
323     printf_P(PSTR("LOG -- "));
324     vfprintf_P(stdout, format, argptr);
325     printf_P(PSTR("\n"));
326     va_end(argptr);
327 }
328 #endif
329 
330 void hci_dump_close(void){
331 #ifdef HAVE_POSIX_FILE_IO
332     close(dump_file);
333 #endif
334     dump_file = -1;
335 }
336 
337 void hci_dump_enable_log_level(int log_level, int enable){
338     if (log_level < HCI_DUMP_LOG_LEVEL_DEBUG) return;
339     if (log_level > HCI_DUMP_LOG_LEVEL_ERROR) return;
340     log_level_enabled[log_level] = enable;
341 }
342 
343