xref: /btstack/src/hci_dump.c (revision d5814ca7a939167d6299da0134b368962bece148)
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 /*
39  *  hci_dump.c
40  *
41  *  Dump HCI trace in various formats:
42  *
43  *  - BlueZ's hcidump format
44  *  - Apple's PacketLogger
45  *  - stdout hexdump
46  *
47  *  Created by Matthias Ringwald on 5/26/09.
48  */
49 
50 #include "btstack-config.h"
51 
52 #include "hci_dump.h"
53 #include "hci.h"
54 #include "hci_transport.h"
55 #include "hci_cmds.h"
56 #include "run_loop.h"
57 #include <stdio.h>
58 
59 #ifndef EMBEDDED
60 #include <fcntl.h>        // open
61 #include <unistd.h>       // write
62 #include <time.h>
63 #include <sys/time.h>     // for timestamps
64 #include <sys/stat.h>     // for mode flags
65 #include <stdarg.h>       // for va_list
66 #endif
67 
68 // BLUEZ hcidump
69 typedef struct {
70 	uint16_t	len;
71 	uint8_t		in;
72 	uint8_t		pad;
73 	uint32_t	ts_sec;
74 	uint32_t	ts_usec;
75     uint8_t     packet_type;
76 }
77 #ifdef __GNUC__
78 __attribute__ ((packed))
79 #endif
80 hcidump_hdr;
81 
82 // APPLE PacketLogger
83 typedef struct {
84 	uint32_t	len;
85 	uint32_t	ts_sec;
86 	uint32_t	ts_usec;
87 	uint8_t		type;   // 0xfc for note
88 }
89 #ifdef __GNUC__
90 __attribute__ ((packed))
91 #endif
92 pktlog_hdr;
93 
94 static int dump_file = -1;
95 #ifndef EMBEDDED
96 static int dump_format;
97 static hcidump_hdr header_bluez;
98 static pktlog_hdr  header_packetlogger;
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 EMBEDDED
110     dump_file = 1;
111 #else
112     dump_format = format;
113     if (dump_format == HCI_DUMP_STDOUT) {
114         dump_file = fileno(stdout);
115     } else {
116 #ifdef _WIN32
117         dump_file = open(filename, O_WRONLY | O_CREAT | O_TRUNC);
118 #else
119         dump_file = open(filename, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
120 #endif
121     }
122 #endif
123 }
124 
125 #ifndef EMBEDDED
126 void hci_dump_set_max_packets(int packets){
127     max_nr_packets = packets;
128 }
129 #endif
130 
131 static void printf_packet(uint8_t packet_type, uint8_t in, uint8_t * packet, uint16_t len){
132     switch (packet_type){
133         case HCI_COMMAND_DATA_PACKET:
134             printf("CMD => ");
135             break;
136         case HCI_EVENT_PACKET:
137             printf("EVT <= ");
138             break;
139         case HCI_ACL_DATA_PACKET:
140             if (in) {
141                 printf("ACL <= ");
142             } else {
143                 printf("ACL => ");
144             }
145             break;
146         case LOG_MESSAGE_PACKET:
147             printf("LOG -- %s\n", (char*) packet);
148             return;
149         default:
150             return;
151     }
152     printf_hexdump(packet, len);
153 }
154 
155 void hci_dump_packet(uint8_t packet_type, uint8_t in, uint8_t *packet, uint16_t len) {
156 
157     if (dump_file < 0) return; // not activated yet
158 
159 #ifdef EMBEDDED
160 // #ifdef HAVE_TICK
161 //     uint32_t time_ms = embedded_get_time_ms();
162 //     printf("[%06u] ", time_ms);
163 // #endif
164     printf_packet(packet_type, in, packet, len);
165 #else
166     // don't grow bigger than max_nr_packets
167     if (dump_format != HCI_DUMP_STDOUT && max_nr_packets > 0){
168         if (nr_packets >= max_nr_packets){
169             lseek(dump_file, 0, SEEK_SET);
170             ftruncate(dump_file, 0);
171             nr_packets = 0;
172         }
173         nr_packets++;
174     }
175 
176     // get time
177     struct timeval curr_time;
178     struct tm* ptm;
179     gettimeofday(&curr_time, NULL);
180     time_t curr_time_secs = curr_time.tv_sec;
181 
182     switch (dump_format){
183         case HCI_DUMP_STDOUT: {
184             /* Obtain the time of day, and convert it to a tm struct. */
185             ptm = localtime (&curr_time_secs);
186             /* Format the date and time, down to a single second. */
187             strftime (time_string, sizeof (time_string), "[%Y-%m-%d %H:%M:%S", ptm);
188             /* Compute milliseconds from microseconds. */
189             uint16_t milliseconds = curr_time.tv_usec / 1000;
190             /* Print the formatted time, in seconds, followed by a decimal point
191              and the milliseconds. */
192             printf ("%s.%03u] ", time_string, milliseconds);
193             printf_packet(packet_type, in, packet, len);
194             break;
195         }
196 
197         case HCI_DUMP_BLUEZ:
198             bt_store_16( (uint8_t *) &header_bluez.len, 0, 1 + len);
199             header_bluez.in  = in;
200             header_bluez.pad = 0;
201             bt_store_32( (uint8_t *) &header_bluez.ts_sec,  0, curr_time.tv_sec);
202             bt_store_32( (uint8_t *) &header_bluez.ts_usec, 0, curr_time.tv_usec);
203             header_bluez.packet_type = packet_type;
204             write (dump_file, &header_bluez, sizeof(hcidump_hdr) );
205             write (dump_file, packet, len );
206             break;
207 
208         case HCI_DUMP_PACKETLOGGER:
209             net_store_32( (uint8_t *) &header_packetlogger, 0, sizeof(pktlog_hdr) - 4 + len);
210             net_store_32( (uint8_t *) &header_packetlogger, 4, curr_time.tv_sec);
211             net_store_32( (uint8_t *) &header_packetlogger, 8, curr_time.tv_usec);
212             switch (packet_type){
213                 case HCI_COMMAND_DATA_PACKET:
214                     header_packetlogger.type = 0x00;
215                     break;
216                 case HCI_ACL_DATA_PACKET:
217                     if (in) {
218                         header_packetlogger.type = 0x03;
219                     } else {
220                         header_packetlogger.type = 0x02;
221                     }
222                     break;
223                 case HCI_SCO_DATA_PACKET:
224                     if (in) {
225                         header_packetlogger.type = 0x09;
226                     } else {
227                         header_packetlogger.type = 0x08;
228                     }
229                     break;
230                 case HCI_EVENT_PACKET:
231                     header_packetlogger.type = 0x01;
232                     break;
233                 case LOG_MESSAGE_PACKET:
234                     header_packetlogger.type = 0xfc;
235                     break;
236                 default:
237                     return;
238             }
239             write (dump_file, &header_packetlogger, sizeof(pktlog_hdr) );
240             write (dump_file, packet, len );
241             break;
242 
243         default:
244             break;
245     }
246 #endif
247 }
248 
249 static int hci_dump_log_level_active(int log_level){
250     if (log_level < 0) return 0;
251     if (log_level > LOG_LEVEL_ERROR) return 0;
252     return log_level_enabled[log_level];
253 }
254 
255 void hci_dump_log(int log_level, const char * format, ...){
256     if (!hci_dump_log_level_active(log_level)) return;
257     va_list argptr;
258     va_start(argptr, format);
259 #ifdef EMBEDDED
260     printf("LOG -- ");
261     vprintf(format, argptr);
262     printf("\n");
263 #else
264     int len = vsnprintf(log_message_buffer, sizeof(log_message_buffer), format, argptr);
265     hci_dump_packet(LOG_MESSAGE_PACKET, 0, (uint8_t*) log_message_buffer, len);
266 #endif
267     va_end(argptr);
268 }
269 
270 #ifdef __AVR__
271 void hci_dump_log_P(int log_level, PGM_P format, ...){
272     if (!hci_dump_log_level_active(log_level)) return;
273     va_list argptr;
274     va_start(argptr, format);
275     printf_P(PSTR("LOG -- "));
276     vfprintf_P(stdout, format, argptr);
277     printf_P(PSTR("\n"));
278     va_end(argptr);
279 }
280 #endif
281 
282 void hci_dump_close(void){
283 #ifndef EMBEDDED
284     close(dump_file);
285     dump_file = -1;
286 #endif
287 }
288 
289 void hci_dump_enable_log_level(int log_level, int enable){
290     if (log_level < 0) return;
291     if (log_level > LOG_LEVEL_ERROR) return;
292     log_level_enabled[log_level] = enable;
293 }
294 
295