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