xref: /btstack/src/hci_dump.c (revision fac60fea7b1f8035236d9793b267925dfa9a010a)
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_cmd.h"
56 #include "btstack_run_loop.h"
57 #include <stdio.h>
58 
59 #ifdef HAVE_POSIX_FILE_IO
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 - struct not used directly, but left here as documentation
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 hcidump_hdr;
78 #define HCIDUMP_HDR_SIZE 13
79 
80 // APPLE PacketLogger - struct not used directly, but left here as documentation
81 typedef struct {
82     uint32_t    len;
83     uint32_t    ts_sec;
84     uint32_t    ts_usec;
85     uint8_t     type;   // 0xfc for note
86 }
87 pktlog_hdr;
88 #define PKTLOG_HDR_SIZE 13
89 
90 static int dump_file = -1;
91 #ifdef HAVE_POSIX_FILE_IO
92 static int dump_format;
93 static uint8_t header_bluez[HCIDUMP_HDR_SIZE];
94 static uint8_t header_packetlogger[PKTLOG_HDR_SIZE];
95 static char time_string[40];
96 static int  max_nr_packets = -1;
97 static int  nr_packets = 0;
98 static char log_message_buffer[256];
99 #endif
100 
101 // levels: debug, info, error
102 static int log_level_enabled[3] = { 1, 1, 1};
103 
104 void hci_dump_open(const char *filename, hci_dump_format_t format){
105 #ifdef HAVE_POSIX_FILE_IO
106     dump_format = format;
107     if (dump_format == HCI_DUMP_STDOUT) {
108         dump_file = fileno(stdout);
109     } else {
110 
111 # ifdef _WIN32
112         dump_file = open(filename, O_WRONLY | O_CREAT | O_TRUNC);
113 # else
114         dump_file = open(filename, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
115 # endif
116 
117     }
118 #else
119     dump_file = 1;
120 #endif
121 }
122 
123 #ifdef HAVE_POSIX_FILE_IO
124 void hci_dump_set_max_packets(int packets){
125     max_nr_packets = packets;
126 }
127 #endif
128 
129 static void printf_packet(uint8_t packet_type, uint8_t in, uint8_t * packet, uint16_t len){
130     switch (packet_type){
131         case HCI_COMMAND_DATA_PACKET:
132             printf("CMD => ");
133             break;
134         case HCI_EVENT_PACKET:
135             printf("EVT <= ");
136             break;
137         case HCI_ACL_DATA_PACKET:
138             if (in) {
139                 printf("ACL <= ");
140             } else {
141                 printf("ACL => ");
142             }
143             break;
144         case LOG_MESSAGE_PACKET:
145             printf("LOG -- %s\n", (char*) packet);
146             return;
147         default:
148             return;
149     }
150     printf_hexdump(packet, len);
151 }
152 
153 void hci_dump_packet(uint8_t packet_type, uint8_t in, uint8_t *packet, uint16_t len) {
154 
155     if (dump_file < 0) return; // not activated yet
156 
157 #ifdef HAVE_POSIX_FILE_IO
158 
159     // don't grow bigger than max_nr_packets
160     if (dump_format != HCI_DUMP_STDOUT && max_nr_packets > 0){
161         if (nr_packets >= max_nr_packets){
162             lseek(dump_file, 0, SEEK_SET);
163             ftruncate(dump_file, 0);
164             nr_packets = 0;
165         }
166         nr_packets++;
167     }
168 
169     // get time
170     struct timeval curr_time;
171     struct tm* ptm;
172     gettimeofday(&curr_time, NULL);
173     time_t curr_time_secs = curr_time.tv_sec;
174 
175     switch (dump_format){
176         case HCI_DUMP_STDOUT: {
177             /* Obtain the time of day, and convert it to a tm struct. */
178             ptm = localtime (&curr_time_secs);
179             /* assert localtime was successful */
180             if (!ptm) break;
181             /* Format the date and time, down to a single second. */
182             strftime (time_string, sizeof (time_string), "[%Y-%m-%d %H:%M:%S", ptm);
183             /* Compute milliseconds from microseconds. */
184             uint16_t milliseconds = curr_time.tv_usec / 1000;
185             /* Print the formatted time, in seconds, followed by a decimal point and the milliseconds. */
186             printf ("%s.%03u] ", time_string, milliseconds);
187             printf_packet(packet_type, in, packet, len);
188             break;
189         }
190 
191         case HCI_DUMP_BLUEZ:
192             little_endian_store_16( header_bluez, 0, 1 + len);
193             header_bluez[2] = in;
194             header_bluez[3] = 0;
195             little_endian_store_32( header_bluez, 4, (uint32_t) curr_time.tv_sec);
196             little_endian_store_32( header_bluez, 8,            curr_time.tv_usec);
197             header_bluez[12] = packet_type;
198             write (dump_file, header_bluez, HCIDUMP_HDR_SIZE);
199             write (dump_file, packet, len );
200             break;
201 
202         case HCI_DUMP_PACKETLOGGER:
203             big_endian_store_32( header_packetlogger, 0, PKTLOG_HDR_SIZE - 4 + len);
204             big_endian_store_32( header_packetlogger, 4,  (uint32_t) curr_time.tv_sec);
205             big_endian_store_32( header_packetlogger, 8, curr_time.tv_usec);
206             switch (packet_type){
207                 case HCI_COMMAND_DATA_PACKET:
208                     header_packetlogger[12] = 0x00;
209                     break;
210                 case HCI_ACL_DATA_PACKET:
211                     if (in) {
212                         header_packetlogger[12] = 0x03;
213                     } else {
214                         header_packetlogger[12] = 0x02;
215                     }
216                     break;
217                 case HCI_SCO_DATA_PACKET:
218                     if (in) {
219                         header_packetlogger[12] = 0x09;
220                     } else {
221                         header_packetlogger[12] = 0x08;
222                     }
223                     break;
224                 case HCI_EVENT_PACKET:
225                     header_packetlogger[12] = 0x01;
226                     break;
227                 case LOG_MESSAGE_PACKET:
228                     header_packetlogger[12] = 0xfc;
229                     break;
230                 default:
231                     return;
232             }
233             write (dump_file, &header_packetlogger, PKTLOG_HDR_SIZE);
234             write (dump_file, packet, len );
235             break;
236 
237         default:
238             break;
239     }
240 #else
241 
242 // #ifdef HAVE_EMBEDDED_TICK
243 //     uint32_t time_ms = btstack_run_loop_embedded_get_time_ms();
244 //     printf("[%06u] ", time_ms);
245 // #endif
246     printf_packet(packet_type, in, packet, len);
247 
248 #endif
249 }
250 
251 static int hci_dump_log_level_active(int log_level){
252     if (log_level < 0) return 0;
253     if (log_level > LOG_LEVEL_ERROR) return 0;
254     return log_level_enabled[log_level];
255 }
256 
257 void hci_dump_log(int log_level, const char * format, ...){
258     if (!hci_dump_log_level_active(log_level)) return;
259     va_list argptr;
260     va_start(argptr, format);
261 #ifdef HAVE_POSIX_FILE_IO
262     int len = vsnprintf(log_message_buffer, sizeof(log_message_buffer), format, argptr);
263     hci_dump_packet(LOG_MESSAGE_PACKET, 0, (uint8_t*) log_message_buffer, len);
264 #else
265     printf("LOG -- ");
266     vprintf(format, argptr);
267     printf("\n");
268 #endif
269     va_end(argptr);
270 }
271 
272 #ifdef __AVR__
273 void hci_dump_log_P(int log_level, PGM_P format, ...){
274     if (!hci_dump_log_level_active(log_level)) return;
275     va_list argptr;
276     va_start(argptr, format);
277     printf_P(PSTR("LOG -- "));
278     vfprintf_P(stdout, format, argptr);
279     printf_P(PSTR("\n"));
280     va_end(argptr);
281 }
282 #endif
283 
284 void hci_dump_close(void){
285 #ifdef HAVE_POSIX_FILE_IO
286     close(dump_file);
287 #endif
288     dump_file = -1;
289 }
290 
291 void hci_dump_enable_log_level(int log_level, int enable){
292     if (log_level < 0) return;
293     if (log_level > LOG_LEVEL_ERROR) return;
294     log_level_enabled[log_level] = enable;
295 }
296 
297