xref: /btstack/src/hci_dump.c (revision 2992c13168ffd5c7ba64c094e594db1ebb030b39)
179662672Smatthias.ringwald /*
21713bceaSmatthias.ringwald  * Copyright (C) 2009 by Matthias Ringwald
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.
161713bceaSmatthias.ringwald  *
171713bceaSmatthias.ringwald  * THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS
181713bceaSmatthias.ringwald  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
191713bceaSmatthias.ringwald  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
201713bceaSmatthias.ringwald  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
211713bceaSmatthias.ringwald  * RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
221713bceaSmatthias.ringwald  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
231713bceaSmatthias.ringwald  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
241713bceaSmatthias.ringwald  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
251713bceaSmatthias.ringwald  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
261713bceaSmatthias.ringwald  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
271713bceaSmatthias.ringwald  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
281713bceaSmatthias.ringwald  * SUCH DAMAGE.
291713bceaSmatthias.ringwald  *
301713bceaSmatthias.ringwald  */
311713bceaSmatthias.ringwald 
321713bceaSmatthias.ringwald /*
3379662672Smatthias.ringwald  *  hci_dump.c
3479662672Smatthias.ringwald  *
35fe1ed1b8Smatthias.ringwald  *  Dump HCI trace in various formats:
36fe1ed1b8Smatthias.ringwald  *
37fe1ed1b8Smatthias.ringwald  *  - BlueZ's hcidump format
38fe1ed1b8Smatthias.ringwald  *  - Apple's PacketLogger
39fe1ed1b8Smatthias.ringwald  *  - stdout hexdump
4079662672Smatthias.ringwald  *
4179662672Smatthias.ringwald  *  Created by Matthias Ringwald on 5/26/09.
4279662672Smatthias.ringwald  */
4379662672Smatthias.ringwald 
4479662672Smatthias.ringwald #include "hci_dump.h"
4579662672Smatthias.ringwald #include "hci.h"
46c6448b67Smatthias.ringwald #include "hci_transport.h"
4779662672Smatthias.ringwald 
4868e27c0fSmatthias.ringwald #ifndef EMBEDDED
4979662672Smatthias.ringwald #include <fcntl.h>        // open
5079662672Smatthias.ringwald #include <arpa/inet.h>    // hton..
5179662672Smatthias.ringwald #include <strings.h>      // bzero
5279662672Smatthias.ringwald #include <unistd.h>       // write
5379662672Smatthias.ringwald #include <stdio.h>
548adf0ddaSmatthias.ringwald #include <time.h>
558b658ebcSmatthias.ringwald #include <sys/time.h>     // for timestamps
56c7b9c559Smatthias.ringwald #include <sys/stat.h>     // for mode flags
5768e27c0fSmatthias.ringwald #endif
5879662672Smatthias.ringwald 
598b658ebcSmatthias.ringwald // BLUEZ hcidump
608b658ebcSmatthias.ringwald typedef struct {
618b658ebcSmatthias.ringwald 	uint16_t	len;
628b658ebcSmatthias.ringwald 	uint8_t		in;
638b658ebcSmatthias.ringwald 	uint8_t		pad;
648b658ebcSmatthias.ringwald 	uint32_t	ts_sec;
658b658ebcSmatthias.ringwald 	uint32_t	ts_usec;
668b658ebcSmatthias.ringwald     uint8_t     packet_type;
678b658ebcSmatthias.ringwald } __attribute__ ((packed)) hcidump_hdr;
6879662672Smatthias.ringwald 
698b658ebcSmatthias.ringwald // APPLE PacketLogger
708b658ebcSmatthias.ringwald typedef struct {
718b658ebcSmatthias.ringwald 	uint32_t	len;
728b658ebcSmatthias.ringwald 	uint32_t	ts_sec;
738b658ebcSmatthias.ringwald 	uint32_t	ts_usec;
748b658ebcSmatthias.ringwald 	uint8_t		type;
758b658ebcSmatthias.ringwald } __attribute__ ((packed)) pktlog_hdr;
768b658ebcSmatthias.ringwald 
7768e27c0fSmatthias.ringwald #ifndef EMBEDDED
788b658ebcSmatthias.ringwald static int dump_file = -1;
798b658ebcSmatthias.ringwald static int dump_format;
808b658ebcSmatthias.ringwald static hcidump_hdr header_bluez;
818b658ebcSmatthias.ringwald static pktlog_hdr  header_packetlogger;
828adf0ddaSmatthias.ringwald static char time_string[40];
83*2992c131Smatthias.ringwald static int max_nr_packets = -1;
84*2992c131Smatthias.ringwald static int nr_packets = 0;
8568e27c0fSmatthias.ringwald #endif
868b658ebcSmatthias.ringwald 
878b658ebcSmatthias.ringwald void hci_dump_open(char *filename, hci_dump_format_t format){
8868e27c0fSmatthias.ringwald #ifndef EMBEDDED
898b658ebcSmatthias.ringwald     dump_format = format;
908adf0ddaSmatthias.ringwald     if (dump_format == HCI_DUMP_STDOUT) {
918adf0ddaSmatthias.ringwald         dump_file = fileno(stdout);
928adf0ddaSmatthias.ringwald     } else {
938adf0ddaSmatthias.ringwald         dump_file = open(filename, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
9479662672Smatthias.ringwald     }
9568e27c0fSmatthias.ringwald #endif
96d9659922Smatthias.ringwald }
9779662672Smatthias.ringwald 
98*2992c131Smatthias.ringwald void hci_dump_set_max_packets(int packets){
99*2992c131Smatthias.ringwald     max_nr_packets = packets;
100*2992c131Smatthias.ringwald }
101*2992c131Smatthias.ringwald 
10279662672Smatthias.ringwald void hci_dump_packet(uint8_t packet_type, uint8_t in, uint8_t *packet, uint16_t len) {
10368e27c0fSmatthias.ringwald #ifndef EMBEDDED
10468e27c0fSmatthias.ringwald 
1058b658ebcSmatthias.ringwald     if (dump_file < 0) return; // not activated yet
1068b658ebcSmatthias.ringwald 
107*2992c131Smatthias.ringwald     // don't grow bigger than max_nr_packets
108*2992c131Smatthias.ringwald     if (dump_format != HCI_DUMP_STDOUT && max_nr_packets > 0){
109*2992c131Smatthias.ringwald         if (nr_packets >= max_nr_packets){
110*2992c131Smatthias.ringwald             lseek(dump_file, 0, SEEK_SET);
111*2992c131Smatthias.ringwald             ftruncate(dump_file, 0);
112*2992c131Smatthias.ringwald             nr_packets = 0;
113*2992c131Smatthias.ringwald         }
114*2992c131Smatthias.ringwald         nr_packets++;
115*2992c131Smatthias.ringwald     }
116*2992c131Smatthias.ringwald 
1178b658ebcSmatthias.ringwald     // get time
1188b658ebcSmatthias.ringwald     struct timeval curr_time;
1198adf0ddaSmatthias.ringwald     struct tm* ptm;
1208b658ebcSmatthias.ringwald     gettimeofday(&curr_time, NULL);
1218b658ebcSmatthias.ringwald 
1228b658ebcSmatthias.ringwald     switch (dump_format){
1238adf0ddaSmatthias.ringwald         case HCI_DUMP_STDOUT:
1248adf0ddaSmatthias.ringwald             /* Obtain the time of day, and convert it to a tm struct. */
1258adf0ddaSmatthias.ringwald             ptm = localtime (&curr_time.tv_sec);
1268adf0ddaSmatthias.ringwald             /* Format the date and time, down to a single second. */
1278adf0ddaSmatthias.ringwald             strftime (time_string, sizeof (time_string), "[%Y-%m-%d %H:%M:%S", ptm);
1288adf0ddaSmatthias.ringwald             /* Compute milliseconds from microseconds. */
1298adf0ddaSmatthias.ringwald             uint16_t milliseconds = curr_time.tv_usec / 1000;
1308adf0ddaSmatthias.ringwald             /* Print the formatted time, in seconds, followed by a decimal point
1318adf0ddaSmatthias.ringwald              and the milliseconds. */
1328adf0ddaSmatthias.ringwald             printf ("%s.%03u] ", time_string, milliseconds);
1338adf0ddaSmatthias.ringwald             switch (packet_type){
1348adf0ddaSmatthias.ringwald                 case HCI_COMMAND_DATA_PACKET:
1358adf0ddaSmatthias.ringwald                     printf("CMD => ");
1368adf0ddaSmatthias.ringwald                     break;
1378adf0ddaSmatthias.ringwald                 case HCI_EVENT_PACKET:
1388adf0ddaSmatthias.ringwald                     printf("EVT <= ");
1398adf0ddaSmatthias.ringwald                     break;
1408adf0ddaSmatthias.ringwald                 case HCI_ACL_DATA_PACKET:
1418adf0ddaSmatthias.ringwald                     if (in) {
1428adf0ddaSmatthias.ringwald                         printf("ACL <= ");
1438adf0ddaSmatthias.ringwald                     } else {
1448adf0ddaSmatthias.ringwald                         printf("ACL => ");
1458adf0ddaSmatthias.ringwald                     }
1468adf0ddaSmatthias.ringwald                     break;
1478adf0ddaSmatthias.ringwald             }
1488adf0ddaSmatthias.ringwald             hexdump(packet, len);
1498adf0ddaSmatthias.ringwald             break;
1508b658ebcSmatthias.ringwald         case HCI_DUMP_BLUEZ:
1518b658ebcSmatthias.ringwald             bt_store_16( (uint8_t *) &header_bluez.len, 0, 1 + len);
1528b658ebcSmatthias.ringwald             header_bluez.in  = in;
1538b658ebcSmatthias.ringwald             header_bluez.pad = 0;
1548b658ebcSmatthias.ringwald             bt_store_32( (uint8_t *) &header_bluez.ts_sec,  0, curr_time.tv_sec);
1558b658ebcSmatthias.ringwald             bt_store_32( (uint8_t *) &header_bluez.ts_usec, 0, curr_time.tv_usec);
1568b658ebcSmatthias.ringwald             header_bluez.packet_type = packet_type;
1578b658ebcSmatthias.ringwald             write (dump_file, &header_bluez, sizeof(hcidump_hdr) );
15879662672Smatthias.ringwald             write (dump_file, packet, len );
1598b658ebcSmatthias.ringwald             break;
1608b658ebcSmatthias.ringwald         case HCI_DUMP_PACKETLOGGER:
1618b658ebcSmatthias.ringwald             header_packetlogger.len = htonl( sizeof(pktlog_hdr) - 4 + len);
1628b658ebcSmatthias.ringwald             header_packetlogger.ts_sec =  htonl(curr_time.tv_sec);
1638b658ebcSmatthias.ringwald             header_packetlogger.ts_usec = htonl(curr_time.tv_usec);
1648b658ebcSmatthias.ringwald             switch (packet_type){
1658b658ebcSmatthias.ringwald                 case HCI_COMMAND_DATA_PACKET:
1668b658ebcSmatthias.ringwald                     header_packetlogger.type = 0x00;
1678b658ebcSmatthias.ringwald                     break;
1688b658ebcSmatthias.ringwald                 case HCI_ACL_DATA_PACKET:
1698b658ebcSmatthias.ringwald                     if (in) {
1708b658ebcSmatthias.ringwald                         header_packetlogger.type = 0x03;
1718b658ebcSmatthias.ringwald                     } else {
1728b658ebcSmatthias.ringwald                         header_packetlogger.type = 0x02;
1738b658ebcSmatthias.ringwald                     }
1748b658ebcSmatthias.ringwald                     break;
1758b658ebcSmatthias.ringwald                 case HCI_EVENT_PACKET:
1768b658ebcSmatthias.ringwald                     header_packetlogger.type = 0x01;
1778b658ebcSmatthias.ringwald                     break;
1788b658ebcSmatthias.ringwald                 default:
1798b658ebcSmatthias.ringwald                     return;
1808b658ebcSmatthias.ringwald             }
1818b658ebcSmatthias.ringwald             write (dump_file, &header_packetlogger, sizeof(pktlog_hdr) );
1828b658ebcSmatthias.ringwald             write (dump_file, packet, len );
1838b658ebcSmatthias.ringwald     }
18468e27c0fSmatthias.ringwald #endif
18579662672Smatthias.ringwald }
18679662672Smatthias.ringwald 
18779662672Smatthias.ringwald void hci_dump_close(){
18868e27c0fSmatthias.ringwald #ifndef EMBEDDED
18979662672Smatthias.ringwald     close(dump_file);
190d9659922Smatthias.ringwald     dump_file = -1;
19168e27c0fSmatthias.ringwald #endif
19279662672Smatthias.ringwald }
19379662672Smatthias.ringwald 
194