xref: /btstack/src/hci_dump.c (revision eb443d3d86c07c2f7d7973304b0b4dac8f914382)
179662672Smatthias.ringwald /*
2a0c35809S[email protected]  * Copyright (C) 2014 BlueKitchen GmbH
31713bceaSmatthias.ringwald  *
41713bceaSmatthias.ringwald  * Redistribution and use in source and binary forms, with or without
51713bceaSmatthias.ringwald  * modification, are permitted provided that the following conditions
61713bceaSmatthias.ringwald  * are met:
71713bceaSmatthias.ringwald  *
81713bceaSmatthias.ringwald  * 1. Redistributions of source code must retain the above copyright
91713bceaSmatthias.ringwald  *    notice, this list of conditions and the following disclaimer.
101713bceaSmatthias.ringwald  * 2. Redistributions in binary form must reproduce the above copyright
111713bceaSmatthias.ringwald  *    notice, this list of conditions and the following disclaimer in the
121713bceaSmatthias.ringwald  *    documentation and/or other materials provided with the distribution.
131713bceaSmatthias.ringwald  * 3. Neither the name of the copyright holders nor the names of
141713bceaSmatthias.ringwald  *    contributors may be used to endorse or promote products derived
151713bceaSmatthias.ringwald  *    from this software without specific prior written permission.
166b64433eSmatthias.ringwald  * 4. Any redistribution, use, or modification is done solely for
176b64433eSmatthias.ringwald  *    personal benefit and not for any commercial purpose or for
186b64433eSmatthias.ringwald  *    monetary gain.
191713bceaSmatthias.ringwald  *
20a0c35809S[email protected]  * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS
211713bceaSmatthias.ringwald  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
221713bceaSmatthias.ringwald  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
231713bceaSmatthias.ringwald  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
241713bceaSmatthias.ringwald  * RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
251713bceaSmatthias.ringwald  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
261713bceaSmatthias.ringwald  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
271713bceaSmatthias.ringwald  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
281713bceaSmatthias.ringwald  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
291713bceaSmatthias.ringwald  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
301713bceaSmatthias.ringwald  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
311713bceaSmatthias.ringwald  * SUCH DAMAGE.
321713bceaSmatthias.ringwald  *
33a0c35809S[email protected]  * Please inquire about commercial licensing options at
34a0c35809S[email protected]  * [email protected]
356b64433eSmatthias.ringwald  *
361713bceaSmatthias.ringwald  */
371713bceaSmatthias.ringwald 
381713bceaSmatthias.ringwald /*
3979662672Smatthias.ringwald  *  hci_dump.c
4079662672Smatthias.ringwald  *
41fe1ed1b8Smatthias.ringwald  *  Dump HCI trace in various formats:
42fe1ed1b8Smatthias.ringwald  *
43fe1ed1b8Smatthias.ringwald  *  - BlueZ's hcidump format
44fe1ed1b8Smatthias.ringwald  *  - Apple's PacketLogger
45fe1ed1b8Smatthias.ringwald  *  - stdout hexdump
4679662672Smatthias.ringwald  *
4779662672Smatthias.ringwald  *  Created by Matthias Ringwald on 5/26/09.
4879662672Smatthias.ringwald  */
4979662672Smatthias.ringwald 
507907f069SMatthias Ringwald #include "btstack_config.h"
51a1d7dd1fSmatthias.ringwald 
5279662672Smatthias.ringwald #include "hci_dump.h"
5379662672Smatthias.ringwald #include "hci.h"
54c6448b67Smatthias.ringwald #include "hci_transport.h"
5556042629SMatthias Ringwald #include "hci_cmd.h"
5682636622SMatthias Ringwald #include "btstack_run_loop.h"
57198a9e1bS[email protected] #include <stdio.h>
5879662672Smatthias.ringwald 
59*eb443d3dSMatthias Ringwald #ifdef HAVE_POSIX_FILE_IO
60d5ea8924S[email protected] #include <fcntl.h>        // open
6179662672Smatthias.ringwald #include <unistd.h>       // write
628adf0ddaSmatthias.ringwald #include <time.h>
638b658ebcSmatthias.ringwald #include <sys/time.h>     // for timestamps
64c7b9c559Smatthias.ringwald #include <sys/stat.h>     // for mode flags
65a1d7dd1fSmatthias.ringwald #include <stdarg.h>       // for va_list
6668e27c0fSmatthias.ringwald #endif
6779662672Smatthias.ringwald 
681a1c8389SMatthias Ringwald // BLUEZ hcidump - struct not used directly, but left here as documentation
698b658ebcSmatthias.ringwald typedef struct {
708b658ebcSmatthias.ringwald     uint16_t    len;
718b658ebcSmatthias.ringwald     uint8_t     in;
728b658ebcSmatthias.ringwald     uint8_t     pad;
738b658ebcSmatthias.ringwald     uint32_t    ts_sec;
748b658ebcSmatthias.ringwald     uint32_t    ts_usec;
758b658ebcSmatthias.ringwald     uint8_t     packet_type;
766c5c6faaSmatthias.ringwald }
776c5c6faaSmatthias.ringwald hcidump_hdr;
78f3e036dcSMatthias Ringwald #define HCIDUMP_HDR_SIZE 13
7979662672Smatthias.ringwald 
801a1c8389SMatthias Ringwald // APPLE PacketLogger - struct not used directly, but left here as documentation
818b658ebcSmatthias.ringwald typedef struct {
828b658ebcSmatthias.ringwald     uint32_t    len;
838b658ebcSmatthias.ringwald     uint32_t    ts_sec;
848b658ebcSmatthias.ringwald     uint32_t    ts_usec;
852df12229Smatthias.ringwald     uint8_t     type;   // 0xfc for note
866c5c6faaSmatthias.ringwald }
876c5c6faaSmatthias.ringwald pktlog_hdr;
88f3e036dcSMatthias Ringwald #define PKTLOG_HDR_SIZE 13
898b658ebcSmatthias.ringwald 
908b658ebcSmatthias.ringwald static int dump_file = -1;
91*eb443d3dSMatthias Ringwald #ifdef HAVE_POSIX_FILE_IO
928b658ebcSmatthias.ringwald static int dump_format;
93f3e036dcSMatthias Ringwald static uint8_t header_bluez[HCIDUMP_HDR_SIZE];
94f3e036dcSMatthias Ringwald static uint8_t header_packetlogger[PKTLOG_HDR_SIZE];
958adf0ddaSmatthias.ringwald static char time_string[40];
962992c131Smatthias.ringwald static int  max_nr_packets = -1;
979ae0c346Smatthias.ringwald static int  nr_packets = 0;
98a1d7dd1fSmatthias.ringwald static char log_message_buffer[256];
9968e27c0fSmatthias.ringwald #endif
1008b658ebcSmatthias.ringwald 
1018a37b10aSMatthias Ringwald // levels: debug, info, error
1028a37b10aSMatthias Ringwald static int log_level_enabled[3] = { 1, 1, 1};
1038a37b10aSMatthias Ringwald 
104a225073eS[email protected] void hci_dump_open(const char *filename, hci_dump_format_t format){
105*eb443d3dSMatthias Ringwald #ifdef HAVE_POSIX_FILE_IO
1068b658ebcSmatthias.ringwald     dump_format = format;
1078adf0ddaSmatthias.ringwald     if (dump_format == HCI_DUMP_STDOUT) {
1088adf0ddaSmatthias.ringwald         dump_file = fileno(stdout);
1098adf0ddaSmatthias.ringwald     } else {
110*eb443d3dSMatthias Ringwald 
111b52eaea5S[email protected] # ifdef _WIN32
112b52eaea5S[email protected]         dump_file = open(filename, O_WRONLY | O_CREAT | O_TRUNC);
113b52eaea5S[email protected] # else
1148adf0ddaSmatthias.ringwald         dump_file = open(filename, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
115b52eaea5S[email protected] # endif
116*eb443d3dSMatthias Ringwald 
11779662672Smatthias.ringwald     }
118*eb443d3dSMatthias Ringwald #else
119*eb443d3dSMatthias Ringwald     dump_file = 1;
12068e27c0fSmatthias.ringwald #endif
121d9659922Smatthias.ringwald }
12279662672Smatthias.ringwald 
123*eb443d3dSMatthias Ringwald #ifdef HAVE_POSIX_FILE_IO
1242992c131Smatthias.ringwald void hci_dump_set_max_packets(int packets){
1252992c131Smatthias.ringwald     max_nr_packets = packets;
1262992c131Smatthias.ringwald }
1277c5f7483Smatthias.ringwald #endif
1282992c131Smatthias.ringwald 
1298a37b10aSMatthias Ringwald static void printf_packet(uint8_t packet_type, uint8_t in, uint8_t * packet, uint16_t len){
130198a9e1bS[email protected]     switch (packet_type){
131198a9e1bS[email protected]         case HCI_COMMAND_DATA_PACKET:
132198a9e1bS[email protected]             printf("CMD => ");
133198a9e1bS[email protected]             break;
134198a9e1bS[email protected]         case HCI_EVENT_PACKET:
135198a9e1bS[email protected]             printf("EVT <= ");
136198a9e1bS[email protected]             break;
137198a9e1bS[email protected]         case HCI_ACL_DATA_PACKET:
138198a9e1bS[email protected]             if (in) {
139198a9e1bS[email protected]                 printf("ACL <= ");
140198a9e1bS[email protected]             } else {
141198a9e1bS[email protected]                 printf("ACL => ");
142198a9e1bS[email protected]             }
143198a9e1bS[email protected]             break;
144198a9e1bS[email protected]         case LOG_MESSAGE_PACKET:
145198a9e1bS[email protected]             printf("LOG -- %s\n", (char*) packet);
146198a9e1bS[email protected]             return;
147198a9e1bS[email protected]         default:
148198a9e1bS[email protected]             return;
149198a9e1bS[email protected]     }
150198a9e1bS[email protected]     printf_hexdump(packet, len);
151198a9e1bS[email protected] }
152198a9e1bS[email protected] 
15379662672Smatthias.ringwald void hci_dump_packet(uint8_t packet_type, uint8_t in, uint8_t *packet, uint16_t len) {
15468e27c0fSmatthias.ringwald 
1558b658ebcSmatthias.ringwald     if (dump_file < 0) return; // not activated yet
1568b658ebcSmatthias.ringwald 
157*eb443d3dSMatthias Ringwald #ifdef HAVE_POSIX_FILE_IO
158*eb443d3dSMatthias Ringwald 
1592992c131Smatthias.ringwald     // don't grow bigger than max_nr_packets
1602992c131Smatthias.ringwald     if (dump_format != HCI_DUMP_STDOUT && max_nr_packets > 0){
1612992c131Smatthias.ringwald         if (nr_packets >= max_nr_packets){
1622992c131Smatthias.ringwald             lseek(dump_file, 0, SEEK_SET);
1632992c131Smatthias.ringwald             ftruncate(dump_file, 0);
1642992c131Smatthias.ringwald             nr_packets = 0;
1652992c131Smatthias.ringwald         }
1662992c131Smatthias.ringwald         nr_packets++;
1672992c131Smatthias.ringwald     }
1682992c131Smatthias.ringwald 
1698b658ebcSmatthias.ringwald     // get time
1708b658ebcSmatthias.ringwald     struct timeval curr_time;
1718adf0ddaSmatthias.ringwald     struct tm* ptm;
1728b658ebcSmatthias.ringwald     gettimeofday(&curr_time, NULL);
173d5ea8924S[email protected]     time_t curr_time_secs = curr_time.tv_sec;
1748b658ebcSmatthias.ringwald 
1758b658ebcSmatthias.ringwald     switch (dump_format){
176a9cf4d77S[email protected]         case HCI_DUMP_STDOUT: {
1778adf0ddaSmatthias.ringwald             /* Obtain the time of day, and convert it to a tm struct. */
178d5ea8924S[email protected]             ptm = localtime (&curr_time_secs);
1798adf0ddaSmatthias.ringwald             /* Format the date and time, down to a single second. */
1808adf0ddaSmatthias.ringwald             strftime (time_string, sizeof (time_string), "[%Y-%m-%d %H:%M:%S", ptm);
1818adf0ddaSmatthias.ringwald             /* Compute milliseconds from microseconds. */
1828adf0ddaSmatthias.ringwald             uint16_t milliseconds = curr_time.tv_usec / 1000;
1838adf0ddaSmatthias.ringwald             /* Print the formatted time, in seconds, followed by a decimal point
1848adf0ddaSmatthias.ringwald              and the milliseconds. */
1858adf0ddaSmatthias.ringwald             printf ("%s.%03u] ", time_string, milliseconds);
186198a9e1bS[email protected]             printf_packet(packet_type, in, packet, len);
1878adf0ddaSmatthias.ringwald             break;
188a9cf4d77S[email protected]         }
1890d79c710Smatthias.ringwald 
1908b658ebcSmatthias.ringwald         case HCI_DUMP_BLUEZ:
1911a1c8389SMatthias Ringwald             bt_store_16( header_bluez, 0, 1 + len);
1921a1c8389SMatthias Ringwald             header_bluez[2] = in;
1931a1c8389SMatthias Ringwald             header_bluez[3] = 0;
194728cd49dSMatthias Ringwald             bt_store_32( header_bluez, 4, curr_time.tv_sec);
195728cd49dSMatthias Ringwald             bt_store_32( header_bluez, 8, curr_time.tv_usec);
1961a1c8389SMatthias Ringwald             header_bluez[12] = packet_type;
197f3e036dcSMatthias Ringwald             write (dump_file, header_bluez, HCIDUMP_HDR_SIZE);
19879662672Smatthias.ringwald             write (dump_file, packet, len );
1998b658ebcSmatthias.ringwald             break;
2000d79c710Smatthias.ringwald 
2018b658ebcSmatthias.ringwald         case HCI_DUMP_PACKETLOGGER:
202f3e036dcSMatthias Ringwald             net_store_32( header_packetlogger, 0, PKTLOG_HDR_SIZE - 4 + len);
2031a1c8389SMatthias Ringwald             net_store_32( header_packetlogger, 4, curr_time.tv_sec);
2041a1c8389SMatthias Ringwald             net_store_32( header_packetlogger, 8, curr_time.tv_usec);
2058b658ebcSmatthias.ringwald             switch (packet_type){
2068b658ebcSmatthias.ringwald                 case HCI_COMMAND_DATA_PACKET:
2071a1c8389SMatthias Ringwald                     header_packetlogger[12] = 0x00;
2088b658ebcSmatthias.ringwald                     break;
2098b658ebcSmatthias.ringwald                 case HCI_ACL_DATA_PACKET:
2108b658ebcSmatthias.ringwald                     if (in) {
2111a1c8389SMatthias Ringwald                         header_packetlogger[12] = 0x03;
2128b658ebcSmatthias.ringwald                     } else {
2131a1c8389SMatthias Ringwald                         header_packetlogger[12] = 0x02;
2148b658ebcSmatthias.ringwald                     }
2158b658ebcSmatthias.ringwald                     break;
2168d675e3dS[email protected]                 case HCI_SCO_DATA_PACKET:
2178d675e3dS[email protected]                     if (in) {
2181a1c8389SMatthias Ringwald                         header_packetlogger[12] = 0x09;
2198d675e3dS[email protected]                     } else {
2201a1c8389SMatthias Ringwald                         header_packetlogger[12] = 0x08;
2218d675e3dS[email protected]                     }
2228d675e3dS[email protected]                     break;
2238b658ebcSmatthias.ringwald                 case HCI_EVENT_PACKET:
2241a1c8389SMatthias Ringwald                     header_packetlogger[12] = 0x01;
2258b658ebcSmatthias.ringwald                     break;
2260d79c710Smatthias.ringwald                 case LOG_MESSAGE_PACKET:
2271a1c8389SMatthias Ringwald                     header_packetlogger[12] = 0xfc;
2280d79c710Smatthias.ringwald                     break;
2298b658ebcSmatthias.ringwald                 default:
2308b658ebcSmatthias.ringwald                     return;
2318b658ebcSmatthias.ringwald             }
232f3e036dcSMatthias Ringwald             write (dump_file, &header_packetlogger, PKTLOG_HDR_SIZE);
2338b658ebcSmatthias.ringwald             write (dump_file, packet, len );
2340d79c710Smatthias.ringwald             break;
2350d79c710Smatthias.ringwald 
2360d79c710Smatthias.ringwald         default:
2370d79c710Smatthias.ringwald             break;
2388b658ebcSmatthias.ringwald     }
239*eb443d3dSMatthias Ringwald #else
240*eb443d3dSMatthias Ringwald 
241*eb443d3dSMatthias Ringwald // #ifdef HAVE_TICK
242*eb443d3dSMatthias Ringwald //     uint32_t time_ms = btstack_run_loop_embedded_get_time_ms();
243*eb443d3dSMatthias Ringwald //     printf("[%06u] ", time_ms);
244*eb443d3dSMatthias Ringwald // #endif
245*eb443d3dSMatthias Ringwald     printf_packet(packet_type, in, packet, len);
246*eb443d3dSMatthias Ringwald 
24768e27c0fSmatthias.ringwald #endif
24879662672Smatthias.ringwald }
24979662672Smatthias.ringwald 
2508a37b10aSMatthias Ringwald static int hci_dump_log_level_active(int log_level){
2518a37b10aSMatthias Ringwald     if (log_level < 0) return 0;
2528a37b10aSMatthias Ringwald     if (log_level > LOG_LEVEL_ERROR) return 0;
2538a37b10aSMatthias Ringwald     return log_level_enabled[log_level];
2548a37b10aSMatthias Ringwald }
2558a37b10aSMatthias Ringwald 
2568a37b10aSMatthias Ringwald void hci_dump_log(int log_level, const char * format, ...){
2578a37b10aSMatthias Ringwald     if (!hci_dump_log_level_active(log_level)) return;
258a1d7dd1fSmatthias.ringwald     va_list argptr;
259a1d7dd1fSmatthias.ringwald     va_start(argptr, format);
260*eb443d3dSMatthias Ringwald #ifdef HAVE_POSIX_FILE_IO
261*eb443d3dSMatthias Ringwald     int len = vsnprintf(log_message_buffer, sizeof(log_message_buffer), format, argptr);
262*eb443d3dSMatthias Ringwald     hci_dump_packet(LOG_MESSAGE_PACKET, 0, (uint8_t*) log_message_buffer, len);
263*eb443d3dSMatthias Ringwald #else
2641df3b679S[email protected]     printf("LOG -- ");
2651df3b679S[email protected]     vprintf(format, argptr);
2661fd51e45S[email protected]     printf("\n");
267a1d7dd1fSmatthias.ringwald #endif
2681df3b679S[email protected]     va_end(argptr);
269a1d7dd1fSmatthias.ringwald }
270a1d7dd1fSmatthias.ringwald 
27120ea11b9S[email protected] #ifdef __AVR__
2728a37b10aSMatthias Ringwald void hci_dump_log_P(int log_level, PGM_P format, ...){
2738a37b10aSMatthias Ringwald     if (!hci_dump_log_level_active(log_level)) return;
27420ea11b9S[email protected]     va_list argptr;
27520ea11b9S[email protected]     va_start(argptr, format);
27620ea11b9S[email protected]     printf_P(PSTR("LOG -- "));
27720ea11b9S[email protected]     vfprintf_P(stdout, format, argptr);
27820ea11b9S[email protected]     printf_P(PSTR("\n"));
27920ea11b9S[email protected]     va_end(argptr);
28020ea11b9S[email protected] }
28120ea11b9S[email protected] #endif
28220ea11b9S[email protected] 
28371de195eSMatthias Ringwald void hci_dump_close(void){
284*eb443d3dSMatthias Ringwald #ifdef HAVE_POSIX_FILE_IO
28579662672Smatthias.ringwald     close(dump_file);
28668e27c0fSmatthias.ringwald #endif
287*eb443d3dSMatthias Ringwald     dump_file = -1;
28879662672Smatthias.ringwald }
28979662672Smatthias.ringwald 
2908a37b10aSMatthias Ringwald void hci_dump_enable_log_level(int log_level, int enable){
2918a37b10aSMatthias Ringwald     if (log_level < 0) return;
2928a37b10aSMatthias Ringwald     if (log_level > LOG_LEVEL_ERROR) return;
2938a37b10aSMatthias Ringwald     log_level_enabled[log_level] = enable;
2948a37b10aSMatthias Ringwald }
2958a37b10aSMatthias Ringwald 
296